1
Fork 0
mirror of git://git.sv.gnu.org/emacs.git synced 2026-01-30 04:10:54 -08:00

Rework remote process support for Tramp's "smb" method

* doc/misc/tramp.texi (Remote processes): Rewrite subsection
"Running remote processes on MS Windows hosts".

* etc/NEWS: Mention remote process support for Tramp's "smb" method.
Presentational fixes and improvements.

* lisp/net/tramp-smb.el (tramp-smb-file-name-handler-alist):
Use `tramp-smb-handle-make-process', `tramp-smb-handle-shell-command'
and `tramp-handle-start-file-process'.
(tramp-smb-matching-line): New defvar.
(tramp-smb-handle-make-process, tramp-smb-handle-shell-command)
(tramp-smb-shell-prompt): New defuns.
(tramp-smb-handle-process-file): Rewrite.
(tramp-smb-handle-start-file-process): Remove.
(tramp-smb-get-localname, tramp-smb-shell-quote-localname):
New optional argument SHARE.
(tramp-smb-send-command): Remove echoed command string.
(tramp-smb-maybe-open-connection): Initialize variables.
(tramp-smb-wait-for-output): Wait more robust.
(tramp-smb-call-winexe): Check for share.  Goto current directory.
(tramp-smb-connection-local-powershell-variables): New defconst.
Set it as connection local variables.

* test/lisp/net/tramp-tests.el (tramp-test28-process-file)
(tramp-test29-start-file-process, tramp-test30-make-process)
(tramp-test32-shell-command, tramp--test-async-shell-command)
(tramp--test-supports-processes-p): Adapt for "smb" method.
(tramp-test34-explicit-shell-file-name)
(tramp-test45-asynchronous-requests): Skip for "smb" method.
(tramp--test-check-files): Skip shell test for some special
characters in the "smb" case.
(tramp-test52-unload): Disable further tests.
This commit is contained in:
Michael Albinus 2025-12-06 13:26:52 +01:00
parent 598a3604d4
commit b1882857ee
6 changed files with 427 additions and 282 deletions

View file

@ -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

View file

@ -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.

View file

@ -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

View file

@ -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

View file

@ -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)))

View file

@ -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