Emacs, tramp, and Guix
I’ve been experimenting with Guix on an unused Intel NUC that’s been collecting dust in my office. The Iinstallation process was easy but remotely
accessing the host has been a problem. ssh-ing to the host works fine but I can’t open remote files over ssh via tramp in Emacs. Every time I try Emacs complains Can't find proper ls
and refuses to open the file. Argh.
tl;dr Add (add-to-list 'tramp-remote-path 'tramp-own-remote-path)
to your Emacs config if tramp can’t find ls
on a Guix-based host.
Keep reading if you’re curious about the process I used to debug my config.
First, I tried to manually reproduce how I thought tramp was interacting with the NUC: ssh user@guix_host "ls /"
. No joy, just a listing of the root directory.
Is tramp using a restricted shell? I repeat the same test running ls
inside of another /bin/sh
instance like so: ssh user@guix_host "sh -c -r \"ls /\""
. It works.
Drat!
Next, I enabled verbose tramp logging by setting tramp-verbose
to 11 and tried opening a remote file again. Afaik this is the only way to see the low level back and forth
between tramp and a remote host. WARNING: This will produce a lot of output and most of it you won’t need. Searching for any occurence of ls
I discovered tramp was
raising an exception in tramp-get-ls-command
.
Aha!
I freshened my local clone of Emacs – I’m using a custom build off the emacs-29
branch – and started looking through tramp’s source for the function’s definition. Some grepping
led me to lisp/net/tramp-sh.el
. Tramp uses a list of paths stored in tramp-remote-path
to locate commands on a remote host. Let’s try (add-to-list 'tramp-remote-path "/run/current-system/profile/bin")
.
Same error. Digging more deeply into how tramp constructs remote paths I discover tramp-own-remote-path
:
Another way to find the remote path is to use the path assigned to the remote user by the remote host. TRAMP does not normally retain this remote path after login. However, tramp-own-remote-path preserves the path value, which can be used to update tramp-remote-path.
This sounds promising.
I know Guix uses shell profiles to set up user search paths. Tramp’s default behavior would be to discard those paths which could account for the error. Following
the docs I add (add-to-list 'tramp-remote-path 'tramp-own-remote-path)
, restart Emacs, and try again. It works!