Ssh – Detect whether the current terminal is through mosh or not

moshsshtty

I am trying to figure out a way to detect whether the current terminal/connection (and under a tmux session as well) is through mosh or not.

From this thread, I found which pseudo-terminal session I am currently on:

$ tty
/dev/pts/69

So, I need to some information of the process that spawned this pseudo-terminal, or owns this tty as a children. With the information, perhaps I might be able to determine whether it is from sshd or mosh. But how can I do that?

Another challenge: If the current shell is under tmux, the retrieved tty might not match the sshd/mosh-server information since tmux also allocates another pseudo-terminal. Regardless of how the tmux session was created, I'll need to distinguish my current connection is from SSH or mosh. How will it be possible?

Some trials:

(1) For SSH, it was possible to find the sshd process that matches the tty:

$ ps x | grep sshd | grep 'pts\/27'
 5270 ?        S      0:00 sshd: wookayin@pts/27

So I can know the current connection is through SSH. However, through mosh, I could not find any relevant information.

(2) Using environment variables like SSH_CLIENT or SSH_TTY might not work because both ssh/mosh set these variables, and it is even wrong inside a tmux session.

Best Answer

I have come up with a decent solution to this, and wrapped it as a simple script: is_mosh:

#!/bin/bash

has_ancestor_mosh() {
    pstree -ps $1 | grep mosh-server
}

is_mosh() {
    # argument handling
    for arg in "$@"; do
        case $arg in
          -v|--verbose) local VERBOSE=YES ;;
          *) ;;
        esac
    done

    if [[ -n "$TMUX" ]]; then
        # current shell is under tmux
        local tmux_current_session=$(tmux display-message -p '#S')
        local tmux_client_id=$(tmux list-clients -t "${tmux_current_session}" -F '#{client_pid}')
        # echo $tmux_current_session $tmux_client_id
        local pid="$tmux_client_id"
    else
        local pid="$$"
    fi

    local mosh_found=$(has_ancestor_mosh $pid)   # or empty if not found
    if [[ -z "$mosh_found" ]]; then
        return 1;    # exit code 1: not mosh
    fi

    test -n "$VERBOSE" && echo "mosh"
    return 0;        # exit code 0: is mosh
}


sourced=0
if [ -n "$ZSH_EVAL_CONTEXT" ]; then
  case $ZSH_EVAL_CONTEXT in *:file) sourced=1;; esac
elif [ -n "$BASH_VERSION" ]; then
  [ "$0" != "$BASH_SOURCE" ] && sourced=1
else
  case ${0##*/} in sh|dash) sourced=1;; esac
fi
if [[ $sourced == 0 ]]; then
  is_mosh $@
fi

The idea is quite simple: (i) to find the tmux client that is attached to the current tmux session, and then (ii) to search its ancestor processes to find if there is a mosh process.

It might not be complete, depending on environments, but I could successfully detect and apply 24-bit color feature only if it's not running under mosh (as mosh doesn't support 24-bit colors). Here is a solution (add these lines to your ~/.vimrc):

" 24-bit true color: neovim 0.1.5+ / vim 7.4.1799+
" enable ONLY if TERM is set valid and it is NOT under mosh
function! s:is_mosh()
  let output = system("is_mosh -v")
  if v:shell_error
    return 0
  endif
  return !empty(l:output)
endfunction

function s:auto_termguicolors()
  if !(has("termguicolors"))
    return
  endif

  if (&term == 'xterm-256color' || &term == 'nvim') && !s:is_mosh()
    set termguicolors
  else
    set notermguicolors
  endif
endfunction
call s:auto_termguicolors()
Related Question