openSUSE:Systemd packaging guidelines

Jump to: navigation, search
This article describes packaging guidelines for systemd services.

RPM macros

Starting with openSUSE 12.1, several RPM macros must be used to package systemd services files.

(Build) Requirements

You should add :

%if 0%{?suse_version} >= 1210
%bcond_without systemd
%else
%bcond_with systemd
%endif
[...]
%if %{with systemd}
BuildRequires: systemd-rpm-macros
%{?systemd_requires}
%endif

You need both the build requirement and the %{?systemd_requires}.

Systemd unit files

Systemd unit files like service, socket, and path files should always be installed in %_unitdir (i.e. /usr/lib/systemd/system) and never in /etc/systemd/system (so they can be overridden by users without conflicting with packaging). An exception are user unit files which must be installed in %{_prefix}/lib/systemd/user (i.e. /usr/lib/systemd/user).

This command is an example of a legal install line where %{S:3} is a reference e.g. to your service file:

install -D -m 644 %{S:3} %{buildroot}%{_unitdir}/foo.service

Systemd unit files should be considered not to be edited by system administrators. For this the directory /etc/systemd/system exists. A service file named foo.service might be overriden by the system administrator creating a file /etc/systemd/system/foo.service.d/my.conf.

Instantiated Service files

If an instantiated service (compare with manual page systemd.unit(5)) is needed then the service file packaged with the RPM should be installed to foo@.service. Thus this command is a legal install line:

install -D -m 644 %{S:3} %{buildroot}%{_unitdir}/foo@.service

The user should instantiate the service via a command like:

sytemctl enable autossh@${my-instance}.service

and override the service file by the system administrator creating a file /etc/systemd/system/foo@${my-instance}.service.d/my.conf

Thus each instance must be individually instantiated and overridden by the system administrator or the appropriate systemd generator (compare with manual page systemd.generator(7)) as specified in http://www.freedesktop.org/wiki/Software/systemd/Generators .

Register systemd unit files in install scripts

Add the following macros to your scripts:

if foo.service, bar.socket, and elsewhere.path are three systemd units you are installing :

%pre
%service_add_pre foo.service bar.socket elsewhere.path

%post
%service_add_post foo.service bar.socket elsewhere.path

%preun
%service_del_preun foo.service bar.socket elsewhere.path

%postun
%service_del_postun foo.service bar.socket elsewhere.path

Compare with the manual pages systemd.service(5), systemd.socket(5), and systemd.path(5) as well as the overview systemd.exec(5) and systemd.unit(5).

If your package is also providing sysv initscripts, those macros will handle sysv initscripts migration transparently (as long as initscripts and systemd services have similar names).

If your package is supposed to build for openSUSE older than 12.1, you should use condition test for those macros.

Since %suse_version > 1320 or %sle_version > 120100 %service_del_preun, %service_del_postun and %systemd_preun know the arguments -f and -n:

  1. -f forces a restart on update when called with %service_del_postun or stop on removal when called from %service_del_preunwhile
  2. -n will prevent active services from being touched on update (%service_del_postun) or uninstall (%service_del_preun).

On older versions or when nothing is specified, the default is that

  1. if the service should NOT be restarted when being updated, you should append in the %postun section, just before %service_del_postun call:
export DISABLE_RESTART_ON_UPDATE=yes
  1. if the service should NOT be stopped when being uninstalled, you should append in the %preun, just before the %service_del_preun call:
export DISABLE_STOP_ON_REMOVAL=yes

Enabling systemd unit files

By default, services are not enabled when package is installed. If you want your service to be enabled by default, you should do a submit request (with OBS) on systemd-presets-branding-openSUSE package, modifying default-openSUSE.preset file by adding e.g.:

enable your_service_name.service

Such an automatically enabled service shouldn't require any manual configuration before it can start properly.

Systemd timers

Systemd timer is a systemd feature that helps to define time activated services or events. It's an alternative to cron.

Since 2018-11-09, a process of migration from cron to systemd timers (fate#323635) is in progress. See boo#1115430 (openSUSE) and bsc#1115399 (SLE, internal) trackerbugs that track all packages that should be migrated for openSUSE and SLE.

Timer and service units

Timer unit is a unit configuration file that ends with .timer suffix and it activates the matching service file (foo.timer activates foo.service). Systemd automatically pair timer and service with the same name but it can be also specified via Unit=.

Timers need to include [Timer] section that defines when the timer is activated. For the whole description of the time format used for timer configuration please see systemd.timer(5). For time and date specification see systemd.time(7).

Timer units can be either provided by upstream or a packager needs to write it himself/herself (e.g. during migration from cron to systemd timers).

Examples

Example logrotate.timer

[Unit]
Description=Daily rotation of log files
Documentation=man:logrotate(8) man:logrotate.conf(5)

[Timer]
OnCalendar=daily
AccuracySec=12h
Persistent=true

[Install]
WantedBy=timers.target

Example logrotate.service

[Unit]
Description=Rotate log files
Documentation=man:logrotate(8) man:logrotate.conf(5)
ConditionACPower=true

[Service]
Type=oneshot
ExecStart=/usr/sbin/logrotate /etc/logrotate.conf

Packaging of the systemd timers

Packaging of the systemd timers is very similar to the packaging of any other systemd unit.

# Pack service and timer files
Source5:      %{name}.service
Source6:      %{name}.timer

# Standard systemd requirements
BuildRequires:  pkgconfig(systemd)
%{?systemd_requires}

# Install service and timer file
%install
install -D -m 0644 %{SOURCE:5} %{buildroot}%{_unitdir}/%{name}.service
install -D -m 0644 %{SOURCE:6} %{buildroot}%{_unitdir}/%{name}.timer

# Use systemd macros in %pre, %post, %preun, %postun sections
%pre
%service_add_pre %{name}.timer

%post
%service_add_post %{name}.timer

%preun
%service_del_preun %{name}.timer

%postun
%service_del_postun %{name}.timer

# List them in the %files section
%files
%{_unitdir}/%{name}.service
%{_unitdir}/%{name}.timer

Enabling and starting

Please note that by default, services are not enabled when the package is installed. You probably want your timer to be enabled by default so you should create a submit request on systemd-presets-branding-openSUSE package, modifying default-openSUSE.preset file by adding enable your_timer_name.timer.

MAILTO

Systemd currently doesn't support equivalent to crons's MAILTO function for sending emails on job failure. See Arch Linux wiki for a workaround.

Creating files and subdirectories in /var/run and /run

Since openSUSE 12.2, /var/run (which is either bind mounted or symlinked to /run) are mounted as tmpfs, so packages should not contain any directories (or files) under /var/run (or /run), since they will disappear at the next reboot.

If new files / directories need to be created there, you should package a tmpfiles.d file (see man tmpfiles.d for the syntax), installed in %_tmpfilesdir(/usr/lib/tmpfiles.d/). One example of such a file:

# create a directory with permissions 0770 owned by user foo and group bar
d /var/run/my_new_directory 0770 foo bar

You should install them in %install section like this:

install -d -m 0755 %{buildroot}/usr/lib/tmpfiles.d/
install -m 0644 %{SOURCE4} %{buildroot}/usr/lib/tmpfiles.d/%{name}.conf

If you expect this file or directory to be available after package installation (and before reboot), remember to add in your package %post section:

%tmpfiles_create %_tmpfilesdir/<file_name>

Backward compatibility

rc symlink

It's still possible to keep the /usr/sbin/rcname symlink. In order to make it work with systemd, link to /usr/sbin/service for each unit (service and target types are supported):

ln -s /usr/sbin/service %{buildroot}%{_sbindir}/rcname

Extra actions

Init scripts sometimes implemented additional actions besides the usual start/stop/status etc. For systemd service files that extra feature doesn't exist. It's still convenient to have for some services though. Therefore /usr/sbin/service implements "legacy actions" like Fedora.

Suppose your previous init script "foo" had an action "frob". Now the service file is called "foo.service" and you want to still support the "frob" action. In Factory since 2014-03-11 (post 13.1) you can create a script

 /usr/lib/initscripts/legacy-actions/foo/frob

that implements the feature you need. Obviously the action is only available when calling the service via /usr/sbin/service, ie

 # service foo frob

or

 # rcfoo frob