Open a file with su/sudo inside Emacs - emacs

Suppose I want to open a file in an existing Emacs session using su or sudo, without dropping down to a shell and doing sudoedit or sudo emacs. One way to do this is
C-x C-f /sudo::/path/to/file
but this requires an expensive round-trip through SSH. Is there a more direct way?
[EDIT] #JBB is right. I want to be able to invoke su/sudo to save as well as open. It would be OK (but not ideal) to re-authorize when saving. What I'm looking for is variations of find-file and save-buffer that can be "piped" through su/sudo.

The nice thing about Tramp is that you only pay for that round-trip to SSH when you open the first file. Sudo then caches your credentials, and Emacs saves a handle, so that subsequent sudo-opened files take much less time.
I haven't found the extra time it takes to save burdening, either. It's fast enough, IMO.

Tramp does not round-trip sudo via SSH, it uses a subshell. See the manual: https://www.gnu.org/software/tramp/#Inline-methods
Therefore, I recommend that you stick with TRAMP.

If you use helm, helm-find-files supports opening a file as root with C-c r.

Not really an answer to the original question, but here's a helper function to make doing the tramp/sudo route a bit easier:
(defun sudo-find-file (file-name)
"Like find file, but opens the file as root."
(interactive "FSudo Find File: ")
(let ((tramp-file-name (concat "/sudo::" (expand-file-name file-name))))
(find-file tramp-file-name)))

Your example doesn't start ssh at all, at least not with my version of TRAMP ("2.1.13-pre"). Both find-file and save-buffer work great.

At least for saving, a sudo-save package was written exactly for that kind of problem.

I recommend you to use advising commands. Put this function in your ~/.emacs
(defadvice ido-find-file (after find-file-sudo activate)
"Find file as root if necessary."
(unless (and buffer-file-name
(file-writable-p buffer-file-name))
(find-alternate-file (concat "/sudo:root#localhost:" buffer-file-name))))

(works only locally. Need to be updated to work correctly via tramp)
A little bit extended Burton's answer:
(defun sudo-find-file (file-name)
"Like find file, but opens the file as root."
(interactive "FSudo Find File: ")
(let ((tramp-file-name (concat "/sudo::" (expand-file-name file-name))))
(find-file tramp-file-name)))
(add-hook 'dired-mode-hook
(lambda ()
;; open current file as sudo
(local-set-key (kbd "C-x <M-S-return>") (lambda()
(interactive)
(message "!!! SUDO opening %s" (dired-file-name-at-point))
(sudo-find-file (dired-file-name-at-point))
))
)
)

Ugh. Perhaps you could open a shell in Emacs and exec sudo emacs.
The problem is that you presumably don't just want to open the file. You want to be able to save it later. Thus you need your root privs to persist, not just exist for opening the file.
Sounds like you want Emacs to become your window manager. It's bloated enough without that. :)

I find sudo edit function very useful for that. After opening a file, press s-e to have sudo access to edit/save the file.

Related

eshell TRAMP find remote file with relative path (or at least less than the full Tramp path)?

I love eshell's TRAMP integration. With it I can do cd /ssh:foo:/etc to
ssh into a remote machine and visit its /etc/ directory. I can also do
find-file motd to open this file in my local emacs. However, what if I need to use sudo to change the file? I know I can give the
full path, like so:
find-file /sudo:foo:/etc/motd
but is there a way to open the file via TRAMPs sudo support, without having to type the full path?
I managed to came up with the following eshell alias that works for me:
alias sff 'find-file "${pwd}/$1"(:s/ssh/sudo/)'
It should be fairly obvious what it does. It prepends the working directory
path, but with the string ssh replaced by sudo. Thus it only works for
remote files accessed over ssh. I rarely edit files using sudo locally, so
that's not a problem for me. However, we can make it work for local files too, at the cost of complexity:
alias sff 'find-file "${pwd}/$1"(:s,^,/sudo::,:s,::/ssh:,:,)'
That is, prepend /sudo:: (which is how to sudo for local files) and
subsequently replace any ocurrence of ::/ssh: with :. (I would have just removed :/ssh:, but eshell's :s/// construct didn't accept an empty
replacement.)
I found an alternative answer that works very well over at EmacsWiki.
Using that you'd still open the file with find-file as usual, but then
invoke M-x sudo-edit-current-file (shown below) to re-open the file as root
using Tramp. I think this is a very elegant solution, because often I
initially just want to look at a file, then later find that I need to edit it.
Here's the function, in case it disappears from the page above:
(set-default 'tramp-default-proxies-alist (quote ((".*" "\\`root\\'" "/ssh:%h:"))))
(require 'tramp)
(defun sudo-edit-current-file ()
(interactive)
(let ((position (point)))
(find-alternate-file
(if (file-remote-p (buffer-file-name))
(let ((vec (tramp-dissect-file-name (buffer-file-name))))
(tramp-make-tramp-file-name
"sudo"
(tramp-file-name-user vec)
(tramp-file-name-host vec)
(tramp-file-name-localname vec)))
(concat "/sudo:root#localhost:" (buffer-file-name))))
(goto-char position)))

Change location of .ido.last history file in Emacs on Windows

Using Emacs with ido mode enabled on Windows, Emacs tries to save a history file .ido.last when exiting. The file is located in C:/.ido.last, but it fails with a permission denied message. This is strange since I actually have access to that folder. However:
Is there a command to change the directory where the .ido.last file gets saved?
Short answer: (setq ido-save-directory-list-file "/some/file/name").
Long answer:
I keep all the little files that remember Emacs's state in a single directory under the user-emacs-directory. I'm not sure what this is on Windows, but I think it's C:\Users\<username>\Application Data\.emacs.d\. On Unix, it's ~/.emacs.d/. The variable user-emacs-directory should be defined by Emacs, no need to set it.
(setq emacs-persistence-directory (concat user-emacs-directory "persistence/"))
(unless (file-exists-p emacs-persistence-directory)
(make-directory emacs-persistence-directory t))
(setq ido-save-directory-list-file (concat emacs-persistence-directory
"ido-last"))
You may want to look at the no-littering package, which sets better default locations for files like this.

Open shell in emacs with a given working directory

I want to have a make-shells command in emacs that will open a number of emacs-shell buffers, each with its own working directory. The idea is that for each project I'm working on, I have a shell that starts out in that project's directory, so I can easily switch between them.
Currently I have this code:
(defun shell-dir (name dir)
(interactive "sShell name: \nDDirectory: ")
(shell name)
(switch-to-buffer name)
(comint-send-string (current-buffer) (concat "cd " dir "\r"))
(sleep-for 0 10)
(dirs))
(defun make-shells ()
(interactive)
(shell-dir "project1" "~/proj/project1")
(shell-dir "project2" "~/proj/project2")
(shell-dir "project3" "~/proj/project3")
(delete-window))
This is pretty ugly, though, and half the time (dirs) doesn't pick up the correct path, so tab completion breaks until I re-run it manually. Is there a built-in way to set the current working directory of the emacs shell? Or would something like CEDET (plus less reliance on the shell vs. emacs modes) be a much better solution to this?
I experienced similar problems with the current directory tracking provided by Emacs, so I wrote one which solves the problem once and forever.
Check it out here.
The short version of what it does is that you modify your shell prompt to include a full path to the current directory (only when running inside Emacs), and the Emacs shell buffer will use that.
This means you never have to do M-x dirs again.
There's also the package dirtrack (shipped with Emacs) which does the same thing.
I like my version better because it removes the path from the prompt. I don't want to see the entire path in my prompt as my current directory is often very long.
Once you use one of the above two solutions, you can simplify your shell-dir routine to be:
(defun shell-dir (name dir)
(interactive "sShell name: \nDDirectory: ")
(let ((default-directory dir))
(shell name)))
One more answer... I found there was a way (on Linux) to make Emacs figure out the current directory properly, by using the /proc filesystem.
http://www.emacswiki.org/emacs/ShellDirtrackByProcfs
That way, you just have to start up the shell in whatever directory and Emacs will automatically figure it out and get the tab-completion etc. right.

Emacs + tramp + plink

I'm trying to get emacs tramp running under Windows XP to work over putty plink on an Amazon EC2 instance. The documentation for doing this is sparse. I can find partial documentation, but none that addresses all the steps required to get this working.
Can anyone provide a walk through, or a pointer to a walk through?
(add-to-list 'load-path
(expand-file-name "C:/tools/emacsw32/emacs/lisp/tramp/lisp"))
(require 'tramp)
;(setq tramp-chunksize "500")
(setq tramp-default-method "plink")
from my dot-emacs file. If I find more notes, I shall add them here.
I'll assume you have a GNU/Linux server you want to access, a username and a .ppk file. Also, Emacs 24.4+.
First set up server in PuTTY Configuration
In section Session, specify Host Name, for example username#server.
Go to section Connection > SSH > Auth and Browse for your "Private key file for authentication".
Back to section Session, name your Saved Sessions, for example putty-test, and click Save button.
Check your connection by clicking the Open button. If it works, you can close these now.
Next, head to your Emacs.
Make sure Emacs knows where your plink.exe is. One way is to just inform Emacs directly in your .emacs, for instance I have at the moment,
(setenv "PATH" (concat "c:/Users/Brady/Documents/putty/;" (getenv "PATH")))
Simply type C-x C-f //plink:putty-test:/ RET. Wait a moment while it connects, and window will open to dired buffer on the remote ~/ directory.
This worked for me on :
Windows 10
Emacs found at https://sourceforge.net/projects/emacsbinw64/files/release/.
cygwin64
Putty.
https://github.com/d5884/fakecygpty
The changes from the original tramp-sh.el is
for cygwin, use fakecygpty with ssh and change the prompt to ##
for plink, remove -ssh option
I have also renamed these method with w to differentiate it.
(when (string-equal system-type "windows-nt")
(add-to-list 'tramp-methods
`("sshw"
(tramp-login-program "fakecygpty ssh")
;; ("%h") must be a single element, see `tramp-compute-multi-hops'.
(tramp-login-args (("-l" "%u" "-o \"StrictHostKeyChecking=no\"") ("-P" "%p") ("-t")
("%h") ("\"")
(,(format
"env 'TERM=%s' 'PROMPT_COMMAND=' 'PS1=%s'"
tramp-terminal-type
"##"))
("/bin/sh") ("\"")))
(tramp-remote-shell "/bin/sh")
(tramp-remote-shell-login ("-l"))
(tramp-remote-shell-args ("-c"))
(tramp-default-port 22))
)
(add-to-list 'tramp-methods
`("plinkw"
(tramp-login-program "plink")
;; ("%h") must be a single element, see `tramp-compute-multi-hops'.
(tramp-login-args (("-l" "%u") ("-P" "%p") ("-t")
("%h") ("\"")
(,(format
"env 'TERM=%s' 'PROMPT_COMMAND=' 'PS1=%s'"
tramp-terminal-type
"$"))
("/bin/sh") ("\"")))
(tramp-remote-shell "/bin/sh")
(tramp-remote-shell-login ("-l"))
(tramp-remote-shell-args ("-c"))
(tramp-default-port 22))
)
)

Dired copy asynchronously

Is there a way to modify/tell dired to copy files asynchronously? If you mark multiple files in dired and then use 'C' to copy them, emacs locks up until every file is copied. I instead want this copy to be started, and for me to continue editing as it goes on in the background. Is there a way to get this behaviour?
EDIT: Actually, C calls 'dired-do-copy' in dired-aux, not in dired itself. Sorry for any confusion.
I think emacs is mostly limited to a single thread - so this may not be directly possible through standard dired commands such as 'C' copy.
However, there is a dired command "dired-do-shell-command" which calls out to a shell to do the work in the background. If you select the files you want to copy and then use key '!' (this runs dired-do-shell-command) then type 'cp ? [destination]' (possibly can use 'copy' if you are on windows). I haven't tested this - so see help on "dired-do-shell-command" for full details.
See also the Emacs function dired-do-async-shell-command.
For an even more generic solution see https://github.com/jwiegley/emacs-async with which you also can evaluate arbitrary Emacs Lisp code through call to a separate Emacs process (which of course incurs a bit of extra latency). More specifically regard file operations see the file dired-async.el in this repo.
Also note that there is work on threading in Emacs under the working name Concurrent Emacs but it's not there yet. See http://www.emacswiki.org/emacs/ConcurrentEmacs for details.
I found this answer quite helpful: https://emacs.stackexchange.com/a/13802/10761. Reading that answer shows that you can make it so that dired will copy with the scp method instead of the ssh method (the latter initially encodes the file with gzip and that can be quite slow). The scp method will only copy with the scp program when the file is larger than tramp-copy-size-limit (which is 10240 by default). Using this scp method in conjunction with dired-async-mode is very nice, as it will not only copy quickly with scp, but it will also do it asynchronously and out of your way.
Also, I think this is useful: https://oremacs.com/2016/02/24/dired-rsync/. It provides this snippet of code to use rsync to copy files in dired:
;;;###autoload
(defun ora-dired-rsync (dest)
(interactive
(list
(expand-file-name
(read-file-name
"Rsync to:"
(dired-dwim-target-directory)))))
;; store all selected files into "files" list
(let ((files (dired-get-marked-files
nil current-prefix-arg))
;; the rsync command
(tmtxt/rsync-command
"rsync -arvz --progress "))
;; add all selected file names as arguments
;; to the rsync command
(dolist (file files)
(setq tmtxt/rsync-command
(concat tmtxt/rsync-command
(shell-quote-argument file)
" ")))
;; append the destination
(setq tmtxt/rsync-command
(concat tmtxt/rsync-command
(shell-quote-argument dest)))
;; run the async shell command
(async-shell-command tmtxt/rsync-command "*rsync*")
;; finally, switch to that window
(other-window 1)))
(define-key dired-mode-map "Y" 'ora-dired-rsync)