Manage your session with systemd
A lot of distribution are making their way to "systemd" as an alternative to System V.
One of the advantages of systemd is that it offers a service management for users, and it is this system that I plan to present here to handle our session. Systemd is still under active development, including the user part. This guide reflects the big change that happened in version 206 of systemd on which most current tutorial are based. This setup has been tested on version 212.
When a user session starts (either remotely via ssh or local), an instance of
systemd --user starts for that user. This instance has the role to
start user services, in a similar way to process 1 but dedicated to the user.
Note that by default these services disappear with the user session
Before version 206 (as far as I can tell), it was the user's responsibility to start (or not) this instance. Now she cannot anymore be started by the user and it must be run by root.
It is started automatically at login provided that the
pam_systemd module is active in
pam for the requested
session. For that, we add a line of the form
-session optional pam_systemd.so
/etc/pam.d/system-loginalready contains this line and concerns any kind of connection (local and remote).
Systemd separates the services and session between slice/scope/service,
making heavy use of the cgroups feature (it is a way to
group a collection of processes and their possible children without possible
escape). A typical system running systemd then looks like
that (you can obtain it with
- Program 1
- Program 2
- systemd --user
- Program 1
- Program 2
- systemd --user
As soon as all the
session-c*.scope of a user are exited, the
user@.service will also be stopped, including all the
sub-services. This is a point to remember! For instance, by default, using
"timer" unit files of systemd as a replacement for cron won't work for the user
if he's not there!
We'll see later how to deal with that.
User services are handled in exactly the same way as system services, except
that the units are sought in different folders (see
systemd --user starts, unit
default.target is started and has the role to start other necessary
systemctl --user and
Services managed by systemd (whether system or user) are managed primarily
through the command
--user), which strongly
depends on a correct dbus installation. First thing to do is thus to ensure that
dbus daemon runs, both at system level (
system bus) and user level (
bus). For the system part, we simply need to add a dependency to
user@.service. We will also normalize the path to the
session bus socket by giving the appropriate environment
DBUS_SESSION_BUS_ADDRESS. Since user@.service is a parent
of all user services, we can use it to transmit the variable
Line 2 of the file indicates that we require that the system
dbus is started, while line 5 concerns the environment variable for
the user's session bus. Don't confuse these services, they correspond to two
In the sequence, we will define the different services that we will need.
They can be defined or activated globally or by user's choice. This depends both
on the location of the unit file (
$HOME/.config/systemd/user) and on the way it is activated (with
--user). Here I will define the services
and sockets associated to the session dbus globally, but I will let the user
chose whether he turns them on or note
Description=D-Bus Message Bus
ExecStart=/usr/bin/dbus-daemon --session --address=systemd: --nofork --nopidfile --systemd-activation
ExecReload=/usr/bin/dbus-send --print-reply --session --type=method_call --dest=org.freedesktop.DBus / org.freedesktop.DBus.ReloadConfig
Description=D-Bus Message Bus Socket
default.targetis the default activated target by systemd).
From now on, the hardest part is done. We will create a unit
my_services.target alias of
systemctl, but with an argument
--user. The rest of the article consists essentially in tricks and interesting specific cases.
After doesn't have the same
effect for a
service unit and a
target unit. A service will be started only once all the
unit quoted in
After are ready (for the definition of
man systemd.service, bloc
Type=), while for a target it is assumed that the services are
part of it (and can then be started concurrently to other
User services that last
By default, services associated to an user (everything that is below
email@example.com in the tree above) are stopped when the user
disconnects. This includes timers, that we would like to use as a replacement
We can bypass that with this command:
loginctl enable-linger user
user@.servicewill be started as soon as boot for the corresponding user, even when he is not there.
I won't go any further into details on how to define the equivelnt of
crontabs for systemd, this kind of feature is already well explained in manual
pages as well as systemd-dedicated websites (see
systemd.timer). The trick given here deals with the issue of having
crontabs equivalent that last for an user even when he's not there.
Usually crontabs send emails when there is output. As far as I know there is no such feature in systemd services and everything is logged in the journal.
Environment variables can become a true headache when we use services. For instance, how can we transmit variables necessary to the services like GPG_AGENT_INFO or SSH_AUTH_SOCK or even DISPLAY. One way to do it is to specify in the unit file a directive of the form
/etc/systemd/system/user@.service.d/environment.conf. We also lose the usual
dynamismof configuration scripts.
Here, we will make use of the command
import-environment to solve our problem. This command permits to import
environment variables that will be included in any subsequent service.
We start by creating a service setenv.service that will start a script loading
the environment. As far as I am concerned, I have to different environment
depending on whether I am in command line or graphical. My service file then
looks like that:
Wants=dbus.service gpg-agent.service ssh-agent.service
After=dbus.service gpg-agent.service ssh-agent.service
firstname.lastname@example.org on my needs. Here I also defined an environment variable inside the unit. The only reason for that is that the part "%t" (/run/user/1000/) is easier to find in a unit file...
Here is an example script to define my environment:
if [ "x$1" = "xcommon" ]; then
systemctl --user import-environment
systemctl --user unset-environment PWD OLDPWD SHLVL _ MANAGERPID
elif [ "x$1" = "xgraphical" ]; then
systemctl --user import-environment XDG_CONFIG_HOME SAL_USE_VCLPLUGIN XDG_MENU_PREFIX DISPLAY
systemctl --user unset-environment VARIABLE1 VARIABLE2
Services that are worth starting no matter if we are in a graphical or
command line remote session are
gpg-agent. However we need to think to transmit environment
variables to the process that are interested (see Above section for more information).
The service envoy should also be considered to manage these agents. I prefer to separate both agent, which is not possible with envoy.
ssh-agent, we can directly indicate in the command where we
want to put the socket, which makes things easier:
ExecStart=/usr/bin/ssh-agent -d -a %t/ssh_auth_sock
gpg-agent, we can only tell him to save the informations in
a file: the socket won't end in a predictable path.
ExecStart=/usr/bin/gpg-agent --daemon --write-env-file %h/.gpg-agent-info
$HOME/.gpg-agent-infoto access the agent socket.
The fact that we don't have a standard place can be a problem if the agent is restarted: most probably, the socket won't be in the same place, and programs that already started won't be able to contact the agent.
Using systemd to manage your graphical session
It can be tempting to use systemd to manage also your graphical session: defining your window manager as a service and be able to switch to another window manager at will without being obliged to restart your session. This can lead to some problems as we will see later.
Graphical session as a service file
The simplest to do there is to define a unit file
graphical@.target if you want to
define several window managers) and activate it in the
script (or whichever is executed when your session starts).
It can be necessary to correctly define the environment variables at that time, in which case each uni will need the service
email@example.com activated as a prerequisite (as indicated
before, it it not sufficient to put it int the
After directive in
As pointed initialy, it is tempting to define your graphical session as a
First, the command systemct returns immediatly. Thus, a
file containing only something like that:
systemctl --user start graphical.target
export DBUS_SESSION_BUS_ADDRESSis necessary to permit dialog with
systemd). There exists programs such as systemd-wait that do exactly what we want to: wait for a service to end.
The second problem can be more annoying, and comes from the difference between the user session and services. If we don't take care, we will end up with something like that:
- systemd-wait -q --user graphique.service failed inactive
- systemd --user
systemd-waitas we put it in the
.xsessionfile. This can have consequences for instance in right management, see next section about polkit.
As specified on Archlinux website,
slim is not compatible with
systemd --user (at least it wasn't at the time when this article
was writtent). What does that mean, and what can we do about it?
First of all, let's have a closer look to how
slim is a connexion manager, that is, a sort of replacement of
login in terminal-like tty's, but with a graphical interface. This has several
implications: it has to deal with the problem of starting an X server, and has
to launch the user session after his connexion. But he also have to take back
control once the user leaves.
As the user logs in, slim start (via pam configuration) an instance of
systemd --user, as well as a
(for instance the .xsession file). It does not have to start an X server, since
it is already there.
However when the user logs out however, it doesn't restart everything and reuses the state in which it was before. In the point of view of systemd, it then ends in the "scope" of the preceding user (even if it is still a root process):
Before log out:
After log out:
This particular behaviour is particularly annoying for the next users. In
Archlinux, slim has been patched recently to avoid this (since
1.3.6-4, 2014-04-21), by forcing slim to quit when a session ends
-nodaemon), and adding a
to the systemd unit file.
Restart directive is
on-failure, but the patch makes slim quit with an error on
Another solution which was the one I used until the patch, if you are not
using Archlinux and don't want to patch slim, is to put a line in
/etc/slim.conf of the form
sessionstop_cmd systemctl restart slim.service
Polkit and administrative rights
Systemd distinguishes between two kind of users, that is "active" and "inactive" users. For instance, if two users are both connected on the same computer (physically), then only one of them is actually "active" (unless you have several monitors). It also makes a difference whether the user is "local" or "remote". This information about a session can be obtained with the command
loginctl show-session "session"
"session"is the id of the session (you can list session ids with
In particular, and that's where it is important, a service is always inactive.
Polkit is a service that permits to give (temporarily or not) more rights to a user. For instance if you can turn off the computer without typing sudo or entering a password, then most probably polkit plays a role here. These rights are aranged depending on the state of the user, whether he is active or inactive or "any".
Polkit and systemd don't share the same active
notions. For polkit, an active/inactive user is local and
active/inactive for systemd. Any other user is in
any (in particular remote users). This rule is hard-coded
check_authorization_sync of file
src/polkitbackend/polkitbackendinteractiveauthority.c in source
code of polkit:
implicit_authorization = polkit_action_description_get_implicit_active (action_desc);
implicit_authorization = polkit_action_description_get_implicit_inactive (action_desc);
implicit_authorization = polkit_action_description_get_implicit_any (action_desc);
I won't go into much more details about polkit here, but we can already see
xterm started as a service, and one started directly (for
instance in your
.xsession) won't have the same right for polkit!
We thus have to be careful about that when we want to use
--user as a session manager, because it will then be unable to shutdown
the computer without using