about summary refs log tree commit diff stats
path: root/mrgrctrnl
blob: 796470c2721ef85ad7eb7a75742dc86a302fb531 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
#!/bin/sh
# mrgrctrnl: configurable ssh tunneler
# Author: Case Duckworth <acdw@acdw.net>
# Version: 0.2
# License: MIT

PRGN="${0##*/}"

usage() {
	cat <<END
$PRGN: make magic ssh tunnels
usage:	$PRGN [-h] [-k]
	$PRGN [-n] [-c CONF | -s SSH]

options:
	-h	show this help
	-k	kill all processes and exit
	-r	restart $PRGN
	-n	do a dry run: just print what would happen
	-c CONFIG	use a different CONFIG file instead of
			\$XDG_CONFIG_HOME/$PRGN/config
	-s CMDLINE	directly input an ssh CMDLINE --
			don't load the config file.
END
}

# entry point
main() {
	__CONFIG="${XDG_CONFIG_HOME:-$HOME/.config}/$PRGN/config"
	__SSHCMD="-N \"\$user@\$machine\" -L \"\$local:\$remote\""
	__PIDF="/tmp/$PRGN.pid"
	_run=true
	_quiet=false
	_use_config=true

	while getopts hqkrnc:s: OPT; do
		case "$OPT" in
		h)
			usage
			exit 0
			;;
		q) _quiet=true ;;
		k)
			tear_down
			pkill -x "$PRGN"
			exit
			;;
		r)
			tear_down
			exec "$PRGN"
			;;
		n) _run=false ;;
		c) __CONFIG="$OPTARG" ;;
		s)
			__SSHCMD="$OPTARG"
			_use_config=false
			;;
		*)
			usage
			exit 1
			;;
		esac
	done

	if $_use_config && [ ! -r "$__CONFIG" ]; then
		echo "!! $PRGN: Cannot find config file '$__CONFIG'."
		echo "Aborting."
		exit 2
	fi

	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

				# shellcheck disable=2030
				if [ -n "$key" ]; then
					__SSHCMD="$__SSHCMD -i \"$key\""
				fi
				if [ -n "$rest" ]; then
					__SSHCMD="$__SSHCMD $rest"
				fi

				eval "tunnel $__SSHCMD" &
			done
	else
		# shellcheck disable=2031
		eval "tunnel $__SSHCMD" &
	fi

	wait
}

tunnel() {
	# shellcheck disable=2086
	$_quiet || echo ssh "$@"
	$_run && {
		touch "$__PIDF"
		while [ -e "$__PIDF" ]; do
			grep -q -e "$*" "$__PIDF" && {
				sleep 3
				continue
			}
			# shellcheck disable=2015
			if ssh "$@" & then
				printf '%s\t%s\n' "$!" "$*" >>"$__PIDF"
			else
				badpid="$!"
				kill "$badpid"
				sed "/^$badpid/d" "$__PIDF" >"$__PIDF~" &&
					mv "$__PIDF~" "$__PIDF"
				continue
			fi
			sleep 3
		done
	}
}

tear_down() {
	printf '%s...' "Killing extant tunnels"
	if [ -e "$__PIDF" ]; then
		while read -r pid _; do
			kill "$pid" 2>/dev/null
		done <"$__PIDF"
		rm "$__PIDF"
	else
		pkill -x ssh
	fi
	echo Done.
}

main "$@"