Commit | Line | Data |
---|---|---|
b16ea8ba MW |
1 | #! /bin/sh |
2 | ||
3 | set -e | |
4 | case $# in | |
5 | 2) ;; | |
6 | *) echo >&2 "usage: $0 {start|stop|restart|status} HOST"; exit 1 ;; | |
7 | esac | |
8 | op=$1 host=$2 | |
9 | ||
10 | writefile () { | |
11 | file=$1; shift | |
12 | echo "$*" >"$file.new" | |
13 | mv "$file.new" "$file" | |
14 | } | |
15 | ||
16 | runssh () { ssh -T -oControlPath="./$host.ctrl" "$@"; } | |
17 | ||
18 | stopit () { | |
19 | ||
20 | ## Initial shutdown protocol. | |
21 | writefile "$host.state" stopping | |
22 | if [ -f "$host.pid" ]; then kill $(cat "$host.pid") 2>/dev/null || :; fi | |
23 | rm -f "$host.pid" | |
24 | ||
25 | ## Shut down an existing connection if there is one. | |
26 | if [ -S "$host.ctrl" ]; then | |
27 | runssh -Oexit "$host" >/dev/null 2>/dev/null || : | |
28 | fi | |
29 | ||
30 | ## If there's still a socket, then work out what to do. | |
31 | if [ -e "$host.ctrl" ]; then | |
32 | ||
33 | ## If the connection's still running then we have a problem. | |
34 | if runssh -Ocheck "$host" >/dev/null 2>/dev/null; then | |
35 | echo >&2 "$0: failed to kill existing connection to $host" | |
36 | exit 2 | |
37 | fi | |
38 | ||
39 | ## Remove the stale socket. | |
40 | rm -f "$host.ctrl" | |
41 | fi | |
42 | ||
43 | ## Update the state. | |
44 | rm -f "$host.state" "$host.pid" | |
45 | } | |
46 | ||
47 | daemon () { | |
48 | ||
49 | ## There doesn't seem to be a better way of getting this. :-( | |
50 | read pid <"$host.daemonpipe" | |
51 | rm -f "$host.daemonpipe" | |
52 | ||
53 | ## Set up shop. | |
54 | trap 'rm -f "$host.pid"; stopit' EXIT INT TERM | |
55 | writefile "$host.pid" "$pid" | |
56 | ||
57 | ## Initial delay. | |
58 | delay=0 | |
59 | ||
60 | ## Keep the connection up for as long as we can. | |
61 | while [ -f "$host.pid" ]; do | |
62 | ||
63 | ## Maybe back off before trying another connection. | |
64 | case $delay in | |
65 | 0) | |
66 | delay=1 | |
67 | ;; | |
68 | *) | |
69 | writefile "$host.state" \ | |
70 | "wait until $(date -d+${delay}sec +"%Y-%m-%d %H:%M:%S %z")" | |
71 | sleep $delay | |
72 | delay=$(( 2*$delay )) | |
73 | if [ $delay -gt 120 ]; then delay=120; fi | |
74 | ;; | |
75 | esac | |
76 | ||
77 | ## Start a new connection. | |
78 | writefile "$host.state" starting | |
79 | if ! runssh -MNnf "$host" >/dev/null; then continue; fi | |
80 | if ! runssh -Ocheck "$host" >/dev/null 2>&1; then | |
81 | echo "connection to $host apparently stillborn" | |
82 | continue | |
83 | fi | |
84 | writefile "$host.state" connected | |
85 | delay=0 | |
86 | ||
87 | ## Wait until it gets torn down. The chicanery with a pipe is because | |
88 | ## the ssh process will continue until either it gets disconnected from | |
89 | ## the server or stdin closes -- so we have to arrange that stdin doesn't | |
90 | ## close. Thanks to Richard Kettlewell for the suggestion. | |
91 | rm -f "$host.pipe"; mkfifo -m400 "$host.pipe" | |
92 | runssh -N "$host" >/dev/null <"$host.pipe" || : | |
93 | rm -f "$host.pipe" | |
94 | writefile "$host.state" disconnected | |
95 | done | |
96 | } | |
97 | ||
98 | startit () { | |
99 | ||
100 | ## If there's already a connection then we have nothing to do. | |
101 | if runssh -Ocheck "$host" >/dev/null 2>/dev/null; then | |
102 | echo >&2 "$0: already connected to $host" | |
103 | exit 0 | |
104 | fi | |
105 | ||
106 | ## Start a daemon which makes connections for us. This is remarkably | |
107 | ## tricky. | |
108 | rm -f "$host.daemonpipe"; mkfifo -m600 "$host.daemonpipe" | |
109 | { daemon& echo $! >"$host.daemonpipe"; } 2>&1 | logger -pdaemon.notice& | |
110 | } | |
111 | ||
112 | ## Main dispatch. | |
113 | case "$op" in | |
114 | start) startit ;; | |
115 | stop) stopit ;; | |
116 | restart) stopit; startit ;; | |
117 | status) | |
118 | if [ -f "$host.state" ] | |
119 | then cat "$host.state" | |
120 | else echo "down" | |
121 | fi | |
122 | ;; | |
123 | *) | |
124 | echo >&2 "usage: $0 {start|stop|restart|status} HOST" | |
125 | exit 1 | |
126 | ;; | |
127 | esac |