29

I try to convert a SysVinit script used on Debian (and derivative distros such as Linux Mint and Ubuntu & Co.) to a systemd service to be used on Fedora or Arch Linux (and derivative distros such as Bridge or Manjaro), but even if the systemd start system is more performant and versatile than the previous, I don't understand how to make simple stuff such as using an "optional" argument on a command line like ExecStart= or ExecRestart= !

Here is what I do with 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
:

So the script above allows to use different arguments including an empty one that will display the message "Usage: ..." when using the following command lines:

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

Now, with systemd, the script should look like this :

[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

Here starts my problems (and my lack of systemd knowledge):

Obviously, systemd doesn't provide a command such as ExecCustom01=, ExecCustom02, etc. that would allow me to create commands for "version" and "try" (and other if needed).

So, I may use ExecRestart in a different manner if I could use an argument to start both the "version" or the "try" command (being said that the "real" restart may be done by starting successively the stop and the start commands).

These "customized" ExecRestart= command could then look like this:

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

and

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

The systemd script could then look like this:

[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

But I don't know if it's possible, and if yes, what is the syntax to use?

Any help would be truly appreciated since even after spending a bunch of hours in the multiple systemd man pages, I couldn't find any explicit sample about how to do that.

Thanks in advance for your time and advice.

Mortein
  • 113
  • 8
Fnux
  • 416
  • 1
  • 4
  • 9
  • 1
    If `/opt/mystuff/myprog` is a binary executable then just so it is called. Otherwise: `ExecStart=/bin/sh -c '/opt/mystuf/myprog -r'` – Sergey Kanaev Apr 30 '14 at 07:14
  • 2
    Why would systemd need to call `version` or `try`? – NuclearPeon Jul 25 '14 at 16:27
  • @ Sergy Kanaev I was expecting a similar way to use the "service" as I can simply do with sysVinit script without being obliged to enter complex command lines. @nuclearPeon, Q: Why would systemd need to call "version" or "try' options? A: Simply because I want to do it and obviously I do it with the sysVinit script sample I provided. – Fnux Aug 22 '14 at 19:41
  • @Fnux What use case is there for a user to call for the version of a service? While I don't disagree with the convenience for a developer to have access to a service version command, systemd is about making services almost self-managing once created. I really don't see a need for a version command. If you really want, you can add the version in `[Unit]`'s `Description=...` option so calling `systemctl status myservice` would return `This script manages myprog: v.0.90.a` or whatever version string you have. – NuclearPeon Aug 22 '14 at 20:24
  • 3
    People do not _expect_ to get the version by calling `service`. (And if they do, they've been using a very poorly designed operating system or otherwise learned some really bad habits.) They expect to run something like `myprog --version` or `myprog -V` directly. So there's no need for this at all. And the `try` verb doesn't make any sense at all. Do or do not; there is no try. There _are_ ways to preserve these old verbs if _really_ necessary, but in this case they don't appear to be necessary. – Michael Hampton Jun 09 '15 at 00:57
  • Stack Overflow is a site for programming and development questions. This question appears to be off-topic because it is not about programming or development. See [What topics can I ask about here](http://stackoverflow.com/help/on-topic) in the Help Center. Perhaps [Super User](http://superuser.com/) or [Unix & Linux Stack Exchange](http://unix.stackexchange.com/) would be a better place to ask. – jww Dec 24 '17 at 01:28

2 Answers2

33

Although systemd indeed does not provide a way to pass command-line arguments for unit files, it is possible to write instances: http://0pointer.de/blog/projects/instances.html

For example: /lib/systemd/system/serial-getty@.service looks something like this:

[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

So, you may start it like:

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

systemd will start different instances of the serial-getty service:

$ 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

It also makes it possible to enable and disable instantiated services separately.

Of course, it lacks much of the power of command line parsing, but in common way it is used as some sort of config files selection. For example, you may look at the Fedora openvpn@.service: http://pkgs.fedoraproject.org/cgit/openvpn.git/tree/openvpn@.service

Mortein
  • 113
  • 8
Hubbitus
  • 5,161
  • 3
  • 41
  • 47
  • 11
    For details about the `%I` and `%i` substitutions, see the _Specifiers_ section of `man 5 systemd.unit` or [systemd.unit](https://www.freedesktop.org/software/systemd/man/systemd.unit.html#Specifiers). – cherdt Mar 10 '17 at 22:31
  • 1
    I particularly love this example because I found it while creating a service to log serial port activity. :) – mrtumnus Sep 18 '18 at 14:08
  • Are the arguments optional? How does the serial-getty service start when the system boots? – Kok How Teh Nov 27 '18 at 10:12
  • 1
    Starts not template, but instances! So if you (installer) do `systemctl enable serial-getty@ttyUSB0`, then it service will be run on start. – Hubbitus Nov 27 '18 at 22:42
  • why is there a minus sign: "ExecStart=-/sbin.." ? – assayag.org Sep 29 '21 at 22:34
  • 1
    @danielassayag, please refer for the [documentation](https://www.freedesktop.org/software/systemd/man/systemd.service.html#ExecStart=) for the possible modifiers. In that example used 'If the executable path is prefixed with "-", an exit code of the command normally considered a failure' – Hubbitus Oct 01 '21 at 10:19
2

To attempt command line arguments directly is not possible.

One alternative might be environment variables (https://superuser.com/questions/728951/systemd-giving-my-service-multiple-arguments).

This is where I found the answer: http://www.freedesktop.org/software/systemd/man/systemctl.html

so sudo systemctl restart myprog -v -- systemctl will think you're trying to set one of its flags, not myprog's flag.

sudo systemctl restart myprog someotheroption -- systemctl will restart myprog and the someotheroption service, if it exists.

assylias
  • 321,522
  • 82
  • 660
  • 783
Juan
  • 51
  • 4
  • 4
    So, even if systemd is promoted as being more versatile and compatible with sysVinit, it seems it's not (for my current case). Anyhow, thanx for your time even if I can't do with systemd what I can do with sysVinit. – Fnux Aug 22 '14 at 19:39