如何在ExecStart命令行中使用参数?

29
我将一个在Debian(以及类似Linux Mint和Ubuntu & Co.的派生发行版)中使用的SysVinit脚本转换为systemd服务,以便在Fedora或Arch Linux(以及类似Bridge或Manjaro的派生发行版)中使用。虽然systemd启动系统比以前更高效和多功能,但我不知道如何做一些简单的事情,比如在命令行上使用“可选”参数,例如ExecStart=ExecRestart=

以下是我在SysVinit中的操作:

#!/bin/sh
### BEGIN INIT INFO
# Provides:          myprog
# Required-Start:    $remote_fs $syslog
# Required-Stop:     $remote_fs $syslog
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: myprog init script
# Descripton:        this script manages myprog
### END INIT INFO
# exit if myprog isn't installed
[ -x "/opt/mystuff/myrpog" ] || exit 0
case "$1" in
  start)
    cd /opt/mystuff
    ./myprog -r
    echo
    ;;
  stop)
    cd /opt/mystuff
    ./myprog -x
    ;;
  restart)
    cd /opt/mystuff
    ./myprog -x && ./myprog -r
    ;;
  version)
    cd /opt/mystuff
    ./myprog -v
    ;;
  try)
    cd /opt/mystuff
    ./myprog
    ;;
  *)
    echo "Usage: sudo service myprog {start|stop|version|try}" >&2
    exit 3
    ;;
esac
:

所以上述脚本允许使用不同的参数,包括一个空参数,当使用以下命令行时将显示消息“Usage:...”:

sudo service myprog start    # Start myprog with the -r argument
sudo service myprog stop     # Stop myprog with the -x argument
sudo service myprog version  # Display the release of myprog in the console
sudo service myprog try      # Start myprog without any argument
sudo service myprog restart  # Stop then start myprog with the -r argument
sudo service myprog          # Display the "Usage:..." message in the console

现在,使用systemd,脚本应该像这样:

现在,使用systemd,脚本应该像这样:

[Unit]
Description=This script manages myprog
ConditionFileExecutable=/opt/mystuff/myprog

[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/opt/mystuf/myprog -r
ExecStop=/opt/mystuff/myprog -x
ExecRestart=/opt/mystuff/myprog -x : /opt/mystuff/myprog -r

[Install]
After=NetworkManager.service

以下是我的问题(以及我对systemd知识的缺乏):

显然,systemd不提供像ExecCustom01=ExecCustom02等这样的命令,可以让我创建“version”和“try”(如果需要的话,还有其他命令)。

因此,如果我能够使用参数同时启动“version”或“try”命令(即“真正”的重启可能通过依次启动停止和启动命令来完成),那么我可以以不同的方式使用ExecRestart

这些“自定义”的ExecRestart= 命令可能如下所示:

sudo systemctl restart myprog     # Without argument for the "try" command

并且

sudo systemctl restart myprog -v  # For the "version" command

系统服务脚本可能如下所示:

[Unit]
Description=This script manages myprog
ConditionFileExecutable=/opt/mystuff/myprog

[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/opt/mystuf/myprog -r
ExecStop=/opt/mystuff/myprog -x
ExecRestart=/opt/mystuff/myprog ????? // This is where I need to use an argument

[Install]
After=NetworkManager.service

但我不知道是否可能,如果可以,使用什么语法?

任何帮助都将不胜感激,因为即使花费了大量时间在多个systemd man页面上,我也找不到如何实现的明确示例。

提前感谢您的时间和建议。


1
如果/opt/mystuff/myprog是一个二进制可执行文件,那么就直接调用它。否则:ExecStart=/bin/sh -c '/opt/mystuf/myprog -r' - Sergey Kanaev
2
systemd 为什么需要调用 versiontry - NuclearPeon
@Sergy Kanaev我希望能够像使用sysVinit脚本一样简单地使用“service”,而不必输入复杂的命令行。@nuclearPeon,问:为什么systemd需要调用“version”或“try”选项?答:因为我想这样做,显然我可以使用我提供的sysVinit脚本示例来实现。 - Fnux
@Fnux 用户调用服务版本有什么用例?虽然我不反对开发人员方便地访问服务版本命令,但systemd的目标是一旦创建服务就使其几乎自管理。我真的看不出需要版本命令。如果你真的想要,你可以在[Unit]Description=...选项中添加版本,这样调用systemctl status myservice将返回This script manages myprog: v.0.90.a或者你有的任何版本字符串。 - NuclearPeon
3
人们并不期望通过调用“service”来获取版本。(如果他们这样做了,说明他们使用的是设计非常糟糕的操作系统或者学到了一些非常糟糕的习惯。) 他们希望直接运行类似于“myprog --version”或“myprog -V”的命令。因此根本没有必要这样做。而且,“try”动词根本就没有意义。要么做,要么不做;不存在尝试。如果确实必须保留这些旧动词,那么有方法可以做到,但在这种情况下似乎没有必要这样做。 - Michael Hampton
Stack Overflow是一个关于编程和开发问题的网站。这个问题似乎不属于编程或开发范畴。请参阅帮助中心中的我可以在这里问什么样的问题。也许超级用户Unix&Linux Stack Exchange更适合提问。 - jww
2个回答

33

尽管systemd确实没有提供一种为单元文件传递命令行参数的方式,但是可以编写实例: http://0pointer.de/blog/projects/instances.html

例如:/lib/systemd/system/serial-getty@.service 看起来类似于这样:

[Unit]
Description=Serial Getty on %I
BindTo=dev-%i.device
After=dev-%i.device systemd-user-sessions.service

[Service]
ExecStart=-/sbin/agetty -s %I 115200,38400,9600
Restart=always
RestartSec=0

那么,你可以这样开始:

$ systemctl start serial-getty@ttyUSB0.service
$ systemctl start serial-getty@ttyUSB1.service

systemd会启动不同的实例来运行serial-getty服务:

$ systemctl status serial-getty@ttyUSB0.service
serial-getty@ttyUSB0.service - Getty on ttyUSB0
      Loaded: loaded (/lib/systemd/system/serial-getty@.service; static)
      Active: active (running) since Mon, 26 Sep 2011 04:20:44 +0200; 2s ago
    Main PID: 5443 (agetty)
      CGroup: name=systemd:/system/getty@.service/ttyUSB0
           5443 /sbin/agetty -s ttyUSB0 115200,38400,9600

它还能够单独启用和禁用已实例化的服务。

当然,它缺乏许多命令行解析的功能,但通常被用作某种类型的配置文件选择。例如,您可以查看 Fedora 的 openvpn@.servicehttp://pkgs.fedoraproject.org/cgit/openvpn.git/tree/openvpn@.service


11
有关 %I%i 替换的详细信息,请参见 man 5 systemd.unitSpecifiers 部分或 systemd.unit - cherdt
1
我特别喜欢这个例子,因为我在创建一个记录串口活动的服务时发现了它。 :) - mrtumnus
参数是否是可选的?串口-getty服务在系统启动时如何启动? - Kok How Teh
1
不是模板,而是实例!因此,如果您(安装程序)执行systemctl enable serial-getty@ttyUSB0,则该服务将在启动时运行。 - Hubbitus
为什么有一个减号:“ExecStart=-/sbin..”? - assayag.org
1
@danielassayag,请参考文档以获取可能的修饰符。在该示例中使用了“如果可执行路径以“-”为前缀,则命令的退出代码通常被视为失败”。 - Hubbitus

2
尝试直接使用命令行参数是不可能的。
一个替代方法可能是环境变量(参见https://superuser.com/questions/728951/systemd-giving-my-service-multiple-arguments)。
我在这里找到了答案:http://www.freedesktop.org/software/systemd/man/systemctl.html 所以,sudo systemctl restart myprog -v -- systemctl会认为你正在尝试设置它的一个标志,而不是myprog的标志。 sudo systemctl restart myprog someotheroption -- 如果存在,则systemctl将重新启动myprog和someotheroption服务。

4
因此,即使systemd被宣传为更加灵活且与sysVinit兼容,但在我的当前情况下似乎并非如此。无论如何,感谢你的时间,即使我无法像使用sysVinit一样使用systemd。 - Fnux

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接