openSUSE:Systemd packaging guidelines
RPM macros
Starting with openSUSE 12.1, the systemd-rpm-macros
package provides several RPM macros to package unit files, tmpfiles and sysusers configuration.
As of 2020-11-20, systemd-rpm-macros
is required by rpm-build
in both Leap and Tumbleweed, so you do not need to request it from a specfile; nonetheless, feel free to be explicit about this dependency, in case rpm-build
drops it in a future version.
BuildRequires: systemd-rpm-macros
Build requirements
If you need to link some compiled sources against systemd libraries, you should use this pkgconfig
virtual provider:
BuildRequires: pkgconfig(libsystemd)
This will allow the build system to pick systemd-mini-devel as needed.
Similarly, if you need to use systemd programs during build, do use
BuildRequires: pkgconfig(systemd)
to allow the use of systemd-mini as needed.
Runtime requirements
The systemd rpm macros such as %service_add_post
that expand to shell code and which invoke systemctl and other related utilities as part of scriptlets are able to detect the absence of systemd and, in fact, work on installations that are completely free of systemd. As such, it is not necessary and even discouraged to use Requires(post): systemd
or the related %systemd_requires
.
If, and only if, the programs that ship as part of the package have systemd commands as a necessity and could otherwise not function may you add the appropriate Requires.
Unit files
Systemd unit files, including but not limited to service, socket, timer 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 an example reference to your service file:
install -D -m 644 %{SOURCE3} %{buildroot}%{_unitdir}/foo.service
Systemd unit files ought not to be edited by system administrators. As a result, the files are not to be marked as %config
in the %files
section of the specfile. Instead, modifications are to be placed by the admin in the /etc/systemd/system
directory, either as a full replacement file such as /etc/systemd/system/foo.service
, or by an extending fragment such as /etc/systemd/system/foo.service.d/my.conf
.
Registering unit files in install scripts
To tell systemd about changed unit files, systemctl daemon-reload
should be invoked. To do so from a specfile, one can use various %service_*
macros provided by systemd-rpm-macros. These macros will handle sysv initscripts migration transparently (as long as initscripts and systemd services have similar names).
%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 supposed to build for openSUSE older than 12.1, you should use condition tests for those macros.
During package update, %service_del_postun
restarts units. If units are not to be restarted, %service_del_postun_without_restart
should be used instead.
Enabling units
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-common-SUSE
or 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.
Recent openSUSE and SUSE products contain two important preset files in two preset packages. systemd-presets-common-SUSE
with 95-default-SUSE.preset
(or 90-default-SUSE.preset
up to Leap 15.0) and systemd-presets-branding-openSUSE
(or generally systemd-presets-branding-brand
) with 90-default-openSUSE.preset
(or 90-default-brand.preset
).
Both are technically equivalent. Depending on a purpose of your service, pick a proper one:
systemd-presets-common-SUSE
is intended for services that are mandatory for a proper function of the system or the package. Placing preset into this file means: All systems should use this value as a default. This package is a common base for both open and enterprise products and it is part of all base installations.
systemd-presets-branding-openSUSE
is intended for vendor customization. Placing a preset here means: It is a wise default for openSUSE, but not enabling this service in other product makes perfect sense. The package structure makes it possible to create a custom brand with a custom systemd-presets-branding-brand
as a part of custom branding. Generic rules for branding packages are explained in openSUSE:Packaging_Branding.
Note: Third party packages can install a custom preset file, but it is not allowed for packages in the openSUSE distribution.
Preset change and upgrade
When you change preset of a service, upgrade scripts calls one-time customization reset. It means, that if you add enable, then it will force-enable the service on all systems during the upgrade. And vice versa, if you delete an enable or add disable, it will force-disable the service during the upgrade.
Installation order
Consider an installed system where systemd is running, and where you plan on installing a program package P2 whose unit files make use of a feature only available in a newer systemd version. An example of such is perhaps the RestrictSUIDSGID
unit directive, which became available in systemd-242.
If P2 is installed on its own, it will have to make do with the available features (which usually means systemd ignores unrecognized lines in unit files). This is not necessarily a bad thing. If however, a newer systemd package is planned to be installed in the same transaction as P2, it can be beneficial to have systemd installed first, so that the features have become available by the time P2 is installed and restarted.
To that end, one can add %{?systemd_ordering}
to the corresponding %package
block (could as well be the implied main subpackage). For example,
Name: foo Version: 0 Release: 0 Requires: bash %{?systemd_ordering} … %post %service_add_post foo.service …
Of course it is still possible to split the transaction and install just P2 on its own, then systemd on its own. It is not %systemd_ordering
's intent to cover this, also because, if there was a hard dependency on anything, you would need to know the exact version number.
(For historians, this was the Github pull request.)
Instantiated services
If an instantiated service (compare with manual page systemd.unit(5)) is needed, the service file in the RPM should be called foo@.service
. Therefore, this command is a legal install line:
install -D -m 644 %{SOURCE3} %{buildroot}%{_unitdir}/foo@.service
The instance template should at present not be passed to %service_*
, as these macros do not deal with them. Likewise, instances should in general not be enabled (from .spec files), but only through presets (see above).
After installation of an RPM package, the administrator can instantiate a service via a command like:
sytemctl enable autossh@someinstance.service
The admin can override the service just like any other unit, that is, by creating a file /etc/systemd/system/foo@someinstance.service.d/my.conf
, or using systemctl edit foo@someinstance
.
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 .
Shipping unit file drop-ins
Users are given the possibility to alter configuration settings for a unit shipped by a package provided by SUSE, without having to modify the unit file itself. This is normally done by the mean of "drop-in" files. A complete description of drop-in files can be found in systemd.unit(5) man page.
To avoid any conflicts that may happen between the drop-in files shipped by SUSE and users' ones, it is recommended to prefix all drop-in filenames with a two-digit number and a dash, to simplify the ordering of the files. For example "29-override.conf".
Furthermore the following ranges of the two-digit number are reserved as follow:
- [0-19] is reserved for systemd upstream
- [20-29] is reserved for systemd shipped by SUSE
- [30-39] is reserved for SUSE packages (other than systemd)
- [40-49] is reserved for 3rd party packages
- 50 is reserved for unit drop-in files created with `systemctl set-property`
Hence by using a two-digit number above 50, users will be assured that none of the drop-in files shipped by SUSE will override users own drop-in files in the future.
Sandboxing
Many services run with root privileges and as such open the system up to local and sometimes remote attacks. Some of this can be mitigated by removing some privileges from the service via setting in the service file. Consider applying the sandbox setting per Systemd Hardening Effort
Timers
Timer units are a systemd feature that helps define time activated services or events. It is an alternative to cron jobs.
Timers are a unit type whose configuration filename ends in .timer
, and this timer unit activates the matching service unit (foo.timer
activates foo.service
). Systemd automatically pairs timer and services with the same name, and a different relationship can be specified by an extra Unit=
line in the timer.
Timers need to include a [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 specifications, see systemd.time(7).
Timer units can be either provided by upstream or by a packager who writes them for the distribution (e.g. during migration from a cron job to a systemd timer).
Like other units, timers should be passed as argument to the %service_*
call, and only be enabled through presets.
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
MAILTO
Systemd currently does not support the equivalent of cron'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 ... install -D -m 0644 %{SOURCE4} %{buildroot}%{_tmpfilesdir}/%{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:
%post %tmpfiles_create %_tmpfilesdir/<file_name>
And don't forget to add it to the %files section, prefixed by the %ghost macro:
%files ... %ghost %{_localstatedir}/run/my_new_directory
This will prevent RPM Lint from warning about tmpfile-not-in-filelist. Users will be able to easily query who created the directory, it will get uninstalled on package removal and other RPM Lint checks will see it.
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):
%install ... mkdir -p %{buildroot}%{_sbindir} ln -s /usr/sbin/service %{buildroot}%{_sbindir}/rcname %files ... %{_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