Systemd

From HerzbubeWiki
Jump to navigation Jump to search

This page has information about systemd.

My professional career started with the job of administrating a number of SCO Unix systems. For this reason I am familiar with how things work under System V, for instance I am used to restart a system by issuing the command init 6. Of course I knew about the controversy among Debian developers when Debian decided to adopt systemd, but I didn' bother to learn more details about systemd than was necessary to understand the debate. Today (in May 2016) I wanted to perform some maintenance tasks which required me to reboot the system, and I was rather shocked to learn that init 6 no longer works. So this page is my poor attempt at hastily collecting all the information that is necessary to allow me to continue administrate my system on a basic level.

My gut reaction after reading the FAQ: Yuck! How is anyone supposed to remember a command like systemctl isolate reboot.target to reboot the system?

Update July 2018: Not sure if this already existed 2 years ago when I wrote the paragraphs above, but these days it's possible to reboot with the fairly simple, easy-to-remember command systemctl reboot.


References


Concepts

systemd provides a dependency system between various entities called "units". There are 12 types of units, here are the most important ones:

  • Service units (to start and stop daemons)
  • Device units
  • Mount units (filesystem mount points)
  • Target units (used to group other units). Targets are similar in concept to SysV runlevels
  • Timer units (job scheduling, i.e. the stuff that traditionally is the domain of the cron service)


Units can have one of several states. The actual meaning of a state for a specific unit depends on the nature of that unit.

  • Active: The unit is started, bound, plugged in, etc.
  • Inactive: The unit is stopped, unbound, unplugged, etc.
  • Activating: Going from inactive to active
  • Deactivating: Going from active to inactive
  • Failed: If a service failed for any reason, e.g. a process crashed or returned an error code on exit


There are two types of dependencies between units:

  • Requirement dependencies. These are used to expresses that in order to function, unit B requires that unit A is also started. systemd calls this a positive requirement. systemd also allows to state negative requirements, which is used to say that units conflict with each other.
  • Ordering dependencies. These are used to express that unit B needs to be started before or after unit A.


I have not yet understood why systemd distinguishes between these two dependency types. For instance, the man page explains that if a requirement dependency is stated without an ordering dependency, then the two units are started in parallel - what the heck is the use of that?!? Because of that, the man page explains, it is therefore common that both requirement and ordering dependencies are placed between two units. So what is the point of all this? Why distinguish between two kinds of dependencies when in the end you will always use both of them? TODO: Requires further research.


Basics

Main command

The basic command to work with systemd is

systemctl

Just issuing this command without any parameters lists all running services:

root@pelargir:~# systemctl
UNIT                LOAD   ACTIVE SUB       DESCRIPTION
[...]
apache2.service     loaded active running   LSB: Apache2 web server
[...]
cron.service        loaded active running   Regular background program processing daemon
[...]


Runlevel commands

The following command is an approximation of what the SysV runlevel command does:

systemctl list-units --type=target

Switch the runlevel:

systemctl isolate runlevel5.target

Use modern names to switch the runlevel. Note that you can omit the ".target" suffix because if missing it's assumed as default.

systemctl isolate poweroff.target     # runlevel 0 (halt system)
systemctl isolate rescue.target       # runlevel 1 (single user mode)
systemctl isolate multi-user.target   # runlevel 3 (multi-user without GUI)
systemctl isolate graphical.target    # runlevel 5 (multi-user with GUI)
systemctl isolate reboot.target       # runlevel 6 (restart system)

Some of these are also available as so-called "system commands" (cf. man systemctl):

systemctl poweroff     # runlevel 0 (halt system)
systemctl rescue       # runlevel 1 (single user mode)
systemctl reboot       # runlevel 6 (restart system)


Service status

The status of a service can be checked with this:

systemctl status cron

This provides quite comprehensive information, including an exerpt of the last log messages pertaining to the service. For instance, the status of the "cron" service looks like this:

root@pelargir:~# systemctl status cron
● cron.service - Regular background program processing daemon
   Loaded: loaded (/lib/systemd/system/cron.service; enabled; vendor preset: enabled)
  Drop-In: /etc/systemd/system/cron.service.d
           └─pelargir.conf
   Active: active (running) since Wed 2018-07-18 19:18:56 CEST; 1 day 23h ago
     Docs: man:cron(8)
 Main PID: 28428 (cron)
   CGroup: /system.slice/cron.service
           └─28428 /usr/sbin/cron -f

Jul 20 19:00:07 pelargir CRON[48224]: pam_unix(cron:session): session closed for user francesca
Jul 20 19:02:01 pelargir CRON[48584]: pam_unix(cron:session): session opened for user logcheck by (uid=0)
Jul 20 19:02:01 pelargir CRON[48585]: (logcheck) CMD (   if [ -x /usr/sbin/logcheck ]; then nice -n10 /usr/sbin/logcheck; fi)
Jul 20 19:02:03 pelargir CRON[48584]: pam_unix(cron:session): session closed for user logcheck
Jul 20 19:09:01 pelargir CRON[49697]: pam_unix(cron:session): session opened for user root by (uid=0)
Jul 20 19:09:01 pelargir CRON[49698]: (root) CMD (  [ -x /usr/lib/php5/sessionclean ] && /usr/lib/php5/sessionclean)
Jul 20 19:09:01 pelargir CRON[49697]: pam_unix(cron:session): session closed for user root
Jul 20 19:10:01 pelargir CRON[49777]: pam_unix(cron:session): session opened for user www-data by (uid=0)
Jul 20 19:10:01 pelargir CRON[49778]: (www-data) CMD ([ -x /usr/share/awstats/tools/update.sh ] && /usr/share/awstats/tools/update.sh)
Jul 20 19:10:08 pelargir CRON[49777]: pam_unix(cron:session): session closed for user www-data


Starting / stopping a service

The following command stop a service. If the service is already stopped this is not an error.

systemctl stop foo

The following command starts a service. If the service is already started this is not an error.

systemctl start foo

The following command restarts a service. If the service is already stopped this is not an error and the service is simply started.

systemctl restart foo

The following command restarts a service. If the service is already stopped this is not an error, but the service is not started.

systemctl try-restart foo

If the service supports it, the following command causes it to "reload", i.e. this typically reloads the configuration.

systemctl reload foo


Enable a service to start when the system boots

The following example command enables the SpamAssassin daemon so that it is started automatically when the system boots. Note that the daemon is not started right now - for that you would have to issue the "start" command.

systemctl enable spamassassin.service

Enabling a service creates all sorts of symlinks, apparently the driver for what exactly is created is the information that is located in the service's "unit file". This is the symlink that was created by the above SpamAssassin enabling command:

root@pelargir:~# find /etc -name spamassassin.service | xargs ls -l
lrwxrwxrwx 1 root root 40 Oct  6 23:06 /etc/systemd/system/multi-user.target.wants/spamassassin.service -> /lib/systemd/system/spamassassin.service

And this is some of the content of SpamAssassin's unit file:

root@pelargir:~# cat /lib/systemd/system/spamassassin.service
[Unit]
Description=Perl-based spam filter using text analysis
[...]
[Install]
WantedBy=multi-user.target

Enabling a service also modifies /etc/init.d/.depend.start and causes the running systemd process to reload its configuration


The following command disables a service:

systemctl disable spamassassin.service


Manipulate a service's configuration

To completely replace a service's configuration, place your replacement file here:

/etc/systemd/system/foo.service

This is the so-called "unit file". But usually it's better to create a "drop-in file" to override some settings from the unit file's main configuration. In this case, place the drop-in file here:

/etc/systemd/system/foo.service.d/bar.conf

Notes:

  • The foo.service.d folder may contain several drop-in files
  • The drop-in files must all have the .conf extension
  • Running the command systemctl edit foo.service automatically creates the folder and adds a file override.conf
  • Read man systemd.unit for details about the configuration file format. The "Example 2. Overriding vendor settings" in the EXAMPLES section has details about the overriding mechanics


Configuring dependencies

Overview

The various requirement types are documented in

man systemd.unit

Requirements can be specified either in the service's main configuration file - the "unit file", or in a "drop-in file", the contents of which override the service's main configuration from the unit file. For details see section Manipulate a service's configuration on this page.


Requires

Requires is hard dependency:

  • If the dependent unit is started, so is the dependency unit. If the dependency unit fails to start, the dependent unit is also not started.
  • If the dependency unit is stopped, so is the dependent unit.
  • If the dependency unit is restarted, so is the dependent unit.
  • If the dependency unit is first stopped, then started again, in two distinct steps, the dependent unit is stopped but NOT started again. This could happen, for instance, if a service is temporarily stopped during package updates, then started again after the package update has finished.


In general, the man page has the following to say:

Often, it is a better choice to use Wants= instead of Requires= in order to achieve a system that is more robust when dealing with failing services.


Example:

[Unit]
Requires=foo.service bar.service


Wants

Wants is soft dependency:

  • If the dependent unit is started, so is the dependency unit. If the dependency unit fails to start, the dependent unit is still started.
  • If the dependency unit is stopped, the dependent unit is not affected.
  • If the dependency unit is restarted, the dependent unit is not affected.
  • If the dependency unit is first stopped, then started again, in two distinct steps, the dependent unit is not affected.


Example:

[Unit]
Wants=foo.service bar.service


Before, After

Before and After are ordering dependency, i.e. they affect in which order units are stopped and started. Ordering dependencies must be specified in addition to requirement dependencies such as Requires and Wants.


[Unit]
Before=foo.service
After=bar.service


Timers vs. cron

Introduction and references

systemd is capable of periodically running jobs, just like the cron service, by way of a special type of unit: Timer units. The scope of systemd is truly amazing - and I am not at all sure whether I like it!

Here are some links that cover the topic:


Basics

A systemd timer launches a systemd service at the specified time(s). The service name is specified in the .timer file by the Unit= option. If the option is omitted, a service with the same name as the timer unit must exist. For instance, the "apt-daily" timer launches the "apt-daily" service:

root@pelargir:~# l /lib/systemd/system/apt-daily.*
-rw-r--r-- 1 root root 225 Sep 13  2017 /lib/systemd/system/apt-daily.service
-rw-r--r-- 1 root root 156 Sep 13  2017 /lib/systemd/system/apt-daily.timer

Typically the .service file of a service that is launched by a timer does not contain an [Install] section, because the service is not supposed to be running on its own. Instead the .timer file of the timer contains the [Install] section. Example:

root@pelargir:~# cat /lib/systemd/system/apt-daily.timer 
[...]

[Install]
WantedBy=timers.target


Starting/stopping timers

Run systemctl without parameters to see all services that are currently running. Towards the end of the list you see the timers that are currently started.

Example:

timers.target                          loaded active active    Timers                             
--------------------------------------------------------------------------------------------------
apt-daily-upgrade.timer                loaded active waiting   Daily apt upgrade and clean activit
apt-daily.timer                        loaded active waiting   Daily apt download activities      
logrotate.timer                        loaded active waiting   Daily rotation of log files        
man-db.timer                           loaded active waiting   Daily man-db regeneration          
phpsessionclean.timer                  loaded active waiting   Clean PHP session files every 30 mi
systemd-tmpfiles-clean.timer           loaded active waiting   Daily Cleanup of Temporary Director

A timer can be started/stopped with these commands:

systemctl start foo.timer
systemctl stop foo.timer

Important: Stopping a timer does not prevent it from being started again when the system starts up the next time. For that you have to also disable the timer. See the next section.


Enabling/disabling timers

A timer that is enabled is automatically started when the system starts up. Enabling a timer adds a symlink to the /etc/systemd/system/timers.target.wants folder, disabling the timer removes the symlink.

Example content of the folder:

root@pelargir:# ls -l /etc/systemd/system/timers.target.wants
total 8
lrwxrwxrwx  1 root root   35 Jul 22  2018 apt-daily.timer -> /lib/systemd/system/apt-daily.timer
lrwxrwxrwx  1 root root   43 Jul 22  2018 apt-daily-upgrade.timer -> /lib/systemd/system/apt-daily-upgrade.timer
lrwxrwxrwx  1 root root   35 Dec 27  2019 logrotate.timer -> /lib/systemd/system/logrotate.timer
lrwxrwxrwx  1 root root   32 Dec 27  2019 man-db.timer -> /lib/systemd/system/man-db.timer
lrwxrwxrwx  1 root root   41 Jul 22  2018 phpsessionclean.timer -> /lib/systemd/system/phpsessionclean.timer

A timer can be enabled/disabled with these commands:

systemctl enable foo.timer
systemctl disable foo.timer

Important: Enabling a timer does not start it if it is currently not started, and disabling a timer does not stop it if it is currently started. For that you have to explicitly start or stop the timer. See the previous section.


Notation

Read man systemd.time, specifically the section "Calendar events", to understand the notation used to specify the times when a timer unit fires. Here's a short overview:

weekdays years-months-days hours:minutes:seconds


Discussion:

  • The weekdays part can be omitted, in which case every week day will match
  • The date part can be omitted, in which case every day will match
  • The time part can be omitted, in which case 00:00:00 is assumed
  • The seconds component can be omitted in the time part, in which case "00" is assumed
  • The seconds component can contain fractions up to 6 decimal places
  • In the date and time parts, any component may be specified as "*" in which case any value will match
  • Every component can be specified as a list of values separated by commas
  • A value can be suffixed with "/" and a repetition value. This matches the value itself and the value plus all multiples of the repetition value.
  • Every component can contain a range of values separated by ".."
  • Some special expressions that can be used
    • minutely = *-*-* *:*:00
    • hourly = *-*-* *:00:00
    • daily = *-*-* 00:00:0
    • monthly = *-*-01 00:00:0
    • weekly = Mon *-*-* 00:00:00
    • yearly = *-01-01 00:00:00
    • quarterly = *-01,04,07,10-01 00:00:00
    • semiannually = *-01,07-01 00:00:00

Examples:

  • Run every Thursday and Sunday at 5 in the morning: Thu,Sun *-*-* 05:00:00
  • Run every week between Thursday and Sunday at 5 in the morning: Thu..Sun *-*-* 05:00:00
  • Run every day on midnight and every three hours thereafter: *-*-* 00/3:00:00


Logging

Traditionally system services write log data to a simple text log file in /var/log. Given the necessary permissions to access a log file, one could use the usual text-based UNIX tools to view or process such a log file.

Of course, systemd also had to reinvent the wheel when it comes to logging. The new approach is to write log data in a binary format (!) into files using the .journal extension in /var/log/journal. Since the files contain binary data, the gateway to access this log data is the command line tool

journalctl

Running the command without any arguments dumps the content of the entire log, using less for paging when run in an interactive shell.

To see only the log output of a specific unit:

journalctl --unit foo

The following command shows only the log output within a specific timeframe, including the boundary timestamps. Both since/until can be omitted. If the date component is omitted the current date is assumed, if the time component is omitted, "00:00:00" is assumed. More options are timestamps that are relative to the current time, or symbolic names such as "today" or "yesterday".

journalctl --since "2024-05-24 00:00:00" --until "2024-05-24 00:01:00"

To see the systemd unit instead of the syslog identifier:

journalctl --output with-unit

To see a continuous live updating log use the -f argument (similar to tail -f). For instance:

journalctl -f --unit foo

Notes:

  • In Debian 11 bullseye, rsyslog was still installed and systemd was configured to forward log data to rsyslog.
  • In Debian 12 bookworm, rsyslog is no longer installed and can be safely removed. See the release notes.
  • Currently on my upgraded Debian 12 system this forwarding does occur, although the ForwardToSyslog option is disabled in /etc/systemd/journald.conf.


Assorted information bits and pieces

/sbin/init

If you have Debian package systemd-sysv installed, /sbin/init should be a symlink to /lib/systemd/systemd. That is indeed the case on my machine:

root@pelargir:~# ls -l /sbin/init 
lrwxrwxrwx 1 root root 20 Feb  4 13:06 /sbin/init -> /lib/systemd/systemd


Automatically restarting service on failure

One of systemd's many capabilities, and this one being actually in scope, is to automatically restart a service, or daemon process, in case it should fail.

The service definition goes into

/etc/systemd/system/foo.service

The key property in the service definition file is the "Restart" property. Here's an example using the common "on-failure" condition:

Restart=on-failure

The "on-failure" condition restarts the service when the process exits with a non-zero exit code, is terminated by a signal (including on core dump, but excluding the signals SIGHUP, SIGINT, SIGTERM or SIGPIP), when an operation (such as service reload) times out, and when the configured watchdog timeout is triggered. There are more conditions than just "on-failure", a thorough description is available from the man page man systemd.service.


Finally, here's a complete example of a service definition that restarts the rather fragile Web Socket service from my project Little Go for the Web. I created the definition by adapting a copy of the MySQL service definition.

ubuntu@ip-172-31-39-57:~$ cat /etc/systemd/system/littlego-web-ws.service 
[Unit]
Description=Little Go for the Web web socket server
After=network.target

[Service]
User=root
Group=root
ExecStart=/usr/local/share/littlego-web/script/startWebSocketServer.sh
TimeoutSec=10
Restart=on-failure


Services without a unit file

The slapd service does not have a unit file in /lib/systemd, yet it is possible to start/stop the service using systemctl. I don't know (yet) how this is possible, I have filed it under "yet another WTF-moment in the happy life of a systemd user".

Querying the service's status prints this:

root@pelargir:~# systemctl status slapd
● slapd.service - LSB: OpenLDAP standalone server (Lightweight Directory Access Protocol)
   Loaded: loaded (/etc/init.d/slapd; generated)
   Active: active (running) since Mon 2019-12-30 15:52:55 CET; 6min ago
     Docs: man:systemd-sysv-generator(8)
  Process: 54340 ExecStart=/etc/init.d/slapd start (code=exited, status=0/SUCCESS)
    Tasks: 4 (limit: 4643)
   Memory: 13.9M
   CGroup: /system.slice/slapd.service
           └─54346 /usr/sbin/slapd -h ldapi:/// ldap://127.0.0.1:389 -g openldap -u openldap -F /etc/ldap/slapd.d


pelargir configuration

The following override for cron makes sure that slapd and nscd must be running before cron is started. Reason: There are cron jobs for users that exist only in LDAP. If cron cannot find those users when it starts up (because the LDAP service is not yet running), it will ignore their jobs.

root@pelargir:~# cat /etc/systemd/system/cron.service.d/pelargir.conf 

# The following dependencies are added to dependencies that already exist.
#
# IMPORTANT: Use "Wants" not "Requires" so that when the dependency units
# are stopped during a package update the dependent unit remains running.
# During system startup, "Wants" has the same effect as "Requires"
# (except that the dependent unit will start even if the dependency units
# fail to start).

[Unit]
Wants=slapd.service nscd.service
After=slapd.service nscd.service