Expand host names in Tramp's ad-hoc multi-hop file names

* doc/misc/tramp.texi (Quick Start Guide): Improve wording.
(Change file name syntax): Say, that `tramp-file-name-regexp' is
not constant.
(Ad-hoc multi-hops): Explain host name expansion.

* etc/NEWS: Mention that host names in Tramp ad-hoc multi-hop file
names must match the previous hop for methods like "su" or "sudo".
Fix typos.

* lisp/net/tramp.el (tramp-find-method, tramp-find-user):
Adapt docstring.
(tramp-find-host): Mark default value.
(tramp-dissect-file-name): Expand host name for hops.
(tramp-dissect-hop-name, tramp-make-tramp-hop-name): New defuns.
(tramp-clear-passwd): Simplify.

* test/lisp/net/tramp-tests.el (tramp-test02-file-name-dissect)
(tramp-test02-file-name-dissect-simplified)
(tramp-test02-file-name-dissect-separate)
(tramp-test26-file-name-completion): Extend tests.
This commit is contained in:
Michael Albinus 2018-10-20 13:38:31 +02:00
parent efb214622a
commit 67d3b40e0c
4 changed files with 188 additions and 61 deletions

View file

@ -464,7 +464,7 @@ this case it is written as @code{host#port}.
@cindex @option{plink} method
If your local host runs an SSH client, and the remote host runs an SSH
server, the most simple remote file name is
server, the simplest remote file name is
@file{@trampfn{ssh,user@@host,/path/to/file}}. The remote file name
@file{@trampfn{ssh,,}} opens a remote connection to yourself on the
local host, and is taken often for testing @value{tramp}.
@ -2459,9 +2459,10 @@ and @code{user@@} parts are optional.
@defvar tramp-file-name-regexp
This variable keeps a regexp which matches the selected remote file
name syntax. However, it is not recommended to use this variable in
external packages, a call of @code{file-remote-p} is much more
appropriate.
name syntax. Its value changes after every call of
@code{tramp-change-syntax}. However, it is not recommended to use
this variable in external packages, a call of @code{file-remote-p} is
much more appropriate.
@ifinfo
@pxref{Magic File Names, , , elisp}
@end ifinfo
@ -2585,9 +2586,9 @@ directory contents.
@cindex multi-hop, ad-hoc
@cindex proxy hosts, ad-hoc
@value{tramp} file name syntax can accommodate ad hoc specification of
@value{tramp} file name syntax can accommodate ad-hoc specification of
multiple proxies without using @code{tramp-default-proxies-alist}
configuration setup(@pxref{Multi-hops}).
configuration setup (@pxref{Multi-hops}).
Each proxy is specified using the same syntax as the remote host
specification minus the file name part. Each hop is separated by a
@ -2600,8 +2601,6 @@ proxy @samp{bird@@bastion} to a remote file on @samp{you@@remotehost}:
@kbd{C-x C-f @value{prefix}ssh@value{postfixhop}bird@@bastion|ssh@value{postfixhop}you@@remotehost@value{postfix}/path @key{RET}}
@end example
Proxies can take patterns @code{%h} or @code{%u}.
@value{tramp} adds the ad-hoc definitions on the fly to
@code{tramp-default-proxies-alist} and is available for re-use
during that Emacs session. Subsequent @value{tramp} connections to
@ -2618,6 +2617,17 @@ For ad-hoc definitions to be saved automatically in
@end lisp
@end defopt
Ad-hoc proxies can take patterns @code{%h} or @code{%u} like in
@code{tramp-default-proxies-alist}. The following file name expands
to user @code{root} on host @code{remotehost}, starting with an
@option{ssh} session on host @code{remotehost}:
@samp{@value{prefix}ssh@value{postfixhop}%h|su@value{postfixhop}remotehost@value{postfix}}.
On the other hand, if a trailing hop does not specifiy a host name,
the host name of the previous hop is reused. Therefore, the following
file name is equivalent to the previous example:
@samp{@value{prefix}ssh@value{postfixhop}remotehost|su@value{postfixhop}@value{postfix}}.
@node Remote processes
@section Integration with other Emacs packages

View file

@ -172,7 +172,7 @@ the data.
+++
** The Network Security Manager now allows more fine-grained control
of what checks to run via the `network-security-protocol-checks'
of what checks to run via the 'network-security-protocol-checks'
variable.
+++
@ -356,7 +356,7 @@ shown in the currently selected window.
** Comint
+++
*** 'send-invisible' is now an obsolete alias for `comint-send-invisible'.
*** 'send-invisible' is now an obsolete alias for 'comint-send-invisible'.
Also, 'shell-strip-ctrl-m' is declared obsolete.
+++
@ -391,7 +391,7 @@ facilities to aid more casual SQL developers layout queries and
complex expressions.
*** 'sql-use-indent-support' (default t) enables SQL indention support.
The `sql-indent' package from ELPA must be installed to get the
The 'sql-indent' package from ELPA must be installed to get the
indentation support in 'sql-mode' and 'sql-interactive-mode'.
*** 'sql-mode-hook' and 'sql-interactive-mode-hook' changed.
@ -420,7 +420,7 @@ This enables more efficient backends. See the docstring of
** Package
*** New function `package-get-version` lets packages query their own version.
*** New function 'package-get-version' lets packages query their own version.
Example use in auctex.el: (defconst auctex-version (package-get-version))
*** New 'package-quickstart' feature.
@ -747,6 +747,10 @@ are obsoleted in GVFS.
*** The user option 'tramp-ignored-file-name-regexp' allows to disable
Tramp for some look-alike remote file names.
+++
*** For some connection methods, like "su" or "sudo", the host name in
ad-hoc multi-hop file names must match the previous hop.
** Register
---
*** The return value of method 'register-val-describe' includes the

View file

@ -1267,7 +1267,7 @@ entry does not exist, return nil."
(defun tramp-find-method (method user host)
"Return the right method string to use.
This is METHOD, if non-nil. Otherwise, do a lookup in
`tramp-default-method-alist'."
`tramp-default-method-alist' and `tramp-default-method'."
(when (and method
(or (string-equal method "")
(string-equal method tramp-default-method-marker)))
@ -1292,7 +1292,7 @@ This is METHOD, if non-nil. Otherwise, do a lookup in
(defun tramp-find-user (method user host)
"Return the right user string to use.
This is USER, if non-nil. Otherwise, do a lookup in
`tramp-default-user-alist'."
`tramp-default-user-alist' and `tramp-default-user'."
(let ((result
(or user
(let ((choices tramp-default-user-alist)
@ -1312,18 +1312,24 @@ This is USER, if non-nil. Otherwise, do a lookup in
(defun tramp-find-host (method user host)
"Return the right host string to use.
This is HOST, if non-nil. Otherwise, it is `tramp-default-host'."
(or (and (> (length host) 0) host)
(let ((choices tramp-default-host-alist)
lhost item)
(while choices
(setq item (pop choices))
(when (and (string-match (or (nth 0 item) "") (or method ""))
(string-match (or (nth 1 item) "") (or user "")))
(setq lhost (nth 2 item))
(setq choices nil)))
lhost)
tramp-default-host))
This is HOST, if non-nil. Otherwise, do a lookup in
`tramp-default-host-alist' and `tramp-default-host'."
(let ((result
(or (and (> (length host) 0) host)
(let ((choices tramp-default-host-alist)
lhost item)
(while choices
(setq item (pop choices))
(when (and (string-match (or (nth 0 item) "") (or method ""))
(string-match (or (nth 1 item) "") (or user "")))
(setq lhost (nth 2 item))
(setq choices nil)))
lhost)
tramp-default-host)))
;; We must mark, whether a default value has been used.
(if (or (> (length host) 0) (null result))
result
(propertize result 'tramp-default t))))
(defun tramp-dissect-file-name (name &optional nodefault)
"Return a `tramp-file-name' structure of NAME, a remote file name.
@ -1343,7 +1349,7 @@ default values are used."
(host (match-string (nth 3 tramp-file-name-structure) name))
(localname (match-string (nth 4 tramp-file-name-structure) name))
(hop (match-string (nth 5 tramp-file-name-structure) name))
domain port)
domain port v)
(when user
(when (string-match tramp-user-with-domain-regexp user)
(setq domain (match-string 2 user)
@ -1359,14 +1365,34 @@ default values are used."
(setq host (replace-match "" nil t host))))
(unless nodefault
(setq method (tramp-find-method method user host)
user (tramp-find-user method user host)
host (tramp-find-host method user host)))
(when hop
(setq v (tramp-dissect-hop-name hop)
hop (and hop (tramp-make-tramp-hop-name v))))
(let ((tramp-default-host
(or (and v (not (string-match "%h" (tramp-file-name-host v)))
(tramp-file-name-host v))
tramp-default-host)))
(setq method (tramp-find-method method user host)
user (tramp-find-user method user host)
host (tramp-find-host method user host)
hop
(and hop
(format-spec hop (format-spec-make ?h host ?u user))))))
(make-tramp-file-name
:method method :user user :domain domain :host host :port port
:localname localname :hop hop)))))
(defun tramp-dissect-hop-name (name &optional nodefault)
"Return a `tramp-file-name' structure of `hop' part of NAME.
See `tramp-dissect-file-name' for details."
(tramp-dissect-file-name
(concat
tramp-prefix-format
(replace-regexp-in-string
(concat tramp-postfix-hop-regexp "$") tramp-postfix-host-format name))
nodefault))
(defun tramp-buffer-name (vec)
"A name for the connection buffer VEC."
(let ((method (tramp-file-name-method vec))
@ -1433,6 +1459,14 @@ the form (METHOD USER DOMAIN HOST PORT LOCALNAME &optional HOP)."
tramp-postfix-host-format
localname)))
(defun tramp-make-tramp-hop-name (vec)
"Construct a Tramp hop name from VEC."
(replace-regexp-in-string
tramp-prefix-regexp ""
(replace-regexp-in-string
(concat tramp-postfix-host-regexp "$") tramp-postfix-hop-format
(tramp-make-tramp-file-name vec 'noloc))))
(defun tramp-completion-make-tramp-file-name (method user host localname)
"Construct a Tramp file name from METHOD, USER, HOST and LOCALNAME.
It must not be a complete Tramp file name, but as long as there are
@ -2313,7 +2347,7 @@ Falls back to normal file name handler if no Tramp file name handler exists."
(tramp-message
v 1 "Interrupt received in operation %s"
(cons operation args)))
;; Propagate the quit signal.
;; Propagate the signal.
(signal (car err) (cdr err)))
;; When we are in completion mode, some failed
@ -4508,13 +4542,7 @@ Invokes `password-read' if available, `read-passwd' else."
(hop (tramp-file-name-hop vec)))
(when hop
;; Clear also the passwords of the hops.
(tramp-clear-passwd
(tramp-dissect-file-name
(concat
tramp-prefix-format
(replace-regexp-in-string
(concat tramp-postfix-hop-regexp "$")
tramp-postfix-host-format hop)))))
(tramp-clear-passwd (tramp-dissect-hop-name hop)))
(auth-source-forget
`(:max 1 ,(and user-domain :user) ,user-domain
:host ,host-port :port ,method))

View file

@ -763,8 +763,8 @@ handled properly. BODY shall not contain a timeout."
"|-:user2@host2"
"|-:user3@host3:/path/to/file"))
(format "/%s:%s@%s|%s:%s@%s|%s:%s@%s:"
"-" "user1" "host1"
"-" "user2" "host2"
"method1" "user1" "host1"
"method2" "user2" "host2"
"method3" "user3" "host3")))
;; Expand `tramp-default-user-alist'.
@ -778,9 +778,9 @@ handled properly. BODY shall not contain a timeout."
"/method1:host1"
"|method2:host2"
"|method3:host3:/path/to/file"))
(format "/%s:%s|%s:%s|%s:%s@%s:"
"method1" "host1"
"method2" "host2"
(format "/%s:%s@%s|%s:%s@%s|%s:%s@%s:"
"method1" "user1" "host1"
"method2" "user2" "host2"
"method3" "user3" "host3")))
;; Expand `tramp-default-host-alist'.
@ -794,9 +794,36 @@ handled properly. BODY shall not contain a timeout."
"/method1:user1@"
"|method2:user2@"
"|method3:user3@:/path/to/file"))
(format "/%s:%s@|%s:%s@|%s:%s@%s:"
"method1" "user1"
"method2" "user2"
(format "/%s:%s@%s|%s:%s@%s|%s:%s@%s:"
"method1" "user1" "host1"
"method2" "user2" "host2"
"method3" "user3" "host3")))
;; Ad-hoc user name and host name expansion.
(setq tramp-default-method-alist nil
tramp-default-user-alist nil
tramp-default-host-alist nil)
(should
(string-equal
(file-remote-p
(concat
"/method1:user1@host1"
"|method2:user2@"
"|method3:user3@:/path/to/file"))
(format "/%s:%s@%s|%s:%s@%s|%s:%s@%s:"
"method1" "user1" "host1"
"method2" "user2" "host1"
"method3" "user3" "host1")))
(should
(string-equal
(file-remote-p
(concat
"/method1:%u@%h"
"|method2:%u@%h"
"|method3:user3@host3:/path/to/file"))
(format "/%s:%s@%s|%s:%s@%s|%s:%s@%s:"
"method1" "user3" "host3"
"method2" "user3" "host3"
"method3" "user3" "host3")))))
(ert-deftest tramp-test02-file-name-dissect-simplified ()
@ -1067,9 +1094,9 @@ handled properly. BODY shall not contain a timeout."
"/host1"
"|host2"
"|host3:/path/to/file"))
(format "/%s|%s|%s@%s:"
"host1"
"host2"
(format "/%s@%s|%s@%s|%s@%s:"
"user1" "host1"
"user2" "host2"
"user3" "host3")))
;; Expand `tramp-default-host-alist'.
@ -1083,9 +1110,35 @@ handled properly. BODY shall not contain a timeout."
"/user1@"
"|user2@"
"|user3@:/path/to/file"))
(format "/%s@|%s@|%s@%s:"
"user1"
"user2"
(format "/%s@%s|%s@%s|%s@%s:"
"user1" "host1"
"user2" "host2"
"user3" "host3")))
;; Ad-hoc user name and host name expansion.
(setq tramp-default-user-alist nil
tramp-default-host-alist nil)
(should
(string-equal
(file-remote-p
(concat
"/user1@host1"
"|user2@"
"|user3@:/path/to/file"))
(format "/%s@%s|%s@%s|%s@%s:"
"user1" "host1"
"user2" "host1"
"user3" "host1")))
(should
(string-equal
(file-remote-p
(concat
"/%u@%h"
"|%u@%h"
"|user3@host3:/path/to/file"))
(format "/%s@%s|%s@%s|%s@%s:"
"user3" "host3"
"user3" "host3"
"user3" "host3"))))
;; Exit.
@ -1670,9 +1723,9 @@ handled properly. BODY shall not contain a timeout."
"/[/user1@host1"
"|/user2@host2"
"|/user3@host3]/path/to/file"))
(format "/[/%s@%s|/%s@%s|%s/%s@%s]"
"user1" "host1"
"user2" "host2"
(format "/[%s/%s@%s|%s/%s@%s|%s/%s@%s]"
"method1" "user1" "host1"
"method2" "user2" "host2"
"method3" "user3" "host3")))
;; Expand `tramp-default-user-alist'.
@ -1686,9 +1739,9 @@ handled properly. BODY shall not contain a timeout."
"/[method1/host1"
"|method2/host2"
"|method3/host3]/path/to/file"))
(format "/[%s/%s|%s/%s|%s/%s@%s]"
"method1" "host1"
"method2" "host2"
(format "/[%s/%s@%s|%s/%s@%s|%s/%s@%s]"
"method1" "user1" "host1"
"method2" "user2" "host2"
"method3" "user3" "host3")))
;; Expand `tramp-default-host-alist'.
@ -1702,9 +1755,36 @@ handled properly. BODY shall not contain a timeout."
"/[method1/user1@"
"|method2/user2@"
"|method3/user3@]/path/to/file"))
(format "/[%s/%s@|%s/%s@|%s/%s@%s]"
"method1" "user1"
"method2" "user2"
(format "/[%s/%s@%s|%s/%s@%s|%s/%s@%s]"
"method1" "user1" "host1"
"method2" "user2" "host2"
"method3" "user3" "host3")))
;; Ad-hoc user name and host name expansion.
(setq tramp-default-method-alist nil
tramp-default-user-alist nil
tramp-default-host-alist nil)
(should
(string-equal
(file-remote-p
(concat
"/[method1/user1@host1"
"|method2/user2@"
"|method3/user3@]/path/to/file"))
(format "/[%s/%s@%s|%s/%s@%s|%s/%s@%s]"
"method1" "user1" "host1"
"method2" "user2" "host1"
"method3" "user3" "host1")))
(should
(string-equal
(file-remote-p
(concat
"/[method1/%u@%h"
"|method2/%u@%h"
"|method3/user3@host3]/path/to/file"))
(format "/[%s/%s@%s|%s/%s@%s|%s/%s@%s]"
"method1" "user3" "host3"
"method2" "user3" "host3"
"method3" "user3" "host3"))))
;; Exit.
@ -3491,6 +3571,7 @@ This tests also `make-symbolic-link', `file-truename' and `add-name-to-file'."
(when (not (memq system-type '(cygwin windows-nt)))
(let ((method (file-remote-p tramp-test-temporary-file-directory 'method))
(host (file-remote-p tramp-test-temporary-file-directory 'host))
(vec (tramp-dissect-file-name tramp-test-temporary-file-directory))
(orig-syntax tramp-syntax))
(when (and (stringp host) (string-match tramp-host-with-port-regexp host))
(setq host (match-string 1 host)))
@ -3501,6 +3582,10 @@ This tests also `make-symbolic-link', `file-truename' and `add-name-to-file'."
(if (tramp--test-expensive-test)
(tramp-syntax-values) `(,orig-syntax)))
(tramp-change-syntax syntax)
;; This has cleaned up all connection data, which are used
;; for completion. We must refill the cache.
(tramp-set-connection-property vec "property" nil)
(let ;; This is needed for the `simplified' syntax.
((method-marker
(if (zerop (length tramp-method-regexp))