Systemd:判断脚本是由 systemd 还是用户运行

Systemd:判断脚本是由 systemd 还是用户运行

我如何判断脚本是由 systemd 调用还是由用户调用?

我为一个老式守护进程创建了一个服务。我们刚刚切换到 systemd,我的一些管理员喜欢直接调用 rc 脚本。在机器重启的情况下,这种方法效果不佳。Systemd 不会遵守,PIDFile=因为新启动的守护进程不属于该服务的 cgroup。

我添加了服务文件和 rc_script。我无法区分用户 ID,因为要使用脚本,用户必须是foobaruser

停止服务现在有问题,因为 systemd 会从已删除的 pid 文件中识别出这一点。

那么我如何知道该脚本是否是由 systemd 调用的?

foob​​ar.服务

[Unit]
Description=Foobar Service
After=syslog.target network.target

[Service]
Type=forking
User=foobaruser
LimitNOFILE=60000
LimitNPROC=8000
TasksMax=8000
PIDFile=/local/foobar/foo.pid
ExecStart=/opt/foobar/rc_script start
ExecStop=/opt/foobar/rc_script stop
TimeoutSec=100
TimeoutStopSec=300
KillMode=none
RemainAfterExit=no
Environment=LANG=de_DE.UTF-8

[Install]
WantedBy=multi-user.target

/opt/foobar/rc_script 骨架

#!/bin/bash
case $1 in
start)
  VAR_IS_SYSTEMD=$( ... script to check if bash is run from systemd ... )
  if [ "$VAR_IS_SYSTEMD" = false ] ; then
    sudo systemctl start foobar.service
    exit
  fi
  # start service within systemd
  ;;
stop)
  # stop service
  ;;
esac

答案1

我同意其他发帖者的观点,您应该尝试重写服务以根本不调用该脚本(如果您的管理员想继续使用它,这是可以的,但 systemd 服务是否需要所有 36 个选项?) - 但检测该脚本是否是从 systemd 调用的也不难。

  • systemd v232 添加了单元调用 ID 的概念,该 ID 会通过$INVOCATION_ID环境变量传递给单元。您可以检查是否已设置。

  • systemd v231+$JOURNAL_STREAM为 stdout 或 stderr 连接到日志的服务设置变量,您的服务似乎就是这种情况,因此如果您使用的是 systemd v231,您也可以检查该变量。(在 v232+ 上,$INVOCATION_ID绝对是更好的选择。)

  • 在旧版 systemd 中,我认为不存在始终存在的环境变量,但您当然可以通过向服务中添加如下内容来自己定义一个环境变量:

    Environment=LAUNCHED_BY_SYSTEMD=1
    

答案2

一个简单的解决方案是使用不同的脚本来启动 systemd。/opt/foobar/rc_script 应该只调用systemctl start foobar.servicesystemctl stop foobar.service

Suse 使用$SYSTEMD_NO_WRAP,但我不知道这是 systemd 标准还是 Suse 特有的。

if test -z "$SYSTEMD_NO_WRAP"; then
...
    /usr/bin/systemctl [start|stop] xxx.service

答案3

对于“Amazon Linux 2”和“Red Hat Enterprise Linux 7”,我找到了一个解决方案。

在这些 Linux 系统上,系统进程PID=1是。systemd启动脚本的comm=systemd父 ID是。PPID1

在其他 Linux 系统中可能并非如此。

#!/bin/bash
VAR_IS_SYSTEMD_SYSTEM=$( [[ $(ps -o comm -p 1 --no-headers | tr -d ' ') == "systemd" ]] && echo true )
echo VAR_IS_SYSTEMD_SYSTEM = ${VAR_IS_SYSTEMD_SYSTEM}

VAR_IS_DAEMON_SCRIPT=$( [[ "${PPID}" == "1" ]] && echo true )
echo VAR_IS_DAEMON_SCRIPT = ${VAR_IS_DAEMON_SCRIPT}

if [[ "${VAR_IS_SYSTEMD_SYSTEM}" = true ]] && [[ "${VAR_IS_DAEMON_SCRIPT}" = true ]]; then
  echo "You are a systemd Daemon Script"
else
  echo "You are a normal Script"
fi

使用的元素/命令:

  • ps -o comm -p 1 --无标头

    获取进程1的命令名称

  • tr-d' '

    从 ps 命令中删除格式化空格

  • “${PPID}”

    bash 进程的父 PID

相关内容