A Systemd service unit for Redshift

Redshift is a small utility that gradually – as day turns into night – shifts the colour composition of the light from your monitor. The later it gets, the ‘warmer’ (i.e. redder) the colour becomes. This may sound odd but the effect is really pleasant and (literally) easy on the eyes. Once you’ve grown used to it, looking at a screen late at night you’ll really notice how harsh and cold the light will appear without it. I owe this discovery to Graham Morrison from Linux Voice. You can read more about it in Graham’s piece on page 78 in the now free issue 14 of Linux Voice (you should also subscribe to LV as it’s a really good magazine).

Redshift is the sort of thing you want to have running as soon as you log into your desktop but occasionally, you want it to turn it off. I occasionally play Spelunky into the wee hours of the night and running redshift while playing the game makes dark levels, well dark. Pitch black, more like. So a classic Gnome autostart file is not ideal as we can’t interact with it (apart from killing the resulting process). Redshift does come with a small tray utility that should allow for turning on and off but I really dislike tray litter and anyway Gnome’s trying to do away with this sort of thing. So systemd to the rescue.

Sadly the redshift package in Ubuntu 16.04 comes without a systemd .service file so I put together one myself.

Now, redshift doesn’t require any special privileges so we should really just run it as our regular user and not as root. Systemd has two options for that: Either run it with the user specific systemd instance or run it with the systemwide systemd instance but order it to drop privileges and run as a non-privileged user.

I chose the latter route for a number of reasons. Firstly, this way I can set it to start at the login screen rather than only when I log in. Secondly, I just have a better experience with this setup as you’re less likely to run into issues with permissions. On the downside you have to use sudo to get it to stop and start so if you do that a lot, this is probably not the right way.

Here’s the file:

[Unit]
Description=Redshift adjusts the color temperature of your screen according to your surroundings
Documentation=http://jonls.dk/redshift/
After=graphical.target

[Service]
User=%i
Type=simple
Environment=DISPLAY=:1
ExecStart=/usr/bin/redshift -l 55.7:12.6 -t 5700:3600 -g 0.8 -m vidmode -v
TimeoutStopSec=5

[Install]
WantedBy=graphical.target

The pertinent bits are as follows.

Load order and dependency

We specify the relationship with graphical.target in two ways. first, we inform systemd of redshift’s dependency on graphical.target (i.e. display manager’s login screen) by setting WantedBy. This is the part that actually causes systemd to start redshift when it realizes that we’re headed for the graphical.target. Second, we specify the loading order by setting After. Using After=graphical.target specifies the load order. Setting After by itself will not cause anything to happen but it modifies what happens due to WantedBy by telling systemd not to start redshift concurrently with the other bits that make up graphical.target but only once we’re at grahical.target. Running startup jobs in parallel is systemd’s ‘thing’ and it’s big innovation vis-a-vis older inits and maybe it’s perfectly fine to load redshift concurrently with the display manager but I prefer to err on the side of caution.

User

As mentioned previously we don’t want to run redshift as root. So setting User=%i tells systemd to take the name of the user from the systemctl command that started/stopped/poked the service and run the program as that user. This is how it works: You name the service file redshift@.service. Then to start it you run sudo systemctl start redshift@myuser.service, to stop it you run sudo systemctl stop redshift@myuser.service, to ask for it’s status you do systemctl status redshift@myuser.service (no need for root privileges to ask for status) and so on. You get the picture. Insert the name of your own desktop user for myuser.

Display

[ Somewhat embarrasingly, this service file actually started to fail on me shortly after writing this post due to this setting having changed. So I added this section. ]

Systemd service files are generally unaware of environmental variables that we take for granted when running an interactive shell in a terminal. This includes the Xorg variable DISPLAY. So we help things along by telling systemd what environment variables to set for the executable, here specifically we set the DISPLAY variable to :1. This variable tells redshift what display to apply it’s changes to. The composition of the variable is [host]:<display>[.screen] but we omit both host (so that the local machine is implied) and screen as that is apparently not much in use any more.

I believe that traditionally, the first display on the local machine is assigned the number 0. Recently however, redshift started failing for me with that setting and I found that the display had shifted to 1.  To get the right number check the environment variable on your own machine by running the following in a terminal (inside of X, needless to say):

echo $DISPLAY

You can find out more about this – or possibly just get more confused – by reading up on this thread on the Arch forums.

Service specifics

ExecStart is obviously the main command. You can puzzle out the meaning of the various flags by checking out the man page but in short it sets my geographical location (so redshift knows when it’s day and night and what season we’re currently having) and inside of what range redshift should manage the colour composition. Finally the -v  flag is for verbose and allows you to follow the changes in colour ‘temperature’ by checking in on the service unit’s status.

The important things here are Type and TimeOutStop. Type=simple simply means that redshift is a program that runs until it’s asked to quit. It doesn’t fork, it doesn’t run and then quit by itself, etc. That way systemd knows what to expect.  This is a very common setting, in fact it’s the default for a service file. TimeOutStop is more special and is prompted by an issue I had with redshift. You see, when asked to quit – by killing it with SIGTERM or Ctrl-C’ing the program running on the console – redshift doesn’t just quit. When it starts up it gradually changes the colour composition of your screen until it gets to the setting that it considers appropriate for that time of day/season. This avoids the jarring effect that would come from an instant change. It tries to do the same thing when quitting, gradually returning the screen to the non-shifted mode. This can take a few seconds but that confuses systemd which expects an instant exit. By setting  TimeOutStop=5  we give redshift 5 seconds to quit to allow for this behaviour. This way, systemd recognizes that the program has exited correctly even though it takes a little time. Alternatively you can run redshift with the -r flag that tells redshift to just switch back and forth instantly.

Redshift is pretty much a must for me these days but it isn’t perfect. Amongst the limitations I have noticed are that any xrandr command (including those that do not change display) causes it to go out for a couple of seconds before returning; unexplained little flickers; and – at least with this setup – if you log out and in again, the process is still running but the effect is no longer there and the service will have to be restarted.

That was my somewhat overthorough explanation of my redshift systemd service file. You could of course also just have a look at the rather simple (but probably perfectly serviceable) one that’s distributed with the Arch package or read up systemd service files by checking out Justin Ellingwood’s (as always) excellent guide. I follow that guy on Twitter even though he never tweets, he’s that good.

Photo by xmodulo

Leave a Reply

Your email address will not be published. Required fields are marked *