Skip to content

Commit

Permalink
Fix tramp sudo syntax; Enable support for sudo to users beside root; …
Browse files Browse the repository at this point in the history
…Ask the user before reopen; Replace find-file advice with hook
  • Loading branch information
Dragoncraft89 committed Feb 3, 2022
1 parent 0dec9e6 commit 7c70781
Showing 1 changed file with 75 additions and 35 deletions.
110 changes: 75 additions & 35 deletions auto-sudoedit.el
Original file line number Diff line number Diff line change
Expand Up @@ -14,31 +14,50 @@

(require 'f)
(require 'tramp)
(require 'recentf)
(require 'dired)

(defun auto-sudoedit-path (curr-path)
"To convert path to tramp using sudo path.
Argument CURR-PATH is current path.
The result is string or nil.
The nil when will be not able to connect by sudo."
;; trampのpathに変換します
(let ((tramp-path
(let* ((file-owner (auto-sudoedit-file-owner curr-path))
(tramp-path
(if (tramp-tramp-file-p curr-path)
(auto-sudoedit-path-from-tramp-ssh-like curr-path)
(auto-sudoedit-path-from-tramp-ssh-like curr-path file-owner)
(concat "/sudo::" curr-path))))
(if (and
;; Current path may not exist; back up to the first existing parent
;; and see if it's writable
(let ((first-existing-path (f-traverse-upwards #'f-exists? curr-path)))
(not (and first-existing-path (f-writable? first-existing-path))))
;; 変換前のパスと同じでなく(2回めの変換はしない)
(not (equal curr-path tramp-path))
;; sudoで開ける場合は変換したものを返します
(f-writable? tramp-path))
tramp-path
nil)))

(defun auto-sudoedit-path-from-tramp-ssh-like (curr-path)
"Argument CURR-PATH is tramp path(that use protocols such as ssh)."
(if (and
;; We must know the file owner's login name
;; If we can't, we don't know which user to sudo as
file-owner
;; The file owner must be different from our current user so that the sudo makes sense
(not (string= file-owner (auto-sudoedit-current-user curr-path)))
;; Current path may not exist; back up to the first existing parent
;; and see if it's writable
;(let ((first-existing-path (f-traverse-upwards #'f-exists? curr-path)))
; (not (and first-existing-path (f-writable? first-existing-path))))
;; 変換前のパスと同じでなく(2回めの変換はしない)
(not (equal curr-path tramp-path))
;; sudoで開ける場合は変換したものを返します
);(f-writable? curr-path))
(cons file-owner tramp-path)
'(nil . nil))))

(defun auto-sudoedit-file-owner (path)
"Determine the login name of the user PATH belongs to."
(file-attribute-user-id (file-attributes path 'string)))

(defun auto-sudoedit-current-user (path)
"Determine the user name visiting PATH. E.g. local Emacs user or ssh login."
(if (tramp-tramp-file-p path)
;; We can't just go by the user in the tramp filename, because it may have been omitted
(tramp-get-remote-uid (tramp-dissect-file-name path) 'string)
(user-login-name)))

(defun auto-sudoedit-path-from-tramp-ssh-like (curr-path new-user)
"Argument CURR-PATH is tramp path(that use protocols such as ssh). NEW-USER is the user for sudo."
(let* ((file-name (tramp-dissect-file-name curr-path))
(method (tramp-file-name-method file-name))
(user (tramp-file-name-user file-name))
Expand All @@ -47,11 +66,10 @@ The nil when will be not able to connect by sudo."
(localname (tramp-file-name-localname file-name))
(hop (tramp-file-name-hop file-name))
(new-method "sudo")
(new-user "root")
(new-host host)
(new-port port)
(new-localname localname)
(new-hop (format "%s%s%s:%s%s|" (or hop "") method (if user (concat user "@") "") host (if port (concat port "#") ""))))
(new-hop (format "%s%s:%s%s%s|" (or hop "") method (if user (concat user "@") "") host (if port (concat port "#") ""))))
;; 最終メソッドがsudoである場合それ以上の変換は無意味なので行わない。
(if (equal method "sudo")
curr-path
Expand All @@ -71,18 +89,38 @@ The nil when will be not able to connect by sudo."
(defun auto-sudoedit-sudoedit (curr-path)
"Open sudoedit. Argument CURR-PATH is path."
(interactive (list (auto-sudoedit-current-path)))
(find-file (auto-sudoedit-path curr-path)))

(defun auto-sudoedit (orig-func &rest args)
"`auto-sudoedit' around-advice.
Argument ORIG-FUNC is original function.
Argument ARGS is original function arguments."
(let* ((curr-path (car args))
(tramp-path (auto-sudoedit-path curr-path)))
(when curr-path
(if tramp-path
(apply orig-func tramp-path (cdr args))
(apply orig-func args)))))
(find-file (cdr (auto-sudoedit-path curr-path))))

(defun auto-sudoedit ()
"`auto-sudoedit' hook for `find-file'. Reopen the buffer via tramp with sudo method."
(let* ((curr-path (auto-sudoedit-current-path))
(remote-info (auto-sudoedit-path curr-path))
(user (car remote-info))
(tramp-path (cdr remote-info)))
(when (and curr-path tramp-path (y-or-n-p (format "This buffer belongs to user %s. Reopen this buffer as user %s? " user user)))
;; We have to tell emacs that this buffer now visits another file (actually the same one, just via tramp sudo)
;; We have to do things differently for normal files and for dired
(when buffer-file-name
(set-visited-file-name tramp-path t))
(when dired-directory
;; Remove the buffer as displaying the old directory path in dired's active buffer list
(dired-unadvertise dired-directory)
(setq list-buffers-directory tramp-path)
(setq dired-directory tramp-path)
(setq default-directory tramp-path)
;; Insert the new directory path in dired's active buffer list
(dired-advertise))
;; Remove the old filename from the recentf-list
;; TODO: Is this a good idea? Could this break something?
(when (string= (car recentf-list) curr-path)
(pop recentf-list))
;; We have changed the way emacs edits the file
;; Therefore we have to reinitialize the buffer (read-only, etc.)
;; Also the file may have not been readable before
;; Revert buffer fixes this for us.
;; Use the arguments to prevent user confirmation
;; (There are no changes that could be discarded in the buffer anyways, it was just opened)
(revert-buffer t t))))

;;;###autoload
(define-minor-mode
Expand All @@ -91,11 +129,13 @@ Argument ARGS is original function arguments."
:init-value 0
:lighter " ASE"
(if auto-sudoedit-mode
(progn
(advice-add 'find-file :around 'auto-sudoedit)
(advice-add 'dired :around 'auto-sudoedit))
(advice-remove 'find-file 'auto-sudoedit)
(advice-remove 'dired 'auto-sudoedit)))
(progn
(add-hook 'find-file-hook #'auto-sudoedit)
(add-hook 'dired-mode-hook #'auto-sudoedit))
(remove-hook 'find-file-hook #'auto-sudoedit)
(remove-hook 'dired-mode-hook #'auto-sudoedit)))

(auto-sudoedit-mode t)

(provide 'auto-sudoedit)

Expand Down

0 comments on commit 7c70781

Please sign in to comment.