Commit 83b1db04 authored by Michael Albinus's avatar Michael Albinus

Add Tramp support of direct asynchronous process invocation

* doc/misc/tramp.texi (Predefined connection information):
Add "direct-async-process".
(Remote processes): New subsection "Improving performance of
asynchronous remote processes".

* lisp/net/tramp-adb.el (tramp-methods) <adb>: Add `tramp-login-program'
and `tramp-login-args'.
(tramp-adb-handle-make-process): Use `tramp-handle-make-process'.
(tramp-adb-maybe-open-connection): Add "set +o vi +o emacs" command.

* lisp/net/tramp-sh.el (tramp-sh-handle-make-process):
Use `tramp-handle-make-process'.
(tramp-sh-file-name-handler-p, tramp-multi-hop-p): New defuns.
(tramp-compute-multi-hops): Use `tramp-multi-hop-p'.

* lisp/net/tramp.el (tramp-dissect-file-name, tramp-dissect-hop-name):
Use `tramp-multi-hop-p'.
(tramp-handle-insert-file-contents, tramp-local-host-p):
Use `tramp-sh-file-name-handler-p'.
(tramp-handle-make-process): New defun.

* test/README: Add another example how to use SELECTOR.

* test/lisp/net/tramp-tests.el (tramp-test03-file-name-method-rules):
Adapt test.
(tramp--test-sh-p): Use `tramp-sh-file-name-handler-p'.
parent b8b25400
......@@ -2053,6 +2053,13 @@ The temporary directory on the remote host. If not specified, the
default value is @t{"/data/local/tmp"} for the @option{adb} method,
@t{"/C$/Temp"} for the @option{smb} method, and @t{"/tmp"} otherwise.
@item @t{"direct-async-process"}
When this property is non-@code{nil}, an alternative, more performant
implementation of @code{make-process} and
@code{start-file-process} is applied. @ref{Improving performance of
asynchronous remote processes} for a discussion of constraints.
@item @t{"posix"}
Connections using the @option{smb} method check, whether the remote
......@@ -2458,10 +2465,9 @@ overwrite as follows:
`(,(regexp-quote "")
"remote-copy-args" (("-l") ("%r"))))
(add-to-list 'tramp-connection-properties
`(,(regexp-quote "")
"remote-copy-args" (("-l") ("%r"))))
@end group
@end lisp
......@@ -3527,6 +3533,70 @@ To open @command{powershell} as a remote shell, use this:
@end lisp
@anchor{Improving performance of asynchronous remote processes}
@subsection Improving performance of asynchronous remote processes
@cindex Asynchronous remote processes
@findex make-process
@findex start-file-process
@value{tramp}'s implementation of @code{make-process} and
@code{start-file-process} requires a serious overhead for
initialization, every process invocation. This is needed for handling
interactive dialogues when connecting the remote host (like providing
a password), and initial environment setup.
Sometimes, this is not needed. Instead of starting a remote shell and
running the command afterwards, it is sufficient to run the command
directly. @value{tramp} supports this by an alternative
implementation of @code{make-process} and @code{start-file-process}.
This is triggered by the connection property
@t{"direct-async-process"}, @xref{Predefined connection information},
which must be set to a non-@code{nil} value. Example:
(add-to-list 'tramp-connection-properties
(list (regexp-quote "@trampfn{ssh,user@@host,}")
"direct-async-process" t))
@end group
@end lisp
However, this approach has different limitations:
It works only for connection methods defined in @file{tramp-sh.el} and
It does not support multi-hop methods.
It does not support interactive user authentication, like password
It does not support a separated error stream.
It cannot be killed via @code{interrupt-process}.
It does not report the remote terminal name via @code{process-tty-name}.
It does not use @code{tramp-remote-path} and
It does not set environment variable @env{INSIDE_EMACS}.
@end itemize
In order to gain even more performance, it is recommended to bind
@code{tramp-verbose} to 0 when running @code{make-process} or
@node Cleanup remote connections
@section Cleanup remote connections
@cindex cleanup
......@@ -4555,9 +4625,8 @@ Abbreviation list expansion can be used to reduce typing long file names:
'("^/xy" . "@trampfn{ssh,,/opt/news/etc/}"))
(add-to-list 'directory-abbrev-alist
'("^/xy" . "@trampfn{ssh,,/opt/news/etc/}"))
@end group
@end lisp
This diff is collapsed.
This diff is collapsed.
......@@ -1482,10 +1482,7 @@ default values are used."
v "Method `%s' is not known." method))
;; Only some methods from tramp-sh.el do support multi-hops.
(when (and
(or (not (tramp-get-method-parameter v 'tramp-login-program))
(tramp-get-method-parameter v 'tramp-copy-program)))
(unless (or (null hop) nodefault non-essential (tramp-multi-hop-p v))
v "Method `%s' is not supported for multi-hops." method)))))))
......@@ -1499,8 +1496,7 @@ See `tramp-dissect-file-name' for details."
tramp-postfix-host-format name))
;; Only some methods from tramp-sh.el do support multi-hops.
(when (or (not (tramp-get-method-parameter v 'tramp-login-program))
(tramp-get-method-parameter v 'tramp-copy-program))
(unless (or nodefault non-essential (tramp-multi-hop-p v))
v "Method `%s' is not supported for multi-hops."
(tramp-file-name-method v)))
......@@ -3519,13 +3515,10 @@ User is always nil."
;; When we shall insert only a part of the file, we
;; copy this part. This works only for the shell file
;; name handlers.
;; name handlers. It doesn't work for crypted files.
(when (and (or beg end)
;; Direct actions aren't possible for
;; crypted directories.
(null tramp-crypt-enabled)
v 'tramp-login-program))
(tramp-sh-file-name-handler-p v)
(null tramp-crypt-enabled))
(setq remote-copy (tramp-make-tramp-temp-file v))
;; This is defined in tramp-sh.el. Let's assume
;; this is loaded already.
......@@ -3640,6 +3633,152 @@ User is always nil."
(load local-copy noerror t nosuffix must-suffix)
(delete-file local-copy)))))
;; 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-handle-make-process (&rest args)
"An alternative `make-process' implementation for Tramp files."
(when args
(with-parsed-tramp-file-name (expand-file-name default-directory) nil
(let ((name (plist-get args :name))
(buffer (plist-get args :buffer))
(command (plist-get args :command))
(coding (plist-get args :coding))
(noquery (plist-get args :noquery))
(connection-type (plist-get args :connection-type))
(filter (plist-get args :filter))
(sentinel (plist-get args :sentinel))
(stderr (plist-get args :stderr)))
(unless (stringp name)
(signal 'wrong-type-argument (list #'stringp name)))
(unless (or (null buffer) (bufferp buffer) (stringp buffer))
(signal 'wrong-type-argument (list #'stringp buffer)))
(unless (consp command)
(signal 'wrong-type-argument (list #'consp command)))
(unless (or (null coding)
(and (symbolp coding) (memq coding coding-system-list))
(and (consp coding)
(memq (car coding) coding-system-list)
(memq (cdr coding) coding-system-list)))
(signal 'wrong-type-argument (list #'symbolp coding)))
(unless (or (null connection-type) (memq connection-type '(pipe pty)))
(signal 'wrong-type-argument (list #'symbolp connection-type)))
(unless (or (null filter) (functionp filter))
(signal 'wrong-type-argument (list #'functionp filter)))
(unless (or (null sentinel) (functionp sentinel))
(signal 'wrong-type-argument (list #'functionp sentinel)))
(unless (or (null stderr) (bufferp stderr) (stringp stderr))
(signal 'wrong-type-argument (list #'stringp stderr)))
(when (and (stringp stderr) (tramp-tramp-file-p stderr)
(not (tramp-equal-remote default-directory stderr)))
(signal 'file-error (list "Wrong stderr" stderr)))
(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 (append `("cd" ,localname "&&")
(mapcar #'tramp-shell-quote-argument command)))
(bmp (and (buffer-live-p buffer) (buffer-modified-p buffer)))
(name1 name)
(i 0)
;; We do not want to raise an error when `make-process'
;; has been started several times in `eshell' and
;; friends.
(while (get-process name1)
;; NAME must be unique as process name.
(setq i (1+ i)
name1 (format "%s<%d>" name i)))
(setq name name1)
;; Set the new process properties.
(tramp-set-connection-property v "process-name" name)
(tramp-set-connection-property v "process-buffer" buffer)
(with-current-buffer (tramp-get-connection-buffer v)
(let* ((login-program
(tramp-get-method-parameter v 'tramp-login-program))
(tramp-get-method-parameter v 'tramp-login-args))
(tramp-get-method-parameter v 'tramp-async-args))
;; We don't create the temporary file. In
;; fact, it is just a prefix for the
;; ControlPath option of ssh; the real
;; temporary file has another name, and it is
;; created and protected by ssh. It is also
;; removed by ssh when the connection is
;; closed. The temporary file name is cached
;; in the main connection process, therefore
;; we cannot use `tramp-get-connection-process'.
(when (tramp-sh-file-name-handler-p v)
(tramp-get-process v) "temp-file"
(when (tramp-sh-file-name-handler-p v)
'tramp-ssh-controlmaster-options v)))
;; Replace `login-args' place holders.
spec (format-spec-make ?t tmpfile)
options (format-spec (or options "") spec)
spec (format-spec-make
?h (or host "") ?u (or user "") ?p (or port "")
?c options ?l "")
;; Add arguments for asynchronous processes.
login-args (append async-args login-args)
;; Expand format spec.
(lambda (x)
(setq x (mapcar (lambda (y) (format-spec y spec)) x))
(unless (member "" x) x))
;; Split ControlMaster options.
(mapcar (lambda (x) (split-string x " ")) login-args))
p (apply
name buffer login-program (append login-args command)))
(tramp-message v 6 "%s" (string-join (process-command p) " "))
;; Set sentinel and filter.
(when sentinel
(set-process-sentinel p sentinel))
(when filter
(set-process-filter p filter))
;; Set query flag and process marker for this
;; process. We ignore errors, because the
;; process could have finished already.
(set-process-query-on-exit-flag p (null noquery))
(set-marker (process-mark p) (point)))
;; We must flush them here already; otherwise
;; `rename-file', `delete-file' or
;; `insert-file-contents' will fail.
(tramp-flush-connection-property v "process-name")
(tramp-flush-connection-property v "process-buffer")
;; Return process.
;; Save exit.
(if (string-match-p tramp-temp-buffer-name (buffer-name))
(set-process-buffer p nil)
(kill-buffer (current-buffer)))
(set-buffer-modified-p bmp))
(tramp-flush-connection-property v "process-name")
(tramp-flush-connection-property v "process-buffer"))))))))
(defun tramp-handle-make-symbolic-link
(target linkname &optional ok-if-already-exists)
......@@ -4706,7 +4845,7 @@ This handles also chrooted environments, which are not regarded as local."
;; The method shall be applied to one of the shell file name
;; handlers. `tramp-local-host-p' is also called for "smb" and
;; alike, where it must fail.
(tramp-get-method-parameter vec 'tramp-login-program)
(tramp-sh-file-name-handler-p vec)
;; Direct actions aren't possible for crypted directories.
(null tramp-crypt-enabled)
;; The local temp directory must be writable for the other user.
......@@ -64,6 +64,11 @@ protect against "make" variable expansion):
make <filename> SELECTOR='"foo$$"'
In case you want to use the symbol name of a test as selector, you can
use it directly:
make <filename> SELECTOR='test-foo-remote'
Note that although the test files are always compiled (unless they set
no-byte-compile), the source files will be run when expensive or
unstable tests are involved, to give nicer backtraces. To run the
......@@ -2001,12 +2001,13 @@ is greater than 10.
(skip-unless (tramp--test-enabled))
;; Multi hops are allowed for inline methods only.
(file-remote-p "/ssh:user1@host1|method:user2@host2:/path/to/file")
:type 'user-error)
(file-remote-p "/method:user1@host1|ssh:user2@host2:/path/to/file")
:type 'user-error)
(let (non-essential)
(expand-file-name "/ssh:user1@host1|method:user2@host2:/path/to/file")
:type 'user-error)
(expand-file-name "/method:user1@host1|ssh:user2@host2:/path/to/file")
:type 'user-error))
;; Samba does not support file names with periods followed by
;; spaces, and trailing periods or spaces.
......@@ -5681,9 +5682,8 @@ This does not support special file names."
(defun tramp--test-sh-p ()
"Check, whether the remote host runs a based method from tramp-sh.el."
(tramp-find-foreign-file-name-handler tramp-test-temporary-file-directory)
(tramp-dissect-file-name tramp-test-temporary-file-directory)))
(defun tramp--test-sudoedit-p ()
"Check, whether the sudoedit method is used."
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment