Jef*_*ler 24 shell-script systemd
我们有一个 shell 脚本——出于各种原因——包装供应商的应用程序。我们有系统管理员和应用程序所有者,他们对 systemd 的熟悉程度参差不齐。因此,在应用程序失败的情况下(systemctl 表示同样多),一些最终用户(包括“root”系统管理员)可能会使用包装脚本“直接”启动应用程序,而不是使用systemctl restart
. 这可能会在重新启动期间导致问题,因为 systemd 不会调用正确的关闭脚本——因为就其而言,应用程序已经停止。
为了帮助指导向 systemd 的过渡,我想更新包装器脚本以确定它是由 systemd 还是由最终用户调用;如果它在systemd之外被调用,我想向调用者打印一条消息,告诉他们使用 systemctl。
如何在 shell 脚本中确定它是否被 systemd 调用?
你可以假设:
systemd 服务的一个例子可能是:
[Unit]
Description=Vendor's Application
After=network-online.target
[Service]
ExecStart=/path/to/wrapper start
ExecStop=/path/to/wrapper stop
Type=forking
[Install]
WantedBy=multi-user.target
Run Code Online (Sandbox Code Playgroud)
我对检测 init system不感兴趣,因为我已经知道它是 systemd。
Jef*_*ler 25
来自Lucas Werkmeister对 Server Fault的翔实回答:
如果您不想依赖这些变量,或者对于 231 之前的 systemd 版本,您可以检查父 PID 是否等于 1:
if [[ $PPID -ne 1 ]]
then
echo "Don't call me directly; instead, call 'systemctl start/stop service-name'"
exit 1
fi >&2
Run Code Online (Sandbox Code Playgroud)
Cha*_*ffy 13
if ! grep -qEe '[.]service$' /proc/self/cgroup; then
echo "This script should be started with systemctl" >&2
exit 1
fi
Run Code Online (Sandbox Code Playgroud)
...或者,如果您知道您希望运行的特定服务名称,并且希望对阻止创建用户会话的错误配置保持稳健:
if ! grep -qEe '/myservice[.]service$' /proc/self/cgroup; then
echo "This service should be started with systemctl start myservice" >&2
exit 1
fi
Run Code Online (Sandbox Code Playgroud)
确定哪个服务(如果有)启动了当前进程的一种方法是检查/proc/self/cgroup
。对于 -systemd
触发的服务,这将包含服务名称;例如:
12:pids:/system.slice/dhcpcd.service
11:rdma:/
10:memory:/system.slice/dhcpcd.service
9:blkio:/system.slice/dhcpcd.service
8:devices:/system.slice/dhcpcd.service
7:hugetlb:/
6:cpuset:/
5:freezer:/
4:cpu,cpuacct:/system.slice/dhcpcd.service
3:net_cls,net_prio:/
2:perf_event:/
1:name=systemd:/system.slice/dhcpcd.service
0::/system.slice/dhcpcd.service
Run Code Online (Sandbox Code Playgroud)
...而对于与用户会话关联的进程,cgroup 将更像/user.slice/user-1000.slice/session-337.scope
(假设这是自上次重新启动以来系统上第 337 个会话的用户,该用户具有 UID 1000)。
如果想要检测正在运行的特定服务,也可以从/proc/self/cgroup
. 考虑,例如:
cgroup_full=$(awk -F: '$1 == 0 { print $3 }' /proc/self/cgroup)
cgroup_short=${cgroup_full##*/}
case $cgroup_full in
/system.slice/*.service) echo "Run from system service ${cgroup_short%.*}";;
/user.slice/*.service) echo "Run from user service ${cgroup_short%.*}";;
*.service) echo "Service ${cgroup_short%.*} type unknown";;
*) echo "Not run from a systemd service; in $cgroup_full";;
esac
Run Code Online (Sandbox Code Playgroud)
想到的另一个明显的解决方案是添加类似
Environment=FROM_SYSTEMD=1
Run Code Online (Sandbox Code Playgroud)
到服务文件,并在该 envvar 上进行测试。
我喜欢 Jeff Schaller 的回答,这可能是 The Right Thing™。另一种方法是使用两个脚本。将实际包装器从/path/to/wrapper
其他文件名移至其他文件名,并在 systemd 单元文件中使用该名称。然后使用原始名称创建另一个脚本,该脚本只显示有用的错误消息。