#!/bin/sh -ue # # Run named port forwards indefinitely. # - Cameron Simpson 08jul2008 # : ${VARRUN:=$HOME/var/run} : ${LOGDIR:=$HOME/var/log} : ${PORTFWD_TARGETS:-''} trace=set-x once= stop= sshcfg=$HOME/rc/ssh/config-pf fwdcfg=$HOME/rc/ssh/portfwd cmd=`basename "$0"` usage="Usage: $cmd [-1] [-stop] targets... -1 Once. Do not restart the tunnel if it dies. -stop Stop named port forwards instead of starting them. -F Ssh configuration file with clause for target. If targets is \"ALL\" then if -stop it means all running portforwards otherwise it means all clauses named *-ALL in the ssh configuration file." opts= badopts= while [ $# -gt 0 ] do case $1 in -1) once=1 opts="$opts $1" ;; -F) sshcfg=$2 opts="$opts $1 $2"; shift ;; -stop) stop=1 opts="$opts $1" ;; --) shift; break ;; -?*)echo "$cmd: unrecognised option: $1" >&2 badopts=1 ;; *) break ;; esac shift done if [ $# = 0 ] then set -- $PORTFWD_TARGETS if [ $# -gt 0 ] then echo "$cmd: no targets, using \$PORTFWD_TARGETS: $*" >&2 else echo "$cmd: missing targets and missing/empty \$PORTFWD_TARGETS" >&2 badopts=1 fi fi [ $badopts ] && { echo "$usage" >&2; exit 2; } not() { "$@" || return 0 return 1 } ok2portfwd() { flag PORTFWD_ANYWAY && return 0 flag PORTFWD_DISABLE && return 1 ssh-add -l >/dev/null || return 1 netstat -rn | egrep '^(default|0\.0\.0\.0) +[1-9]' >/dev/null || return 1 [ -x "$HOME/bin-local/do-portfwd" ] || return 0 "$HOME/bin-local/do-portfwd" } # send SIGTERM to a process and wait for it to exit synckill() { _synckill_verbose= if [ "x$1" = x-v ] then shift _synckill_verbose=1 fi kill "$1" || return 1 while kill -0 "$1" 2>/dev/null && sleep 1 do [ -z "$_synckill_verbose" ] || printf . done [ -z "$_synckill_verbose" ] || echo } if [ "x$*" = xALL ] then if [ $stop ] then set -- `ls "$VARRUN/." | sed -n 's/^portfwd\.//p'` else set -- `sed -n 'y/ / / s/$/ / s/^ *// s/ */ /g s/^[Hh][Oo][Ss][Tt] .* \([^ ][^ ]*-ALL\) .*/\1/p' "$sshcfg"` fi fi if [ $# -gt 1 ] then ( set -x for target do "$0" $opts -- "$target" & done wait ) exit $? fi target=$1 shift xit=0 monpid= sshpid= trap ': trap on 1 2 and 15 ... if [ -n "$sshpid" ] then printf "%s" "$cmd: $target: SIGTERM received, killing ssh ($sshpid) " synckill -v "$sshpid" if [ -n "$monpid" ] then printf "%s" "$cmd: $target: SIGTERM received, also killing monitor ($monpid) " synckill -v "$monpid" fi fi exit 1 ' 1 2 15 first=1 while : do # reload config on every pass cfg_pidfile=portfwd.$target.pid cfg_command=: cfg_outlog=portfwd-$target # /dev/null cfg_monitor= if [ -s "$fwdcfg" ] then wcv=`winclausevars "$fwdcfg" "$target" cfg` eval "$wcv" fi case $cfg_pidfile in /*) ;; *) cfg_pidfile=$VARRUN/$cfg_pidfile ;; esac case $cfg_outlog in /*) ;; *) cfg_outlog=$LOGDIR/$cfg_outlog ;; esac if opid=`ifpid -q -v "$cfg_pidfile"` then printf "%s" "$cmd: stopping $target, pid $opid " synckill -v "$opid" || : >"$cfg_pidfile" else if [ "$stop" ] then echo "$cmd: no valid pid to kill in $cfg_pidfile" >&2 fi fi [ $stop ] && exit 0 $trace \ sshto ServerAliveInterval=37 -n -F "$sshcfg" "$target" \ "( $cfg_command ) &2 while sleep 61 && printf " "; do :; done ' \ >>"$cfg_outlog" "$cfg_pidfile" echo "$target: $cfg_pidfile: $sshpid" [ $first ] || alert "RESTARTED: sshpf $target" & # start monitor if [ -n "$cfg_monitor" ] then sh -c "$cfg_monitor" & monpid=$! else monpid= fi # wait for tunnel to exit or tunnel permission to expire while pfx "ssh-$target" kill -0 "$sshpid" && ok2portfwd do sleep 2 done if synckill "$sshpid" then sshpid= alert "KILLED: sshpf $target" & else sshpid= alert "EXITED: sshpf $target" & fi # stop monitor [ -z "$monpid" ] || kill "$monpid" 2>/dev/null [ $once ] && break # pause before restart, and indefinitely if no tunnel permission while : do sleep 2 ok2portfwd && break done first= done exit $xit