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 error
Waiting 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
~/.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
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
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.
- Difference between emacs and emacs-lucid packages - Ask Ubuntu
- x11 forwarding - Aligning .Xauthority between GDM and SSH - Stack Overflow