diff --git a/lisp/net/tramp-cache.el b/lisp/net/tramp-cache.el index cbdaa48fc0e..721b7be123f 100644 --- a/lisp/net/tramp-cache.el +++ b/lisp/net/tramp-cache.el @@ -713,5 +713,7 @@ for all methods. Resulting data are derived from connection history." ;;; TODO: ;; ;; * Use multisession.el, starting with Emacs 29.1. +;; +;; Use `with-memoization', starting with Emacs 29.1. ;;; tramp-cache.el ends here diff --git a/lisp/net/tramp-container.el b/lisp/net/tramp-container.el index 8429208b44b..6e82bc67be1 100644 --- a/lisp/net/tramp-container.el +++ b/lisp/net/tramp-container.el @@ -551,6 +551,7 @@ see its function help for a description of the format." (tramp-login-args (("exec") ("-it") ("-u" "%u") + ("-e" ,(format "TERM=%s" tramp-terminal-type)) ("%h") ("%l"))) (tramp-direct-async (,tramp-default-remote-shell "-c")) @@ -565,6 +566,7 @@ see its function help for a description of the format." (tramp-login-args (("exec") ("-it") ("-u" "%u") + ("-e" ,(format "TERM=%s" tramp-terminal-type)) ("%h") ("%l"))) (tramp-direct-async (,tramp-default-remote-shell "-c")) @@ -583,6 +585,7 @@ see its function help for a description of the format." (tramp-login-args (("exec") ("-it") ("-u" "%u") + ("-e" ,(format "TERM=%s" tramp-terminal-type)) ("%h") ("%l"))) (tramp-direct-async (,tramp-default-remote-shell "-c")) @@ -597,6 +600,7 @@ see its function help for a description of the format." (tramp-login-args (("exec") ("-it") ("-u" "%u") + ("-e" ,(format "TERM=%s" tramp-terminal-type)) ("%h") ("%l"))) (tramp-direct-async (,tramp-default-remote-shell "-c")) @@ -754,6 +758,8 @@ see its function help for a description of the format." `(,tramp-apptainer-method (tramp-login-program ,tramp-apptainer-program) (tramp-login-args (("shell") + ("--env" + ,(format "TERM=%s" tramp-terminal-type)) ("instance://%h") ("%h"))) ; Needed for multi-hop check. (tramp-remote-shell ,tramp-default-remote-shell) @@ -777,6 +783,8 @@ see its function help for a description of the format." (tramp-login-args (("shell") ("-q") ("--uid" "%u") + ("-E" + ,(format "TERM=%s" tramp-terminal-type)) ("%h"))) (tramp-remote-shell ,tramp-default-remote-shell) (tramp-remote-shell-login ("-l")) diff --git a/lisp/net/tramp-sh.el b/lisp/net/tramp-sh.el index eb446f4e2cd..679d76d0f57 100644 --- a/lisp/net/tramp-sh.el +++ b/lisp/net/tramp-sh.el @@ -190,7 +190,10 @@ The string is used in `tramp-methods'.") `("scp" (tramp-login-program "ssh") (tramp-login-args (("-l" "%u") ("-p" "%p") ("%c") - ("-e" "none") ("%h"))) + ("-e" "none") + ("-o" ,(format "SetEnv=\"TERM=%s\"" + tramp-terminal-type)) + ("%h"))) (tramp-async-args (("-q"))) (tramp-direct-async ("-t" "-t")) (tramp-remote-shell ,tramp-default-remote-shell) @@ -208,6 +211,8 @@ The string is used in `tramp-methods'.") (tramp-login-args (("-l" "%u") ("-p" "%p") ("%c") ("-e" "none") ("-t" "-t") ("-o" "RemoteCommand=\"%l\"") + ("-o" ,(format "SetEnv=\"TERM=%s\"" + tramp-terminal-type)) ("%h"))) (tramp-async-args (("-q"))) (tramp-remote-shell ,tramp-default-remote-shell) @@ -223,7 +228,10 @@ The string is used in `tramp-methods'.") `("rsync" (tramp-login-program "ssh") (tramp-login-args (("-l" "%u") ("-p" "%p") ("%c") - ("-e" "none") ("%h"))) + ("-e" "none") + ("-o" ,(format "SetEnv=\"TERM=%s\"" + tramp-terminal-type)) + ("%h"))) (tramp-async-args (("-q"))) (tramp-direct-async t) (tramp-remote-shell ,tramp-default-remote-shell) @@ -254,7 +262,10 @@ The string is used in `tramp-methods'.") `("ssh" (tramp-login-program "ssh") (tramp-login-args (("-l" "%u") ("-p" "%p") ("%c") - ("-e" "none") ("%h"))) + ("-e" "none") + ("-o" ,(format "SetEnv=\"TERM=%s\"" + tramp-terminal-type)) + ("%h"))) (tramp-async-args (("-q"))) (tramp-direct-async ("-t" "-t")) (tramp-remote-shell ,tramp-default-remote-shell) @@ -265,6 +276,8 @@ The string is used in `tramp-methods'.") (tramp-login-program "ssh") (tramp-login-args (("-l" "%u") ("-p" "%p") ("%c") ("-e" "none") ("-t" "-t") + ("-o" ,(format "SetEnv=\"TERM=%s\"" + tramp-terminal-type)) ("-o" "RemoteCommand=\"%l\"") ("%h"))) (tramp-async-args (("-q"))) @@ -301,6 +314,7 @@ The string is used in `tramp-methods'.") ;; remote host echoes the command. ;; The "-p" argument doesn't work reliably, see Bug#50594. (tramp-login-args (("SUDO_PROMPT=P\"\"a\"\"s\"\"s\"\"w\"\"o\"\"r\"\"d\"\":") + (,(format "TERM=%s" tramp-terminal-type)) ("sudo") ("-u" "%u") ("-s") ("-H") ("%l"))) (tramp-remote-shell ,tramp-default-remote-shell) @@ -4123,12 +4137,33 @@ This function expects to be in the right *tramp* buffer." (unless (char-equal ?~ (aref d 0)) (setq newdl (cons d newdl)))) (setq dirlist (nreverse newdl)))) - (when (tramp-send-command-and-check - vec (format "(unalias %s; %s command -v %s)" - progname - (if dirlist (concat "PATH=" (string-join dirlist ":")) "") - progname)) - (string-trim (tramp-get-buffer-string (tramp-get-connection-buffer vec))))) + (let ((command + (concat + (when dirlist (format "PATH=%s " (string-join dirlist ":"))) + "command -v " progname)) + (pipe-buf (tramp-get-remote-pipe-buf vec)) + tmpfile chunk chunksize) + (when (if (length< command pipe-buf) + (tramp-send-command-and-check vec command) + ;; Use a temporary file. We cannot use `write-region' + ;; because setting the remote path happens in the early + ;; connection handshake, and not all external tools are + ;; determined yet. + (setq command (concat command "\n") + tmpfile (tramp-make-tramp-temp-file vec)) + (while (not (string-empty-p command)) + (setq chunksize (min (length command) (/ pipe-buf 2)) + chunk (substring command 0 chunksize) + command (substring command chunksize)) + (tramp-send-command + vec (format "printf \"%%b\" \"$*\" %s >>%s" + (tramp-shell-quote-argument chunk) + (tramp-shell-quote-argument tmpfile)))) + (tramp-send-command-and-check + vec (format ". %s && rm -f %s" tmpfile tmpfile))) + + (string-trim + (tramp-get-buffer-string (tramp-get-connection-buffer vec)))))) ;; On hydra.nixos.org, the $PATH environment variable is too long to ;; send it. This is likely not due to PATH_MAX, but PIPE_BUF. We @@ -4162,12 +4197,11 @@ variable PATH." (setq chunksize (min (length command) (/ pipe-buf 2)) chunk (substring command 0 chunksize) command (substring command chunksize)) - (tramp-send-command vec (format - "printf \"%%b\" \"$*\" %s >>%s" - (tramp-shell-quote-argument chunk) - (tramp-shell-quote-argument tmpfile)))) - (tramp-send-command vec (format ". %s" tmpfile)) - (tramp-send-command vec (format "rm -f %s" tmpfile)))))) + (tramp-send-command + vec (format "printf \"%%b\" \"$*\" %s >>%s" + (tramp-shell-quote-argument chunk) + (tramp-shell-quote-argument tmpfile)))) + (tramp-send-command vec (format ". %s && rm -f %s" tmpfile tmpfile)))))) ;; ------------------------------------------------------------ ;; -- Communication with external shell -- @@ -5108,6 +5142,7 @@ Goes through the list `tramp-inline-compress-commands'." (t "-3"))) +;;;###tramp-autoload (defun tramp-timeout-session (vec) "Close the connection VEC after a session timeout. If there is just some editing, retry it after 5 seconds." diff --git a/lisp/net/tramp-smb.el b/lisp/net/tramp-smb.el index aeb7c01c03f..db961c97523 100644 --- a/lisp/net/tramp-smb.el +++ b/lisp/net/tramp-smb.el @@ -162,6 +162,7 @@ this variable \"client min protocol=NT1\"." "NT_STATUS_PASSWORD_MUST_CHANGE" "NT_STATUS_RESOURCE_NAME_NOT_FOUND" "NT_STATUS_REVISION_MISMATCH" + "NT_STATUS_RPC_SS_CONTEXT_MISMATCH" "NT_STATUS_SHARING_VIOLATION" "NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE" "NT_STATUS_UNSUCCESSFUL" @@ -316,7 +317,7 @@ Operations not mentioned here will be handled by the default Emacs primitives.") ;; Options for remote processes via winexe. (defcustom tramp-smb-winexe-program "winexe" "Name of winexe client to run. -If it isn't found in the local $PATH, the absolute path of winexe +If it isn't found in the local $PATH, the absolute path of \"winexe\" shall be given. This is needed for remote processes." :group 'tramp :version "24.3" @@ -488,12 +489,13 @@ arguments to pass to the OPERATION." (args (list (concat "//" host "/" share) "-E")) (options tramp-smb-options)) - (if (tramp-string-empty-or-nil-p user) - (setq args (append args (list "-N"))) - (setq args (append args (list "-U" user)))) + (setq args + (append args + (if (tramp-string-empty-or-nil-p user) + (list "-N") + (list "-U" (if domain (concat domain "/" user) user))) + (when port (list "-p" port)))) - (when domain (setq args (append args (list "-W" domain)))) - (when port (setq args (append args (list "-p" port)))) (when tramp-smb-conf (setq args (append args (list "-s" tramp-smb-conf)))) (while options @@ -779,12 +781,13 @@ PRESERVE-UID-GID and PRESERVE-EXTENDED-ATTRIBUTES are completely ignored." (args (list (concat "//" host "/" share) "-E")) (options tramp-smb-options)) - (if (tramp-string-empty-or-nil-p user) - (setq args (append args (list "-N"))) - (setq args (append args (list "-U" user)))) + (setq args + (append args + (if (tramp-string-empty-or-nil-p user) + (list "-N") + (list "-U" (if domain (concat domain "/" user) user))) + (when port (list "-p" port)))) - (when domain (setq args (append args (list "-W" domain)))) - (when port (setq args (append args (list "-p" port)))) (when tramp-smb-conf (setq args (append args (list "-s" tramp-smb-conf)))) (while options @@ -1251,6 +1254,7 @@ PRESERVE-UID-GID and PRESERVE-EXTENDED-ATTRIBUTES are completely ignored." (tramp-set-connection-property v " process-buffer" (or outbuf (generate-new-buffer tramp-temp-buffer-name))) + (tramp-flush-connection-property v " process-exit-status") (with-current-buffer (tramp-get-connection-buffer v) ;; Preserve buffer contents. (narrow-to-region (point-max) (point-max)) @@ -1366,12 +1370,13 @@ PRESERVE-UID-GID and PRESERVE-EXTENDED-ATTRIBUTES are completely ignored." (string-replace "\n" "," acl-string))) (options tramp-smb-options)) - (if (tramp-string-empty-or-nil-p user) - (setq args (append args (list "-N"))) - (setq args (append args (list "-U" user)))) + (setq args + (append args + (if (tramp-string-empty-or-nil-p user) + (list "-N") + (list "-U" (if domain (concat domain "/" user) user))) + (when port (list "-p" port)))) - (when domain (setq args (append args (list "-W" domain)))) - (when port (setq args (append args (list "-p" port)))) (when tramp-smb-conf (setq args (append args (list "-s" tramp-smb-conf)))) (while options @@ -1906,16 +1911,19 @@ If ARGUMENT is non-nil, use it as argument for (share (setq args (list (concat "//" host "/" share)))) (t (setq args (list "-g" "-L" host )))) - (if (tramp-string-empty-or-nil-p user) - (setq args (append args (list "-N"))) - (setq args (append args (list "-U" user)))) + (setq args + (append args + (if (tramp-string-empty-or-nil-p user) + (list "-N") + (list "-U" (if domain (concat domain "/" user) user))) + (when port (list "-p" port)))) - (when domain (setq args (append args (list "-W" domain)))) - (when port (setq args (append args (list "-p" port)))) (when tramp-smb-conf (setq args (append args (list "-s" tramp-smb-conf)))) (dolist (option options) (setq args (append args (list "--option" option)))) + ;; For debugging. + (setq args (append args (list "-d" "1"))) (when argument (setq args (append args (list argument)))) @@ -2026,6 +2034,8 @@ Removes smb prompt. Returns nil if an error message has appeared." (when (tramp-file-name-port vec) (tramp-error vec 'file-error "Port not supported for remote processes")) + ;; In case of "NT_STATUS_RPC_SS_CONTEXT_MISMATCH", the remote server + ;; is a Samba server. winexe cannot install the respective service there. (tramp-smb-maybe-open-connection vec (format @@ -2037,12 +2047,14 @@ Removes smb prompt. Returns nil if an error message has appeared." ;; Suppress "^M". Shouldn't we specify utf8? (set-process-coding-system (tramp-get-connection-process vec) 'raw-text-dos) - ;; Set width to 128. This avoids mixing prompt and long error messages. + ;; Set width to 128 ($bufsize.Width) or 102 ($winsize.Width), + ;; respectively. $winsize.Width cannot be larger. This avoids + ;; mixing prompt and long error messages. (tramp-smb-send-command vec "$rawui = (Get-Host).UI.RawUI") (tramp-smb-send-command vec "$bufsize = $rawui.BufferSize") (tramp-smb-send-command vec "$winsize = $rawui.WindowSize") (tramp-smb-send-command vec "$bufsize.Width = 128") - (tramp-smb-send-command vec "$winsize.Width = 128") + (tramp-smb-send-command vec "$winsize.Width = 102") (tramp-smb-send-command vec "$rawui.BufferSize = $bufsize") (tramp-smb-send-command vec "$rawui.WindowSize = $winsize")) @@ -2069,5 +2081,7 @@ Removes smb prompt. Returns nil if an error message has appeared." ;; several places, especially in `tramp-smb-handle-insert-directory'. ;; ;; * Keep a separate connection process per share. +;; +;; * Keep a permanent connection process for `process-file'. ;;; tramp-smb.el ends here diff --git a/lisp/net/tramp-sshfs.el b/lisp/net/tramp-sshfs.el index 3176236128a..cbc083a1fe0 100644 --- a/lisp/net/tramp-sshfs.el +++ b/lisp/net/tramp-sshfs.el @@ -62,6 +62,8 @@ (tramp-login-program "ssh") (tramp-login-args (("-q") ("-l" "%u") ("-p" "%p") ("-e" "none") ("%a" "%a") + ("-o" ,(format "SetEnv=\"TERM=%s\"" + tramp-terminal-type)) ("%h") ("%l"))) (tramp-direct-async t) (tramp-remote-shell ,tramp-default-remote-shell) diff --git a/lisp/net/tramp.el b/lisp/net/tramp.el index 67aa2cd0fdb..24298e8e09a 100644 --- a/lisp/net/tramp.el +++ b/lisp/net/tramp.el @@ -6255,15 +6255,22 @@ the remote host use line-endings as defined in the variable (process-send-string p string))))))) (defun tramp-process-sentinel (proc event) - "Flush file caches and remove shell prompt." + "Flush file caches and remove shell prompt. +Set exit status of PROC as connection property \" process-exit-status\"." (unless (process-live-p proc) (let ((vec (process-get proc 'tramp-vector)) (buf (process-buffer proc)) (prompt (tramp-get-connection-property proc "prompt"))) (when vec - (tramp-message vec 5 "Sentinel called: `%S' `%s'" proc event) + (tramp-message + vec 5 "Sentinel called: `%S' event: `%s' status: %s" + proc event (process-exit-status proc)) (tramp-flush-connection-properties proc) - (tramp-flush-directory-properties vec "/")) + (tramp-flush-directory-properties vec "/") + ;; Sometimes, the process has been deleted already before we + ;; can retrieve the exit status. + (tramp-set-connection-property + vec " process-exit-status" (process-exit-status proc))) (when (buffer-live-p buf) (with-current-buffer buf (when (and prompt (tramp-search-regexp (rx (literal prompt)))) diff --git a/test/lisp/net/tramp-tests.el b/test/lisp/net/tramp-tests.el index 349abdc44c9..b60b29fc3e6 100644 --- a/test/lisp/net/tramp-tests.el +++ b/test/lisp/net/tramp-tests.el @@ -278,9 +278,10 @@ being the result.") (| "rclone" "sshfs") ".") (file-name-nondirectory file))) (tramp--test-message "Delete %s" file) - (if (file-directory-p file) - (delete-directory file 'recursive) - (delete-file file)))))) + (ignore-errors ;; Wrong permissions? + (if (file-directory-p file) + (delete-directory file 'recursive) + (delete-file file))))))) ;; Cleanup connection. (tramp-cleanup-connection tramp-test-vec nil 'keep-password)) @@ -3761,11 +3762,7 @@ This tests also `access-file', `file-readable-p', (tmp-name2 (tramp--test-make-temp-name nil quoted)) ;; File name with "//". (tmp-name3 - (format - "%s%s" - (file-remote-p tmp-name1) - (replace-regexp-in-string - "/" "//" (file-remote-p tmp-name1 'localname)))) + (replace-regexp-in-string "/" "//" (file-local-name tmp-name1))) ;; `file-ownership-preserved-p' is implemented only in tramp-sh.el. (test-file-ownership-preserved-p (tramp--test-sh-p)) attr) @@ -3887,28 +3884,13 @@ This tests also `access-file', `file-readable-p', ;; symlinked files to a non-existing or cyclic target. (when test-file-ownership-preserved-p (should (file-ownership-preserved-p tmp-name2 'group))) - (delete-file tmp-name2))) + (delete-file tmp-name2)) - ;; Check, that "//" in symlinks are handled properly. - (with-temp-buffer - (let ((default-directory ert-remote-temporary-file-directory)) - (shell-command - (format - "ln -s %s %s" - (tramp-file-name-localname - (tramp-dissect-file-name tmp-name3)) - (tramp-file-name-localname - (tramp-dissect-file-name tmp-name2))) - t))) - (when (file-symlink-p tmp-name2) + ;; Check, that "//" in symlinks are handled properly. + (make-symbolic-link tmp-name3 tmp-name2) + (should (file-symlink-p tmp-name2)) (setq attr (file-attributes tmp-name2)) - (should - (string-equal - (file-attribute-type attr) - (funcall - (if (tramp--test-sshfs-p) #'file-name-nondirectory #'identity) - (tramp-file-name-localname - (tramp-dissect-file-name tmp-name3))))) + (should (string-equal (file-attribute-type attr) tmp-name3)) (delete-file tmp-name2)) (when test-file-ownership-preserved-p @@ -5164,7 +5146,8 @@ This tests also `make-symbolic-link', `file-truename' and `add-name-to-file'." ;; (tramp--test-message "%s" (tramp-get-buffer-string trace-buffer)) ;; (untrace-function #'tramp-completion-file-name-handler) ;; (untrace-function #'completion-file-name-table) - (tramp-change-syntax orig-syntax)))) + (tramp-change-syntax orig-syntax) + (tramp-cleanup-connection tramp-test-vec 'keep-debug 'keep-password)))) (defun tramp--test-split-on-boundary (s) "Return completion boundaries for string S." @@ -5177,15 +5160,12 @@ This tests also `make-symbolic-link', `file-truename' and `add-name-to-file'." ;; boundaries are always incorrect before that. (skip-unless (tramp--test-emacs31-p)) - (should (equal (tramp--test-split-on-boundary "/ssh:user@host:foo") - '("/ssh:user@host:" . "foo"))) - (should (equal (tramp--test-split-on-boundary "/ssh:user@host:/~/foo") - '("/ssh:user@host:/~/" . "foo"))) - (should (equal (tramp--test-split-on-boundary "/ssh:user@host:/usr//usr/foo") - '("/ssh:user@host:/usr//usr/" . "foo"))) - (should (equal (tramp--test-split-on-boundary - "/ssh:user@host:/ssh:user@host://usr/foo") - '("/ssh:user@host:/ssh:user@host://usr/" . "foo")))) + (let ((remote (file-remote-p ert-remote-temporary-file-directory))) + (dolist + (file `(,remote ,(concat remote "/~/") + ,(concat remote "/usr//usr/") ,(concat remote remote "//usr/"))) + (should (equal (tramp--test-split-on-boundary (concat file "foo")) + `(,file . "foo")))))) (ert-deftest tramp-test27-load () "Check `load'." @@ -6511,6 +6491,7 @@ INPUT, if non-nil, is a string sent to the process." (file-remote-p default-directory 'localname))) ;; The shell "sh" shall always exist. (should (executable-find "sh" 'remote)) + ;; Since the last element in `exec-path' is the current ;; directory, an executable file in that directory will be ;; found. @@ -6539,19 +6520,19 @@ INPUT, if non-nil, is a string sent to the process." (skip-unless (tramp--test-sh-p)) (skip-unless (not (tramp--test-crypt-p))) - (let* ((tmp-name (tramp--test-make-temp-name)) + (let* ((tmp-name1 (tramp--test-make-temp-name)) (default-directory ert-remote-temporary-file-directory) (orig-exec-path (exec-path)) (tramp-remote-path tramp-remote-path) (orig-tramp-remote-path tramp-remote-path) - path) + tmp-name2 path) ;; The "flatpak" method modifies `tramp-remote-path'. (skip-unless (not (tramp-compat-connection-local-p tramp-remote-path))) (unwind-protect (progn ;; Non existing directories are removed. (setq tramp-remote-path - (cons (file-remote-p tmp-name 'localname) tramp-remote-path)) + (cons (file-remote-p tmp-name1 'localname) tramp-remote-path)) (tramp-cleanup-connection tramp-test-vec 'keep-debug 'keep-password) (should (equal (exec-path) orig-exec-path)) (setq tramp-remote-path orig-tramp-remote-path) @@ -6564,11 +6545,11 @@ INPUT, if non-nil, is a string sent to the process." ;; We make a super long `tramp-remote-path'. (unless (tramp--test-container-oob-p) - (make-directory tmp-name) - (should (file-directory-p tmp-name)) + (make-directory tmp-name1) + (should (file-directory-p tmp-name1)) (while (length< (string-join orig-exec-path ":") 5000) (let ((dir (make-temp-file - (file-name-as-directory tmp-name) 'dir))) + (file-name-as-directory tmp-name1) 'dir))) (should (file-directory-p dir)) (setq tramp-remote-path (append @@ -6591,12 +6572,33 @@ INPUT, if non-nil, is a string sent to the process." (should (string-equal path (string-join (butlast orig-exec-path) ":")))) ;; The shell "sh" shall always exist. - (should (executable-find "sh" 'remote)))) + (should (executable-find "sh" 'remote)) + + ;; Since the last element in `exec-path' is the current + ;; directory, an executable file in that directory will be + ;; found. + (setq tmp-name2 + (expand-file-name + "foo" + (concat (file-remote-p default-directory) + (car (last orig-exec-path 2))))) + (write-region "foo" nil tmp-name2) + (should (file-exists-p tmp-name2)) + + (set-file-modes tmp-name2 #o777) + (should (file-executable-p tmp-name2)) + (should + (string-equal + (executable-find (file-name-nondirectory tmp-name2) 'remote) + (file-remote-p tmp-name2 'localname))) + (should-not + (executable-find + (concat (file-name-nondirectory tmp-name2) "foo") 'remote)))) ;; Cleanup. + (ignore-errors (delete-directory tmp-name1 'recursive)) (tramp-cleanup-connection tramp-test-vec 'keep-debug 'keep-password) - (setq tramp-remote-path orig-tramp-remote-path) - (ignore-errors (delete-directory tmp-name 'recursive))))) + (setq tramp-remote-path orig-tramp-remote-path)))) (tramp--test-deftest-direct-async-process tramp-test35-remote-path)