summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBrian Cully <bjc@kublai.com>2022-10-26 21:10:21 -0400
committerJim Porter <jporterbugs@gmail.com>2022-11-05 12:05:05 -0700
commit062d16c3ebed270aa1cb64b6b6cd09c36002d4a5 (patch)
tree102387d967fa933aefc35166441eac4dfe521c84
parent8a49a888532c955ae81855636a65b446508cb361 (diff)
downloademacs-062d16c3ebed270aa1cb64b6b6cd09c36002d4a5.tar.gz
Add the "doas" alias to eshell.
* lisp/eshell/em-tramp.el (eshell/doas): new function. (eshell--method-wrap-directory): new function. (eshell/sudo): accept '-s'/'--shell' for interactive use. * test/lisp/eshell/em-tramp-tests.el (em-tramp-test/sudo-shell) (em-tramp-test/sudo-user-shell) (em-tramp-test/doas-basic) (em-tramp-test/doas-user) (em-tramp-test/doas-shell) (em-tramp-test/doas-user-shell): new tests. * etc/NEWS: mention new 'doas' eshell command. * doc/misc/eshell.texi: add 'doas' command documentation.
-rw-r--r--doc/misc/eshell.texi9
-rw-r--r--etc/NEWS5
-rw-r--r--lisp/eshell/em-tramp.el78
-rw-r--r--test/lisp/eshell/em-tramp-tests.el75
4 files changed, 138 insertions, 29 deletions
diff --git a/doc/misc/eshell.texi b/doc/misc/eshell.texi
index ff368c9dc41..96873a3f9a5 100644
--- a/doc/misc/eshell.texi
+++ b/doc/misc/eshell.texi
@@ -717,9 +717,12 @@ current environment.
@cmindex su
@itemx sudo
@cmindex sudo
-Uses TRAMP's @command{su} or @command{sudo} method @pxref{Inline methods, , , tramp}
-to run a command via @command{su} or @command{sudo}. These commands
-are in the eshell-tramp module, which is disabled by default.
+@itemx doas
+@cmindex doas
+Uses TRAMP's @command{su}, @command{sudo}, or @command{doas} method
+@pxref{Inline methods, , , tramp} to run a command via @command{su},
+@command{sudo}, or @command{doas}. These commands are in the
+eshell-tramp module, which is disabled by default.
@item substitute
diff --git a/etc/NEWS b/etc/NEWS
index df755c6ed12..89da8aa63f7 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -370,6 +370,11 @@ node in the Eshell manual for more details.
*** Eshell pipelines now only pipe stdout by default.
To pipe both stdout and stderr, use the '|&' operator instead of '|'.
+*** New eshell built-in command 'doas'.
+The privilege-escalation program 'doas' has been added to the existing
+'su' and 'sudo' commands from the 'eshell-tramp' module. The external
+command may still be accessed by using '*doas'.
+
---
** The 'delete-forward-char' command now deletes by grapheme clusters.
This command is by default bound to the <Delete> function key
diff --git a/lisp/eshell/em-tramp.el b/lisp/eshell/em-tramp.el
index aebbc36e71d..3daac1db3b4 100644
--- a/lisp/eshell/em-tramp.el
+++ b/lisp/eshell/em-tramp.el
@@ -40,9 +40,10 @@
(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))
@@ -52,7 +53,7 @@
(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")
@@ -91,6 +92,21 @@ Become another USER during a login session.")
(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.
@@ -99,34 +115,44 @@ Uses the system sudo through TRAMP's sudo method."
"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:
diff --git a/test/lisp/eshell/em-tramp-tests.el b/test/lisp/eshell/em-tramp-tests.el
index 8969c1e2294..6cc35ecdb1b 100644
--- a/test/lisp/eshell/em-tramp-tests.el
+++ b/test/lisp/eshell/em-tramp-tests.el
@@ -85,4 +85,79 @@
`(,(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