#!/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." badopts= while [ $# -gt 0 ] do case $1 in -1) once=1 ;; -F) sshcfg=$2; shift ;; -stop) stop=1 ;; --) shift; break ;; -?*)echo "$cmd: unrecognised option: $1" >&2 badopts=1 ;; *) break ;; esac shift done if [ $# = 0 ] then if [ -n "$PORTFWD_TARGETS" ] then set -- $PORTFWD_TARGETS else echo "$cmd: missing targets and no \$PORTFWD_TARGETS envvar" >&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 for target do cfg_pidfile=portfwd.$target.pid cfg_command=: cfg_outlog=/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" fi if [ -z "$stop" ] then ( sshpid= trap 'if [ -n "$sshpid" ] then printf "%s" "$cmd: $target: SIGTERM received, killing ssh " synckill -v "$sshpid" fi exit 1 ' 1 15 first=1 while : do $trace \ sshto -n -F "$sshcfg" "$target" \ "( $cfg_command ) & "'exec 1>&2 exec 3<&0 cat <&3 >/dev/null & catpid=$$ exec 3<&- while sleep 61 && echo . && kill -0 "$catpid" 2>/dev/null do : done kill "$catpid" 2>/dev/null ' \ >>"$cfg_outlog" & sshpid=$! [ $first ] || alert "RESTARTED: sshpf $target" & if [ -n "$cfg_monitor" ] then sh -c "$cfg_monitor" & monpid=$! else monpid= fi while kill -0 "$sshpid" && ok2portfwd do sleep 2 done if synckill "$sshpid" then sshpid= alert "KILLED: sshpf $target" & else sshpid= alert "EXITED: sshpf $target" & fi [ -z "$monpid" ] || kill "$monpid" 2>/dev/null [ $once ] && break while not ok2portfwd do sleep 2 done first= done ) &2 & echo $! >"$cfg_pidfile" echo "$target: $cfg_pidfile: $!" fi done