Browse Source

New article: Using systemd for X sessions

Michał Góral 2 weeks ago
parent
commit
8ab594d8dc
2 changed files with 200 additions and 1 deletions
  1. 199
    0
      content/post/systemd-x-sessions.md
  2. 1
    1
      themes/tale

+ 199
- 0
content/post/systemd-x-sessions.md View File

@@ -0,0 +1,199 @@
1
+---
2
+date: 2019-05-29
3
+tags:
4
+- linux
5
+- Debian
6
+- systemd
7
+title: Using systemd for X sessions
8
+---
9
+Recently I wrote an [article about starting X sessions][xsession] in Debian.
10
+One of stages of Xsession is running *~/.xsessionrc* script, which, according
11
+to *xsession(5)* manual should be a source of global environment variables. It
12
+can be, however, a convenient place for keeping all programs which should start
13
+together with X session - because, generally speaking, it is executed for every
14
+session type, no matter what.  I used this approach for several years and, for
15
+reasons highlighted later, I find it suboptimal. In this article I'd like to
16
+explore another possibility - using systemd user mode for managing user X
17
+sessions.
18
+
19
+  [xsession]: /post/xsession
20
+
21
+<!--more-->
22
+
23
+## xsessionrc approach
24
+
25
+Our xsessionrc can look like this:
26
+
27
+```bash
28
+#!/bin/bash
29
+xrdb - merge "$HOME/.Xresources
30
+
31
+mpd &
32
+xterm -e ncmpcpp &
33
+
34
+firefox &
35
+thunderbird &
36
+```
37
+
38
+There are several problems with such approach (and, to be fair, with any simple
39
+script used to run a session):
40
+
41
+1. Programs often depend on each other. For example ncmpcpp (client of <abbr
42
+   title="Music Player Daemon">MPD</abbr>) doesn't make sense to start
43
+   before MPD itself, but MPD might delay its start for any reason or even not
44
+   start at all e.g. due to configuration error.
45
+2. Some programs (like MPD) won't automatically exit with user session. If
46
+   session is restarted, it will cause problems, because they might use
47
+   resources (like sockets) which are already used their other instances.
48
+3. If programs break you have to manually restart it - which usually means
49
+   grepping through xsessionrc in search of exact set of switches used for its
50
+   invocation. I'm sorry, I don't remember my longitude and latitude passed to
51
+   redshift.
52
+4. Programs start simultaneously and they all write their output to
53
+   *~/.xsession-errors* at the same time. If that's not enough, GTK programs
54
+   are typically VERY verbose and soon finding anything in xsession-errors
55
+   becomes a horror story. Sure, you can do something like `mpd >mpd.out
56
+   2>mpd.err`, but in practice I've never heard about anyone doing something
57
+   like that.
58
+5. Probably it's yet another mechanism which manages session. For example, i3
59
+   can also start programs from inside its configuration file and most desktop
60
+   environments autostarts programs depending on their own sets of rules. I
61
+   like having a choice, but I hate using several mechanisms at once, each with
62
+   its own set of quirks and design choices.
63
+
64
+That's why in the back of my had I had this idea of creating a uniform system
65
+for managing my login sessions. I already used systemd for some programs
66
+(especially for some in-house scripts at work, which tend to break every now
67
+and then so systemd auto-restarts them) and was quite happy with it, so I
68
+thougt to give it a try and manage all of my session startup this way.
69
+
70
+## systemd approach
71
+
72
+First of all, I don't want to reconfigure my whole system and right now
73
+*Xsession* script is rather tightly coupled with the rest of Debian. If I
74
+ever have to configure X session on a new computer, I want to simply symlink
75
+some files with GNU Stow (you can read about how I approach my configuration in
76
+[dotfiles series][dotfiles]). Besides, I'd be rewriting big parts of Xsession
77
+anyway, because it out-of-the-box contains some very handy bits (like
78
+**injecting environment to systemd via DBus**, which usually is a separate
79
+topic and in Debian we have it for free).
80
+
81
+Long story short: I modified *~/.xsession* to contain exactly 2 lines:
82
+
83
+```sh
84
+#!/bin/sh
85
+systemctl --user start --wait xsession.target
86
+```
87
+
88
+Above invocation of systemctl can be of course placed in a _*.desktop_ file.
89
+Just keep in mind that it's important to use `--wait` flag, which disallows
90
+immediate exit, which in turn would stop X session right after it.
91
+
92
+*xsession.target* looks like this:
93
+
94
+```ini
95
+[Unit]
96
+Description=X session managed by systemd
97
+BindsTo=graphical-session.target
98
+```
99
+
100
+*xsession.target* is a synchronization point. User units (started with
101
+`systemctl --user`) cannot wait for targets from system-wide systemctl calls,
102
+like *graphical.target*, *sound.target* etc. That's because user units are
103
+spawned in a separate daemon, spawned for each user, which has no knowledge of
104
+other systemd instances. By hooking into built-in Xsession mechanism we ensured
105
+that when *xsession.target* becomes active, all resources needed by graphical
106
+programs (`$DISPLAY` and `$XAUTHORITY`) are available.
107
+
108
+*xsession.target* binds to a special target: **graphical-session.target**,
109
+which is active as long as any other active unit requires it. It also acts as
110
+an alias for any graphical session (such as GNOME, KDE, i3, awesome, ...):
111
+other units, which are part of X session should contain
112
+`PartOf=graphical-session.target`. This way they'll be stopped when
113
+*graphical-session.target* stops. They also don't need to be changed if i3wm
114
+were to be replaced with e.g. some other window manager.
115
+
116
+Example unit started in session looks like this:
117
+
118
+```ini
119
+[Unit]
120
+Description=Compton compositor for X11
121
+PartOf=graphical-session.target
122
+
123
+[Service]
124
+ExecStart=/usr/bin/compton --config "%h/.config/compton/compton.conf"
125
+
126
+[Install]
127
+WantedBy=graphical-session.target
128
+```
129
+
130
+*xsession.target* is explicitly required by only one other unit:
131
+*i3wm.service*, which handles starting and stopping window manager:
132
+
133
+```ini
134
+[Unit]
135
+Description=i3 Window Manager
136
+PartOf=graphical-session.target
137
+
138
+[Service]
139
+ExecStart=/usr/local/bin/i3
140
+ExecStopPost=/bin/systemctl --user stop graphical-session.target
141
+Restart=on-failure
142
+
143
+[Install]
144
+RequiredBy=xsession.target
145
+```
146
+
147
+Note that *xsession.target* itself doesn't require anything by itself (via
148
+`Requires=`). That's because I prefer to add and remove programs to autostart
149
+via `systemctl enable` and `systemctl disable` instead of editing systemd unit
150
+files.
151
+
152
+Another interesting part is `ExecStopPost=`, which stops graphical-session (and
153
+all of its parts) whenever i3 quits. To quit a session,
154
+*graphical-session.target* must be stopped one way or another and I decided
155
+to keep a behaviour that I'm familiar with: window manager acts as a session's
156
+master and whenever it quits, the whole session is killed as well - it didn't
157
+always work for me before, but it's one of the features of systemd.
158
+
159
+### tmux
160
+
161
+Killing the whole session has some quirks though. When I log out, systemd kills
162
+tmux server.
163
+
164
+I like the way systemd works because it's easy enough to mark specific programs
165
+which should be kept after session ends and by default I don't want most of
166
+daemons to survive logging out. For instance: if I run HTTP server, then it's
167
+purposefully run to display some personal pages and it has no purpose to exist after
168
+I log out. If I run syncthing, I want it to operate only when I'm logged to my
169
+system. If I wanted a daemon to run all the time, then it's a system service
170
+and should be run via systemctl without `--user` switch (syncthing for example
171
+ships with systemd units which support exactly that use case - useful for
172
+infrastructure with a single "master" syncthing server).
173
+
174
+However, programs like tmux and screen are special, because they're
175
+specifically designed to daemonize to survive user sessions. This can be
176
+achieved by running them through `systemd-run --remain-after-exit` or even
177
+`systemd-run --scope --user` (which should [finally work][scope-pr] in recent
178
+systemd versions).
179
+
180
+To do this automatically, tmux can be aliased:
181
+
182
+```sh
183
+tmux() {
184
+    systemd-run --scope --user tmux "$@"
185
+}
186
+```
187
+
188
+And basically this is everything. Just put all _*.service_ files in
189
+*~/.config/systemd/user* and enable/disable the ones which you want to
190
+autostart by `systemctl --user enable/disable`. It's super convienient, because
191
+you don't have to remember exact commands, just run `systemctl --user enable
192
+redshift.service`.
193
+
194
+Source code presented in this article is also available in [git
195
+repository][source-code], so take a look if it makes more sense to you.
196
+
197
+  [dotfiles]: /tags/dotfiles/
198
+  [scope-pr]: https://github.com/systemd/systemd/pull/8125
199
+  [source-code]: https://git.goral.net.pl/mgoral/blog-code/src/branch/master/systemd-x-sessions

+ 1
- 1
themes/tale

@@ -1 +1 @@
1
-Subproject commit 02224f556100fb617c0e089e1ae671ce8ffc4354
1
+Subproject commit ff27ba087f869cd94c0d56dd1de7e082a4db8a3c

Loading…
Cancel
Save