2011-12-10

system administration with emacs

When performing system administration tasks, one often needs to edit files owned by root.

For both security and safety reasons, it's a good idea to do as little as possible as root (or with root privileges). For that reason, you probably don't want to run Emacs as 'root', because it's simply too powerful. I often see people use vi (usually, vim) instead – but since it allows you to do just about anything as well (like running shell commands), that's not much of an improvement.

Now, a while back we discussed editing files owned by root with tramp – you can use emacs with your normal user-account, and use sudo to write the root-owned file. That makes it much harder to screw things up.

Another reason people use vi for little editing jobs is because its startup time is significantly shorter than the startup time for a new emacs instance. For that, however, we have emacs daemon.

Combining tramp and emacs daemon and a shell function:

# edit file with root privs
function E() {
         emacsclient -c -a emacs "/sudo:root@localhost:$1"
}               

Now, we can very quickly edit any file owned by root using 'E' --

$ E /etc/hosts

So, if you prefer emacs, there's little reason to use vi, even for editing system files – although I have to admit that it takes time to evict 'sudo vi <system file>' from my muscle memory.

Update reader Yi Wang mentions that, in fact, we can make this a bit more general using sudoedit; so, instead of using Tramp, we can use:

# edit file with root privs
alias E="SUDO_EDITOR=\"emacsclient -c -a emacs\" sudoedit"   

This works also without absolute paths.

13 comments:

Yi Wang said...

Yes, I also used to edit via tramp. There is one annoying thing: I need to provide the absolute path. It doesn't work to "cd /etc" and then "E hosts".

Now I use an alternative way: export emacs as the user's default editor, and then sudoedit /etc/hosts

djcb said...

@Yi Wang: ah, that's clever; 'sudoedit' copies the file in the background to basically do the same thing that tramp does.

Jyothis said...

slightly modified version which takes care of relative paths:
function E() {
filename=$1
without_beg_slash="${1##/}"
if [[ $without_beg_slash == $1 ]];then
filename="${PWD%//}/$1"
fi
emacsclient -c -a emacs "/sudo:root@localhost:$filename"
}

Anonymous said...

Very useful.

How do you make both a frame- and terminal version of Yi Wang's solution?

export T_EDITOR="emacsclient -t"

and then using this for the terminal version didn't work.

Anonymous said...

Here is a way to reopen read only files with sudo automatically from inside Emacs :

http://tsdh.wordpress.com/2008/08/20/re-open-read-only-files-as-root-automagically/

Thomas Koch said...

man sudoedit:

The editor specified by the policy is run to edit the temporary files. The sudoers policy uses the SUDO_EDITOR, VISUAL and EDITOR environment variables (in that order). If none of SUDO_EDITOR, VISUAL or EDITOR are set, the first program listed in the editor sudoers(5) option is used.

djcb said...

@Anonymous: to have a separate terminal version, just define a second macro (in your .bashrc or equivalent):

alias T="SUDO_EDITOR=\"emacsclient -t -a emacs\" sudoedit"

now, "T" should do the right thing.

Anonymous said...

Just tried your solution and remembered what I disliked about it: Backupfiles in my homedirectory.

I saw a way to prevent this on the emacswiki somewhere, but I'd personally rather not take that risk.

Alexander Kojevnikov said...

Another option is to use zile which is a lightweight editor with Emacs shortcuts.

Jianing YANG said...

The tramp method is still useful since there is no 'sudoedit' on Mac OSX.

Anonymous said...

Can anyone see right offhand why this does NOT work?

Started emacs daemon with:
emacs --daemon=prog

So emacs daemon named prog running in bg.

Now in .bashrc:
'(set-background-color "DeepPink4")' -e '(set-foreground-color "beige")' -e '(set-cursor-color "khaki")'E () { emacsclient -e '(set-background-color "DeepPink4")' -e '(set-foreground-color "beige")' -e '(set-cursor-color "khaki")' -s prog -c -a emacs "/sudo:root@localhost:$1"; }

But when called like:
`E /root/somefile'

The frame is created for a moment and then closes. I see the error in xterm:
E /root/new_crontab
x
x
nil
-error Symbol's value as variable is void: /sudo:root@localhost:/root/new_crontab
'(set-background-color "DeepPink4")' -e '(set-foreground-color "beige")' -e '(set-cursor-color "khaki")'

What am I doing wrong?

Anonymous said...

A little piece of the .bashrc function got omitted so reproduced here in full:

E () { emacsclient -e '(set-background-color "DeepPink4")' -e '(set-foreground-color "beige")' -e '(set-cursor-color "khaki")'emacsclient -e '(set-background-color "DeepPink4")' -e '(set-foreground-color "beige")' -e '(set-cursor-color "khaki")' -s prog -c -a emacs "/sudo:root@localhost:$1"; }

Anonymous said...

function E() {
emacsclient -t -a "" "/sudo::$@"
}

This starts a new daemon as the user if it's not running already (-a "") and uses TRAMP so that there doesn't have to be another daemon for "root"