Writing a service that depends on Xorg
I'm trying to write a user level service for redshift
, and it needs to wait until Xorg is up and running. My current service file looks like this:
[Unit]
Description=Redshift
After=graphical.target
[Service]
Environment=DISPLAY=:0
ExecStart=/bin/redshift -l 28:-13 -t 5300:3300 -b 0.80:0.91 -m randr
Restart=always
[Install]
WantedBy=default.target
However, it seems that it attempts to start before Xorg is up, and I have to manually start the service afterwards. I guess I'm using the wrong After=
target. Any hints?
Solution 1:
I've been researching this and grawity's answer seems out of date. You can now setup user services with systemd that run with as part of the user's session. They can have DISPLAY and XAUTHORITY set (currently in Arch, Debian Stretch+ and Ubuntu).
This makes sense over the previous recommendations of using desktop autostart files, as you get process management just like you would a system level app (restart, etc).
Best docs right now is the Arch wiki; Systemd/User
TLDR version;
- Create desired *.service file in
~/.config/systemd/user/
- Run
systemctl --user enable [service]
(exclude .service suffix) - Optionally run
systemctl --user start [service]
to start now - Use
systemctl --user status [service]
to check how it's doing
A couple other useful commands.
-
systemctl --user list-unit-files
- view all user units -
systemctl --user daemon-reload
- if you edit a .service file
-- Later...
I upgraded and converted most of my session daemons to systemd .service files. So I can add a couple of additional notes.
There was no default hook to run the services at login, so you must trigger it yourself. I do it from my ~/.xsession
file.
systemctl --user import-environment PATH DBUS_SESSION_BUS_ADDRESS
systemctl --no-block --user start xsession.target
The first line imports some environment variables into the systemd user session and the second kicks off the target. My xsession.target
file;
[Unit]
Description=Xsession running
BindsTo=graphical-session.target
My xbindkeys.service
as an example.
[Unit]
Description=xbindkeys
PartOf=graphical-session.target
[Service]
ExecStart=/usr/bin/xbindkeys -n -f ${HOME}/projects/dotfiles/.xbindkeysrc
Restart=always
[Install]
WantedBy=xsession.target
Solution 2:
The usual hint is "don't". redshift
is not a system-wide service – it would have a separate instance for each session, and it needs to know about how to connect to that specific session's Xorg.
(Xorg isn't a system service either – only the display manager is, and it also launches a separate Xorg for each session. // graphical.target
will tell you when the display manager is ready, but it says nothing about when the DM actually starts the first – or all – displays.)
Just starting it on boot with DISPLAY=:0
is not enough, for there is no guarantee that there's exactly one display at any given time, nor that it is always :0
(for example, if Xorg crashes leaving a stale lockfile, the next one would run at :1
as it would think :0
is still occupied); you also need to set the path to your XAUTHORITY
file as X11 requires authentication; and make sure redshift
gets restarted if you ever log out & log in again.
So how to start it? Almost always, the desktop environment has several methods of starting its own session services. See an older post which already describes the two usual ones; the ~/.xprofile
script and the ~/.config/autostart/*.desktop
location.
If you use startx, you can use ~/.xinitrc
to start such things. Standalone window managers often have their own startup/init scripts; e.g. ~/.config/openbox/autostart
for Openbox.
What's common to all these methods is that the program is started from within the session – avoiding all the problems listed above.
Solution 3:
Here is what I just created as workaround to the not yet available graphical-session.target
(On my Kubuntu 16.04 system):
- Create a pseudo systemd user unit which brings the graphical-session.target up and down.
Create ~/.config/systemd/user/xsession.target
with following contents:
[Unit] Description = Xsession up and running BindsTo=graphical-session.target
Tell systemd about this new unit:
$> systemctl --user daemon-reload
- Create autostart and shutdown scripts which controls the
xsession.target
via the currently available mechanics of the Ubuntu 16.04 desktop.
Create ~/.config/autostart-scripts/xsession.target-login.sh
with following contents:
#!/bin/bash if ! systemctl --user is-active xsession.target &> /dev/null then /bin/systemctl --user import-environment DISPLAY XAUTHORITY /bin/systemctl --user start xsession.target fi
Create ~/.config/plasma-workspace/shutdown/xsession.target-logout.sh
with following contents:
#!/bin/bash if systemctl --user is-active xsession.target &> /dev/null then /bin/systemctl --user stop xsession.target fi
Make the scripts executable:
$> chmod +x ~/.config/autostart-scripts/xsession.target-login.sh $> chmod +x ~/.config/plasma-workspace/shutdown/xsession.target-logout.sh
Note: these two files are placed where KDE will pick them up for autostart and shutdown. The files maybe placed somewhere else for other desktop environments (e.g. Gnome) - but I don't know about those environments.
Note: This workaround lacks support of multi desktop sessions. It only handles the graphical-session.target
correctly as long as only one active X11 session is run on a machine (but that's the case for most of us linux users).
- Create your own systemd user units which depend on
graphical-session.target
and have them run cleanly while being logged in on your desktop.
As example @mkaito's unit should look like this:
[Unit] Description=Redshift PartOf=graphical-session.target [Service] ExecStart=/bin/redshift -l 28:-13 -t 5300:3300 -b 0.80:0.91 -m randr Restart=always
(Don't forget to do a daemon-reload
after editing your units!)
- Reboot your machine, login and verfiy your units are started as expected
$> systemctl --user status graphical-session.target ● graphical-session.target - Current graphical user session Loaded: loaded (/usr/lib/systemd/user/graphical-session.target; static; vendor preset: enabled) Active: active since Don 2017-01-05 15:08:42 CET; 47min ago Docs: man:systemd.special(7) $> systemctl --user status your-unit...
At some future day (will it be Ubuntu 17.04?) my workaround become obsolete since the system will handle the graphical-session.target
correctly itself.
At that day just remove the autostart and shutdown script and also the xsession.target
- your custom user units may stay untouched and just work.