Emacs daemon as a runit service

Posted by Hristos T. on 2018-06-08, under the category Emacs
Tag: 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.
I'll also go into detail about how to configure 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.