Re-open read-only files as root automagically

Posted: August 20, 2008 in Emacs, GNU/Linux
Tags: ,

I have my emacs running as server and two small wrapper scripts ec and et which connect to it with emacsclient (either creating a new frame or opening a terminal frame).

Because I want to do all my system administration (editing files in /etc/) with emacs and don’t want to start another emacs instance as root, I use TRAMP to switch to superuser mode automagically if the opened file is read-only.

Here’s the code (UPDATED: Uses defadvice instead of hooking into find-file-hook, which had the bad effect of find-file-hook running twice.):

(defun th-rename-tramp-buffer ()
  (when (file-remote-p (buffer-file-name))
    (rename-buffer
     (format "%s:%s"
             (file-remote-p (buffer-file-name) 'method)
             (buffer-name)))))

(add-hook 'find-file-hook
          'th-rename-tramp-buffer)

(defadvice find-file (around th-find-file activate)
  "Open FILENAME using tramp's sudo method if it's read-only."
  (if (and (not (file-writable-p (ad-get-arg 0)))
           (y-or-n-p (concat "File "
                             (ad-get-arg 0)
                             " is read-only.  Open it as root? ")))
      (th-find-file-sudo (ad-get-arg 0))
    ad-do-it))

(defun th-find-file-sudo (file)
  "Opens FILE with root privileges."
  (interactive "F")
  (set-buffer (find-file (concat "/sudo::" file))))

Now whenever I type ec /some/file/i/have/no/permissions/to/write (or emacsclient [-c|-t] /some/root/file, emacs asks me to re-open it using TRAMP’s sudo method.

Advertisements
Comments
  1. paul r says:

    Hi,
    I use something similar here, and I also use this little hack :

    8<——————————–
    (defun my-tramp-header-line-function ()
    (when (string-match “^/su\\(do\\)?:” default-directory)
    (setq header-line-format
    (format-mode-line “—– THIS BUFFER IS VISITED WITH ROOT PRIVILEGES —–”
    ‘font-lock-warning-face))))

    (add-hook ‘find-file-hooks ‘my-tramp-header-line-function)
    8<——————————–

    so that I’m warned whenever I’m editing something as root. It would be redundant with your hack to prefix buffer name with sudo, though.

  2. JFM says:

    I also do something similar, except that along with my ’em’ script (which runs emacsclient, emacsclient -nw, or emacs as needed), I also have a script called ‘se’ (for sudo edit), which rewrites the filename as a tramp sudo path and then calls my ’em’ script.

  3. iNode says:

    How do you use it with zsh?
    What tramp-shell-prompt-pattern variable value?
    (I’m can’t do it working with zsh)

  4. Tassilo Horn says:

    Hi iNode,

    with ZSH you have to disable the Z Line Editor. So I have this in my ~/.zshrc:

    if [[ ${TERM} != “dumb” ]]; then
    # Setup my default prompt
    else
    prompt off
    # switch of the zsh line editor, cause emacs shell and TRAMP doesn’t work
    # with it.
    unsetopt zle
    fi

  5. Niels says:

    Hi

    That is some very usefull code :) However as I have several files I own that is read-only (files under version control with rcs), it would be nice not to be asked about opening my own files as root. I thought that should be easy to change even with my limited elisp knowledge. I have tried some thing like this:

    (defun th-find-file-sudo-maybe ()
    “Re-finds the current file as root if it’s read-only after
    querying the user.”
    (interactive)
    (let ((file (buffer-file-name)))
    (and
    ; (not (file-writable-p file))
    (equal 0 (nth 3 (file-attributes file)))
    (y-or-n-p “File is owned by root. Open it as root? “)
    (progn
    (kill-buffer (current-buffer))
    (th-find-file-sudo file)))))

    Well, it works, but it asks the question “File is owned by root. Open it as root? ” two times!? I have no clue why, as testing if the file is owned by root

    (equal 0 (nth 3 (file-attributes file)))

    returns t just as

    (not (file-writable-p file))

  6. Tassilo Horn says:

    I guess your problem is due to find-file-hook being run twice. I post a better version which uses defadvice.

    Bye,
    Tassilo

  7. Fernando says:

    Put the buffer renaming within the advice, and added special case for when you are the owner of the file:

    (defadvice find-file (around my-find-file activate)
    “Open FILENAME using tramp’s sudo method if it’s read-only and
    not owned by current user.”
    (let* ((my-filename (ad-get-arg 0))
    (file-owner-uid (nth 2 (file-attributes my-filename))))

    (if (not (file-writable-p my-filename))
    (if (and (not (= file-owner-uid (user-uid)))
    (y-or-n-p (concat “File ” my-filename ” is read-only. Open it as root? “)))
    (progn
    (ad-set-arg 0 (concat “/sudo::” my-filename))
    ad-do-it
    (rename-buffer
    (format “%s:%s”
    (file-remote-p (buffer-file-name) ‘method)
    (buffer-name))))

    (if (and (= file-owner-uid (user-uid))
    (y-or-n-p (concat “File ” my-filename ” is read-only. Make buffer writable? “)))
    (progn
    ad-do-it
    (toggle-read-only -1))))

    ad-do-it)))

  8. hi mate, happy holiday and i enjoy my time here.

  9. anime says:

    Working as root, problem solved. If you really value your convenience far beyond the security …

    It occurs to me also that I edit a protected file and forget sudo. But my editors let me know before I can change anything, because it simply will not let me (because the file is read-only. If I can even open it.) But then the output, press Ctrl-a, “sudo”, ENTER , and that’s it (yes, I uploaded the timeout a little.)

    But … It really has nothing to do and often result. Oh, and I do not use Gnome / KDE, so annoying pop-ups do not happen. ;-)

    Closing, let me quote the Eagles “Get Over It.” Just get used to it. It will benefit in the long term. Otherwise, you can not probably get used to sloppy security. And that can hurt others too.

  10. stan says:

    I can’t get this to work at all anymore. It used to work fine, but now after typing the password nothing happens. I’ve even tried putting the code in a separate file and “emacs -q -l seperatefile”. Same thing happens.

    • tsdh80 says:

      I’m still using this with a very current emacs bzr version, so in theory it should still work. What exactly doesn’t work? Do you get any error messages?

  11. stan says:

    I’ve found the error. I’t was zsh related, but none ot the comments here or on emacs wiki helped, except for the simplest solution (except for not using zsh). Everything works after putting:

    (eval-after-load ‘tramp ‘(setenv “SHELL” “/bin/bash”))

    • tsdh80 says:

      Well, then your solution is to use bash instead of zsh for tramp. That doesn’t make a difference for the purpose of this posting, but rest assured, it should also work with zsh. Maybe your zsh copy was updated and now you have a super-funky prompt that tramp doesn’t understand anymore? To cope with that, I have this in my ~/.zshrc:

      #** Running inside Emacs or a dumb terminal
      
      if [[ -n ${INSIDE_EMACS} || ${TERM} == "dumb" ]]; then
          prompt walters
          unsetopt zle
      fi

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s