Emacs daemon as a runit "user service"

Posted:

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. runsvdir - starts and monitors a collection of runsv(8) processes Does runit support user-specific services?

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. Is it possible to allow a user other than root to control a service

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!

Footnotes And References