(defgroup eshell-tramp nil
"This module defines commands that use TRAMP in a way that is
not transparent to the user. So far, this includes only the
- built-in su and sudo commands, which are not compatible with
- the full, external su and sudo commands, and require the user
- to understand how to use the TRAMP sudo method."
+ built-in su, sudo and doas commands, which are not compatible
+ with the full, external su, sudo, and doas commands, and
+ require the user to understand how to use the TRAMP sudo
+ method."
:tag "TRAMP Eshell features"
:group 'eshell-module))
(add-hook 'pcomplete-try-first-hook
'eshell-complete-host-reference nil t))
(setq-local eshell-complex-commands
- (append '("su" "sudo")
+ (append '("su" "sudo" "doas")
eshell-complex-commands)))
(autoload 'eshell-parse-command "esh-cmd")
(put 'eshell/su 'eshell-no-numeric-conversions t)
+(defun eshell--method-wrap-directory (directory method &optional user)
+ "Return DIRECTORY as accessed by a Tramp METHOD for USER."
+ (let ((user (or user "root"))
+ (dir (file-local-name (expand-file-name directory)))
+ (prefix (file-remote-p directory))
+ (host (or (file-remote-p directory 'host)
+ tramp-default-host))
+ (rmethod (file-remote-p directory 'method))
+ (ruser (file-remote-p directory 'user)))
+ (if (and prefix (or (not (string-equal rmethod method))
+ (not (string-equal ruser user))))
+ (format "%s|%s:%s@%s:%s"
+ (substring prefix 0 -1) method user host dir)
+ (format "/%s:%s@%s:%s" method user host dir))))
+
(defun eshell/sudo (&rest args)
"Alias \"sudo\" to call Tramp.
"sudo" args
'((?h "help" nil nil "show this usage screen")
(?u "user" t user "execute a command as another USER")
+ (?s "shell" nil shell "start a shell instead of executing COMMAND")
:show-usage
:parse-leading-options-only
- :usage "[(-u | --user) USER] COMMAND
+ :usage "[(-u | --user) USER] (-s | --shell) | COMMAND
Execute a COMMAND as the superuser or another USER.")
- (throw 'eshell-external
- (let* ((user (or user "root"))
- (host (or (file-remote-p default-directory 'host)
- tramp-default-host))
- (dir (file-local-name (expand-file-name default-directory)))
- (prefix (file-remote-p default-directory))
- (default-directory
- (if (and prefix
- (or
- (not
- (string-equal
- "sudo"
- (file-remote-p default-directory 'method)))
- (not
- (string-equal
- user
- (file-remote-p default-directory 'user)))))
- (format "%s|sudo:%s@%s:%s"
- (substring prefix 0 -1) user host dir)
- (format "/sudo:%s@%s:%s" user host dir))))
- (eshell-named-command (car args) (cdr args))))))
+ (let ((dir (eshell--method-wrap-directory default-directory "sudo" user)))
+ (if shell
+ (throw 'eshell-replace-command
+ (eshell-parse-command "cd" (list dir)))
+ (throw 'eshell-external
+ (let ((default-directory dir))
+ (eshell-named-command (car args) (cdr args))))))))
(put 'eshell/sudo 'eshell-no-numeric-conversions t)
+(defun eshell/doas (&rest args)
+ "Call Tramp's doas method with ARGS.
+
+Uses the system doas through Tramp's doas method."
+ (eshell-eval-using-options
+ "doas" args
+ '((?h "help" nil nil "show this usage screen")
+ (?u "user" t user "execute a command as another USER")
+ (?s "shell" nil shell "start a shell instead of executing COMMAND")
+ :show-usage
+ :parse-leading-options-only
+ :usage "[(-u | --user) USER] (-s | --shell) | COMMAND
+Execute a COMMAND as the superuser or another USER.")
+ (let ((dir (eshell--method-wrap-directory default-directory "doas" user)))
+ (if shell
+ (throw 'eshell-replace-command
+ (eshell-parse-command "cd" (list dir)))
+ (throw 'eshell-external
+ (let ((default-directory dir))
+ (eshell-named-command (car args) (cdr args))))))))
+
+(put 'eshell/doas 'eshell-no-numeric-conversions t)
+
(provide 'em-tramp)
;; Local Variables:
`(,(format "/sudo:USER@%s:%s" tramp-default-host default-directory)
("echo" ("-u" "hi")))))))
+(ert-deftest em-tramp-test/sudo-shell ()
+ "Test Eshell `sudo' command with -s/--shell option."
+ (dolist (args '(("--shell")
+ ("-s")))
+ (should (equal
+ (catch 'eshell-replace-command (apply #'eshell/sudo args))
+ `(eshell-trap-errors
+ (eshell-named-command
+ "cd"
+ (list ,(format "/sudo:root@%s:%s"
+ tramp-default-host default-directory))))))))
+
+(ert-deftest em-tramp-test/sudo-user-shell ()
+ "Test Eshell `sudo' command with -s and -u options."
+ (should (equal
+ (catch 'eshell-replace-command (eshell/sudo "-u" "USER" "-s"))
+ `(eshell-trap-errors
+ (eshell-named-command
+ "cd"
+ (list ,(format "/sudo:USER@%s:%s"
+ tramp-default-host default-directory)))))))
+
+(ert-deftest em-tramp-test/doas-basic ()
+ "Test Eshell `doas' command with default user."
+ (cl-letf (((symbol-function 'eshell-named-command)
+ #'mock-eshell-named-command))
+ (should (equal
+ (catch 'eshell-external (eshell/doas "echo" "hi"))
+ `(,(format "/doas:root@%s:%s"
+ tramp-default-host default-directory)
+ ("echo" ("hi")))))
+ (should (equal
+ (catch 'eshell-external (eshell/doas "echo" "-u" "hi"))
+ `(,(format "/doas:root@%s:%s"
+ tramp-default-host default-directory)
+ ("echo" ("-u" "hi")))))))
+
+(ert-deftest em-tramp-test/doas-user ()
+ "Test Eshell `doas' command with specified user."
+ (cl-letf (((symbol-function 'eshell-named-command)
+ #'mock-eshell-named-command))
+ (should (equal
+ (catch 'eshell-external (eshell/doas "-u" "USER" "echo" "hi"))
+ `(,(format "/doas:USER@%s:%s"
+ tramp-default-host default-directory)
+ ("echo" ("hi")))))
+ (should (equal
+ (catch 'eshell-external
+ (eshell/doas "-u" "USER" "echo" "-u" "hi"))
+ `(,(format "/doas:USER@%s:%s"
+ tramp-default-host default-directory)
+ ("echo" ("-u" "hi")))))))
+
+(ert-deftest em-tramp-test/doas-shell ()
+ "Test Eshell `doas' command with -s/--shell option."
+ (dolist (args '(("--shell")
+ ("-s")))
+ (should (equal
+ (catch 'eshell-replace-command (apply #'eshell/doas args))
+ `(eshell-trap-errors
+ (eshell-named-command
+ "cd"
+ (list ,(format "/doas:root@%s:%s"
+ tramp-default-host default-directory))))))))
+
+(ert-deftest em-tramp-test/doas-user-shell ()
+ "Test Eshell `doas' command with -s and -u options."
+ (should (equal
+ (catch 'eshell-replace-command (eshell/doas "-u" "USER" "-s"))
+ `(eshell-trap-errors
+ (eshell-named-command
+ "cd"
+ (list ,(format "/doas:USER@%s:%s"
+ tramp-default-host default-directory)))))))
+
;;; em-tramp-tests.el ends here