Systemd Packaging Draft

From FedoraProject

Jump to: navigation, search
Warning (medium size).png
This page is a draft only
It is still under construction and content may change. Do not rely on the information on this page.
Note.png
You are probably looking for https://fedoraproject.org/wiki/TomCallaway/Systemd_Revised_Draft -- not this text!

Systemd is the new Fedora init system. We need to document the following areas for packagers and system administrators:

  • Scriptlets for systemd -- these go in the spec files so we need them
  • How to write unit files -- packagers often get stuck writing init scripts for starting services. Since systemd is new, it'll be even more so.

Also need to make some decisions:

Documentation on how to do equivalent things in systemd as in SysVinit/Upstart in SysVinit compatibility are also necessary but probably should go to the documentation team and live in the general Fedora wiki instead of in the Packaging Guidelines. SysVinit to Systemd Cheatsheet started for this purpose.

Contents

Pages needing updates

https://fedoraproject.org/wiki/Packaging/Guidelines#Initscripts

https://fedoraproject.org/wiki/Packaging:ScriptletSnippets#Initscripts_Conventions

Need something comparable to this for systemd: https://fedoraproject.org/wiki/Packaging:SysVInitScript

Foundational decisions

SysVinit requirement

I justify the current guidelines requirement for a SysVinit script to be that there is an init script for the default initsystem. With this idea in mind, I'd change the requirement to be "There must be a init configuration for the default Fedora init system. In Fedora < 14, that's upstart (which understands sysVinit scripts and event files). In Fedora 14 or greater, that's systemd which understands sysvinit scripts or unit files.

There's another argument for SysVinit scripts -- they're often a lowest common denominator among people wanting to switch between init systems. (At least, upstart, SysVinit, and systemd in Fedora). If that's the reason then we should probably include that rationale in the guideline. We also should consider how the sysvinit script is to be tested. Does the sysvinit script override a systemd unit file? Do we have QA test booting Fedora with upstart? If we don't test that the init scripts work, I think that the argument that the init scripts are there for compatibility becomes much weaker.

Service start

Currently the Guidelines tell packagers not to start services when they install. The system administrator must do that themselves. There are a few updates that need to be done here:

  1. Do we want this at all? -- I think the answer is yes.
  2. Are there some exceptions? -- I think the answer is yes even now. Certain services are so core to how Linux works so we enable them when we install them. We need to come up with two things:
    1. a list of programs that we knowingly want this to be the case for.
    2. pick out the characteristics of those programs so we can write a Guidelines that people can use to judge if their service should start by itself or not. [ Most D-Bus services were enabled by default in F14 (i.e. udisks needed no manual enabling, always has been started whenever used). In F-14 we run more D-Bus services by default than SysV services. Hence one could say the majority of services were actually enabled by default. It might make sense to allow disabling some D-Bus services that so far could not be disabled. ] [ Services for the default install should be enabled by default, too. ]

Current Guidelines have the following required properties:

  • Don't turn on a service on upgrade if it was explicitly disabled:
  • Default for most network-listening services is off
  • Do not start the service after installation as they may be in changeroots, in an installer context, or in other situations where you don't want the services started.

Note that "systemctl install" is smart enough to detect a chroot() environment and will not start a service after making a configuration change.

Some bugs about bus/socket activation causing services to start

Scriptlets

This seems to be the common case where we install something but want the system administrator to explicitly enable it:

Requires(post): systemd-units
Requires(preun): systemd-units
Requires(postun): systemd-units

%post
if [ $1 -eq 1 ]; then
        # Package install, not upgrade
        /bin/systemctl daemon-reload >/dev/null 2>&1 || :
fi

%preun
if [ $1 -eq 0 ] ; then
        # Package removal, not upgrade
        /bin/systemctl disable httpd.service > /dev/null 2>&1 || :
        /bin/systemctl stop httpd.service > /dev/null 2>&1 || :
fi

%postun
/bin/systemctl daemon-reload >/dev/null 2>&1 || :
if [ $1 -ge 1 ] ; then
        # Package upgrade, not uninstall
        /bin/systemctl try-restart httpd.service >/dev/null 2>&1 || :
fi

This is probably what we want for "Core" systems that are enabled for the next reboot:

Requires(post): systemd-units
Requires(preun): systemd-units
Requires(postun): systemd-units

%post
if [ $1 -eq 1 ]; then
        # Package install, not upgrade
	/bin/systemctl enable httpd.service >/dev/null 2>&1 || :
fi

%preun
if [ $1 -eq 0 ] ; then
        # Package removal, not upgrade
        /bin/systemctl disable httpd.service >/dev/null 2>&1 || :
        /bin/systemctl stop httpd.service > /dev/null 2>&1 || :
fi

%postun  
/bin/systemctl daemon-reload >/dev/null 2>&1 || :
if [ $1 -ge 1 ] ; then
        # Package upgrade, not uninstall
        /bin/systemctl try-restart httpd.service >/dev/null 2>&1 || :
fi

There's no special handling for socket, hardware or bus activated services necessary. All that matters is that the unit files needing installation are listed on the systemctl command lines.

To facilitate upgrades from older packages carrying SysV scripts, use a snippet like the following:

Requires(triggerun): systemd-units

%triggerun -- httpd < 0.47.11-1
if /sbin/chkconfig httpd ; then
        /bin/systemctl enable httpd.service >/dev/null 2>&1 || :
fi

Where httpd is the package name and 0.47.11-1 the first package version that carries systemd units. This will enable the systemd unit if the SysV service was enabled.

For further details see http://0pointer.de/public/systemd-man/daemon.html, the section regarding "Installing Systemd Service Files".

Bus Activation

In order to allow parallel startup of a D-Bus service and its consumers it is essential that D-Bus services can be bus activated, and that the D-Bus activation is forwarded to systemd so that you end up with a single service instance only. That means all existing D-Bus services that have previously not been started via bus activation but have been started via SysV scripts should be updated to be bus activatable. This is easily implemented by dropping a D-Bus .service file in /usr/share/dbus/system-services/ which uses the SystemdService= directive to redirect activation to systemd. See the D-Bus documentation for more information. (Don't be confused by the fact that both systemd and D-Bus use the .service file suffix, they are different things, though the D-Bus .service often refers to the systemd .service file for the same program with the SystemdService= directive.)

Note that traditionally bus activated services could not be disabled. systemd does not change this, however in some cases where making it possible to disable a service is desirable (e.g. avahi, NetworkManager), you can easily implement that by directing the D-Bus service to a symlinked alias name of the real service file which is controlled via systemctl enable/disable and which then points to the real service. Example: set SystemdService=dbus-org.freedesktop.Avahi.service instead of SystemdService=avahi-daemon.service, and then make /etc/systemd/system/dbus-org.freedesktop.Avahi.service a symlink → /lib/systemd/system/avahi-daemon.service. A full example for the Avahi case:

The D-Bus .service file /usr/share/dbus-1/system-services/org.freedesktop.Avahi.service:

[D-BUS Service]
Name=org.freedesktop.Avahi
SystemdService=dbus-org.freedesktop.Avahi.service

# This service should not be bus activated if systemd isn't running,
# so that activation won't conflict with the init script startup.
Exec=/bin/false

The system .service file /lib/systemd/system/avahi-daemon.service:

[Unit]
Description=Avahi mDNS/DNS-SD Stack
Requires=avahi-daemon.socket
After=syslog.target

[Service]
Type=dbus
BusName=org.freedesktop.Avahi
ExecStart=/usr/sbin/avahi-daemon -s
ExecReload=/usr/sbin/avahi-daemon -r
NotifyAccess=main

[Install]
WantedBy=multi-user.target
Also=avahi-daemon.socket
Alias=dbus-org.freedesktop.Avahi.service

The Alias= line ensures that the existance of the /etc/systemd/system/dbus-org.freedesktop.Avahi.service symlink can be controlled by "systemctl enable" and "systemctl disable".

In general it is also recommended to supply native systemd units for all services that are already bus activatable, so that these services can be controlled and supervised centrally like any other service with tools such as systemctl. A similar logic like the one shown above should apply.

Writing unit files

Every .service file needs the following options set in the [Unit] section:

[Unit]
Description=A Human readable string describing the service (not the service file!)
After=syslog.target

The latter line is only necessary if the service can log to syslog (most can, if in doubt). The Description= line should describe the service, not the service file. i.e. "Apache Web Server" is a good description, "Starts and Stops the Apache Web Server" is a bad one.

It should carry the following option in the [Service] section:

[Service]
Type=...
BusName=...
ExecStart=...
ExecReload=...

The Type= setting is very important. For D-Bus services this should be "dbus", for traditional services "forking" is usually a good idea, for services not offering any interfaces to other services "simple" is best. For "one-short" scripts "oneshot" is ideal, often combined with RemainAfterExit=. See http://0pointer.de/public/systemd-man/systemd.service.html for further discussion on the topic. Type= may be ommited if it would be set to "simple", since that is the default. In order to make things more reasonable and keep them short, we recommed omitting this option in this case.

BusName= should be set for all services connecting to D-Bus. (i.e. it is a must for those where Type=dbus, but might make sense otherwise, too) Omit this option if your service does not take a name on the bus.

ExecStart= is necessary for all services.

ExecReload= should be specified for all services supporting reload. It is highly recommended to add code here that synchronously reloads the configuration file here (i.e. /bin/kill -HUP $MAINPID is usually a poor choice, due to its asynchronous nature). Omit this option if your service does not support reloading.

In the [Install] section the following option should be set:

[Install]
WantedBy=...

The recommended parameters for WantedBy are either multi-user.target (for most system services) or graphical.target (for services related to the UI).

For more information regarding these options see http://0pointer.de/public/systemd-man/systemd.unit.html and http://0pointer.de/public/systemd-man/systemd.service.html

Strictly speaking ExecStart= (in the [Service] section) is the only option really necessary for a .service file. However, in Fedora we ask you to add the other options mentioned here, too.

Real-life example:

[Unit]
Description=ABRT Automated Bug Reporting Tool
After=syslog.target

[Service]
Type=dbus
BusName=com.redhat.abrt
ExecStart=/usr/sbin/abrtd -d -s

[Install]
WantedBy=multi-user.target

Note that for some services involved in early boot or late shut down different rules might apply. This document only covers normal services.

Naming units

You should name the unit files after the sysv service file name. I.e. /etc/rc.d/init.d/foobar should have its systemd counterpart in /lib/systemd/system/foobar.service. This will enable auomatic fallback in systemd: only if a native file doesn't exist by a specific name it will fall back to the sysv file of the same service.

It is our intention to unify service names of well-known software across distributions, so that we can eventually ship the service files in the upstream packages. Hence it is a good idea to discuss service names with maintainers of the same packages in other distributions and agree on a common name. Then, install the service file under the agreed name and add a compability symlink for the old SysV name.

Name service files after the implementation, not the generic type of software. i.e. a good name is "apache.service", a bad name "httpd.service".

Filesystem placement

Unit files belong in /lib/systemd/system/. They are architecture independent (hence, not %{_lib}) and needed early in the boot process.

(SystemVinit has a macro for this: %{_initddir}. I believe that rpmlint will issue a warning on spec files that use "/lib/" directly so having a macro for this would be a good idea.)

Use EnvironmentFile= to support /etc/sysconfig files. You may then refer to variables set in sysconfig files with ${FOOBAR} and $FOOBAR, in the ExecStart= lines (and related lines). (${FOOBAR} expands the variable into one word, $FOOBAR splits up the variable value at whitespace into multiple words)

Options to avoid

For most services, we do not want to use requirement dependencies such as Requires= or Wants=. Instead exclusively use ordering dependencies: Before= and After=. This is used to implement loose coupling: if two services are started at the same time their startup is properly ordered, but do not make it strictly necessary to run one if the other is started.

If you use a requirement dependency, use Wants= rather than Requires=, to make things a little bit more robust. If you use a requirement dependency in almost all cases you should also add an ordering dependency, as ordering and requirement dependencies are orthogonal in systemd (!).

Here's an example of this common case:

  1. A web application needs postgres to store its data.
  2. It is set to start After postgres. On startup, the web application does not start until postgres does.
  3. Once running, the system administrator needs to restart postgres due to a config tweak.
  4. Since only After was used, the web application may be temporarily unable to serve some requests but it does not need to restart in order to serve pages after the database comes back up.

Avoid referring to runlevelX.target units in all lines taking unit names (such as WantedBy), these are legacy names existing for compatibility with SysV only.

Avoid Names=. Usually it is a better idea to symlink an additional name in the file system. Note that a name listed in Names= is only useful when a service file is already loaded. However, systemd loads only the service files actually referred to in another loaded service, and uses the filenames during the search. Hence a name in Names= is not useful as a search key, but a symlink in the file system is. Also do not put a (redundant) Names=foobar.service line into a file called foobar.service. We want to keep our service files short.

Avoid introducing new sysconfig files. Use EnvironmentFile= only for compatibility with existing sysconfig files.

Avoid using StandardOutput=kmsg. Use StandardOutput=syslog instead. (same for StandardError=).

Comments, Discussion

For the 'Start service' question, I think the packaging guidelines should be clarified to discriminate between services that are in the default install, and things that an admin explicitly installs. Services in the default install should almost always be on by default (because they are needed for a working system, which is why they are in the default install). Optional services that are installed explicitly should be off by default after installation. --mclasen 19:30, 9 September 2010 (UTC)

For 'Writing unit files', we probably need a good, simple example of a typical service unit file. There is pretty exhaustive information about the file formats and different kind of service files available in the various man pages shipped with systemd, and we should refer to those:

[systemd.unit(7)] [systemd.service(7)] [systemd.socket(7)] etc --mclasen 19:58, 9 September 2010 (UTC)