Emacs daemon as a runit "user service"
A while back, I wrote about setting up a runit service for an Emacs daemon. The idea behind that post was that you'd have a system-level service for your user Emacs session. But what if you want a "user service", like what systemd-using folks have? Read on to find out how to replicate this with runit!
What Is A User Service?
First off, what even is a user service? As a reference, see this piece by Bozhidar Batsov about running Emacs with systemd's version of user services. With runit, it's simply a runsvdir
that your user can write to. 1 2
Your User's Runsvdir
Before you can have a user service for your Emacs daemon, you've got to have a service for your user services! This is because runit has no specific notion of user versus system services. So if you want a user to be able to easily manage some services, simply give them their own runsvdir
.
The /etc/sv/hristos-runsvdir/run
file for that looks like this:
#!/bin/sh
user=hristos
exec 2>&1
exec chpst -u $user runsvdir /home/$user/.local/sv
Enable that, and now your user can write to $HOME/.local/sv
as a service directory. It's also possible to enable logging for this or any child services, if desired.
The Emacs Daemon User Service
The "user service" file (/home/hristos/.local/sv/emacs-daemon/run
) doesn't look much different than the system-wide version:
#!/bin/sh
export USER=hristos
export HOME=/home/$USER
export PATH=$HOME/.local/bin:$PATH
# .. any other variables you need can go here ..
cd $HOME
exec /usr/bin/emacs --fg-daemon=$USER-emacsd
The most noteable change here is that the command no longer uses chpst
to run as a certain user, and the trailing output redirection (2>&1
) is also omitted due to that being handled by the parent "user service" service as described above.
Functions/Shortcuts
As before, I'm using some fish functions to simplify interracting with services:
# A shortcut function for interacting with user services:
function usv
env SVDIR=$HOME/.local/sv sv $argv
end
# A shortcut function for launching an Emacs client process that connects to my daemon:
function emacs
command emacsclient --socket-name=hristos-emacsd -c -n $argv ; or \
zenity --error --no-wrap --text 'Failed to connect to the Emacs daemon!' ^/dev/null
end
I call that emacs
function externally like this: fish -c "emacs"
.
Sudo Rules?
The method described above, utilizing "user services", is a bit better because it doesn't require any kind of privilege escalation outside of enabling the initial runsvdir
. But can you use a system-level service without such escalation?
It turns out it is indeed also possible to have an Emacs daemon at the system-level but not require sudo
or other privilege elevation methods. That's definitely worth checking out if a "user service" isn't something you need. 3
Conclusion
There you have it, "user services" for runit and an Emacs daemon service for it. Is it one-to-one, the same as in systemd-land? No, but it is close enough where it matters and allows quite a bit of flexibility. Happy daemon-ing!