Wednesday, August 07, 2024

systemd advanced topics

Basic Commands

# start / stop
systemctl start myservice
systemctl stop service

# reload after making changes to systemd unit files
# note: a reboot also does this
systemctl daemon-reload

# list dependencies of everything
systemctl  list-dependencies

# list dependencies for a given unit file
systemctl list-dependencies myservice.service

# list reverse dependencies  for a given unit file
systemctl list-dependencies myservice.service --reverse --all

Locations

Location of unit files
/etc/systemd/system/

Location of OS installed unit files (so users don't break the system)
/usr/lib/systemd/system/

Location of  runtime systemd unit files
/run/systemd/system

Various types of unit files ".target", ".service", ".path", ".mount", etc

.target = is like a "runlevel" - it lets you tie many unit files under it so you can enable/disable entire groups of things at once

.service = is a running process.

.path = lets you watch for changes to a path using inotify, and then trigger another unit file

.mount = lets you mount filesystems using systemd. /etc/fstab is automatically turned into unit files

and more...

Sections of a unit file

[Unit]  Metadata and trigger ordering dependencies

Description = description about the unit file

Before = ensures that units specified here are triggered before this unit

After = units listed here are triggered after this unit

Requires = list of units that must be running/completed before this one

Wants = similar to requires but ignores units that are not found or fail

[Install]  Metadata and trigger ordering dependencies

WantedBy = reverse dependency for "wants". units listed here must wait for this unit to run/complete before they can run

RequriedBy = reverse dependency for "requires".

Activating the Install section

Items listed in the [Install] section do not take affect until you run "systemctl enable myservice.service". Or conversely "systemctl disable myservice.service".  For example, say you have a unit file as such:

[Unit]
Description = myservice.service

[Install]
WantedBy = multi-user.target
RequiredBy = cloud-final.target


When you run "systemctl enable myservice.service", a symlink to this unit file will be created in /etc/systemd/system/multi-user.target.wants/ and in /etc/systemd/system/cloud-final.target/. This will make the dependency take effect.

Note: if the /etc/systemd/system/multi-user.target.wants/ dir does not exist, it will be created
Note: this also works for all types unit files (e.g, you can run 'systemctl enable myservice.path')

[Target | Service | Path | Mount | etc] Type specific unit file configuration

Services
 
[Service]
# Types:
# simple = "will consider the unit started immediately after the main service process has been forked off"
# oneshot = "is similar to simple; however, the service manager will consider the unit up after the main process exits."
Type = simple
 
# If True, service shall be considered active even when all its processes exited.
RemainAfterExit =
true

# command to run
ExecStart = /bin/bash -c 'echo hi'
 
ExecStartPre=...
ExecStartPost=...

Path
Waits for a path to create or update
https://www.freedesktop.org/software/systemd/man/latest/systemd.path.html


Conditionally starting a service

Two types: 
- Conditions fail (quietly) if a unit is run and a condition is not met.  
- Asserts fail and return an error

Run if file exists and not empty 

[Unit]
ConditionPathExists = /opt/empty.txt
ConditionFileNotEmpty = /opt/empty.txt

Run if file exists and not empty 

[Unit] ConditionPathExists =! /opt/empty.txt 

Run if either abc.txt or def.txt exists and user is root

[Unit] ConditionPathExists=| /tmp/abc.txt ConditionPathExists=| /tmp/def.txt ConditionUser = root

 
List of conditions
https://www.freedesktop.org/software/systemd/man/latest/systemd.unit.html#Conditions%20and%20Asserts 

Path unit: Running unit based on path

Example unit file

[Unit]
Description="Trigger unit based on /tmp/test path"

[Path]
PathModified=/tmp/test
# other options PathExists=, PathExistsGlob=, PathChanged=, PathModified=, DirectoryNotEmpty=

Unit=unit-to-trigger.service

[Install]
WantedBy=multi-user.target