LCFG Systemd cookbook
Adding an LCFG component to systemd
The following macro call declares that the lcfg-updaterpms component should start after the lcfg-auth, lcfg-client and lcfg-file components have been started. The value of 'lcfgmultiuser' for the last parameter tells systemd is required as part of the process of producing a stable system (ie one where no further boot-time reboots will occur).
REGISTER_COMPONENT_WITH_SYSTEMD(updaterpms,,lcfg-auth.service lcfg-client.service lcfg-file.service,lcfgmultiuser)
Start service after all reboots have completed
When a system boots, various LCFG components (e.g. updaterpms, kernel, hardware, network) may flag that a reboot is required to apply important changes. It is recommended that any services which are not required in the early part of the boot process (e.g. apache) be configured to start
after the
lcfg-multi-user-stable.target
target has been reached. Once that target has been reached in the boot sequence LCFG will not enforce any further reboot requests.
For example, an LCFG component could be configured like this:
REGISTER_COMPONENT_WITH_SYSTEMD(yum,,lcfg-multi-user-stable.target,multiusertarget)
Other services can be configured like this:
LCFG_SYSTEMD_UNIT_WANTEDBY(lldpd,lldpd.service,multiusertarget)
LCFG_SYSTEMD_WAIT(lldpd,lldpd.service,lcfg-multi-user-stable.target)
Enabling a standalone service
To enable a simple standalone service (e.g. ypbind)
LCFG_SYSTEMD_UNIT_WANTEDBY(ypbind,ypbind.service,multiusertarget)
The macro takes LCFG tag name, service unit and target.
To find out the correct target name (
multiusertarget
in this case) you can look at the
[Install]
section of the unit file (which has
WantedBy=multi-user.target
)
In the filesystem this effectively does:
ln -s '/usr/lib/systemd/system/ypbind.service' '/etc/systemd/system/multi-user.target.wants/ypbind.service'
Enabling systemd debugging output
You can enable systemd debugging output (to the console) by setting the following resource :-
!systemd.loglevel mSET(debug)
This produces very verbose output, so it's best to debug systemd issues on a machine whose console output is stored to file (eg a KVM guest).
You can redirect the output of systemd messages to other locations (eg 'journal' or 'syslog'), using the
systemd.logtarget
resource, but it seems that this deadlocks the system if
systemd.loglevel
is set to 'debug'.
Capturing console input from a systemd unit
If you wish to capture console input from a systemd unit, you need to set
StandardInput
for your unit to
tty-force
. The following resources configure the lcfg-updaterpms component so that it can read from the console.
!systemd.specopts_lcfgupdaterpms mADD(stdin)
systemd.specopt_lcfgupdaterpms_stdin StandardInput=tty-force
Delaying the start of pre-defined units
To delay the start of a pre-defined unit (ie one defined solely in /usr) you can use the
LCFG_SYSTEMD_WAIT(tag,delayed-unit,dependency-unit)
macro.
For example, the following delays the start of the display-manager service until after the lcfg-multi-user-stable target has been reached.
LCFG_SYSTEMD_WAIT(dm,display-manager.service, lcfg-multi-user-stable.target)
Detecting which init is used on a system
The init used on a system (e.g. SysVinit, upstart, systemd, launchd) is recorded in the
sysinfo.init
resource. The
ControllingDaemons page has information on how to write init-independent components which manage daemons.
Delaying the start of a unit to after a fully routed network is available
Some units, eg lcfg-updaterpms, need a fully routed network connection to start correctly. The target
internet-online.target
is reached once such a connection is available and such units should wait on this target.
For LCFG components, add
internet-online.target
to the 'after' parameter to REGISTER_COMPONENT_WITH_SYSTEMD. Eg. :-
REGISTER_COMPONENT_WITH_SYSTEMD(updaterpms,,lcfg-auth.service lcfg-client.service lcfg-file.service internet-online.target,lcfgmultiuser)
For pre-defined units, use LCFG_SYSTEMD_WAIT. Eg. :-
LCFG_SYSTEMD_WAIT(afs,openafs-client.service, internet-online.target)
Delaying the start of a component until after its associated service unit has started
When using an LCFG component merely to (re)configure a daemon managed by an pre-defined (ie stock RHEL) service unit, it is often necessary to start the component
after the service unit has finished starting up.
This can be achieved by adding the name of the service unit in the 3rd parameter of REGISTER_COMPONENT_WITH_SYSTEMD. Eg in the following, the lcfg-openssh component won't be started until after the sshd.service unit has finished starting up.
REGISTER_COMPONENT_WITH_SYSTEMD(openssh,,sshd.service,multiusertarget)
If you don't want to modify the REGISTER_COMPONENT_WITH_SYSTEMD call for some reason, for example you wish to delay just on one machine, the following will achieve the same result as the above :-
systemd.after_lcfgopenssh mADD(sshd.service)
Note that the unit tag created by REGISTER_COMPONENT_WITH_SYSTEMD for an lcfg component is the name of the component prefixed by 'lcfg'. There is no '-', for obvious reasons.
Running a command before a service starts
It is possible to run commands before any service is started by using
ExecStartPre
. Here is an example of running a command prior to the autofs service being started:
!systemd.extraconfigs mADD(homelink)
!systemd.extraunit_homelink mSET(autofs.service)
!systemd.extralines_homelink mSET(1 2)
!systemd.extraline_homelink_1 mSET([Service])
!systemd.extraline_homelink_2 mSETQ("ExecStartPre=/usr/bin/bash -c 'test -L /home || ( rmdir /home && ln -s /autofs/nethome /home ) || logger \"Failed to fix /home link for autofs\"'")
It is worth noting that this is added to a
Service
section. Also note that the command must be an absolute path to an executable, if shell code is required the path to the shell should be specified.
Keep processes running after unit is stopped
By default when a unit/service is stopped all processes that were launched, even if backgrounded, will be killed. This behaviour can be modified by tweaking the
KillMode
option (see systemd.kill(5) manual page for full details). By default it is set to
control-group
which means that all processes in the same cgroup as the parent will be killed. To keep child processes running it's probably best to change it to
process
. In LCFG resources this can be done like this:
!systemd.extraconfigs mADD(sleepkill)
systemd.extraunit_sleepkill systemd-suspend.service
systemd.extralines_sleepkill head killmode
systemd.extraline_sleepkill_head [Service]
systemd.extraline_sleepkill_killmode KillMode=none
This is particularly an issue for the
sleep.target
as it also has the
StopWhenUnneeded
option ( see systemd.unit(5) manual page for details) set to
yes
which means it is stopped once a machine has woken.
How to change the default target
The default target will typically either be
multi-user.target
(usually servers) or
graphical.target
(usually desktops). To
permanently change the default target for a machine use the
systemd.link_defaulttarget
resource:
!systemd.link_defaulttarget mSET(multi-user.target)
To
temporarily change the default target you can override the setting by editting the linux kernel command line through grub to append something like
systemd.unit=multi-user.target
-- Main.ascobie - 2014-10-09