#!/usr/bin/env bash # Copyright (C) 2020-2022 Sean Whitton # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or (at # your option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # Launcher for connections to remote persistent shell sessions, via mosh or a # mechanism for manual, but convenient, user-triggered reconnects. # # Advantage of the latter over autossh is avoidance of surprise dialogs asking # to authorise the use of SSH keys. And thus works better than autossh with # short ServerAliveInterval and low ServerAliveCountMax ssh client config, # which are desirable for interactive sessions. # Hosts I want to mosh to. # Be conservative: it's not just that mosh is available on that machine and # it's not a highly security-sensitive machine, but also that, e.g., the host # reboots not much more often than the workstation that'll be connecting, to # minimise zombie clients. mosh_hosts=(athena.silentflame.com) getopt=$(getopt -s bash -o "" \ -l 'container-cmd:,container-name:' -n ssh-and-tmux -- "$@") eval "set - $getopt" while true; do case "$1" in '--container-cmd') container_cmd=$2; shift 2; continue ;; '--container-name') container_name=$2; shift 2; continue ;; '--') shift; break ;; esac done host="$1" session="${2:-default}" if printf '%s\0' "${mosh_hosts[@]}" \ | grep -Fxqz "$host"; then use_mosh=true; else use_mosh=false; fi if [ -n "$container_name" ]; then home="\\\$HOME" display_host="$container_name" else home="\$HOME" display_host="$host" fi # Go via a login shell such that if there is no root tmux daemon yet, it ends # up with a PATH as though the first tmux session were launched from an # interactive login shell. This affects launching programs directly from tmux # (e.g. my C-\ e binding) for *all* sessions. # # mosh does execvp directly so for simplicity we assume remote SHELL is bash. cmd=$(printf "tmux new-session -A -c %s -s $session" $home) if $use_mosh; then cmd=(bash -l -c "$cmd") else cmd=(\$SHELL -l -c "\"$cmd\"") fi [ -n "$container_name" ] \ && cmd=($(printf "$container_cmd" "$container_name") -- "${cmd[@]}") if $use_mosh; then exec mosh "$host" -- "${cmd[@]}" else # The idea behind quitting on d, C-c or C-d was so that the user can type # C-d C-d to kill the remote shell and then kill this script, C-\ d d to # detach tmux and then kill this script, or similar. # # We now die when ssh exits zero, so this feature may be obsolete. echo -ne "\033]0;tmux $session on $display_host\007" prompt="Press any key to retry tmux $session" prompt+=" on $display_host; d/C-d/C-c to give up .. " while true; do ssh -t "$host" "${cmd[*]}" && exit clear read -n 1 -r -s -p "$prompt" ch clear [ "$ch" = $'\04' -o "$ch" = d ] && echo && exit done fi