diff options
-rwxr-xr-x | mrgrctrnl | 208 |
1 files changed, 120 insertions, 88 deletions
diff --git a/mrgrctrnl b/mrgrctrnl index af29269..796470c 100755 --- a/mrgrctrnl +++ b/mrgrctrnl | |||
@@ -1,103 +1,135 @@ | |||
1 | #!/bin/sh | 1 | #!/bin/sh |
2 | # mrgrctrnl | 2 | # mrgrctrnl: configurable ssh tunneler |
3 | # configurable ssh tunneler | 3 | # Author: Case Duckworth <acdw@acdw.net> |
4 | # by Case Duckworth <acdw@acdw.net> | 4 | # Version: 0.2 |
5 | # version 0.1 | 5 | # License: MIT |
6 | # LICENSE: MIT | ||
7 | 6 | ||
8 | usage() { | 7 | PRGN="${0##*/}" |
9 | cat <<-END | ||
10 | mrgrctrnl: make magic ssh tunnels | ||
11 | usage: mrgrctrnl [-h] [-c CONF] [-d] [-k] | ||
12 | 8 | ||
13 | -h show this help | 9 | usage() { |
14 | -c CONF use config file CONF. | 10 | cat <<END |
15 | . default: \$XDG_CONFIG_HOME/mrgrctrnl/config | 11 | $PRGN: make magic ssh tunnels |
16 | -d do a dry run: don't actually tunnel | 12 | usage: $PRGN [-h] [-k] |
17 | -k kill all processes and exit | 13 | $PRGN [-n] [-c CONF | -s SSH] |
18 | 14 | ||
19 | END | 15 | options: |
20 | exit "${1:-0}" | 16 | -h show this help |
17 | -k kill all processes and exit | ||
18 | -r restart $PRGN | ||
19 | -n do a dry run: just print what would happen | ||
20 | -c CONFIG use a different CONFIG file instead of | ||
21 | \$XDG_CONFIG_HOME/$PRGN/config | ||
22 | -s CMDLINE directly input an ssh CMDLINE -- | ||
23 | don't load the config file. | ||
24 | END | ||
21 | } | 25 | } |
22 | 26 | ||
23 | die() { | 27 | # entry point |
24 | [ "$#" -eq 0 ] && exit 1 | 28 | main() { |
25 | case "$1" in | 29 | __CONFIG="${XDG_CONFIG_HOME:-$HOME/.config}/$PRGN/config" |
26 | 0-9*) | 30 | __SSHCMD="-N \"\$user@\$machine\" -L \"\$local:\$remote\"" |
27 | ec="$1" | 31 | __PIDF="/tmp/$PRGN.pid" |
28 | shift | 32 | _run=true |
29 | ;; | 33 | _quiet=false |
30 | *) ec=1 ;; | 34 | _use_config=true |
31 | esac | ||
32 | printf '!!%s: %s\n' "mrgrctrnl" "$*" | ||
33 | exit "$ec" | ||
34 | } | ||
35 | 35 | ||
36 | demolish_tunnels() { | 36 | while getopts hqkrnc:s: OPT; do |
37 | printf '%s...' "Demolishing tunnels" | 37 | case "$OPT" in |
38 | if [ -f "$pidf" ]; then | 38 | h) |
39 | while read -r pid; do | 39 | usage |
40 | [ -z "$pid" ] && continue | 40 | exit 0 |
41 | kill "$pid" 2>/dev/null | 41 | ;; |
42 | done <"$pidf" | 42 | q) _quiet=true ;; |
43 | rm "$pidf" | 43 | k) |
44 | else | 44 | tear_down |
45 | pkill -x ssh | 45 | pkill -x "$PRGN" |
46 | fi | 46 | exit |
47 | printf '%s.\n' "Done" | 47 | ;; |
48 | } | 48 | r) |
49 | tear_down | ||
50 | exec "$PRGN" | ||
51 | ;; | ||
52 | n) _run=false ;; | ||
53 | c) __CONFIG="$OPTARG" ;; | ||
54 | s) | ||
55 | __SSHCMD="$OPTARG" | ||
56 | _use_config=false | ||
57 | ;; | ||
58 | *) | ||
59 | usage | ||
60 | exit 1 | ||
61 | ;; | ||
62 | esac | ||
63 | done | ||
49 | 64 | ||
50 | config="${XDG_CONFIG_HOME:=$HOME/.config}/mrgrctrnl/config" | 65 | if $_use_config && [ ! -r "$__CONFIG" ]; then |
51 | pidf=/tmp/mrgrctrnl.pid | 66 | echo "!! $PRGN: Cannot find config file '$__CONFIG'." |
52 | dry_run=false | 67 | echo "Aborting." |
68 | exit 2 | ||
69 | fi | ||
53 | 70 | ||
54 | while getopts hc:dkr opt; do | 71 | if $_use_config; then |
55 | case "$opt" in | 72 | awk '{sub(/#.*$/,"");print}' "$__CONFIG" | |
56 | h) usage ;; | 73 | while read -r machine user local remote key rest; do |
57 | c) config="$OPTARG" ;; | 74 | if [ -z "$machine" ] || [ -z "$user" ] || |
58 | d) dry_run=true ;; | 75 | [ -z "$local" ] || [ -z "$remote" ]; then |
59 | k) | 76 | continue |
60 | demolish_tunnels | 77 | fi |
61 | pkill -x mrgrctrnl | ||
62 | exit | ||
63 | ;; | ||
64 | r) | ||
65 | demolish_tunnels | ||
66 | exec mrgrctrnl | ||
67 | exit | ||
68 | ;; | ||
69 | \?) usage 2 ;; | ||
70 | *) usage 2 ;; | ||
71 | esac | ||
72 | done | ||
73 | shift "$((OPTIND - 1))" | ||
74 | 78 | ||
75 | [ -f "$config" ] || die "Need a config! Edit $config" | 79 | # shellcheck disable=2030 |
76 | if [ -e "$pidf" ] && ! "$dry_run"; then | 80 | if [ -n "$key" ]; then |
77 | demolish_tunnels | 81 | __SSHCMD="$__SSHCMD -i \"$key\"" |
78 | fi | 82 | fi |
83 | if [ -n "$rest" ]; then | ||
84 | __SSHCMD="$__SSHCMD $rest" | ||
85 | fi | ||
79 | 86 | ||
80 | awk '{sub(/#.*$/,"");print}' "$config" | | 87 | eval "tunnel $__SSHCMD" & |
81 | while read -r machine user local remote key rest; do | 88 | done |
82 | if [ -z "$machine" ] || [ -z "$user" ] || | 89 | else |
83 | [ -z "$local" ] || [ -z "$remote" ]; then | 90 | # shellcheck disable=2031 |
84 | continue | 91 | eval "tunnel $__SSHCMD" & |
85 | fi | 92 | fi |
86 | 93 | ||
87 | set -- -N "$user@$machine" -L "$local:$remote" | 94 | wait |
88 | [ -n "$key" ] && set -- "$@" -i "$key" | 95 | } |
89 | #shellcheck disable=2086 | ||
90 | [ -n "$rest" ] && set -- "$@" $rest | ||
91 | 96 | ||
92 | echo ssh "$@" | 97 | tunnel() { |
98 | # shellcheck disable=2086 | ||
99 | $_quiet || echo ssh "$@" | ||
100 | $_run && { | ||
101 | touch "$__PIDF" | ||
102 | while [ -e "$__PIDF" ]; do | ||
103 | grep -q -e "$*" "$__PIDF" && { | ||
104 | sleep 3 | ||
105 | continue | ||
106 | } | ||
107 | # shellcheck disable=2015 | ||
108 | if ssh "$@" & then | ||
109 | printf '%s\t%s\n' "$!" "$*" >>"$__PIDF" | ||
110 | else | ||
111 | badpid="$!" | ||
112 | kill "$badpid" | ||
113 | sed "/^$badpid/d" "$__PIDF" >"$__PIDF~" && | ||
114 | mv "$__PIDF~" "$__PIDF" | ||
115 | continue | ||
116 | fi | ||
117 | sleep 3 | ||
118 | done | ||
119 | } | ||
120 | } | ||
93 | 121 | ||
94 | touch "$pidf" | 122 | tear_down() { |
95 | "$dry_run" || { | 123 | printf '%s...' "Killing extant tunnels" |
96 | while [ -f "$pidf" ]; do | 124 | if [ -e "$__PIDF" ]; then |
97 | ssh "$@" || break | 125 | while read -r pid _; do |
98 | echo "$!" >>"$pidf" | 126 | kill "$pid" 2>/dev/null |
99 | done & | 127 | done <"$__PIDF" |
100 | } | 128 | rm "$__PIDF" |
101 | done | 129 | else |
130 | pkill -x ssh | ||
131 | fi | ||
132 | echo Done. | ||
133 | } | ||
102 | 134 | ||
103 | wait | 135 | main "$@" |