From Mageia wiki
Jump to: navigation, search

System Services

Certain classes of packages require specific attention to detail. One of these classes is "system services". That is, daemons that run at a system level.

While this is just an application like any other, there are certain conventions that are typically followed and as such there are various utilities and helpers in place to make packaging easier.

Service Management

Mageia uses systemd to manage services, however this is mostly abstracted away at a packaging level.

If you have a systemd service called called e.g. myservice.service, then it should be installed in /usr/lib/systemd/system/myservice.service. This can be referred to in file lists as %{_unitdir}/myservice.service.

In order to deal with automatically enabling the service on package installation, restarting it on upgrade and disabling it on removal we have a few helper macros:

 Requires(post):   rpm-helper >= %{rpmhelper_required_version}
 Requires(preun):  rpm-helper >= %{rpmhelper_required_version}
 
 %post
 %_post_service myservice
 
 %preun
 %_preun_service myservice


This will automatically expand to do the necessary commands to deal the the vast majority of service management.

NB: Under most circumstances the "myservice" part will be the same as the package name. In this case you can use e.g. "%_post_service %{name}" which is generally easier for copy+paste.

NB2: The version of rpm-helper is specified here as forward planning for future distribution upgrades. When these helper scripts change, they may need to do additional things to ensure a working system. When upgrading from an older version of Mageia, it is essential to install these updated scripts early in the process. By keeping the version defined in a macro, we can increase it easily and simply rebuild any packages that use it.

NB3: "myservice" will only be enabled automatically via rpm-helper after installation if the .service file has an [Install] section with a proper target, e.g. WantedBy=multi-user.target otherwise it will be seen as a "static" type and needs to be manually enabled by the user

Creating Users

Many daemons and system services need to run as their own user for security purposes. In order to ease the creation of users during package installation (and to remove the users when the package is removed), there are various helper macros available. Assuming your package is called "myservice", you have a systemd unit called myservice.service and that service is configured to run as the user "myservice", then your spec file would look something like:

 Requires(pre):    rpm-helper >= %{rpmhelper_required_version}
 Requires(post):   rpm-helper >= %{rpmhelper_required_version}
 Requires(preun):  rpm-helper >= %{rpmhelper_required_version}
 Requires(postun): rpm-helper >= %{rpmhelper_required_version}
 %pre
 %_pre_useradd %{name} /dev/null /bin/false
 
 %post
 %_post_service %{name}
 
 %preun
 %_preun_service %{name}
 
 %postun
 %_postun_userdel %{name}

Transient folders (/run, /var/run, /var/lock)

Starting from Mageia 3, /var/run and /var/lock are just symlinks to /run and /run/lock respectively. In addition, /run is a tmpfs which means you cannot package any files in these folders.

Many services, however, need a place to store "pid files" (small files containing the process id of the running service) or sockets used for communication with other applications. These files would traditionally be stored in /run (in the past it would have been /var/run). If the service runs with root permissions this is typically not a problem, but as mentioned above services are often configured to run as their own user and are thus less privileged and are unable to create files and folders in the /run tree.

One way around this is to create a systemd-tmpfiles config snippet and ship it with your package. tmpfiles is a part of systemd and simply allows very simple rules regarding temporary files to be handled.

Say your service requires the folder /run/myservice/ to be owned by a user myservice, your spec file would look something like this:

 Source0: %{name}-%{version}.tar.xz
 Source1: %{name}-tmpfiles.conf
 
 Requires(pre):    rpm-helper >= %{rpmhelper_required_version}
 Requires(post):   rpm-helper >= %{rpmhelper_required_version}
 Requires(post):   systemd >= %{systemd_required_version}
 Requires(preun):  rpm-helper >= %{rpmhelper_required_version}
 Requires(postun): rpm-helper >= %{rpmhelper_required_version}
 
 %install
 …
 %{__install} -D -p -m 0644 %{SOURCE1} %{buildroot}%{_tmpfilesdir}/%{name}.conf
 
 %pre
 %_pre_useradd %{name} /dev/null /bin/false
 
 %post
 %_tmpfilescreate %{name}
 %_post_service %{name}
 
 %preun
 %_preun_service %{name}
 
 %postun
 %_postun_userdel %{name}
 
 %files
 …
 %{_tmpfilesdir}/%{name}.conf

In the above a small file has been added to the SOURCES folder. myservice-tmpfiles.conf contains:

 d /run/myservice 0755 myservice myservice

This file specifies the location, mode, user and group that for the file or (in this case) folder in question. It is important to only call the %_tmpfilescreate macro after creating the user (%post runs after %pre so this is typically not a problem), and before any service management itself (i.e. before any call to %_post_service). This is because the service management code will likely require the folder to start or restart the service. For more info on the format of tmpfiles snippets, see tmpfiles.d(5).

NB: The %_tmpfilescreate process is run explicitly here (as opposed to via a generic filetrigger) because if a package is upgraded (or started automatically on first install depending on how %_post_service operates) then the files/folders defined by the tmpfiles config will likely be required at that point in time, rather than at the end of the installation transaction.