From Fedora Project Wiki
Warning.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.

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.

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".

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.

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)

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).

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= 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

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)