From Fedora Project Wiki

(Remove runlevel 2 and 4)
(Fix directory in symlink)
Line 221: Line 221:
         # implement: https://bugzilla.redhat.com/show_bug.cgi?id=616857
         # implement: https://bugzilla.redhat.com/show_bug.cgi?id=616857
         if chkconfig --level 1 httpd ; then
         if chkconfig --level 1 httpd ; then
             ln -sf ../httpd.service /etc/systemd/system/rescue.target.wants/ 2>&1 >/dev/null
             ln -sf /lib/systemd/system/httpd.service /etc/systemd/system/rescue.target.wants/ 2>&1 >/dev/null
         multiuser=0
         multiuser=0
         if chkconfig --level 3 httpd; then
         if chkconfig --level 3 httpd; then
             ln -sf ../httpd.service /etc/systemd/system/multi-user.target.wants/ 2>&1 >/dev/null
             ln -sf /lib/systemd/system/httpd.service /etc/systemd/system/multi-user.target.wants/ 2>&1 >/dev/null
             multiuser=1
             multiuser=1
         fi
         fi
Line 230: Line 230:
             # If it's already in multi-user, it will be inherited automatically
             # If it's already in multi-user, it will be inherited automatically
             if [ $multiuser -eq 0 ] ; then
             if [ $multiuser -eq 0 ] ; then
                 ln -sf ../httpd.service /etc/systemd/system/graphical.target.wants/ 2>&1 >/dev/null
                 ln -sf /lib/systemd/system/httpd.service /etc/systemd/system/graphical.target.wants/ 2>&1 >/dev/null
             fi
             fi
         else
         else
Line 244: Line 244:
         # Previous SysVinit packages won't have a %postun that will invoke this
         # Previous SysVinit packages won't have a %postun that will invoke this
         /bin/systemctl daemon-reload >/dev/null 2>&1 || :
         /bin/systemctl daemon-reload >/dev/null 2>&1 || :
         ### FIXME: Do we need a systemctl try-restart in here for the same reasons?
         ### FIXME: It seems we need a systemctl try-restart in here for the same reasons?
         # Or maybe only if the service was configured to run in the "runlevel" that we're currently in?
         # Or maybe only if the service was configured to run in the "runlevel" that we're currently in?
         # /bin/systemctl try-restart httpd.service > /dev/null 2>&1 || :
         # /bin/systemctl try-restart httpd.service > /dev/null 2>&1 || :

Revision as of 21:40, 28 February 2011

Fedora systemd Services

This document describes the guidelines for systemd services, for use and inclusion in Fedora packages.

Unit Files

The systemd equivalent for an SysV initscript is called a Unit file. Each package that contains software that wants/needs to start a service at boot must have a systemd unit file.

SysV Initscripts
Packages may also provide a SysV initscript file, but are not required to do so. This format is considered legacy, but Fedora still contains init mechanisms such as upstart which do not support the systemd unit file format. If present, the SysV initscript(s) must go into an optional subpackage, so as not to confuse sysadmins. The guidelines for SysV initscripts can be found here: Packaging:SysVInitScript
Upstream Support
systemd unit files ideally are reusable across distributions and shipped along the upstream packages. Please consider working with upstream to integrate the systemd files you prepare in the upstream sources. Information for developers on how to integrate systemd support best with their build system you may find on http://0pointer.de/public/systemd-man/daemon.html

Naming

Unit files for services have a naming scheme of foobar.service. When considering what basename to use, keep the following advice in mind:

  • Unit files must be named after the software implementation that they support, as opposed to the generic type of software. So, a good name would be "apache-httpd.service", and a bad name would be "httpd.service", as there are multiple httpd implementations, but only one Apache httpd.

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.

Basic format

Case Sensitivity
All fields in a unit file are case sensitive.

Every .service file must begin with a [Unit] section:

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

The Description= line must not exceed 80 characters, and must describe the service, and not the service file. For example, "Apache Web Server" is a good description, but "Starts and Stops the Apache Web Server" is a bad one.

After specifies that this service may only start after the listed services have started. In this example, the service starts after syslog since syslog can be used for logging. (Most daemon programs can use syslog for logging so when in doubt, it's good to include this.)

Next, the .service file must have a [Service] section:

[Service]
Type=...
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-shot" scripts "oneshot" is ideal, often combined with RemainAfterExit=. See http://0pointer.de/public/systemd-man/systemd.service.html for further discussion on the topic. Since "simple" is the default type, .service files which would normally set Type=simple may simply omit the Type line altogether.

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. This line defines the string that you would run to start the service daemon, along with any necessary options.

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.

Finally, the .service file should have an [Install] section:

[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 you must add the other options mentioned here (as applicable).

Support for /etc/sysconfig files

If your service supports /etc/sysconfig files, then you must use EnvironmentFile=/etc/sysconfig/foobar in your .service file, in the [Service] section.

Example:

[Service]
Type=forking
EnvironmentFile=-/etc/sysconfig/httpd
ExecStart=/usr/sbin/httpd $OPTIONS
ExecReload=/usr/sbin/httpd $OPTIONS -k restart

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)

The "-" on the EnvironmentFile= line ensures that no error messages is generated if the sysconfig file does not exist. Since traditionally sysconfig files have been optional you should always include the "-" when using this directive.

Ideally unit files are shipped along upstream packages. In order to make adoption of upstream unit files easy please do not introduce new /etc/sysconfig files or options, as /etc/sysconfig files are Fedora-specific. Please support sysconfig files only to maintain compatibility with previous Fedora release. The recommended way for administrators to reconfigure systemd service files is to copy them from /lib/systemd/system to /etc/systemd/system and modify them there. Unit files in /etc/systemd/system override those in /lib/systemd/system if they otherwise carry the same name.

Fields to avoid

For most services, we do not want to use requirement dependencies in the [Unit] section, such as Requires= or Wants=. Instead exclusively use ordering dependencies: Before= and After=. This is used to implement loose coupling: if someone asks two services to start at the same time, systemd will properly order their startup but 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 postgresql to store its data.
  2. It is set to start After postgresql. On startup, the web application does not start until postgresql does.
  3. Once running, the system administrator needs to restart postgresql 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= (in the [Unit] section). 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 using StandardOutput=kmsg. Use StandardOutput=syslog instead. (same for StandardError=). syslog is a better target, because it is where service logging should primarily go. While messages written to kmsg eventually end up in syslog too they are normally attributed to the kernel, and lose much of its meta data. StandardOutput=syslog is recommended for all services which generate output on stdout or stderr which shall be piped to syslog. Note that if StandardOutput=syslog is set StandardError=syslog is implied unless StandardError= is explicitly configured too. That basically means that you never need to use StandardError= in normal cases. Just use StandardOutput=syslog. A full description of these options can be found here: http://0pointer.de/public/systemd-man/systemd.exec.html

Example Unit file

This is an example systemd unit .service file for ABRT:

[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

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 the D-Bus activation request is forwarded from the D-Bus system bus to systemd so that you end up with only a single instance of the service, even if a service is triggered by both boot-up and activation. If historically your D-Bus service was not bus-activated but started via a SysV init script, it should be updated to use bus activation. This may be implemented by dropping a D-Bus .service file in /usr/share/dbus/system-services/ and use the SystemdService= directive therein to redirect the activation to systemd.

.service
systemd and D-Bus both use the .service file suffix but they are different things. In a systemd driven init process, the D-Bus .service file will often refer to the systemd .service file for the same program with the SystemdService directive.

Here's an example for a D-Bus bus-activable service. The ConsoleKit bus activation file /usr/share/dbus-1/system-services/org.freedesktop.ConsoleKit.service:

[D-BUS Service]
Name=org.freedesktop.ConsoleKit
Exec=/usr/sbin/console-kit-daemon --no-daemon
User=root
SystemdService=console-kit-daemon.service

And the matching systemd unit file /lib/systemd/system/console-kit-daemon.service:

[Unit]
Description=Console Manager
After=syslog.target

[Service]
Type=dbus
BusName=org.freedesktop.ConsoleKit
ExecStart=/usr/sbin/console-kit-daemon --no-daemon

As you can see SystemdService= is used in the D-Bus activation file to bind the systemd service to the D-Bus service.

Traditionally, bus activated D-Bus services could not be disabled without uninstalling them entirely. systemd allows you to disable services by making D-Bus invoke an alias systemd service name (that can be created or removed to enable/disable activation) as an intermediary for the real service.

You can easily implement disabling by directing the D-Bus service to an alias name of the real service file (in the filesystem this shows up as a symlink placed in /etc/systemd/system to the real service file). This alias is then controlled via "systemctl enable" and "systemctl disable". It is a good idea (though technically not necessary) to name this alias name after the D-Bus bus name of the service, prefixed with "dbus-". Example for Avahi, a service that the admin might need to disable: set SystemdService=dbus-org.freedesktop.Avahi.service instead of SystemdService=avahi-daemon.service in the D-Bus activation file, and then make dbus-org.freedesktop.Avahi.service an optional alias of avahi-daemon.service that can be controlled via the Alias= directive in the [Install] section of the systemd service file. This directive is then read by "systemctl enable" and "systemctl disable" to create resp. remove a symlink to make the service available resp. unavailable under this additional name. A full example for the Avahi case:

Here is the D-Bus .service file for Avahi (/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

Here is the Avahi systemd unit .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".

Note that the creation/removal of the alias symlinks should be done with "systemctl enable" and "systemctl disable" only. You should not create these symlinks manually.

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.

See the D-Bus documentation for more information about bus activation: http://dbus.freedesktop.org/doc/dbus-specification.html#message-bus-starting-services

Packaging

Filesystem locations

Packages with systemd unit files must put them into %{_unitdir}. %{_unitdir} evaluates to /lib/systemd/system on all Fedora systems (F-15+). Unit files are architecture independent (hence, not %{_lib}) and needed early in the boot process.

%files section

Systemd unit .service files must not be marked as %config files. If the user wants to reconfigure a .service file he should copy the .service file from /lib/systemd/system to /etc/systemd/system and edit it there. Unit files in /etc/systemd/system override those in /lib/systemd/system if they carry the same name.

Unit files in spec file scriptlets

Add to scriptletsnippets instead and leave a link here. --abadger1999 15:49, 8 December 2010 (UTC)
Since we turned over which services may autostart to fesco, we need to revise the next paragraph. The current fesco plans allow a great many more things to autostart than previously. It's probably more accurate to say that most services can be on by default if the configuration files they ship with follows certain rules and then link to the fesco document. --abadger1999 02:12, 24 February 2011 (UTC)

Most systemd services must be disabled by default, especially if they listen on the network. On upgrade, a package may only restart a service if it is running; it may not start it if it is off. Also, the service may not enable itself if it is currently disabled.

These are the recommended scriptlets to conform to these requirements

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

%post
if [ $1 -eq 1 ] ; then 
    # Initial installation 
    /bin/systemctl daemon-reload >/dev/null 2>&1 || :
elif [ $1 -ge 2 ] ; then
    # Package upgrade
    if [ -f %{_initddir}/httpd ] ; then
        # Purposefully ignore runlevels 0 and 6 (there aren't any packages in Fedora that implement
        # init scripts that start on shutdown) and  2 and 4 (for custom runlevels, the user needs
        # implement a custom target of their own).
        ### FIXME: Current chkconfig breaks the check.  Talk with notting about what fix to
        # implement: https://bugzilla.redhat.com/show_bug.cgi?id=616857
        if chkconfig --level 1 httpd ; then
            ln -sf /lib/systemd/system/httpd.service /etc/systemd/system/rescue.target.wants/ 2>&1 >/dev/null
        multiuser=0
        if chkconfig --level 3 httpd; then
            ln -sf /lib/systemd/system/httpd.service /etc/systemd/system/multi-user.target.wants/ 2>&1 >/dev/null
            multiuser=1
        fi
        if chkconfig --level 5 httpd;
            # If it's already in multi-user, it will be inherited automatically
            if [ $multiuser -eq 0 ] ; then
                ln -sf /lib/systemd/system/httpd.service /etc/systemd/system/graphical.target.wants/ 2>&1 >/dev/null
            fi
        else
            if [ $multiuser -eq 1 ] ; then
                ### FIXME: Need to decide how to deal with this case.
                # Option 1) Do not support this as systemd tries to have graphical be a strict
                # superset of multi-user.  Release note this and what it means in this case.
                # Option 2) Try to mask out the service in graphical with something like this:
                # ln -sf /dev/null /etc/systemd/system/graphical.target.wants/ 2>&1 >/dev/null
            fi
        fi

        # Previous SysVinit packages won't have a %postun that will invoke this
        /bin/systemctl daemon-reload >/dev/null 2>&1 || :
        ### FIXME: It seems we need a systemctl try-restart in here for the same reasons?
        # Or maybe only if the service was configured to run in the "runlevel" that we're currently in?
        # /bin/systemctl try-restart httpd.service > /dev/null 2>&1 || :

        # Unregister with the SystemV init system
        /sbin/chkconfig --del httpd
    fi
fi

%preun
if [ $1 -eq 0 ] ; then
    # Package removal, not upgrade
    /bin/systemctl --no-reload 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

Note that /bin/systemctl daemon-reload will automatically detect new systemd unit .service files placed into %{_unitdir}. There is no equivalent to 'chkconfig --add <service>', because it is unnecessary.

If your service should be enabled by default (which is the exception, see below) then use the following %post scriptlet instead of the one shown above:

%post
if [ $1 -eq 1 ] ; then 
    # Initial installation 
    /bin/systemctl enable httpd.service >/dev/null 2>&1 || :
elif [ $1 -ge 2 ] ; then
    # Package upgrade
    if [ -f %{_initddir}/httpd ] ; then
        ### FIXME: we'll want a loop that makes these changes for all sysv runlevels
        # We also need to map the runlevels to the proper systemd targets:
        # (0 => poweroff, 1 => rescue, (2, 3, 4) => multi-user, 5 => graphical, 6=> reboot)
        # If we're going to macroize this, then we might as well also write code to not duplicate
        # in graphical things that are in multi-user (since graphical includes multi-user)
        ### FIXME: Current chkconfig breaks the check.  Talk with notting about what fix to
        # implement: https://bugzilla.redhat.com/show_bug.cgi?id=616857
        if /sbin/chkconfig --level 3 httpd ; then
            ln -sf ../httpd.service /etc/systemd/system/multi-user.target.wants/ 2>&1 >/dev/null
        fi
        # Previous SysVinit packages won't have a %postun that will invoke this
        /bin/systemctl daemon-reload >/dev/null 2>&1 || :
        ### FIXME: Do we need a systemctl try-restart in here for the same reasons?
        # Or maybe only if the service was configured to run in the "runlevel" that we're currently in?
        # /bin/systemctl try-restart httpd.service > /dev/null 2>&1 || :

        # Unregister with the SystemV init system
        /sbin/chkconfig --del httpd
    fi
fi

Why don't we....

  • Enable most services by default?

Some core services must be enabled by default for a functional system, but the default for most network-listening scripts is off. This is done for better security. We have multiple tools that can enable services, including GUIs.

  • Start the service after installation?

Installations can be in changeroots, in an installer context, or in other situations where you don't want the services autostarted.