[Systemd] simple service examples
Creating simple services in GNU/Linux managed by systemd.
Although, a lot of controversy exists in the Linux community regarding systemd, it is still the most widely used service management software. Therefore, it is beneficial for Linux system administrators to know how systemd works; how it manages services; what are targets etc.
Here I will give a few examples for creating new services and configuring them. Firstly, I would like to remind the reader of some basic commands regarding systemd. Secondly, I will give a description of some important options in the Service section of a systemd unit file and finally I will give a couple of examples and their implementation.
Some basic commands
The way for a Linux user to interface with systemd is through the command systemctl
. This command has a wide variety of options, from which you can select and later on view service information, make changes to a particular service, create new services etc. (Actually here I use the term ‘service’ to describe what systemd perceives as units [unit files], in order for the reader to have an easier time understanding the inner workings of systemd. )
Get the status of a service.
systemctl status <name of service>
The status option passed to the systemctl
command displays status information about a particular service. The status of the “NetworkManager” service is enabled and currently active(running) as shown on Figure 1.
Start/Stop a service
Started/Stopped — Is the service running at the current moment.
systemctl start/stop <name of service>
Enable/Disable a service
Enabled/Disabled — does the service start on boot;
systemctl enable/disable <name of service>
Reload a service
systemctl reload <name of service>
Kill a service
systemctl kill <name of service>
Reload daemons/services
systemctl daemon-reload
The [Service] section in a Unit file
The service section in any unit file has its own specific properties. You can find a detailed explanation of each property here [or here, this is the Arch manual page ]. In this article I will go over some essential ones that are often required in order to have a normal execution process in a particular service.
Type=
Type specifies the startup type of the process that systemd will execute when it starts this particular unit file. Allows values for this option are: simple, exec, forking, oneshot, dbus, notify, idle .
simple — process is considered started immediately after the main service process has been forked off by systemd.
exec — process is considered started immediately after the main service process has been executed by systemd.
forking — Firstly, systemd will call fork() on the specified executable, then systemd will wait for the parent process to exit and afterwards the child process will be considered the main process. ( It is usually recommended to combine this option with PIDFile=, so that systemd can later reliably identify the process. )
oneshot — process is considered started after the main process exits.
dbus — this type resembles simple, however it will usually acquire a name on DBus. ( It is usually recommended to combine this option with BusName=, to specify the name of DBus that the process will acquire. )
idle — this type resembles simple, however the execution is delayed until all active jobs are dispatched.
ExecStart=
This option specifies the command/executable that needs to be run by systemd. (It is usually combined with ExecStartPre= and ExecStartPost= to specify what to run before and after the command appointed to ExecStart= respectively. )
Restart=
This option specifies should the service have a specific restart behavior and if so what should it be. The configuration of this option includes the following values: no, always, on-success, on-failure, on-abnormal, on-abort and on-watchdog
. ( It is usually combines with RestartSec= to specify the amount of time systemd has to wait after a particular trigger in order to restart the service. )
Table 1 shows the relationship between restart settings and exit cause (link to documentation). The cells in Table 1 marked with X represent the situations when systemd will consider restarting a particular service. We can see that for a clean exit code / signal [return 0] if the restart option is set to always, the particular service will always restart, no matter the cause. However, still looking at the clean exit code/signal row, if the restart option is set to on-failure, systemd will not restart the specified service.
I will give a couple of examples I consider worth mentioning and leave the trivial ones. Trivial examples are, for example, cases when Restart=no, on-success etc. The reason being that when restart is set to no, it is obvious that the service will not restart, no matter the cause/exit code. The same applies for on-success, the service will restart ONLY when the executed program has exited with code 0 and nothing else.
[ Example 1 ] Type=simple, NO restart
We create a simple service file named simple.service in /etc/systemd/system
. [Root privileges required to write in this directory] In the [Service] section we need to specify the type of the process that will be executed, and the path to the executable. Set the type to simple and the executable path will point to a simple bash script printing the current date and time. After saving the changes to the service file reload the daemons in systemd and start the service.
Run the following commands to reload all the services, execute the example service and read the log file.
systemctl daemon-reload
systemctl start simple.service
journalctl -xeu simple.service
We can see that the service was started by systemd on Sep 20 17:57:05. It executed the bash script printing the current date and time and if exited successfully. Afterwards systemd deactivated the service successfully.
[ Example 2 ] Type=simple, Restart=always
We create a simple service file named simple.service in /etc/systemd/system
. [Root privileges required to write in this directory] In the [Service] section we need to specify the type of the process that will be executed, and the path to the executable. Set the type to simple and the executable path will point to a simple bash script printing the current date and time. The Restart= option will be set to always, which means , no matter the exit code, the program/service will always be re-executed on a specified interval. Use the RestartSec= option to set the restart interval in seconds. After saving the changes to the service file reload the daemons in systemd and start the service.
Run the following commands to reload all the services, execute the example service and read the log file.
systemctl daemon-reload
systemctl start simple.service
journalctl -xeu simple.service
We can see that the service was being started by systemd at the beginning of the log shown on the picture above. After a successful deactivation systemd notifies the user about the scheduled restart of that particular service and also provides a restart count, signifying how many times the service has been restarted till now. (In the current example the service has restarted 5 times)
[ Example 3 ] Type=simple, Restart=on-failure
We create a simple service file named simple.service in /etc/systemd/system
. [Root privileges required to write in this directory] In the [Service] section we need to specify the type of the process that will be executed, and the path to the executable. Set the type to simple and the executable path will point to a simple C program printing the text “This program exits with 1” and exiting with code 1 [failure].
The Restart= option will be set to on-failure, which means , if the exit code of the program is 1, signifying failure, the program will be re-executed on a specified interval. Use the RestartSec= option to set the restart interval in seconds. After saving the changes to the service file reload the daemons in systemd and start the service.
Run the following commands to reload all the services, execute the example service and read the log file.
systemctl daemon-reload
systemctl start simple_exit1.service
journalctl -xeu simple_exit1.service
We can see that the service was being started by systemd at the beginning of the log shown on the picture above. After the service has started, we see that the compiled C program exits with code 1 meaning failure. This event triggers the restart of the service, nevertheless systemd waits the amount of seconds specified in the RestartSec= option before it restarts the service. (In the current example the service has restarted 3 times)
[ Example 4 ] Type=simple, Restart=on-abort
We create a simple service file named simple.service in /etc/systemd/system
. [Root privileges required to write in this directory] In the [Service] section we need to specify the type of the process that will be executed, and the path to the executable. Set the type to simple and the executable path will point to a simple C program printing the text “This program exits with ABORT” and exiting with code 1 [abort].
The Restart= option will be set to on-abort, which means , if the exit code of the program is 6[or the abort() function is called], signifying core-dump or some other reason, the program will be re-executed on a specified interval. Use the RestartSec= option to set the restart interval in seconds. After saving the changes to the service file reload the daemons in systemd and start the service.
Run the following commands to reload all the services, execute the example service and read the log file.
systemctl daemon-reload
systemctl start simple_exit132.service
journalctl -xeu simple_exit132.service
We can see that the service was being started by systemd at the beginning of the log shown on the picture above. After the service has started, we see that the compiled C program exits with code dumped meaning abort. This event triggers the restart of the service, nevertheless systemd waits the amount of seconds specified in the RestartSec= option before it restarts the service. (In the current example the service has restarted 2 times)
[ Example 5 ] Type=notify, Restart=on-watchdog
We create a simple service file named simple.service in /etc/systemd/system
. [Root privileges required to write in this directory] In the [Service] section we need to specify the type of the process that will be executed, and the path to the executable. Set the type to notify and the executable path will point to a simple C program printing . The WatchdogSec= option specifies the interval in which systemd will expect to receive a message from the program being executed by the service. (Please, read sd_notify documentation) If no message has been received, systemd will kill the process and consider the service as failed.
The C program used in this example will include the following libraries: stdio.h, stdlib.h, unistd.h, time.h, systemd/sd-daemon.h
[ Important! Compiling this program will require you to include -lsystemd-daemon gcc <output file> <.c file> -lsystemd-daemon (or -lsystemd depending on your distribution)
]
Firstly, we set the standard out buffer to NULL, then we declare a variable for the watchdog notification interval and we call sd_watchdog_enable to enable watchdog and check whether the service manager expects watchdog keep-alive notifications from a service. The manual suggests to set the notification interval to half of the maximum watchdog interval. Secondly, because systemd will wait until notification to start the service, we print a message “Waiting 5 seconds before notifying ready state…” and waiting 5 seconds.
We call sd_notify with two arguments, 0 and “READY=1” signifying that the program has finished loading and preparing, therefore the state is ready for execution.
Finally, the program enters in infinite loop. On each iteration the program checks if the watchdog is enabled. watchdogEnabled value can vary between negative and positive values. On failure, this call returns a negative errno-style error code. If the service manager expects watchdog keep-alive notification messages to be sent, > 0 is returned, otherwise 0 is returned. Only if the return value is > 0, the usec
parameter is valid after the call.
The sd_notify with parameters 0 and “WATCHDOG” tells systemd to update the watchdog timestamp. This is the keep-alive ping that services need to issue in regular intervals if WatchdogSec=
is enabled for it.
Run the following commands to reload all the services, execute the example service and read the log file.
systemctl daemon-reload
systemctl start simple_watchdog.service
journalctl -xeu simple_watchdog.service
We can see that the service was being started by systemd at the beginning of the log shown on the picture above. After the service has started, we see that the compiled C program prints the watchdog status and displays half the maximum interval. Afterwards we wait 5 seconds between 11:21:27~32 and the program prints the READY message. Then we enter into the infinite loop and we can see clearly the intervals at which sd_notify with parameter WATCHDOG=1 is being called, as well as the iteration number.
The time difference between iteration <1>
and <2>
can be seen as 8 seconds, however, the real difference is 7.5 seconds, namely half the WatchdogSec= amount. [7500000 micro seconds] This is also the reason we see a difference of 8 seconds between iteration <1>
and <2>
, and a difference of 7 seconds between iteration <2>
and <3>
.
If, for some reason, the program does not sent a sd_notify keep-alive alert within the WatchdogSec=, systemd will restart the service, of course after waiting RestartSec= amount of time.
Conclusion
Using systemd to control the services on GNU/Linux machines is essential for any system administrator in the present day. Although, a significant amount of people are still against the philosophy behind the working methods and implementations of systemd, it still is the most widely used service manager in the Linux community.
My personal opinion is that, despite the differences in the community, acquiring new skills and expanding your knowledge base on a particular topic is far more important than arguing the pros and cons of an object. So, even if you do not like systemd, you can still put aside some time to understand it better and elevate your point of view, maybe you will see something you have missed in the past.
I hope that, with these examples, I have helped you clear out any misunderstanding about services in systemd and have made you a better sysadmin.
Additional Information about the examples
In the examples that I have presented, I only consider the success state when both the settings and exit code of the particular program coincide, thus excluding other scenarios. For example, when the Restart= option is set to on-watchdog and the program exits with abnormal exit code, which will indicate a positive restart condition was satisfied.