Running per-session programs in sway
A common task people want to perform is running a set of given programs during every Wayland session.
GNOME and KDE have their own approaches and graphical utilities for down this. For those of us who don’t use a desktop environment, how about in Sway?
A bit of experimentation appears to show that this syntax is a pretty reasonable way to do this:
<span style="color:#323232;"># Power notification support
</span><span style="color:#323232;">exec_always flock -n $XDG_RUNTIME_DIR/$WAYLAND_DISPLAY-poweralertd poweralertd -s
</span><span style="color:#323232;">
</span><span style="color:#323232;"># Make clipboard persist after application termination
</span><span style="color:#323232;">exec_always flock -n $XDG_RUNTIME_DIR/$WAYLAND_DISPLAY-wl-paste wl-paste --watch clipman store
</span>
Thought I’d share for anyone else running into this.
For some Sway users, that may be enough. But if anyone wants to read more history, here goes!
Background and rationale:
Once upon a time, it was conventional to have a shell script, ~/.xinitrc
, that was invoked whenever someone started up an X server with startx
after logging in on an (initially) text terminal. Any per-session commands could be invoked here.
Later, when display managers showed up, it became common for a Linux machine to go straight to a graphical display at boot and show a login prompt from there. xdm
showed up; if it was started, it’d run another shell script, ~/.xsession
. A lot of people, including myself, just symlinked ~/.xsession
to ~/.xinitrc
.
Still later, some desktop environments, like GNOME or KDE or some less-popular ones, introduced their own schemes for storing a list of programs to start when the graphical environment came up.
Wayland+sway – to my initial surprise and annoyance – isn’t really geared up for that. In theory, you can use whatever login manager you want. I use a non-standard login manager – greetd to launch agreety
(and I’d use emptty if it were in Debian bookworm) which lets me log in on a terminal. But these don’t provide functionality to run a startup script. This kind of makes sense – on X11, once the display manager starts things up, X11 can run programs, whereas Wayland really requires a functioning compositor to be going, which means that Sway really needs to be up and running. So maybe it makes sense for the compositor, Sway, to handle launching startup programs in the graphical environment, rather than the login manager. but Wayland compositors don’t have even a semi-convention for a “login script” like ~/.xinitrc
or /.xsession/.xprofile` or an equivalent, which surprised me. That might be because Wayland compositors are heavier than their X11 window manager analogs, and perhaps its less-expected for people to be switching among them.or
What Sway does is to, in its config file, ~/.config/sway/config
, have two directives, exec
and exec_always
. One can make them invoke a script. These can be handy. But they don’t quite what I’d ideally like them to do.
You see, Sway – like some X11 window managers – has the ability to permit a “reload”, where it re-reads its config files. That’s handy! If an X11 window manager couldn’t do that, when you changed its config file, you’d have to close all your graphical programs, log out, and log in again to confirm that it did what you wanted. You don’t have that problem with Sway. You can just change its config file, ask Sway to do a reload, and it’ll be “re-applied”. And then exec
and exec_always
come into play – the former will run a program only when Sway initially starts, but not when it does a “reload”. The latter will run a command each time, both at Sway start and each time Sway reloads its config file.
For some programs, exec
and exec_always
are sufficient. Maybe you just want to make sure that a program has been run and then terminated at least once in your current session.
But that isn’t normally what I want to do. By far my overwhelming need – and I suspect this is true of others – is that I want to have some kind of daemon running and persisting in the background of my session.
Some daemons try to be clever. If you try to run multiple instances at once, the new instance will just bail out. blueman-applet
is like this. And if your daemon works like this, then running exec_always
is fine. If you run a new instance and there’s an already-running instance, the new one will just bail out.
But some daemons don’t – they just start up another instance. So every time you reload your Sway config, exec_always
will start another instance of that daemon. I have a couple of daemons like that. poweralertd
notifies me when my laptop battery is getting low, for example. If I just let poweralertd
do its own thing and start it via exec_always
, then when my battery gets low, if I’ve reloaded my Sway config 5 times, I’ll have 5 instances running, and get 5 warnings when my battery gets low.
But running exec
isn’t ideal either, because then you have to give up on Sway “reapplying” your config when you reload it. If I want to have a new daemon running in the background of my Wayland session, I don’t want to have to log out to ensure that my config is working correctly.
Now, at this point, I suspect that a number of people think “Aha! What about systemd?”
So, not everyone is a huge fan of systemd. It is a very large software package that provides a lot of useful functionality to most present-day Linux systems. So you might not want to tie yourself to systemd.
But more-problematic – while systemd does have the ability to manage both “system” daemons that run one instance per system, typically come up when the system does, and “user” daemons, one instance per user…that isn’t quite correct for Wayland. It’s reasonable for a user to have multiple concurrent Wayland sessions on a Linux machine. Maybe it might make sense to selectively share some functionality among those, like one mpd
instance to play music – dunno about that. But you definitely don’t want to have random Wayland programs run in each session running one-instance-per-user, because otherwise, any additional Wayland session will have the programs just not come up in that new session.
It looks like some people out there have recognized that this is an issue. uwsm looks to my quick glance at being a stab in the direction of “per-Wayland-session systemd-based management”. But whether-or-not it could be used, it’s not in Debian bookworm, and I want to use stock software for basic stuff like getting my desktop up on a new system.
Hence, we get to the above flock-based approach. So, let’s say that one wants to have a program like wl-paste
running persistently, but only one per-session. How?
We want to have only one instance running at once. Traditionally, the way to achieve that on a Unix system is to create a file, then establish a “file lock” on it via the flock(2)
function; this is guaranteed by the OS to be an atomic operation, only one can occur. There’s a command, flock(1)
, which does all this at one go – it creates a file if it doesn’t exist, establishes a file lock on it, and then, while continuing to run, runs a specified command. When a process goes away, the OS releases the file lock, so when the invoked command (here, wl-paste
) exits, the flock
process exits, and the file lock goes away. By default, flock
will block if there’s a lockfile with a held lock, which is what you want if you just want to make sure that two commands wait to avoid interfering with each other but with -n
, it’ll just fail; this is the behavior you want if you want to make sure that you have one, but only one, daemon active.
And we want to have one instance per session, not per user. The $XDG_RUNTIME_DIR
environment variable provides a temporary directory per-user…but not per session. $WAYLAND_DISPLAY
is guaranteed to be unique per session for a given user. So any path containing both $XDG_RUNTIME_DIR
AND $WAYLAND_DISPLAY
is going to be unique per-session; we just need an extra bit of text (“-poweralertd”) to make it unique to a given daemon per session.
Given how this wasn’t an immediately-obvious approach to me, thought that I’d point it out to anyone else who might be using Sway and want per-session daemons running.