From c5814e7f24558a98e5151811228b637127d72968 Mon Sep 17 00:00:00 2001 From: Jim Porter Date: Tue, 10 Jan 2023 15:35:18 -0800 Subject: [PATCH] Add 'user-uid-for-file' to get the effective UID for remote files In particular, this lets Eshell show a "#" root prompt sigil when the user has sudo'ed via "cd /sudo::" (bug#60722). * lisp/simple.el (user-uid-for-file): New function. * lisp/net/tramp.el (tramp-file-name-for-operation): Add 'user-uid-for-file'. (tramp-handle-user-uid-for-file): New function. * lisp/net/tramp-adb.el (tramp-adb-file-name-handler-alist): * lisp/net/tramp-crypt.el (tramp-crypt-file-name-handler-alist): * lisp/net/tramp-gvfs.el (tramp-gvfs-file-name-handler-alist): * lisp/net/tramp-rclone.el (tramp-rclone-file-name-handler-alist): * lisp/net/tramp-sh.el (tramp-sh-file-name-handler-alist): * lisp/net/tramp-smb.el (tramp-smb-file-name-handler-alist): * lisp/net/tramp-sshfs.el (tramp-sshfs-file-name-handler-alist): * lisp/net/tramp-sudoedit.el (tramp-sudoedit-file-name-handler-alist): Add 'user-uid-for-file'. * lisp/net/tramp-archive.el (tramp-archive-file-name-handler-alist): Add comment about 'user-uid-for-file'. * lisp/eshell/em-prompt.el (eshell-prompt-function): Use 'user-uid-for-file'. * lisp/eshell/esh-var.el (eshell-variable-aliases-list): Add '$UID'. * test/lisp/eshell/esh-var-tests.el (esh-var-test/uid-var): New test. * doc/lispref/os.texi (User Identification): Document 'user-uid-for-file'. * doc/lispref/files.texi (Magic File Names): Mention 'user-uid-for-file'. * doc/misc/eshell.texi (Variables): Document '$UID'. Add a missing index entry for '$INSIDE_EMACS'. * etc/NEWS: Announce 'user-uid-for-file'. --- doc/lispref/files.texi | 2 ++ doc/lispref/os.texi | 9 +++++++++ doc/misc/eshell.texi | 8 ++++++++ etc/NEWS | 5 +++++ lisp/eshell/em-prompt.el | 2 +- lisp/eshell/esh-var.el | 1 + lisp/net/tramp-adb.el | 1 + lisp/net/tramp-archive.el | 5 +++++ lisp/net/tramp-crypt.el | 1 + lisp/net/tramp-gvfs.el | 1 + lisp/net/tramp-rclone.el | 1 + lisp/net/tramp-sh.el | 1 + lisp/net/tramp-smb.el | 1 + lisp/net/tramp-sshfs.el | 1 + lisp/net/tramp-sudoedit.el | 1 + lisp/net/tramp.el | 10 ++++++++++ lisp/simple.el | 10 ++++++++++ test/lisp/eshell/esh-var-tests.el | 4 ++++ 18 files changed, 63 insertions(+), 1 deletion(-) diff --git a/doc/lispref/files.texi b/doc/lispref/files.texi index 5cc4c1e7ddf..7201bcc9c2c 100644 --- a/doc/lispref/files.texi +++ b/doc/lispref/files.texi @@ -3412,6 +3412,7 @@ Magic File Names @code{temporary-file-directory}, @code{unhandled-file-name-directory}, @code{unlock-file}, +@code{user-uid-for-file}, @code{vc-registered}, @code{verify-visited-file-modtime},@* @code{write-region}. @@ -3473,6 +3474,7 @@ Magic File Names @code{temporary-file-directory}, @code{unhandled-file-name-directory}, @code{unlock-file}, +@code{user-uid-for-file}, @code{vc-regis@discretionary{}{}{}tered}, @code{verify-visited-file-modtime}, @code{write-region}. diff --git a/doc/lispref/os.texi b/doc/lispref/os.texi index 3be7036f637..43f1b6976fc 100644 --- a/doc/lispref/os.texi +++ b/doc/lispref/os.texi @@ -1277,6 +1277,15 @@ User Identification This function returns the effective @acronym{UID} of the user. @end defun +@defun user-uid-for-file filename +This function returns the effective @acronym{UID} of the user +associated with the file named @var{filename}. If @var{filename} is +local, this is equivalent to @code{user-uid}, but for remote files +(@pxref{Remote Files, , , emacs, The GNU Emacs Manual}), it will +return the @acronym{UID} for the user associated with that remote +connection; if the remote connection has no associated user, it will +instead return -1. + @cindex GID @defun group-gid This function returns the effective @acronym{GID} of the Emacs process. diff --git a/doc/misc/eshell.texi b/doc/misc/eshell.texi index fc7d52eb711..c40ff58f42c 100644 --- a/doc/misc/eshell.texi +++ b/doc/misc/eshell.texi @@ -983,6 +983,13 @@ Variables the value will automatically update to reflect the search path on that host. +@vindex $UID +@item $UID +This returns the effective @acronym{UID} for the current user. This +variable is connection-aware, so when the current directory is remote, +its value will be @acronym{UID} for the user associated with that +remote connection. + @vindex $_ @item $_ This refers to the last argument of the last command. With a @@ -1014,6 +1021,7 @@ Variables copied to the environment, so external commands invoked from Eshell can consult them to do the right thing. +@vindex $INSIDE_EMACS @item $INSIDE_EMACS This variable indicates to external commands that they are being invoked from within Emacs so they can adjust their behavior if diff --git a/etc/NEWS b/etc/NEWS index 068f7a27db8..8a190600d44 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -235,6 +235,11 @@ compared reliably at all. This warning can be suppressed using 'with-suppressed-warnings' with the warning name 'suspicious'. ++++ +** New function 'user-uid-for-file'. +This function is like 'user-uid', but is aware of file name handlers, +so it will return the remote UID for remote files. + * Changes in Emacs 30.1 on Non-Free Operating Systems diff --git a/lisp/eshell/em-prompt.el b/lisp/eshell/em-prompt.el index 52d46282c52..36b6c5e0a1b 100644 --- a/lisp/eshell/em-prompt.el +++ b/lisp/eshell/em-prompt.el @@ -52,7 +52,7 @@ eshell-prompt-load-hook (defcustom eshell-prompt-function (lambda () (concat (abbreviate-file-name (eshell/pwd)) - (if (= (user-uid) 0) " # " " $ "))) + (if (= (user-uid-for-file default-directory) 0) " # " " $ "))) "A function that returns the Eshell prompt string. Make sure to update `eshell-prompt-regexp' so that it will match your prompt." diff --git a/lisp/eshell/esh-var.el b/lisp/eshell/esh-var.el index 811bb9957cf..ac48fd01bec 100644 --- a/lisp/eshell/esh-var.el +++ b/lisp/eshell/esh-var.el @@ -162,6 +162,7 @@ eshell-variable-aliases-list ("COLUMNS" ,(lambda () (window-body-width nil 'remap)) t t) ("LINES" ,(lambda () (window-body-height nil 'remap)) t t) ("INSIDE_EMACS" eshell-inside-emacs t) + ("UID" ,(lambda () (user-uid-for-file default-directory)) nil t) ;; for esh-ext.el ("PATH" (,(lambda () (string-join (eshell-get-path t) (path-separator))) diff --git a/lisp/net/tramp-adb.el b/lisp/net/tramp-adb.el index 493a9fb39a9..6a473d03e9f 100644 --- a/lisp/net/tramp-adb.el +++ b/lisp/net/tramp-adb.el @@ -188,6 +188,7 @@ tramp-adb-file-name-handler-alist (tramp-set-file-uid-gid . ignore) (unhandled-file-name-directory . ignore) (unlock-file . tramp-handle-unlock-file) + (user-uid-for-file . tramp-handle-user-uid-for-file) (vc-registered . ignore) (verify-visited-file-modtime . tramp-handle-verify-visited-file-modtime) (write-region . tramp-adb-handle-write-region)) diff --git a/lisp/net/tramp-archive.el b/lisp/net/tramp-archive.el index a2add1ed73a..e1f7a060783 100644 --- a/lisp/net/tramp-archive.el +++ b/lisp/net/tramp-archive.el @@ -300,6 +300,7 @@ tramp-archive-file-name-handler-alist (tramp-set-file-uid-gid . ignore) (unhandled-file-name-directory . ignore) (unlock-file . ignore) + (user-uid-for-file . tramp-archive-handle-user-uid-for-file) (vc-registered . ignore) (verify-visited-file-modtime . tramp-handle-verify-visited-file-modtime) (write-region . tramp-archive-handle-not-implemented)) @@ -701,6 +702,10 @@ tramp-archive-handle-temporary-file-directory (let ((default-directory (file-name-directory archive))) (temporary-file-directory)))) +(defun tramp-archive-handle-user-uid-for-file (filename) + "Like `user-uid-for-file' for file archives." + (user-uid-for-file (tramp-archive-gvfs-file-name filename))) + (defun tramp-archive-handle-not-implemented (operation &rest args) "Generic handler for operations not implemented for file archives." (let ((v (ignore-errors diff --git a/lisp/net/tramp-crypt.el b/lisp/net/tramp-crypt.el index 507fd432419..d2e4805a737 100644 --- a/lisp/net/tramp-crypt.el +++ b/lisp/net/tramp-crypt.el @@ -239,6 +239,7 @@ tramp-crypt-file-name-handler-alist (tramp-set-file-uid-gid . tramp-crypt-handle-set-file-uid-gid) (unhandled-file-name-directory . ignore) (unlock-file . tramp-crypt-handle-unlock-file) + (user-uid-for-file . tramp-handle-user-uid-for-file) (vc-registered . ignore) (verify-visited-file-modtime . tramp-handle-verify-visited-file-modtime) (write-region . tramp-handle-write-region)) diff --git a/lisp/net/tramp-gvfs.el b/lisp/net/tramp-gvfs.el index cca7a5fe247..66e5313e516 100644 --- a/lisp/net/tramp-gvfs.el +++ b/lisp/net/tramp-gvfs.el @@ -833,6 +833,7 @@ tramp-gvfs-file-name-handler-alist (tramp-set-file-uid-gid . tramp-gvfs-handle-set-file-uid-gid) (unhandled-file-name-directory . ignore) (unlock-file . tramp-handle-unlock-file) + (user-uid-for-file . tramp-handle-user-uid-for-file) (vc-registered . ignore) (verify-visited-file-modtime . tramp-handle-verify-visited-file-modtime) (write-region . tramp-handle-write-region)) diff --git a/lisp/net/tramp-rclone.el b/lisp/net/tramp-rclone.el index 4018fa3aa29..2d25668b3ac 100644 --- a/lisp/net/tramp-rclone.el +++ b/lisp/net/tramp-rclone.el @@ -153,6 +153,7 @@ tramp-rclone-file-name-handler-alist (tramp-set-file-uid-gid . ignore) (unhandled-file-name-directory . ignore) (unlock-file . tramp-handle-unlock-file) + (user-uid-for-file . tramp-handle-user-uid-for-file) (vc-registered . ignore) (verify-visited-file-modtime . tramp-handle-verify-visited-file-modtime) (write-region . tramp-handle-write-region)) diff --git a/lisp/net/tramp-sh.el b/lisp/net/tramp-sh.el index 4647600071c..c812300babf 100644 --- a/lisp/net/tramp-sh.el +++ b/lisp/net/tramp-sh.el @@ -1121,6 +1121,7 @@ tramp-sh-file-name-handler-alist (tramp-set-file-uid-gid . tramp-sh-handle-set-file-uid-gid) (unhandled-file-name-directory . ignore) (unlock-file . tramp-handle-unlock-file) + (user-uid-for-file . tramp-handle-user-uid-for-file) (vc-registered . tramp-sh-handle-vc-registered) (verify-visited-file-modtime . tramp-sh-handle-verify-visited-file-modtime) (write-region . tramp-sh-handle-write-region)) diff --git a/lisp/net/tramp-smb.el b/lisp/net/tramp-smb.el index d6f3cca9733..240a4523e36 100644 --- a/lisp/net/tramp-smb.el +++ b/lisp/net/tramp-smb.el @@ -304,6 +304,7 @@ tramp-smb-file-name-handler-alist (tramp-set-file-uid-gid . ignore) (unhandled-file-name-directory . ignore) (unlock-file . tramp-handle-unlock-file) + (user-uid-for-file . tramp-handle-user-uid-for-file) (vc-registered . ignore) (verify-visited-file-modtime . tramp-handle-verify-visited-file-modtime) (write-region . tramp-smb-handle-write-region)) diff --git a/lisp/net/tramp-sshfs.el b/lisp/net/tramp-sshfs.el index 27b2854e451..b81e84a213d 100644 --- a/lisp/net/tramp-sshfs.el +++ b/lisp/net/tramp-sshfs.el @@ -159,6 +159,7 @@ tramp-sshfs-file-name-handler-alist (tramp-set-file-uid-gid . ignore) (unhandled-file-name-directory . ignore) (unlock-file . tramp-handle-unlock-file) + (user-uid-for-file . tramp-handle-user-uid-for-file) (vc-registered . ignore) (verify-visited-file-modtime . tramp-handle-verify-visited-file-modtime) (write-region . tramp-sshfs-handle-write-region)) diff --git a/lisp/net/tramp-sudoedit.el b/lisp/net/tramp-sudoedit.el index 2660dbb1fac..c115b17f98f 100644 --- a/lisp/net/tramp-sudoedit.el +++ b/lisp/net/tramp-sudoedit.el @@ -149,6 +149,7 @@ tramp-sudoedit-file-name-handler-alist (tramp-set-file-uid-gid . tramp-sudoedit-handle-set-file-uid-gid) (unhandled-file-name-directory . ignore) (unlock-file . tramp-handle-unlock-file) + (user-uid-for-file . tramp-handle-user-uid-for-file) (vc-registered . ignore) (verify-visited-file-modtime . tramp-handle-verify-visited-file-modtime) (write-region . tramp-handle-write-region)) diff --git a/lisp/net/tramp.el b/lisp/net/tramp.el index b8475b7cb48..5be0347ff25 100644 --- a/lisp/net/tramp.el +++ b/lisp/net/tramp.el @@ -2589,6 +2589,8 @@ tramp-file-name-for-operation file-locked-p lock-file make-lock-file-name unlock-file ;; Emacs 29+ only. abbreviate-file-name + ;; Emacs 30+ only. + user-uid-for-file ;; Tramp internal magic file name function. tramp-set-file-uid-gid)) (if (file-name-absolute-p (nth 0 args)) @@ -3710,6 +3712,14 @@ tramp-handle-abbreviate-file-name vec (concat "~" (substring filename (match-beginning 1)))) (tramp-make-tramp-file-name (tramp-dissect-file-name filename))))) +(defun tramp-handle-user-uid-for-file (filename) + "Like `user-uid' for Tramp files." + (or (tramp-get-remote-uid (tramp-dissect-file-name filename) 'integer) + ;; Some handlers for `tramp-get-remote-uid' return nil if they + ;; can't get the UID; always return -1 in this case for + ;; consistency. + -1)) + (defun tramp-handle-access-file (filename string) "Like `access-file' for Tramp files." (setq filename (file-truename filename)) diff --git a/lisp/simple.el b/lisp/simple.el index bbcb32cb04f..adcbe835e5f 100644 --- a/lisp/simple.el +++ b/lisp/simple.el @@ -4730,6 +4730,16 @@ shell-command--same-buffer-confirm action)) (user-error "Shell command in progress")))) +(defun user-uid-for-file (filename) + "Return the effective uid for FILENAME. +For local files, this is equivalent to `user-uid' (which see), +but for remote files, this returns the effective uid for that +remote connection; if the remote connection has no associated +user, it will instead return -1." + (if-let ((handler (find-file-name-handler filename 'user-uid-for-file))) + (funcall handler 'user-uid-for-file filename) + (user-uid))) + (defun max-mini-window-lines (&optional frame) "Compute maximum number of lines for echo area in FRAME. As defined by `max-mini-window-height'. FRAME defaults to the diff --git a/test/lisp/eshell/esh-var-tests.el b/test/lisp/eshell/esh-var-tests.el index 3f602798dbe..0cc1b92266f 100644 --- a/test/lisp/eshell/esh-var-tests.el +++ b/test/lisp/eshell/esh-var-tests.el @@ -746,6 +746,10 @@ esh-var-test/path-var/preserve-across-hosts (format "cd %s" ert-remote-temporary-file-directory)) (eshell-match-command-output "echo $PATH" (regexp-quote remote-path))))) +(ert-deftest esh-var-test/uid-var () + "Test that $UID is equivalent to (user-uid) for local directories." + (eshell-command-result-equal "echo $UID" (user-uid))) + (ert-deftest esh-var-test/last-status-var-lisp-command () "Test using the \"last exit status\" ($?) variable with a Lisp command" (with-temp-eshell -- 2.25.1