systemd 初始化脚本创建工作目录

systemd 初始化脚本创建工作目录

我正在创建一个 systemd 脚本来根据需要运行程序。当我运行它时,工作目录可能不存在。因此,我想创建该目录作为脚本的一部分。但是,我收到错误,因为指定的工作目录不存在:

[Service]
User=Inplant
Group=Inplant
ExecStartPre=/bin/mkdir -p /home/inplant/IPSdevice/JAI1
WorkingDirectory=~/IPSdevice/JAI1
ExecStart=~/IPSdevice JAI1 

有没有办法创建工作目录作为 systemd 脚本的一部分。我想运行如下程序:

mkdir ~/IPSdevice/JAI1; cd ~/IPSdevice/JAI1; ~/IPSdevice JAI1

通过将波浪号更改为路径,我能够解决第一个问题。现在我遇到了一个不同的问题:

    Aug 06 09:48:53 ubuntu systemd[1]: Started JAI Device Service.
-- Subject: Unit app-ips-jai1.service has finished start-up
-- Defined-By: systemd
-- Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
-- 
-- Unit app-ips-jai1.service has finished starting up.
-- 
-- The start-up result is done.
Aug 06 09:48:53 ubuntu systemd[103782]: app-ips-jai1.service: Failed at step CHDIR spawning /home/inplant/IPSdevice/IPSdevice: No such file or directory
-- Subject: Process /home/inplant/IPSdevice/IPSdevice could not be executed
-- Defined-By: systemd
-- Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
-- 
-- The process /home/inplant/IPSdevice/IPSdevice could not be executed and failed.
-- 
-- The error number returned by this process is 2.

系统脚本是:

[Unit]
Description=JAI Device Service
# When systemd stops or restarts the app.service, the action is propagated to this unit
PartOf=app.service
# Start this unit after the app.service start
After=app-ips.service

[Service]
# Pretend that the component is running
RuntimeDirectory=IPS-JAI1
User=inplant
Group=inplant
ExecStartPre=mkdir -p /home/inplant/IPSdevice/JAI1
WorkingDirectory=/home/inplant/IPSdevice/JAI1
#ExecStart=/home/inplant/IPSdevice JAI1 > /dev/nul 2>&1 & echo $! > /var/run/jai1.pid
ExecStart=/home/inplant/IPSdevice/IPSdevice JAI1
# Restart the service on non-zero exit code when terminated by a signal other than SIGHUP, SIGINT, SIGTERM or SIGPIPE
#Restart=on-failure

[Install]
# This unit should start when app.service is starting
WantedBy=app-ips.service

通常,我通过以用户“inplant”身份登录并执行以下命令来启动服务:

cd ~/IPSdevice
mkdir -p JAI
cd JAI
../IPSdevice JAI

答案1

部分问题是您使用~, systemd 不扩展~~user,因此您需要在命令中指定完整路径。

此外,您不能真正在 中 创建目录,ExecStartPre=然后在 中 使用它WorkingDirectory=,因为 systemd 会WorkingDirectory=在执行任何命令之前进行检查。 (事实上​​,所有命令都ExecStartPre=将在同一目录下运行。)

为了实现这一点,请使用 shell 脚本来ExecStart=准备环境和exec守护程序。

例如,创建一个/home/inplant/start_inplant.sh

#!/bin/sh
set -eu
mkdir -p /home/inplant/IPSdevice/"$1"
cd /home/inplant/IPSdevice/"$1"
exec /home/inplant/IPSdevice/IPSdevice "$1"

在你的单元文件中,使用:

[服务]
运行时目录=IPS-JAI1
用户=体内
组=种植体
环境=LD_LIBRARY_PATH=/opt/pleora/ebus_sdk/Ubuntu-x86_64/lib:/opt/pleora/ebus_sdk/Ubuntu-x86_64/lib/genicam/bin/Linux64_x64:/opt/xview2_64/lib/:/opt/GO_SDK/lib /linux_x64
环境=GENICAM_ROOT_V3_0=/opt/pleora/ebus_sdk/Ubuntu-x86_64/lib/genicam
ExecStart=/home/inplant/start_inplant.sh JAI1
# 当被 SIGHUP、SIGINT、SIGTERM 或 SIGPIPE 以外的信号终止时,以非零退出代码重新启动服务
重新启动=失败时

不需要WorkingDirectory=ExecStartPre=在您的单位文件中。您还可以决定在 shell 脚本本身中导出环境变量,由您决定是否喜欢其中之一。

一个重要的部分是使用exec在 shell 脚本中。这使得 shell 将自身替换为正在执行的程序,以便该程序在与 shell 启动时相同的 PID 下运行。这很重要,因为 systemd 将使用该 PID 来控制守护进程(例如,在停止服务时杀死它),因此您希望它是守护进程而不是 shell 脚本。另外,您不想让额外的 shell 进程运行,只是无缘无故地占用空间。

请注意,您实际上~在你的start_inplant.shshell 脚本中使用。由于它是 shell 脚本,因此它将允许并执行 shell 通常执行的所有扩展。


请注意您对这个问题的其他答案:

这实际上不起作用:

ExecStartPre=cd /home/inplant/IPSdevice/JAI1

systemd 并不真正运行 shell 命令(并且cd是内置的 shell。)这仅在存在/bin/cd/usr/bin/cd二进制文件时才起作用,但不存在这样的事情(因为更改目录仅影响当前进程,因此外部二进制文件没有意义,因为它只会影响自身并退出,你不会看到它的效果。)

即使您使用 shell 显式运行该命令:

ExecStartPre=/bin/sh -c "cd /home/inplant/IPSdevice/JAI1"

这也不起作用,因为这只会影响当前的 shell,并且更改目录的效果不会传播到任何其他命令,特别是该ExecStart=命令。

我不知道这是怎么回事工作文件对你来说,因为如果命令ExecStartPre=失败,systemd 不会真正运行单元文件,尽管我可以看到 systemd 忽略这一行(并记录它),因为它认为它的语法不正确(systemd 的版本需要它们运行的​​命令的完整路径,直到最近的版本)并且它只是跳过该行并转到下一行。

如果确实如此,那么您的守护进程正在运行,/home/inplant/IPSdevice/而不是/home/inplant/IPSdevice/JAI1/,也许它在那里工作得很好,我对此不会感到惊讶,因为守护进程(特别是写得好的守护进程)并不真正关心哪个守护进程他们开始的目录,所以也许这个要求一开始并不存在。

此外,该命令:

ExecStartPre=/bin/chown inplant.inplant /home/inplant/IPSdevice/JAI1

应该是不必要的,因为默认情况下命令将使用和ExecStartPre=指定的凭据运行,因此您已经使用该用户创建一个目录(特别是,这意味着该用户需要拥有创建该目录的权限),因此无需更改所有权,因为这应该已经是创建的目录的所有权。User=Group=mkdirinplant

答案2

波形符是特定于 shell 的扩展,但单元文件中指定的指令不使用 shell 运行。您必须将 替换~为目录的绝对路径。 Systemd 正在寻找一个名称为 的工作目录~/IPSDevice/JAI1

答案3

我发现即使 ExecStartPre 创建了工作目录,也无法将其设置为不存在的目录。我必须在 ExecStartPre 指令中使用 cd 来更改到创建目录后的目录。这是工作文件:

[Unit]
Description=JAI Device Service
# When systemd stops or restarts the app.service, the action is propagated to this unit
PartOf=app.service
# Start this unit after the app.service start
After=app-ips.service

[Service]
RuntimeDirectory=IPS-JAI1
User=inplant
Group=inplant
Environment=LD_LIBRARY_PATH=/opt/pleora/ebus_sdk/Ubuntu-x86_64/lib:/opt/pleora/ebus_sdk/Ubuntu-x86_64/lib/genicam/bin/Linux64_x64:/opt/xview2_64/lib/:/opt/GO_SDK/lib/linux_x64
Environment=GENICAM_ROOT_V3_0=/opt/pleora/ebus_sdk/Ubuntu-x86_64/lib/genicam
ExecStartPre=/bin/mkdir -p /home/inplant/IPSdevice/JAI1
ExecStartPre=/bin/chown inplant.inplant /home/inplant/IPSdevice/JAI1
ExecStartPre=cd /home/inplant/IPSdevice/JAI1
ExecStartPre=/bin/ls -al /home/inplant/IPSdevice/JAI1
WorkingDirectory=/home/inplant/IPSdevice/
ExecStart=/home/inplant/IPSdevice/IPSdevice JAI1
# Restart the service on non-zero exit code when terminated by a signal other than SIGHUP, SIGINT, SIGTERM or SIGPIPE
Restart=on-failure

[Install]
# This unit should start when app.service is starting
WantedBy=app-ips.service
```

相关内容