Emacs daemon as a runit service
Updated: See this entry on an Emacs daemon "user service" with runit!
So, you want to run an Emacs daemon as a runit service - and you also want to connect to it in your desktop session. Thanks to the new --fg-daemon
option in Emacs 26.x you now can! I'm going to describe how to set up the service, as well as sudo
rules for managing it without requiring a password each time. Read on for the exciting details!
Requirements
Before you get too excited, there's a few prerequisites you'll need for this to be possible:
-
Emacs 26+: As I've already mentioned, the
--fg-daemon
option was just added in Emacs 26 so you'll need that version. - Runit: It doesn't have to be your PID1 (but maybe it should be?), but you of course need it running, doing its thing.
sudo
as well as hotkeys for OpenBox.
The Service
The service "run" file is below:
#!/bin/sh
export HOME=/home/hristos
cd $HOME
exec chpst -u hristos:hristos /usr/bin/emacs --fg-daemon=hristos-emacsd 2>&1
Nothing too crazy for a runit service here. I export my user's home directory as $HOME
, this way Emacs can find my ~/.emacs.d/
directory. I'm also naming the server's socket -- this way I can just pass that name to emacsclient
when I want to attatch:
$ emacsclient --socket-name=hristos-emacsd -c -n
This is nicer than having to give the path to the server's socket; easier for a human to reason about.
Optional Logging
This is optional, but I prefer to add logging to all runit services. Doing this is standard fare for a runit service; Create a direcory called "log" in the root of your "emacs-daemon" service directory, and within that put a file called "run" with the below contents:
#!/bin/sh
exec logger -t emacs-daemon
Make sure you have something like rsyslog
installed, and then you'll be able to find your "emacs-daemon" service logs in /var/log/syslog
or wherever your logs go. You can of course look at this output in the *Messages*
buffer, but what the hay am I right?
Run It
Now's the time to enable the service so that it can be used:
# ln -s /path/to/emacs-daemon /var/service/
Alter the paths to match where your things actually are, and then that's it. The Emacs daemon is now running.
Connecting
As I showed above, you can now connect with a simple invocation of emacsclient
, but some aliases would be a lot better. For fish, I use this function:
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
Now, anytime I invoke emacs
from the command line, I'll attach to the daemon. If it isn't running (and if you have zenity installed), then a message will pop up saying it was unable to connect. And for you OpenBox users out there, add the following in your rc.xml
:
<keybind key="W-e">
<action name="Execute">
<startupnotify>
<enabled>true</enabled>
<name>Emacs</name>
</startupnotify>
<command>fish -c "emacs"</command>
</action>
</keybind>
Now you can open Emacs, and connect to your daemon with a simple press of "Meta+E"!
Sudo Rules
Although it's actually simple to close the daemon from within Emacs itself, thus prompting runit to relaunch a new process, it's still great to be able to use sv
to manage the service if need be. Even better, you can use sudo rules to allow you to do this without needing a password. Put the following in /etc/sudoers.d/emacs-daemon
:
Cmnd_Alias EMACSD_CMDS=/usr/bin/sv check emacs-daemon,/usr/bin/sv start emacs-daemon,/usr/bin/sv stop emacs-daemon,/usr/bin/sv restart emacs-daemon
hristos ALL=(ALL) NOPASSWD: EMACSD_CMDS
Of course replace user names and commands as needed, but that's all you need.
Conclusion
For a long time I managed an Emacs daemon outside of runit (or any other service manager) and it was fine; One can run emacsclient
with the --alternative-editor=''
option and that will start the daemon if it's not already running. But having it start with your system, and having simple system-level controls for it is pretty useful and worthwhile considering the small amounts of effort required to implement.