Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow using the default login shell for remote connections #706

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -480,6 +480,34 @@ fake-newlines are removed on entering copy-mode and re-inserted
on leaving copy mode. Also truncate-lines is set to t on entering
copy-mode and set to nil on leaving.

## `vterm-tramp-shells`

The shell that gets run in the vterm for tramp.

This has to be a list of pairs of the format:
`(TRAMP-METHOD SHELL)`

The `TRAMP-METHOD` is a method string as used by tramp (e.g., `"ssh"`).
Use t as `TRAMP-METHOD` to specify a default shell for all methods.
Specific methods always take precedence over `t`.

Set SHELL to `'login-shell` to use the user's login shell on the remote host.
The login-shell detection currently works for POSIX-compliant remote hosts that
have the `getent` command (regular GNU/Linux distros, *BSDs, but not MacOS X
unfortunately).
You can specify an additional second `SHELL` command as a fallback
that is used when the login-shell detection fails, e.g.,
`'(("ssh" login-shell "/bin/bash") ...)`
If no second `SHELL` command is specified with `'login-shell`, vterm will
fall back to tramp's shell.

Examples:
- Usee the default login shell for all methods, except for docker.
`'((t login-shell) ("docker" "/bin/sh"))`
- Use the default login shell for ssh and scp, fall back to "/bin/bash".
Use tramp's default shell for all other methods.
`'(("ssh" login-shell "/bin/bash") ("scp" login-shell "/bin/bash"))`

## Keybindings

If you want a key to be sent to the terminal, bind it to `vterm--self-insert`,
Expand Down
69 changes: 66 additions & 3 deletions vterm.el
Original file line number Diff line number Diff line change
Expand Up @@ -172,11 +172,26 @@ the executable."
:type 'string
:group 'vterm)

(defcustom vterm-tramp-shells '(("docker" "/bin/sh"))
(defcustom vterm-tramp-shells
'(("ssh" login-shell) ("scp" login-shell) ("docker" "/bin/sh"))
"The shell that gets run in the vterm for tramp.

`vterm-tramp-shells' has to be a list of pairs of the format:
\(TRAMP-METHOD SHELL)"
\(TRAMP-METHOD SHELL)

Use t as TRAMP-METHOD to specify a default shell for all methods.
Specific methods always take precedence over t.

Set SHELL to \\='login-shell to use the user's login shell on the host.
The login-shell detection currently works for POSIX-compliant remote
hosts that have the getent command (regular GNU/Linux distros, *BSDs,
but not MacOS X unfortunately).

You can specify an additional second SHELL command as a fallback
that is used when the login-shell detection fails, e.g.,
\\='((\"ssh\" login-shell \"/bin/bash\") ...)
If no second SHELL command is specified with \\='login-shell, vterm will
fall back to tramp's shell."
:type '(alist :key-type string :value-type string)
:group 'vterm)

Expand Down Expand Up @@ -818,11 +833,59 @@ Exceptions are defined by `vterm-keymap-exceptions'."
(setq next-error-function 'vterm-next-error-function)
(setq-local bookmark-make-record-function 'vterm--bookmark-make-record))

(defun vterm--tramp-get-shell (method)
"Get the shell for a remote location as specified in `vterm-tramp-shells'.
The argument METHOD is the method string (as used by tramp) to get the shell
for, or t to get the default shell for all methods."
(let* ((specs (cdr (assoc method vterm-tramp-shells)))
(first (car specs))
(second (cadr specs)))
;; Allow '(... login-shell) or '(... 'login-shell).
(if (or (eq first 'login-shell)
(and (consp first) (eq (cadr first) 'login-shell)))
;; If the first element is 'login-shell, try to determine the user's
;; login shell on the remote host. This should work for all
;; POSIX-compliant systems with the getent command in PATH. This
;; includes regular GNU/Linux distros, *BSDs, but not MacOS X. If
;; the login-shell determination fails at any point, the second
;; element in the shell spec is used (if present, otherwise nil is
;; returned).
(let* ((entry (ignore-errors
(with-output-to-string
(with-current-buffer standard-output
;; The getent command returns the passwd entry
;; for the specified user independently of the
;; used name service (i.e., not only for static
;; passwd files, but also for LDAP, etc).
;;
;; Use a shell command here to get $LOGNAME.
;; Using the tramp user does not always work as
;; it can be nil, e.g., with ssh host configs.
;; $LOGNAME is defined in all POSIX-compliant
;; systems.
(unless (= 0 (process-file-shell-command
"getent passwd $LOGNAME"
nil (current-buffer) nil))
(error "Unexpected return value"))
;; If we have more than one line, the output is
;; not the expected single passwd entry.
;; Most likely, $LOGNAME is not set.
(when (> (count-lines (point-min) (point-max)) 1)
(error "Unexpected output"))))))
(shell (when entry
;; The returned Unix passwd entry is a colon-
;; separated line. The 6th (last) element specifies
;; the user's shell.
(nth 6 (split-string entry ":" nil "[ \t\n\r]+")))))
(or shell second))
first)))

(defun vterm--get-shell ()
"Get the shell that gets run in the vterm."
(if (ignore-errors (file-remote-p default-directory))
(with-parsed-tramp-file-name default-directory nil
(or (cadr (assoc method vterm-tramp-shells))
(or (vterm--tramp-get-shell method)
(vterm--tramp-get-shell t)
(with-connection-local-variables shell-file-name)
vterm-shell))
vterm-shell))
Expand Down