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:
- All X11 applications worked, except for
emacsclient
, which fails with the errorWaiting for Emacs...X11 connection rejected because of wrong authentication.
- 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:
- Difference between emacs and emacs-lucid packages - Ask Ubuntu
- https://bugzilla.gnome.org/show%5Fbug.cgi?id=85715
- https://gitlab.gnome.org/GNOME/gtk/issues/221
- x11 forwarding - Aligning .Xauthority between GDM and SSH - Stack Overflow