#!/usr/bin/env bash _scriptname="semiautossh-tunnel" set -u set -e ############################################################################ # semiautossh-tunnel: Attempts to mimic autossh, for systems where that # # application is not installed. # # # # WARNING: If you change this, never, ever, rely on other files # # from the Cathedral environment. This script is # # supposed to be able to function independently. # ############################################################################ foreground=false declare -i delay=5 function printusage { cat - >&2 <] "--" will, in most cases, be needed to separate the arguments passed to ssh from the ones used internally by this script. Use it! Options: -p Specify which remote port to monitor. For this to make sense, the *should* include an instruction to forward that remote port to somewhere. -f Run in foreground. Prevents semiautossh-tunnel from dropping to the background. Useful for debugging purposes. -d How long to wait before checking that the background process still runs. By default, if "-f" is not specified, this script will start a background command, sleep $delay seconds, then verify it's running. This option changes that delay. Set to 0 to disable the check. Example: semiautossh-tunnel -p 2222 -d 10 -- foo@bar.com -p 22 -R 2222:localhost:22 Connects as foo, to port 22 on bar.com, forwarding remote port 2222 to local port 22, and also monitoring that 2222 is open on the remote host. Will also wait 10 seconds (-d 10) before assuming the background process started successfully. EOF } OPTIND=1 while getopts ":fp:d:" opt; do case "$opt" in f) foreground=true ;; p) port=$OPTARG ;; d) delay=$OPTARG ;; [?]) echo "Error: Unknown option -${OPTARG}" >&2 printusage exit 1 ;; :) echo "Error: Option -${OPTARG} requires an argument" >&2 printusage exit 1 ;; esac done shift $((OPTIND-1)) if [[ "${1:-}" = "--" ]]; then shift; fi if [[ -n "${port:-}" ]] && ( [[ "$port" == *[!0-9]* ]] || (( "${port:-0}" < 0 )) || (( "${port:-0}" > 65535 )) ); then echo "The remote monitor port must be an integer, (N >= 0) && (N <= 65535)" >&2 exit 1 fi if (( $# == 0 )); then printusage exit 0 fi if ! $foreground; then nohup ${_scriptname} -f ${port:+-p $port} "--" "$@" &>/dev/null & pid=$! if (( delay > 0 )); then while true; do sleep 1 if ! kill -0 $pid &>/dev/null; then echo "failed to start background process!" >&2 echo "Check your arguments, perhaps with \"-f\"." >&2 exit 1 fi (( --delay )) || break done fi echo "Background process started. PID: $pid" disown exit 0 fi function killssh { if (( sshpid != 0 )) && kill -0 "$sshpid" 2>/dev/null; then echo "Killing SSH ($sshpid)" kill -SIGTERM "$sshpid" 2>/dev/null || : fi sshpid=0 } function cleanup { local code=$? trap : HUP INT TERM EXIT set +e killssh exit $code } echo "Script PID : $$" while true; do sshpid=0 trap cleanup HUP INT TERM EXIT while read -rt 60 line; do if (( sshpid == 0 )); then sshpid=${line} echo "SSH PID : $sshpid" continue fi if [[ "$line" != *[!0-9]* ]]; then echo "Connection open (${line})" else echo "${line}" fi done < <( ssh -o "KeepAlive yes" -q -g "$@" "\ echo 'Authenticated'; \ function fail \ { \ echo 'Forwarded port CLOSED!'; \ echo 'Bailing!'; \ exit 1; \ }; \ while true; do \ date +%s; \ ${port:+"netstat -ntl | awk '{print \$4}' | grep -q \":${port}\$\" && echo 'Forwarded port OK' || fail; "} \ sleep 10; \ done\ " & echo $! echo "Opening connection" ) echo "Disconnected" killssh sleep 10 || exit 0 done