Added ability to send mail when something goes wrong
[freeradius.git] / scripts / radwatch.in
1 #!/bin/sh
2 ######################################################################
3 #
4 #    This program is free software; you can redistribute it and/or modify
5 #    it under the terms of the GNU General Public License as published by
6 #    the Free Software Foundation; either version 2 of the License, or
7 #    (at your option) any later version.
8 #
9 #    This program is distributed in the hope that it will be useful,
10 #    but WITHOUT ANY WARRANTY; without even the implied warranty of
11 #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 #    GNU General Public License for more details.
13 #
14 #    You should have received a copy of the GNU General Public License
15 #    along with this program; if not, write to the Free Software
16 #    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
17 #
18 #    Copyright (C) 2009 Network RADIUS SARL <info@networkradius.com>
19 #
20 ######################################################################
21 #
22 # radwatch - Start the radius daemon and restart upon crash.
23 #
24 #  It also catches signals sent to it, and then re-sends those signals
25 #  to the radius server it is watching.
26 #
27 #  If you want to watch and re-start the server, we recommend
28 #  reading the file doc/supervise-radiusd.txt
29
30 #
31 #  This simplifies the script, and avoids most issues with (say)
32 #  Debian re-naming "radiusd" to "freeradius".
33 #
34 name=radiusd
35
36 prefix=@prefix@
37 exec_prefix=@exec_prefix@
38 sbindir=@sbindir@
39 localstatedir=@localstatedir@
40 logdir=@logdir@
41 rundir=${localstatedir}/run/${name}
42 sysconfdir=@sysconfdir@
43 pid_file=${rundir}/${name}.pid
44 log_file=${logdir}/${name}_safe.log
45
46 RADIUSD=$sbindir/${name}
47 RADDBDIR=${sysconfdir}/raddb
48
49 #
50 #  If you want to send email, define this field to be an email address.
51 #  This part of the functionality hasn't been well tested, so please
52 #  test it before putting it into production.
53 #
54 #  It also presumes that you have a functioning mail system on
55 #  the maching running RADIUS.  You will need to check that the
56 #  "mail" command exists, and sends mail to the address below, e.g.:
57 #
58 #     echo test | mail -s "Testing" $MAILTO
59 #
60 #  If you receive the message, then enable MAILTO.  Otherwise, fix
61 #  your mail system so that it delivers mail.
62 #
63 MAILTO=
64
65 #
66 #  Allow "radiusd_safe -X" for testing the radiusd_safe functionality.
67 #
68 ARGS="$@"
69
70 test -f $RADIUSD || exit 0
71 test -f $RADDBDIR/radiusd.conf || exit 0
72
73 ulimit -c unlimited
74
75 #
76 #  See if the PID file exists.  It might have been left over after
77 #  a crash, or it might be because the RADIUS server is still running.
78 #
79 if test -f $pid_file
80 then
81     PID=`cat $pid_file`
82     #
83     #  Check if the process exists, AND if it has the right name
84     #
85     if ps -p $PID | grep $name > /dev/null
86     then
87         echo "`date +'%a %b %e %H:%M:%S %Y'` : Fatal: A $name process already exists at PID $PID.  We cannot start another one." >> $log_file
88         echo "A $name process already exists"
89         exit 1
90     fi
91     
92     #
93     #  A RADIUS server doesn't exist.  Delete the stale PID file.
94     #
95     rm -f $pid_file
96     if test -f $pid_file
97     then
98         echo "`date +'%a %b %e %H:%M:%S %Y'` : Fatal: Cannot remove the pid file: $pid_file" >> $log_file
99         echo "Fatal error: Cannot remove the pid file: $pid_file"
100         echo "Please remove it manually and start $0 again"
101         echo "$name daemon not started"
102         exit 1
103     fi
104 fi
105
106 started=0
107 restarts=0
108 last_email=0
109
110 #
111 #  Loop forever, or until we're told to exit via a signal.
112 #
113 while :
114 do
115     mysig=
116     trap 'mysig=yes' HUP TERM INT QUIT TSTP
117
118     #
119     #  The first time around, just start the server.
120     #  After that, see if we are re-starting in the same second
121     #  as the last time.  If so, sleep for a second.  Otherwise,
122     #  if we're not starting in the same second, then just restart
123     #  the server.
124     #
125     #  This helps prevent CPU spikes when something goes catastrophically
126     #  wrong, and the server re-starts continuously.  (e.g. disk full, etc.)
127     #
128     if test "$started" != "0"
129     then
130         #  Send mail when the server starts
131         if test "$MAILTO" != ""
132         then
133             now=`date +"%s"`
134             restarts=`expr $restarts + 1`
135
136             # send email the first time it restarts
137             if test "$last_email" = "0"
138             then
139                     cat |  mail -s "ERROR - $name died, restarting.." $MAILTO <<EOF
140 $name has restarted unexpectedly at $now
141
142 See $log_file for details.  Last 20 lines are:
143
144 ----------------------------------------------------------------------
145 `tail -n 20 $log_file`
146 EOF
147                 $last_email="$now"
148                 restarts=0
149             else
150                 #  Send email only once every hour.
151                 if test "$now" -gt `expr $last_email + 3600`
152                 then
153                     cat |  mail -s "ERROR - $name died, restarting.." $MAILTO <<EOF
154 $name has restarted $restarts times since last email at $last_email
155
156 See $log_file for details.  Last 100 lines are:
157
158 ----------------------------------------------------------------------
159 `tail -n 100 $log_file`
160 EOF
161                     $last_email="$now"
162                     restarts=0
163                 fi
164             fi
165         fi
166
167         if test "$started" = `date +"%s"`
168         then
169             sleep 1
170         fi
171     fi
172     started=`date +"%s"`
173
174     eval "$RADIUSD -f $ARGS < /dev/null >> $log_file 2>&1 &"
175     PID=$!
176     
177     if test "$?" != "0"
178     then
179         echo "Failed to start $name.  See $log_file for details"
180         echo "$name daemon not started"
181         exit 1
182     fi
183
184     echo $PID > $pid_file
185
186     #
187     #  Wait for the process to exit.
188     #
189     wait $PID
190     code=$?
191     
192     case "$code" in
193         0)
194             echo "`date +'%a %b %e %H:%M:%S %Y'` : Info: $name exited normally.  Exiting" | tee -a $log_file
195             break
196             ;;
197
198         127)
199             echo "`date +'%a %b %e %H:%M:%S %Y'` : Info: $name exited unexpectedly.  Restarting it." | tee -a $log_file
200             ;;
201
202         *)
203             #
204             #  The server exited of its own accord.
205             #
206             if test "$code" -lt 128
207             then
208                 echo "`date +'%a %b %e %H:%M:%S %Y'` : Info: $name exited unexpectedly on exit code $code.  Restarting it." | tee -a $log_file
209             else
210                 sig=`expr $code - 128`
211
212                 #
213                 #  Was the signal sent to us, or to the child process?
214                 #
215                 if test "$mysig" = "yes"
216                 then
217                     echo "`date +'%a %b %e %H:%M:%S %Y'` : Info: Caught signal $sig: Signalling $name to exit." | tee -a $log_file
218                     kill -`expr $code - 128` $PID
219                     break
220                 else
221                     echo "`date +'%a %b %e %H:%M:%S %Y'` : Info: $name exited unexpectedly on signal $sig.  Restarting it." | tee -a $log_file
222                 fi
223             fi
224             ;;
225     esac
226 done
227
228 rm -f $pid_file
229 exit 0