Function: tramp-maybe-open-connection

tramp-maybe-open-connection is a byte-compiled function defined in tramp-sh.el.gz.

Signature

(tramp-maybe-open-connection VEC)

Documentation

Maybe open a connection VEC.

Does not do anything if a connection is already open, but re-opens the connection if a previous connection has died for some reason.

Source Code

;; Defined in /usr/src/emacs/lisp/net/tramp-sh.el.gz
(defun tramp-maybe-open-connection (vec)
  "Maybe open a connection VEC.
Does not do anything if a connection is already open, but re-opens the
connection if a previous connection has died for some reason."
  ;; During completion, don't reopen a new connection.
  ;; Same for slide-in timer or process-{filter,sentinel}.
  (unless (tramp-connectable-p vec)
    (throw 'non-essential 'non-essential))

  (with-tramp-debug-message vec "Opening connection"
    (let ((p (tramp-get-connection-process vec))
	  (process-name (tramp-get-connection-property vec " process-name"))
	  (process-environment (copy-sequence process-environment))
	  (pos (with-current-buffer (tramp-get-connection-buffer vec) (point))))

      ;; If Tramp opens the same connection within a short time frame,
      ;; there is a problem.  We shall signal this.
      (unless (or (process-live-p p)
                  (and (processp p) (not non-essential))
		  (not (tramp-file-name-equal-p
			vec (car tramp-current-connection)))
		  (time-less-p
		   (time-since (cdr tramp-current-connection))
		   (or tramp-connection-min-time-diff 0)))
	(throw 'suppress 'suppress))

      ;; If too much time has passed since last command was sent, look
      ;; whether process is still alive.  If it isn't, kill it.  When
      ;; using ssh, it can sometimes happen that the remote end has
      ;; hung up but the local ssh client doesn't recognize this until
      ;; it tries to send some data to the remote end.  So that's why
      ;; we try to send a command from time to time, then look again
      ;; whether the process is really alive.
      (condition-case nil
	  (when (and (time-less-p
		      60 (time-since
			  (tramp-get-connection-property p "last-cmd-time" 0)))
		     (process-live-p p))
	    (tramp-send-command vec "echo are you awake" t t)
	    (unless (and (process-live-p p)
			 (tramp-wait-for-output p 10))
	      ;; The error will be caught locally.
	      (tramp-error vec 'file-error "Awake did fail")))
	(file-error
	 (tramp-cleanup-connection vec t)
	 (setq p nil)))

      ;; New connection must be opened.
      (condition-case err
	  (unless (process-live-p p)
	    (catch 'uname-changed
	      ;; Start new process.
	      (when (and p (processp p))
		(delete-process p))
	      (setenv "LC_ALL" (tramp-get-local-locale vec))
	      (if (stringp tramp-histfile-override)
		  (setenv "HISTFILE" tramp-histfile-override)
		(if tramp-histfile-override
		    (progn
		      (setenv "HISTFILE")
		      (setenv "HISTFILESIZE" "0")
		      (setenv "HISTSIZE" "0"))))
	      (unless (stringp tramp-encoding-shell)
                (tramp-error vec 'file-error "`tramp-encoding-shell' not set"))
	      (let* ((current-host tramp-system-name)
		     (target-alist (tramp-compute-multi-hops vec))
		     (previous-hop tramp-null-hop)
		     ;; We will apply `tramp-ssh-or-plink-options'
		     ;; only for the first hop.
		     (options (tramp-ssh-or-plink-options vec))
		     (process-connection-type tramp-process-connection-type)
		     (process-adaptive-read-buffering nil)
		     ;; There are unfortunate settings for "cmdproxy"
		     ;; on W32 systems.
		     (process-coding-system-alist nil)
		     (coding-system-for-read nil)
		     (extra-args (tramp-get-sh-extra-args tramp-encoding-shell))
		     ;; This must be done in order to avoid our file
		     ;; name handler.
		     (p (apply
			 #'tramp-start-process vec
			 (tramp-get-connection-name vec)
			 (tramp-get-connection-buffer vec)
			 (append
			  `(,tramp-encoding-shell)
			  (and extra-args (split-string extra-args))
			  (and tramp-encoding-command-interactive
			       `(,tramp-encoding-command-interactive))))))

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

		;; Check whether process is alive.
		(tramp-barf-if-no-shell-prompt
		 p 10
		 "Couldn't find local shell prompt for %s"
		 tramp-encoding-shell)

		;; Now do all the connections as specified.
		(while target-alist
		  (let* ((hop (car target-alist))
			 (l-method (tramp-file-name-method hop))
			 (l-user (tramp-file-name-user hop))
			 (l-domain (tramp-file-name-domain hop))
			 (l-host (tramp-file-name-host hop))
			 (l-port (tramp-file-name-port hop))
			 (remote-shell
			  (tramp-get-method-parameter hop 'tramp-remote-shell))
			 (extra-args (tramp-get-sh-extra-args remote-shell))
			 (async-args
			  (flatten-tree
			   (tramp-get-method-parameter hop 'tramp-async-args)))
			 (connection-timeout
			  (tramp-get-method-parameter
			   hop 'tramp-connection-timeout
			   tramp-connection-timeout))
			 (command
			  (tramp-get-method-parameter
			   hop 'tramp-login-program))
			 ;; 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'.
			 (tmpfile
			  (with-tramp-connection-property
			      (tramp-get-process vec) "temp-file"
			    (tramp-compat-make-temp-name)))
			 r-shell)

		    ;; Check, whether there is a restricted shell.
		    (dolist (elt tramp-restricted-shell-hosts-alist)
		      (when (string-match-p elt current-host)
			(setq r-shell t)))
		    (setq current-host l-host)

		    ;; Set hop and password prompt vector.
		    (tramp-set-connection-property p "hop-vector" hop)
		    (tramp-set-connection-property
		     p "pw-vector"
		     (if (tramp-get-method-parameter
			  hop 'tramp-password-previous-hop)
			 (let ((pv (copy-tramp-file-name previous-hop)))
			   (setf (tramp-file-name-method pv) l-method)
			   pv)
		       (make-tramp-file-name
			:method l-method :user l-user :domain l-domain
			:host l-host :port l-port)))

		    ;; Set session timeout.
		    (when-let* ((timeout
				 (tramp-get-method-parameter
				  hop 'tramp-session-timeout)))
		      (tramp-set-connection-property
		       p "session-timeout" timeout))

		    ;; Replace `login-args' place holders.
		    (setq
		     command
		     (string-join
		      (append
		       ;; We do not want to see the trailing local
		       ;; prompt in `start-file-process'.
		       (unless r-shell '("exec"))
		       `(,command)
		       ;; Add arguments for asynchronous processes.
		       (when process-name async-args)
		       (tramp-expand-args
			hop 'tramp-login-args nil
			?h (or l-host "") ?u (or l-user "") ?p (or l-port "")
			?c (format-spec options (format-spec-make ?t tmpfile))
			?n (concat
			    "2>" (tramp-get-remote-null-device previous-hop))
			?l (concat remote-shell " " extra-args " -i"))
		       ;; A restricted shell does not allow "exec".
		       (when r-shell '("&&" "exit")) '("||" "exit"))
		      " "))

		    ;; Send the command.
		    (with-tramp-progress-reporter
			vec 3
			(format "Opening connection%s for %s%s using %s"
				(if (tramp-string-empty-or-nil-p process-name)
				    "" (concat " " process-name))
				(if (tramp-string-empty-or-nil-p l-user)
				    "" (concat l-user "@"))
				(tramp-file-name-host-port hop) l-method)
		      (tramp-send-command vec command t t)
		      (tramp-process-actions
		       p vec
		       (min
			pos (with-current-buffer (process-buffer p) (point-max)))
		       tramp-actions-before-shell connection-timeout))

		    ;; Next hop.
		    (tramp-flush-connection-property p "hop-vector")
		    (tramp-flush-connection-property p "pw-vector")
		    (setq options ""
			  target-alist (cdr target-alist)
			  previous-hop hop)))

		;; Activate session timeout.
		(when (tramp-get-connection-property p "session-timeout")
		  (run-at-time
		   (tramp-get-connection-property p "session-timeout") nil
		   #'tramp-timeout-session vec))

		;; Make initial shell settings.
		(with-tramp-progress-reporter
		    vec 3
		    (format "Setup connection%s for %s%s using %s"
			    (if (tramp-string-empty-or-nil-p process-name)
				"" (concat " " process-name))
			    (if (tramp-string-empty-or-nil-p
				 (tramp-file-name-user vec))
				"" (concat (tramp-file-name-user vec) "@"))
			    (tramp-file-name-host-port vec)
			    (tramp-file-name-method vec))
		  (tramp-open-connection-setup-interactive-shell p vec))

		;; Mark it as connected.
		(tramp-set-connection-property p "connected" t))))

	;; Cleanup, and propagate the signal.
	((error quit)
	 (tramp-cleanup-connection vec t)
	 (signal (car err) (cdr err)))))))