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:

  1. 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

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

    ReplyDelete
  3. 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"
    }

    ReplyDelete
  4. 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.

    ReplyDelete
  5. 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/

    ReplyDelete
  6. 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.

    ReplyDelete
  7. @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.

    ReplyDelete
  8. 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.

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

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

    ReplyDelete
  11. 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?

    ReplyDelete
  12. 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"; }

    ReplyDelete
  13. 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"

    ReplyDelete