Remote Emacs on Gnome using X
Mar 17, 2020
Allard Hendriksen
4 minute read

Since Friday, I have started working from home. Since my Emacs configurations between home and work are mostly sychronized, this was not a major inconvencience. For programming, however, I would rather work on my workstation.

My first attempt at remote programming in emacs was using the terminal (emacsclient -t). There were all kinds of small inconveniences that made this a unpleasant experience.

The second attempt was to use X11-forwarding, by logging in using

ssh -X workstation
emacsclient -c

This approach did not work for two reasons:

  1. All X11 applications worked, except for emacsclient, which fails with the error Waiting for Emacs...X11 connection rejected because of wrong authentication.
  2. When the SSH connection closes, the emacs daemon dies. This is terrible because you lose all unsaved work and kill all running sub-processes.

Fortunately, both problems are fixable!

Xauth and emacs

To reiterate, I could not get emacsclient working over X11, but somehow other X11 apps (emacs and evince) were working.. I got this error:

ssh -X workstation
emacsclient -c
Waiting for Emacs...X11 connection rejected because of wrong authentication.

*ERROR*: Display X.Y.Z.Z:10.0 can’t be opened

The problem, as it turns out, is that the X11 Xauthority file, is stored in ~/.Xauthority by SSH. The remote emacs-daemon, on the other hand, expects the cookie to be located in $XDG_RUNTIME_DIR/gdm/Xauthority, which is /run/$USER/gdm/Xauthority for me. This appears to be a Gnome-specific issue. When comparing the two, I found that they were different:

ssh -X workstation
xauth -f /run/user/$USER/gdm/Xauthority  list
<redacted>/unix:  MIT-MAGIC-COOKIE-1  5890ca0aefa--snip--
<redacted>:11  MIT-MAGIC-COOKIE-1  2654237db33f0d--snip--
ssh -X workstation
xauth -f .Xauthority  list
<redacted>/unix:  MIT-MAGIC-COOKIE-1  5890ca0aefa--snip--
<redacted>:11  MIT-MAGIC-COOKIE-1  2654237db33f0d--snip--
<redacted>:10  MIT-MAGIC-COOKIE-1  42f86b7030620c--snip--

We can merge the cookie lists as follows:

ssh -X workstation
xauth -f /run/user/$USER/gdm/Xauthority merge .Xauthority
# and list the result:
xauth -f /run/user/$USER/gdm/Xauthority  list

So to run a remote emacs, just make sure you merge your cookies before starting emacs. For instance, like this

ssh -X workstation
xauth -f $XDG_RUNTIME_DIR/gdm/Xauthority merge .Xauthority
emacsclient -c

Or, in one line

ssh -X workstation 'xauth -f $XDG_RUNTIME_DIR/gdm/Xauthority merge .Xauthority && emacsclient -c'

Now we can start emacs, but we still cannot quit without losing work!

X and emacs

Since 2002, there has been a bug which makes it difficult to exit Emacs over X11 without killing it. This makes it difficult to use Emacs in daemon-mode. Fortunately, whenever Emacs dies due to this bug, it helpfully points out what the issue is:

Warning: due to a long standing Gtk+ bug
https://gitlab.gnome.org/GNOME/gtk/issues/221
Emacs might crash when run in daemon mode and the X11 connection is unexpectedly lost.
Using an Emacs configured with --with-x-toolkit=lucid does not have this problem.

To solve this issue, install the emacs-lucid package.

sudo apt install emacs-lucid # Debian-based systems
sudo dnf install emacs-lucid # Red-hat based systems

On Debian-based systems, the ‘normal’ Emacs is replaced by the Lucid variant. On Fedora, an additional binary is installed in /usr/bin/emacs-lucid. I let Systemd manage my emacs daemon, so I had to add an additional user-level service that specifically uses the Lucid variant of emacs.

To create such a service on the remote system, first copy the ‘normal’ emacs service file to your user-space systemd configuration directory:

cp /usr/lib/systemd/user/emacs.service ~/.config/systemd/user/emacs-lucid.service

Then edit so it contains

[Unit]
Description=Emacs: the extensible, self-documenting text editor

[Service]
Type=forking
# ExecStart=/usr/bin/emacs --daemon     <---- change this line
ExecStart=/usr/bin/emacs-lucid --daemon <---- into this
ExecStop=/usr/bin/emacsclient --eval "(kill-emacs)"
Restart=always

[Install]
WantedBy=default.target

I do not think it’s possible to run both ‘normal’ and Lucid-style Emacs daemons at the same time, and I do not want to try. So first disable and stop the current emacs daemon:

systemctl --user disable emacs
systemctl --user stop emacs

Load the new Emacs lucid configuration:

systemctl --user daemon-reload

And start the Emacs Lucid daemon:

systemctl --user start emacs-lucid

That’s it! Now you can remotely start and stop Emacs without losing work.

Resources: