#!/bin/sh # mrgrctrnl: configurable ssh tunneler # Author: Case Duckworth # Version: 0.3.1 # License: MIT PRGN="${0##*/}" # entry point main() { # flags _RUN=true _LOG=true _USE_CONFIG=true _RESTART=false # options __CONFIG="${XDG_CONFIG_HOME:-$HOME/.config}/$PRGN/config" __PIDF="/tmp/$PRGN.pid" __CMDF="/tmp/$PRGN.cmd" __SSH="$(command -v autossh || command -v ssh)" __SSH_ARGS="-nT -N \"\$user@\$machine\" -L \"\$local:\$remote\"" # parse options while getopts hkrnqSAc:s: OPT; do case "$OPT" in h) usage exit 0 ;; k) untunnel pkill -x "$PRGN" exit "$?" ;; r) _RESTART=true ;; n) _RUN=false ;; q) _LOG=false ;; S) __SSH='ssh' ;; A) __SSH='autossh' ;; c) __CONFIG="$OPTARG" ;; s) __SSH_ARGS="$OPTARG" _USE_CONFIG=false ;; *) exit 4 ;; esac done shift $((OPTIND - 1)) # save command invocation (for -r) if $_RESTART; then untunnel exec "$PRGN" $(cat "$__CMDF") else printf '%s\n' "$*" >"$__CMDF" fi # make sure ssh is installed if ! command -v "$__SSH" >/dev/null; then log "Not installed: $__SSH" exit 2 fi # make sure the config is readable if $_USE_CONFIG && [ ! -r "$__CONFIG" ]; then log "Cannot find config file $__CONFIG; aborting." exit 3 fi # load the config if $_USE_CONFIG; then awk '{sub(/#.*$/,"");print}' "$__CONFIG" | while read -r machine user local remote key rest; do if [ -z "$machine" ] || [ -z "$user" ] || [ -z "$local" ] || [ -z "$remote" ]; then continue fi case "$local" in :*) local="localhost$local" ;; esac case "$remote" in :*) remote="localhost$remote" ;; esac # shellcheck disable=2030 if [ -n "$key" ]; then __SSH_ARGS="$__SSH_ARGS -i \"$key\"" fi if [ -n "$rest" ]; then __SSH_ARGS="$__SSH_ARGS $rest" fi eval "tunnel_${__SSH##*/} $__SSH_ARGS" & done else #shellcheck disable=2031 eval "tunnel_${__SSH##*/} $__SSH_ARGS" & fi wait } usage() { cat <>"$__PIDF" else badpid="$!" kill "$badpid" sed "/^$badpid/d" "$__PIDF" >"$__PIDF~" && mv "$__PIDF~" "$__PIDF" continue fi sleep 3 done } tunnel_autossh() { set -- autossh -f "$@" log "$@" $_RUN || return "$@" && printf '%s\t%s\n' "$!" "$*" >"$__PIDF" } untunnel() { log "Killing tunnels" if [ -e "$__PIDF" ]; then while read -r pid _; do kill "$pid" 2>/dev/null done <"$__PIDF" rm "$__PIDF" else pkill -x ssh pkill -x autossh fi log "Done" } log() { $_LOG && printf '%s: %s\n' "$PRGN" "$*" >&2 } main "$@"