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