diff --git a/doc/misc/tramp.texi b/doc/misc/tramp.texi index 424bd8436e4..286f3d42ddb 100644 --- a/doc/misc/tramp.texi +++ b/doc/misc/tramp.texi @@ -4438,7 +4438,7 @@ uid=0(root) gid=0(root) groups=0(root) @anchor{Running a debugger on a remote host} @subsection Running a debugger on a remote host -@cindex @file{gud.el} +@cindex @file{gud.el} file @cindex @code{gdb} @cindex @code{perldb} @@ -4489,38 +4489,60 @@ Arguments of the program to be debugged must be literal, can take relative or absolute paths, but not remote paths. +@anchor{Running remote processes on MS Windows hosts} @subsection Running remote processes on MS Windows hosts @cindex @command{winexe} @cindex @command{powershell} -@command{winexe} runs processes on a remote MS Windows host, and -@value{tramp} can use it for @code{process-file} and -@code{start-file-process}. +@strong{Note}: This is an experimental feature. +The program @command{winexe} from the Samba suite runs processes on a +remote MS Windows host, and @value{tramp} uses it for +@code{make-process}, @code{process-file} and +@code{start-file-process}. It does not work for remote Samba servers. + +Redirection from stdin and to sterr as well as process filters are not +supported (yet). + +@c FIXME: Verify powershell version. +@vindex tramp-smb-winexe-program @code{tramp-smb-winexe-program} specifies the local @command{winexe} -command. Powershell V2.0 on the remote host is required to run -processes triggered from @value{tramp}. +program. Powershell V2.0 on the remote MS Windows host is required to +run processes triggered from @value{tramp}. -@code{explicit-shell-file-name} and @code{explicit-*-args} have to -be set properly so @kbd{M-x shell @key{RET}} can open a proper remote -shell on a MS Windows host. To open @command{cmd}, set it as follows: +@c https://woshub.com/enable-remote-access-to-admin-shares-in-workgroup/ +The remote user on the remote MS Windows host must be member of the +local computer Administrators group of that remote MS Windows host. +Since @command{winexe} uses the administrative share @file{ADMIN$} of +that remote MS Windows host, the remote MS Windows host must be either +part of an Active Directory domain, or the Remote UAC (User Account +Control for remote connections) must be disabled. The latter can be +achieved by creating the +@samp{HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System\ LocalAccountTokenFilterPolicy} +parameter in the MS Windows registry as DWORD (32-bit) with the value 1. -@lisp -@group -(setq explicit-shell-file-name "cmd" - explicit-cmd-args '("/q")) -@end group -@end lisp +@vindex tramp-smb-connection-local-powershell-profile +@vindex tramp-smb-prompt +@cindex @file{.emacs_powershell} file +@code{shell} and @code{shell-command} are configured to use +@command{powershell} on the remote MS Windows host. If you want to +change this, consider using another connection-local profile but +@code{tramp-smb-connection-local-powershell-profile}. + +If you run a remote @code{shell}, you might consider to add the +following line to your local @file{~/.emacs_powershell} file: + +@smallexample +function prompt @{ "PS " + ((Get-Location).Path | Convert-Path) + "> " @} +@end smallexample @noindent -To open @command{powershell} as a remote shell, use this: +Note, that the configured prompt must match @code{tramp-smb-prompt}. -@lisp -@group -(setq explicit-shell-file-name "powershell" - explicit-powershell-args '("-file" "-")) -@end group -@end lisp +If you run a remote @code{shell-command}, you must use +@command{powershell} syntax. For example, if you want to run a shell +command from @code{dired}, use the command @t{"(ls * ).Name"} instead +of @t{"ls"}. @subsection Remote process connection type diff --git a/etc/NEWS b/etc/NEWS index 5b064108606..f896241b24d 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -1100,7 +1100,7 @@ blocks. +++ *** New command 'hs-toggle-all'. -This command hide or show all the blocks in the current buffer. +This command hides or shows all the blocks in the current buffer. +++ *** New user option 'hs-display-lines-hidden'. @@ -1584,10 +1584,8 @@ behavior included additional information about the originating message. The new variable 'message-header-use-obsolete-in-reply-to', nil by default, can be set to a non-nil value to restore the previous behavior. -** Message - +++ -*** 'message-subject-re-regexp' default value is now derived from 'mail-re-regexps'. +*** 'message-subject-re-regexp' default value is derived from 'mail-re-regexps'. 'mail-re-regexps' is a new user option that is easier to customize than 'message-subject-re-regexp'. 'message-subject-re-regexp' is still honored if it was already set. @@ -1748,7 +1746,7 @@ This contains the list of regular expressions used to match "Re:" and international variants of it when modifying the Subject field in replies. -** mairix +** Mairix --- *** 'mairix-search' now keeps its own minibuffer history. @@ -2027,6 +2025,11 @@ This feature is experimental. --- *** Implementation of filesystem notifications for connection method "smb". ++++ +*** Remote process support has been rewritten for the "smb" connection method. +For more information, see "(tramp) Running remote processes on MS +Windows hosts" in the Tramp manual. + +++ *** New functions to extend the set of operations with a remote implementation. The new functions 'tramp-add-external-operation' and @@ -2545,9 +2548,9 @@ bindings: - 'C-x v O D' is bound to 'vc-root-diff-outgoing'. +++ -*** New display of outgoing revisions count in VC-Dir. -If there are outgoing revisions, VC-Dir now includes a count of how many -in its headers, to remind you to push them. +*** New display of outgoing revisions count in VC Directory. +If there are outgoing revisions, VC Directory now includes a count of +how many in its headers, to remind you to push them. +++ *** New user option 'vc-async-checkin' to enable async checkin operations. diff --git a/lisp/net/tramp-adb.el b/lisp/net/tramp-adb.el index 5d6c053868b..8ea559bc9b3 100644 --- a/lisp/net/tramp-adb.el +++ b/lisp/net/tramp-adb.el @@ -756,6 +756,7 @@ PRESERVE-UID-GID and PRESERVE-EXTENDED-ATTRIBUTES are completely ignored." (with-current-buffer outbuf (insert-buffer-substring (tramp-get-connection-buffer v))) (when (and display (get-buffer-window outbuf t)) (redisplay)))) + ;; When the user did interrupt, we should do it also. We use ;; return code -1 as marker. (quit diff --git a/lisp/net/tramp-sh.el b/lisp/net/tramp-sh.el index 5ed3e91da24..0f709b69323 100644 --- a/lisp/net/tramp-sh.el +++ b/lisp/net/tramp-sh.el @@ -3369,6 +3369,7 @@ will be used." (insert (tramp-get-buffer-string (tramp-get-connection-buffer v)))) (when (and display (get-buffer-window outbuf t)) (redisplay)))) + ;; When the user did interrupt, we should do it also. We use ;; return code -1 as marker. (quit diff --git a/lisp/net/tramp-smb.el b/lisp/net/tramp-smb.el index fdda945f120..7805db7a6b7 100644 --- a/lisp/net/tramp-smb.el +++ b/lisp/net/tramp-smb.el @@ -239,6 +239,7 @@ See `tramp-actions-before-shell' for more info.") . tramp-handle-directory-files-and-attributes) (dired-compress-file . ignore) (dired-uncache . tramp-handle-dired-uncache) + ;; TODO: Add implementation. (exec-path . ignore) (expand-file-name . tramp-smb-handle-expand-file-name) (file-accessible-directory-p . tramp-handle-file-accessible-directory-p) @@ -286,7 +287,7 @@ See `tramp-actions-before-shell' for more info.") (make-directory-internal . ignore) (make-lock-file-name . tramp-handle-make-lock-file-name) (make-nearby-temp-file . tramp-handle-make-nearby-temp-file) - (make-process . ignore) + (make-process . tramp-smb-handle-make-process) (make-symbolic-link . tramp-smb-handle-make-symbolic-link) (memory-info . ignore) (process-attributes . ignore) @@ -297,8 +298,8 @@ See `tramp-actions-before-shell' for more info.") (set-file-selinux-context . ignore) (set-file-times . tramp-smb-handle-set-file-times) (set-visited-file-modtime . tramp-handle-set-visited-file-modtime) - (shell-command . tramp-handle-shell-command) - (start-file-process . tramp-smb-handle-start-file-process) + (shell-command . tramp-smb-handle-shell-command) + (start-file-process . tramp-handle-start-file-process) (substitute-in-file-name . tramp-smb-handle-substitute-in-file-name) (temporary-file-directory . tramp-handle-temporary-file-directory) (tramp-get-home-directory . tramp-smb-handle-get-home-directory) @@ -1266,6 +1267,79 @@ PRESERVE-UID-GID and PRESERVE-EXTENDED-ATTRIBUTES are completely ignored." (unless (file-directory-p dir) (tramp-error v 'file-error "Couldn't make directory %s" dir)))) +(defvar tramp-smb-matching-line nil + "Regexp to delete from current buffer.") + +(defun tramp-smb-delete-matching-lines (string) + "Delete matching lines in current buffer. +Remove this function from `comint-preoutput-filter-functions'." + (save-excursion + (goto-char (point-min)) + (unless + (zerop (delete-matching-lines tramp-smb-matching-line)) + (setq tramp-smb-matching-line nil) + (remove-hook 'comint-preoutput-filter-functions + #'tramp-smb-delete-matching-lines 'local)) + string)) + +;; We use BUFFER also as connection buffer during setup. Because of +;; this, its original contents must be saved, and restored once +;; connection has been setup. +(defun tramp-smb-handle-make-process (&rest args) + "Like `make-process' for Tramp files. +If method parameter `tramp-direct-async' and connection-local variable +`tramp-direct-async-process' are non-nil, an alternative implementation +will be used." + (if (tramp-direct-async-process-p args) + (apply #'tramp-handle-make-process args) + (tramp-skeleton-make-process args nil t + (let* ((command (string-join command " ")) + (bmp (and (buffer-live-p buffer) (buffer-modified-p buffer))) + p) + (with-tramp-saved-connection-properties + v '(" process-name" " process-buffer") + (unwind-protect + (save-excursion + (save-restriction + ;; Set the new process properties. + (tramp-set-connection-property v " process-name" name) + (tramp-set-connection-property v " process-buffer" buffer) + ;; Activate narrowing in order to save BUFFER contents. + (with-current-buffer (tramp-get-connection-buffer v) + (let ((buffer-undo-list t)) + (narrow-to-region (point-max) (point-max)) + (tramp-smb-call-winexe v) + (tramp-message v 6 "%s" command) + (tramp-send-string v command) + (setq + tramp-smb-matching-line (rx bol (literal command) eol)) + (add-hook 'comint-preoutput-filter-functions + #'tramp-smb-delete-matching-lines nil 'local))) + (setq p (tramp-get-connection-process v)) + ;; Set sentinel and filter. + (when sentinel + (set-process-sentinel p sentinel)) + (when filter + (set-process-filter p filter)) + (process-put p 'remote-command orig-command) + ;; Set query flag and process marker for this + ;; process. We ignore errors, because the process + ;; could have finished already. + (ignore-errors + (set-process-query-on-exit-flag p (null noquery)) + (set-marker (process-mark p) (point))) + ;; Return value. + p)) + + ;; Save exit. + ;; FIXME: Does `tramp-get-connection-buffer' return the proper value? + (with-current-buffer (tramp-get-connection-buffer v) + (if (string-search tramp-temp-buffer-name (buffer-name)) + (progn + (set-process-buffer (tramp-get-connection-process v) nil) + (kill-buffer (current-buffer))) + (set-buffer-modified-p bmp))))))))) + (defun tramp-smb-handle-make-symbolic-link (target linkname &optional ok-if-already-exists) "Like `make-symbolic-link' for Tramp files." @@ -1286,120 +1360,62 @@ PRESERVE-UID-GID and PRESERVE-EXTENDED-ATTRIBUTES are completely ignored." (defun tramp-smb-handle-process-file (program &optional infile destination display &rest args) "Like `process-file' for Tramp files." - ;; The implementation is not complete yet. - (when (and (numberp destination) (zerop destination)) - (error "Implementation does not handle immediate return")) - - (with-parsed-tramp-file-name (expand-file-name default-directory) nil - (let* ((name (file-name-nondirectory program)) - (name1 name) - (i 0) - input tmpinput outbuf command ret) - - ;; Determine input. - (when infile - (setq infile (file-name-unquote (expand-file-name infile))) - (if (tramp-equal-remote default-directory infile) - ;; INFILE is on the same remote host. - (setq input (tramp-unquote-file-local-name infile)) - ;; INFILE must be copied to remote host. - (setq input (tramp-make-tramp-temp-file v) - tmpinput (tramp-make-tramp-file-name v input)) - (copy-file infile tmpinput t)) - ;; Transform input into a filename powershell does understand. - (setq input (format "//%s%s" host input))) - - ;; Determine output. - (cond - ;; Just a buffer. - ((bufferp destination) - (setq outbuf destination)) - ;; A buffer name. - ((stringp destination) - (setq outbuf (get-buffer-create destination))) - ;; (REAL-DESTINATION ERROR-DESTINATION) - ((consp destination) - ;; output. - (cond - ((bufferp (car destination)) - (setq outbuf (car destination))) - ((stringp (car destination)) - (setq outbuf (get-buffer-create (car destination)))) - ((car destination) - (setq outbuf (current-buffer)))) - ;; stderr. - (tramp-warning v "%s" "STDERR not supported")) - ;; 't - (destination - (setq outbuf (current-buffer)))) + ;; STDERR is not impelmemted. + (when (consp destination) + (setcdr destination `(,tramp-cache-undefined))) + (tramp-skeleton-process-file program infile destination display args + (let ((name + (string-replace "*tramp" "*tramp process" (tramp-buffer-name v)))) + ;; Transform input into a filename powershell does understand. + (when input + (setq input + (and (not (string-equal input (tramp-get-remote-null-device v))) + (format "//%s%s" host input)))) ;; Construct command. (setq command (string-join (cons program args) " ") command (if input (format - "get-content %s | & %s" + "Get-Content %s | & %s" (tramp-smb-shell-quote-argument input) command) - (format "& %s" command))) - - (while (get-process name1) - ;; NAME must be unique as process name. - (setq i (1+ i) - name1 (format "%s<%d>" name i))) + (format "%s" command))) ;; Call it. (condition-case nil (with-tramp-saved-connection-properties v '(" process-name" " process-buffer") ;; Set the new process properties. - (tramp-set-connection-property v " process-name" name1) + (tramp-set-connection-property v " process-name" name) (tramp-set-connection-property - v " process-buffer" - (or outbuf (generate-new-buffer tramp-temp-buffer-name))) - (tramp-flush-connection-property v " process-exit-status") + ;; v " process-buffer" + ;; (or outbuf (generate-new-buffer tramp-temp-buffer-name))) + v " process-buffer" (generate-new-buffer tramp-temp-buffer-name)) (with-current-buffer (tramp-get-connection-buffer v) ;; Preserve buffer contents. (narrow-to-region (point-max) (point-max)) (tramp-smb-call-winexe v) - (when (tramp-smb-get-share v) - (tramp-smb-send-command - v (format "cd //%s%s" host - (tramp-smb-shell-quote-argument - (file-name-directory localname))))) - (tramp-smb-send-command v command) - ;; Preserve command output. - (narrow-to-region (point-max) (point-max)) - (let ((p (tramp-get-connection-process v))) - (tramp-smb-send-command v "exit $lasterrorcode") - (while (process-live-p p) - (sleep-for 0.1) - (setq ret (process-exit-status p)))) - (delete-region (point-min) (point-max)) - (widen))) + (tramp-flush-connection-property v " process-exit-status") + (tramp-smb-send-command + v (format "%s; exit -not $?" command)) + (while (not (setq ret (tramp-get-connection-property + v " process-exit-status"))) + (sleep-for 0.1)) + (unless (natnump ret) (setq ret 1)) + ;; We should add the output anyway. + (when outbuf + (with-current-buffer outbuf + (insert-buffer-substring (tramp-get-connection-buffer v))) + (when (and display (get-buffer-window outbuf t)) (redisplay))))) ;; When the user did interrupt, we should do it also. We use ;; return code -1 as marker. (quit + (kill-buffer (tramp-get-connection-buffer v)) (setq ret -1)) ;; Handle errors. (error - (setq ret 1))) - - ;; We should redisplay the output. - (when (and display outbuf (get-buffer-window outbuf t)) (redisplay)) - - ;; Cleanup. We remove all file cache values for the connection, - ;; because the remote process could have changed them. - (when tmpinput (delete-file tmpinput)) - ;; FIXME: Does connection-property " process-buffer" still exist? - (unless outbuf - (kill-buffer (tramp-get-connection-property v " process-buffer"))) - (when process-file-side-effects - (tramp-flush-directory-properties v "/")) - - ;; Return exit status. - (if (equal ret -1) - (keyboard-quit) - ret)))) + (kill-buffer (tramp-get-connection-buffer v)) + (setq ret 1)))))) (defun tramp-smb-handle-rename-file (filename newname &optional ok-if-already-exists) @@ -1543,62 +1559,18 @@ PRESERVE-UID-GID and PRESERVE-EXTENDED-ATTRIBUTES are completely ignored." (tramp-smb-shell-quote-localname v) (format-time-string "%Y:%m:%d-%H:%M:%S" (tramp-defined-time time)))))) -;; We use BUFFER also as connection buffer during setup. Because of -;; this, its original contents must be saved, and restored once -;; connection has been setup. -(defun tramp-smb-handle-start-file-process (name buffer program &rest args) - "Like `start-file-process' for Tramp files." - (with-parsed-tramp-file-name default-directory nil - (let* ((buffer - (if buffer - (get-buffer-create buffer) - ;; BUFFER can be nil. We use a temporary buffer. - (generate-new-buffer tramp-temp-buffer-name))) - (command (string-join (cons program args) " ")) - (bmp (and (buffer-live-p buffer) (buffer-modified-p buffer))) - (name1 name) - (i 0) - p) - (unwind-protect - (with-tramp-saved-connection-properties - v '(" process-name" " process-buffer") - (save-excursion - (save-restriction - (while (get-process name1) - ;; NAME must be unique as process name. - (setq i (1+ i) - name1 (format "%s<%d>" name i))) - ;; Set the new process properties. - (tramp-set-connection-property v " process-name" name1) - (tramp-set-connection-property v " process-buffer" buffer) - ;; Activate narrowing in order to save BUFFER contents. - (with-current-buffer (tramp-get-connection-buffer v) - (let ((buffer-undo-list t)) - (narrow-to-region (point-max) (point-max)) - (tramp-smb-call-winexe v) - (when (tramp-smb-get-share v) - (tramp-smb-send-command - v (format - "cd //%s%s" - host - (tramp-smb-shell-quote-argument - (file-name-directory localname))))) - (tramp-message v 6 "(%s); exit" command) - (tramp-send-string v command))) - (setq p (tramp-get-connection-process v)) - (when program - (process-put p 'remote-command (cons program args))) - ;; Return value. - p))) +(defun tramp-smb-handle-shell-command + (command &optional output-buffer error-buffer) + "Like `shell-command' for Tramp files." - ;; Save exit. - ;; FIXME: Does `tramp-get-connection-buffer' return the proper value? - (with-current-buffer (tramp-get-connection-buffer v) - (if (string-search tramp-temp-buffer-name (buffer-name)) - (progn - (set-process-buffer (tramp-get-connection-process v) nil) - (kill-buffer (current-buffer))) - (set-buffer-modified-p bmp))))))) + (let* (;; "& wait" is added by `dired-shell-stuff-it'. + (asynchronous + (string-match-p + (rx (? (* blank) "& wait") (* blank) "&" (* blank) eos) command)) + (command (substring command 0 asynchronous)) + (command (string-join `("\"" "&" "{" ,command "}" "\"") " ")) + (command (if asynchronous (concat command "; exit &") command))) + (tramp-handle-shell-command command output-buffer error-buffer))) (defun tramp-smb-handle-substitute-in-file-name (filename) "Like `substitute-in-file-name' for Tramp files. @@ -1661,25 +1633,28 @@ VEC or USER, or if there is no home directory, return nil." (when (string-match (rx bol (? "/") (group (+ (not "/"))) "/") localname) (match-string 1 localname))))) -(defun tramp-smb-get-localname (vec) +(defun tramp-smb-get-localname (vec &optional share) "Return the file name of LOCALNAME. +If SHARE is non-nil, include the share name. If VEC has no cifs capabilities, exchange \"/\" by \"\\\\\"." (save-match-data (let ((localname (tramp-file-name-unquote-localname vec))) - (setq - localname - (if (string-match - (rx bol (? "/") (+ (not "/")) (group "/" (* nonl))) localname) - ;; There is a share, separated by "/". - (if (not (tramp-smb-get-cifs-capabilities vec)) - (mapconcat - (lambda (x) (if (equal x ?/) "\\" (char-to-string x))) - (match-string 1 localname) "") - (match-string 1 localname)) - ;; There is just a share. - (if (string-match (rx bol (? "/") (group (+ (not "/"))) eol) localname) - (match-string 1 localname) - ""))) + (unless share + (setq + localname + (if (string-match + (rx bol (? "/") (+ (not "/")) (group "/" (* nonl))) localname) + ;; There is a share, separated by "/". + (if (not (tramp-smb-get-cifs-capabilities vec)) + (mapconcat + (lambda (x) (if (equal x ?/) "\\" (char-to-string x))) + (match-string 1 localname) "") + (match-string 1 localname)) + ;; There is just a share. + (if (string-match + (rx bol (? "/") (group (+ (not "/"))) eol) localname) + (match-string 1 localname) + "")))) ;; Sometimes we have discarded `substitute-in-file-name'. (when (string-match (rx (group "$$") (| "/" eol)) localname) @@ -1939,7 +1914,13 @@ function waits for output unless NOOUTPUT is set." (tramp-smb-maybe-open-connection vec) (tramp-message vec 6 "%s" command) (tramp-send-string vec command) - (unless nooutput (tramp-smb-wait-for-output vec))) + (unless nooutput + (prog1 + (tramp-smb-wait-for-output vec) + (with-current-buffer (tramp-get-connection-buffer vec) + (save-excursion + (goto-char (point-min)) + (delete-matching-lines (rx bol (literal command) eol))))))) (defun tramp-smb-maybe-open-connection (vec &optional argument) "Maybe open a connection to HOST, log in as USER, using `tramp-smb-program'. @@ -2047,6 +2028,11 @@ If ARGUMENT is non-nil, use it as argument for (let* (coding-system-for-read (process-connection-type tramp-process-connection-type) + ;; There might be some unfortune values of + ;; `tramp-smb-connection-local-default-system-variables'. + ;(path-separator (default-value 'path-separator)) + ;(null-device (default-value 'null-device)) + ;(exec-suffixes (default-value 'exec-suffixes)) (p (apply #'tramp-start-process vec (tramp-get-connection-name vec) (tramp-get-connection-buffer vec) @@ -2054,6 +2040,10 @@ If ARGUMENT is non-nil, use it as argument for tramp-smb-winexe-program tramp-smb-program) args))) + ;; Set sentinel. Initialize variables. + (set-process-sentinel p #'tramp-process-sentinel) + (setq tramp-current-connection (cons vec (current-time))) + ;; Set connection-local variables. (tramp-set-connection-local-variables vec) @@ -2104,7 +2094,9 @@ Removes smb prompt. Returns nil if an error message has appeared." (inhibit-read-only t)) ;; Read pending output. - (while (not (search-forward-regexp tramp-smb-prompt nil t)) + (tramp-accept-process-output p) + (while (and (process-live-p p) + (not (search-forward-regexp tramp-smb-prompt nil t))) (while (tramp-accept-process-output p)) (goto-char (point-min))) (tramp-message vec 6 "%S\n%s" p (buffer-string)) @@ -2138,6 +2130,10 @@ 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")) + ;; Check share. + (unless (tramp-smb-get-share vec) + (tramp-error vec 'file-error "Default directory must contain a share.")) + ;; 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 @@ -2150,7 +2146,9 @@ 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) - + ;; Enable UTF-8 encoding. Suppress "^M". + ;; (set-process-coding-system (tramp-get-connection-process vec) 'utf-8-dos) + ;; (tramp-smb-send-command vec "$PSDefaultParameterValues['*:Encoding'] = 'utf8'") ;; Set width to 128 ($bufsize.Width) or 102 ($winsize.Width), ;; respectively. $winsize.Width cannot be larger. This avoids ;; mixing prompt and long error messages. @@ -2160,16 +2158,23 @@ Removes smb prompt. Returns nil if an error message has appeared." (tramp-smb-send-command vec "$bufsize.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")) + (tramp-smb-send-command vec "$rawui.WindowSize = $winsize") + ;; Goto `default-directory'. + (tramp-smb-send-command + vec (format + "cd //%s%s" + (tramp-file-name-host vec) + (tramp-smb-shell-quote-localname vec 'share)))) (defun tramp-smb-shell-quote-argument (s) "Similar to `shell-quote-argument', but uses Windows cmd syntax." (let ((system-type 'ms-dos)) (tramp-unquote-shell-quote-argument s))) -(defun tramp-smb-shell-quote-localname (vec) - "Call `tramp-smb-shell-quote-argument' on localname of VEC." - (tramp-smb-shell-quote-argument (tramp-smb-get-localname vec))) +(defun tramp-smb-shell-quote-localname (vec &optional share) + "Call `tramp-smb-shell-quote-argument' on localname of VEC. +SHARE will be passed to the call of `tramp-smb-get-localname'." + (tramp-smb-shell-quote-argument (tramp-smb-get-localname vec share))) ;;; Default connection-local variables for Tramp. @@ -2194,6 +2199,57 @@ Removes smb prompt. Returns nil if an error message has appeared." `(:application tramp :protocol ,tramp-smb-method) 'tramp-smb-connection-local-default-system-profile) +;; (defconst tramp-smb-connection-local-bash-variables +;; '((explicit-shell-file-name . "bash") +;; (explicit-bash-args . ("--norc" "--noediting" "-i")) +;; (shell-file-name . "bash") +;; (shell-command-switch . "-c")) +;; "Default connection-local bash variables for remote smb connections.") + +;; (connection-local-set-profile-variables +;; 'tramp-smb-connection-local-bash-profile +;; tramp-smb-connection-local-bash-variables) + +(defconst tramp-smb-connection-local-powershell-variables + `((explicit-shell-file-name . "powershell") + (explicit-powershell-args . ("-file" "-")) + (shell-file-name . "powershell") + (shell-command-switch . "-command") + (shell-history-file-name . t)) + "Default connection-local powershell variables for remote smb connections.") + +(connection-local-set-profile-variables + 'tramp-smb-connection-local-powershell-profile + tramp-smb-connection-local-powershell-variables) + +(defun tramp-smb-shell-prompt () + "Set `comint-prompt-regexp' to a proper value." + ;; Used for remote `shell-mode' buffers. + (when (tramp-smb-file-name-p default-directory) + (setq-local comint-prompt-regexp tramp-smb-prompt))) + +;; (defconst tramp-smb-connection-local-cmd-variables +;; '((explicit-shell-file-name . "cmd") +;; (explicit-cmd-args . ("/Q")) +;; (shell-file-name . "cmd") +;; (shell-command-switch . "/C")) +;; "Default connection-local cmd variables for remote smb connections.") + +;; (connection-local-set-profile-variables +;; 'tramp-smb-connection-local-cmd-profile +;; tramp-smb-connection-local-cmd-variables) + +(with-eval-after-load 'shell + (connection-local-set-profiles + `(:application tramp :protocol ,tramp-smb-method) + 'tramp-smb-connection-local-powershell-profile) + (add-hook 'shell-mode-hook + #'tramp-smb-shell-prompt) + (add-hook 'tramp-smb-unload-hook + (lambda () + (remove-hook 'shell-mode-hook + #'tramp-smb-shell-prompt)))) + (add-hook 'tramp-unload-hook (lambda () (unload-feature 'tramp-smb 'force))) diff --git a/test/lisp/net/tramp-tests.el b/test/lisp/net/tramp-tests.el index 76e9a47e41b..b2caa3ebdf1 100644 --- a/test/lisp/net/tramp-tests.el +++ b/test/lisp/net/tramp-tests.el @@ -5338,18 +5338,36 @@ This tests also `make-symbolic-link', `file-truename' and `add-name-to-file'." kill-buffer-query-functions) (unwind-protect (progn - ;; We cannot use "/bin/true" and "/bin/false"; those paths - ;; do not exist on MS Windows. - (should (zerop (process-file "true"))) - (should-not (zerop (process-file "false"))) + ;; In the "smb" case, default-directory must have a share. + (when (tramp--test-smb-p) + (let ((default-directory + (concat (file-remote-p default-directory) "/"))) + (should-not + (zerop (process-file "exit" nil nil nil "-not" "$true"))))) + (should + (zerop + (if (tramp--test-smb-p) + ;; $true is converted to 1 in Powershell. + (process-file "exit" nil nil nil "-not" "$true") + (process-file "true")))) + (should-not + (zerop + (if (tramp--test-smb-p) + ;; $false is converted to 0 in Powershell. + (process-file "exit" nil nil nil "-not" "$false") + (process-file "false")))) (should-not (zerop (process-file "binary-does-not-exist"))) ;; Return exit code. - (should (= 42 (process-file - (tramp--test-shell-file-name) nil nil nil - (tramp--test-shell-command-switch) "exit 42"))) + ;; FIXME: Make it work with the shell also in the "smb" case. + (should + (= 42 (if (tramp--test-smb-p) + (process-file "exit" nil nil nil "42") + (process-file + (tramp--test-shell-file-name) nil nil nil + (tramp--test-shell-command-switch) "exit 42")))) ;; Return exit code in case the process is interrupted, ;; and there's no indication for a signal describing string. - (unless (tramp--test-sshfs-p) + (unless (or (tramp--test-sshfs-p) (tramp--test-smb-p)) (let (process-file-return-signal-string) (should (= (+ 128 2) @@ -5358,7 +5376,7 @@ This tests also `make-symbolic-link', `file-truename' and `add-name-to-file'." (tramp--test-shell-command-switch) "kill -2 $$"))))) ;; Return string in case the process is interrupted and ;; there's an indication for a signal describing string. - (unless (tramp--test-sshfs-p) + (unless (or (tramp--test-sshfs-p) (tramp--test-smb-p)) (let ((process-file-return-signal-string t)) (should (string-match-p @@ -5375,7 +5393,12 @@ This tests also `make-symbolic-link', `file-truename' and `add-name-to-file'." (with-temp-buffer (write-region "foo" nil tmp-name) (should (file-exists-p tmp-name)) - (should (zerop (process-file "ls" nil destination nil fnnd))) + (should + (zerop + (if (tramp--test-smb-p) + (process-file + (format "(ls %s).Name" fnnd) nil destination) + (process-file "ls" nil destination nil fnnd)))) (with-current-buffer (if (bufferp destination) destination (current-buffer)) ;; "ls" could produce colorized output. @@ -5390,7 +5413,12 @@ This tests also `make-symbolic-link', `file-truename' and `add-name-to-file'." (goto-char (point-max))) ;; Second run. The output must be appended. - (should (zerop (process-file "ls" nil destination t fnnd))) + (should + (zerop + (if (tramp--test-smb-p) + (process-file + (format "(ls %s).Name" fnnd) nil destination t) + (process-file "ls" nil destination t fnnd)))) (with-current-buffer (if (bufferp destination) destination (current-buffer)) ;; "ls" could produce colorized output. @@ -5416,7 +5444,9 @@ This tests also `make-symbolic-link', `file-truename' and `add-name-to-file'." (write-region "foo" nil tmp-name) (should (file-exists-p tmp-name)) (should (zerop (process-file "cat" tmp-name t))) - (should (string-equal "foo" (buffer-string))) + (should + (string-equal + (if (tramp--test-smb-p) "foo\n" "foo") (buffer-string))) (should-not (get-buffer-window (current-buffer) t)) (delete-file tmp-name))) @@ -5433,7 +5463,8 @@ This tests also `make-symbolic-link', `file-truename' and `add-name-to-file'." ;; (delete-file tmp-name))) ;; Check remote and local STDERR. - (unless (tramp--test-sshfs-p) + ;; FIXME: tramp-smb.el should implement this. + (unless (or (tramp--test-sshfs-p) (tramp--test-smb-p)) (dolist (local '(nil t)) (setq tmp-name (tramp--test-make-temp-name local quoted)) (should-not @@ -5475,30 +5506,32 @@ This tests also `make-symbolic-link', `file-truename' and `add-name-to-file'." kill-buffer-query-functions command proc) ;; Simple process. - (unwind-protect - (with-temp-buffer - (setq command '("cat") - proc - (apply #'start-file-process "test1" (current-buffer) command)) - (should (processp proc)) - (should (equal (process-status proc) 'run)) - (should (equal (process-get proc 'remote-command) command)) - (process-send-string proc "foo\n") - (process-send-eof proc) - ;; Read output. - (with-timeout (10 (tramp--test-timeout-handler)) - (while (< (- (point-max) (point-min)) (length "foo")) - (while (accept-process-output proc 0 nil t)))) - ;; Some `cat' implementations do not support the `cat -' - ;; call. We skip then. - (skip-unless - (not - (string-match-p (rx "cat: -: input file is output file\n") - (buffer-string)))) - (should (string-match-p "foo" (buffer-string)))) + (unless (tramp--test-smb-p) + (unwind-protect + (with-temp-buffer + (setq command '("cat") + proc + (apply + #'start-file-process "test1" (current-buffer) command)) + (should (processp proc)) + (should (equal (process-status proc) 'run)) + (should (equal (process-get proc 'remote-command) command)) + (process-send-string proc "foo\n") + (process-send-eof proc) + ;; Read output. + (with-timeout (10 (tramp--test-timeout-handler)) + (while (< (- (point-max) (point-min)) (length "foo")) + (while (accept-process-output proc 0 nil t)))) + ;; Some `cat' implementations do not support the `cat -' + ;; call. We skip then. + (skip-unless + (not + (string-match-p (rx "cat: -: input file is output file\n") + (buffer-string)))) + (should (string-match-p "foo" (buffer-string)))) - ;; Cleanup. - (ignore-errors (delete-process proc))) + ;; Cleanup. + (ignore-errors (delete-process proc)))) ;; Simple process using a file. (unwind-protect @@ -5512,7 +5545,7 @@ This tests also `make-symbolic-link', `file-truename' and `add-name-to-file'." (should (equal (process-get proc 'remote-command) command)) ;; Read output. (with-timeout (10 (tramp--test-timeout-handler)) - (while (< (- (point-max) (point-min)) (length "foo")) + (while (not (string-match-p "foo" (buffer-string))) (while (accept-process-output proc 0 nil t)))) (should (string-match-p "foo" (buffer-string)))) @@ -5522,6 +5555,8 @@ This tests also `make-symbolic-link', `file-truename' and `add-name-to-file'." (delete-file tmp-name))) ;; Process filter. + ;; FIXME: tramp-smb.el should implement this. + (unless (tramp--test-smb-p) (unwind-protect (with-temp-buffer (setq command '("cat") @@ -5542,7 +5577,7 @@ This tests also `make-symbolic-link', `file-truename' and `add-name-to-file'." (should (string-match-p "foo" (buffer-string)))) ;; Cleanup. - (ignore-errors (delete-process proc))) + (ignore-errors (delete-process proc)))) ;; Disabled process filter. It doesn't work reliable. (unless t @@ -5672,32 +5707,33 @@ If UNSTABLE is non-nil, the test is tagged as `:unstable'." (should-not (apply #'make-process nil)) ; Use `apply' to avoid warnings. ;; Simple process. - (unwind-protect - (with-temp-buffer - (setq command '("cat") - proc - (make-process - :name "test1" :buffer (current-buffer) :command command - :file-handler t)) - (should (processp proc)) - (should (equal (process-status proc) 'run)) - (should (equal (process-get proc 'remote-command) command)) - (process-send-string proc "foo\n") - (process-send-eof proc) - ;; Read output. - (with-timeout (10 (tramp--test-timeout-handler)) - (while (< (- (point-max) (point-min)) (length "foo")) - (while (accept-process-output proc 0 nil t)))) - ;; Some `cat' implementations do not support the `cat -' - ;; call. We skip then. - (skip-unless - (not - (string-match-p (rx "cat: -: input file is output file\n") - (buffer-string)))) - (should (string-match-p "foo" (buffer-string)))) + (unless (tramp--test-smb-p) + (unwind-protect + (with-temp-buffer + (setq command '("cat") + proc + (make-process + :name "test1" :buffer (current-buffer) :command command + :file-handler t)) + (should (processp proc)) + (should (equal (process-status proc) 'run)) + (should (equal (process-get proc 'remote-command) command)) + (process-send-string proc "foo\n") + (process-send-eof proc) + ;; Read output. + (with-timeout (10 (tramp--test-timeout-handler)) + (while (< (- (point-max) (point-min)) (length "foo")) + (while (accept-process-output proc 0 nil t)))) + ;; Some `cat' implementations do not support the `cat -' + ;; call. We skip then. + (skip-unless + (not + (string-match-p (rx "cat: -: input file is output file\n") + (buffer-string)))) + (should (string-match-p "foo" (buffer-string)))) - ;; Cleanup. - (ignore-errors (delete-process proc))) + ;; Cleanup. + (ignore-errors (delete-process proc)))) ;; Simple process using a file. (unwind-protect @@ -5713,7 +5749,7 @@ If UNSTABLE is non-nil, the test is tagged as `:unstable'." (should (equal (process-get proc 'remote-command) command)) ;; Read output. (with-timeout (10 (tramp--test-timeout-handler)) - (while (< (- (point-max) (point-min)) (length "foo")) + (while (not (string-match-p "foo" (buffer-string))) (while (accept-process-output proc 0 nil t)))) (should (string-match-p "foo" (buffer-string)))) @@ -5723,6 +5759,8 @@ If UNSTABLE is non-nil, the test is tagged as `:unstable'." (delete-file tmp-name))) ;; Process filter. + ;; FIXME: tramp-smb.el should implement this. + (unless (tramp--test-smb-p) (unwind-protect (with-temp-buffer (setq command '("cat") @@ -5745,7 +5783,7 @@ If UNSTABLE is non-nil, the test is tagged as `:unstable'." (should (string-match-p "foo" (buffer-string)))) ;; Cleanup. - (ignore-errors (delete-process proc))) + (ignore-errors (delete-process proc)))) ;; Disabled process filter. It doesn't work reliable. (unless t @@ -5801,7 +5839,9 @@ If UNSTABLE is non-nil, the test is tagged as `:unstable'." ;; Process with stderr buffer. "telnet" does not cooperate with ;; three processes. - (unless (or (tramp--test-telnet-p) (tramp-direct-async-process-p)) + ;; FIXME: tramp-smb.el should implement this. + (unless (or (tramp--test-telnet-p) (tramp--test-smb-p) + (tramp-direct-async-process-p)) (let ((stderr (generate-new-buffer "*stderr*"))) (unwind-protect (with-temp-buffer @@ -5833,7 +5873,8 @@ If UNSTABLE is non-nil, the test is tagged as `:unstable'." (ignore-errors (kill-buffer stderr))))) ;; Process with stderr file. - (unless (tramp-direct-async-process-p) + ;; FIXME: tramp-smb.el should implement this. + (unless (or (tramp--test-smb-p) (tramp-direct-async-process-p)) (unwind-protect (with-temp-buffer (setq command '("cat" "/does-not-exist") @@ -6093,9 +6134,11 @@ If UNSTABLE is non-nil, the test is tagged as `:unstable'." INPUT, if non-nil, is a string sent to the process." (let ((proc (async-shell-command command output-buffer error-buffer)) (delete-exited-processes t)) - (should (equal (process-get proc 'remote-command) - (with-connection-local-variables - `(,shell-file-name ,shell-command-switch ,command)))) + ;; `tramp-smb-handle-shell-command' modifies the command. + (unless (tramp--test-smb-p) + (should (equal (process-get proc 'remote-command) + (with-connection-local-variables + `(,shell-file-name ,shell-command-switch ,command))))) (cl-letf (((symbol-function #'shell-command-sentinel) #'ignore)) (when (stringp input) (process-send-string proc input)) @@ -6139,7 +6182,9 @@ INPUT, if non-nil, is a string sent to the process." (should (file-exists-p tmp-name)) (funcall this-shell-command - (format "ls %s" (file-name-nondirectory tmp-name)) + (format + (if (tramp--test-smb-p) "(ls %s).Name" "ls %s") + (file-name-nondirectory tmp-name)) (current-buffer)) ;; "ls" could produce colorized output. (goto-char (point-min)) @@ -6154,7 +6199,8 @@ INPUT, if non-nil, is a string sent to the process." (ignore-errors (delete-file tmp-name))) ;; Test `{async-}shell-command' with error buffer. - (unless (tramp-direct-async-process-p) + ;; FIXME: tramp-smb.el should implement this. + (unless (or (tramp--test-smb-p) (tramp-direct-async-process-p)) (let ((stderr (generate-new-buffer "*stderr*"))) (unwind-protect (with-temp-buffer @@ -6170,7 +6216,8 @@ INPUT, if non-nil, is a string sent to the process." (ignore-errors (kill-buffer stderr)))))) ;; Test sending string to `async-shell-command'. - (when (tramp--test-asynchronous-processes-p) + (when (and (not (tramp--test-smb-p)) + (tramp--test-asynchronous-processes-p)) (unwind-protect (with-temp-buffer (write-region "foo" nil tmp-name) @@ -6550,6 +6597,8 @@ INPUT, if non-nil, is a string sent to the process." :tags '(:expensive-test :tramp-asynchronous-processes) (skip-unless (tramp--test-enabled)) (skip-unless (tramp--test-supports-processes-p)) + ;; FIXME: Make it work despite if ~/.emacs_powershell. + (skip-unless (not (tramp--test-smb-p))) (let ((default-directory ert-remote-temporary-file-directory) explicit-shell-file-name kill-buffer-query-functions @@ -7552,13 +7601,13 @@ This requires restrictions of file name syntax." (defun tramp--test-supports-processes-p () "Return whether the method under test supports external processes." (unless (tramp--test-crypt-p) - ;; We use it to enable/disable tests in a given test run, for - ;; example for remote processes on MS Windows. - (if (tramp-connection-property-p - tramp-test-vec "tramp--test-supports-processes-p") - (tramp-get-connection-property - tramp-test-vec "tramp--test-supports-processes-p") - (or (tramp--test-adb-p) (tramp--test-sh-p) (tramp--test-sshfs-p))))) + (or (tramp--test-adb-p) (tramp--test-sh-p) (tramp--test-sshfs-p) + (and (tramp--test-smb-p) + (file-writable-p + (file-name-concat + (file-remote-p ert-remote-temporary-file-directory) + ;; We check a directory on the "ADMIN$" share. + "ADMIN$" "Boot")))))) (defun tramp--test-supports-set-file-modes-p () "Return whether the method under test supports setting file modes." @@ -7728,7 +7777,11 @@ This requires restrictions of file name syntax." ;; `default-directory' with special characters. See ;; Bug#53846. (when (and (tramp--test-expensive-test-p) - (tramp--test-supports-processes-p)) + (tramp--test-supports-processes-p) + ;; FIXME: tramp-smb.el should implement this. + (not (and (tramp--test-smb-p) + (string-match-p + (rx (or (any "[$") (not ascii))) file1)))) (let ((default-directory file1)) (dolist (this-shell-command (append @@ -7988,6 +8041,7 @@ process sentinels. They shall not disturb each other." (skip-unless (not (tramp--test-telnet-p))) (skip-unless (not (tramp--test-box-p))) (skip-unless (not (tramp--test-windows-nt-p))) + (skip-unless (not (tramp--test-smb-p))) (with-timeout (tramp--test-asynchronous-requests-timeout (tramp--test-timeout-handler)) @@ -8912,7 +8966,10 @@ Since it unloads Tramp, it shall be the last test to run." (require 'tramp) (require 'tramp-archive) (should (featurep 'tramp)) - (should (featurep 'tramp-archive))) + (should (featurep 'tramp-archive)) + + ;; Disabled further tests. + (setq tramp--test-enabled-checked '(t))) (defun tramp-test-all (&optional interactive) "Run all tests for \\[tramp]. @@ -8944,6 +9001,11 @@ If INTERACTIVE is non-nil, the tests are run interactively." ;; * Implement `tramp-test31-interrupt-process' and ;; `tramp-test31-signal-process' for "adb", "sshfs" and for direct ;; async processes. Check, why they don't run stable. +;; * Fix the limitations for "smb" in `tramp-test28-process-file', +;; `tramp-test29-start-file-process', `tramp-test30-make-process', +;; `tramp-test32-shell-command', +;; `tramp-test34-explicit-shell-file-name' and +;; `tramp--test-check-files'. ;; * Check, why `tramp-test45-asynchronous-requests' often fails. The ;; famous reentrant error? ;; * Check, why direct async processes do not work for