#!/bin/bash # This script runs and keeps a watchful eye on a running Squid instance. # If squid should die unexpectedly, it can run the commands of your choice. # If squid comes back, another set of commands can be run. Additionally, you # can use a separate invocation of this script to signal the daemonized watcher # to start and stop immediately (useful to add to your squid init scripts so # you don't take unnecessary action when you _want_ to shutdown squid). # # We use this script to enable/disable iptables rules on the host that # redirect port 80 traffic to our squid box. Should squid stop working, we # want the redirection to stop (and traffic to pass normally). Once squid # returns, the watcher should notice and re-enable the forwarding rules. # You'll see our rules in the "start_hook" and "stop_hook" functions. Change # them to whatever you see fit. # # You can run the program from INIT by adding this to /etc/inittab: # sq:2345:respawn:/path/to/squid-watcher poll < /dev/null > /dev/null 2>&1 # # Author: Jason Healy # # $Id: squid-watcher 1131 2008-10-13 17:15:46Z jhealy $ # Where is squid installed? SQUID=/usr/sbin/squid SQUIDCLIENT=/usr/bin/squidclient # Log via syslog (set to facility and priority or "" to disable) SYSLOG="daemon.warning" # Log via e-mail (set to e-mail address you'd like to use, or "" to disable) EMAIL="" # Amount of time between polling runs (seconds) DELAY=20 # URL to fetch to determine if cache is working (pick a reliable one!) # Using empty "" string means that we'll just check for squid process TESTURL="http://www.example.com/" # hook to execute when squid has started running and is ready to process start_hook() { # divert packets coming over the bridge destined for port 80 to our # squid instance running on port 81 /sbin/iptables -t tproxy -A PREROUTING -p tcp -m tcp -s 172.16.0.0/12 \ -d '!' 172.16.0.0/12 --dport 80 -j TPROXY --on-port 81 2>&1 } # hook to execute when squid has stopped or is not running any more stop_hook() { # remove the transparent redirection rule /sbin/iptables -t tproxy -D PREROUTING -p tcp -m tcp -s 172.16.0.0/12 \ -d '!' 172.16.0.0/12 --dport 80 -j TPROXY --on-port 81 2>&1 } ########################################################################### ### No need to customize below here (unless you know what you're doing) ### ########################################################################### NAME=squid-watcher PIDFILE="/var/run/$NAME.pid" LCKFILE="/var/run/$NAME.lck" # Signal names to send for various actions. Shouldn't need to change, # unless you really want to reassign the signal handlers SIGSTART="USR1" SIGSTOP="USR2" SIGINFO="HUP" # Set program status. We use this to figure out what state the script # is in. Possible values: # STOPPED - Squid has been properly shut down and is supposed to be off # RUNNING - Squid is running and responding to requests properly # BROKEN - Squid was shut down improperly, or is not servicing requests # # Initially, we don't know what state squid is in, so we assume it's # running and try to recover. STATUS="BROKEN" # Function: Log any messages log() { if [ -n "$*" ]; then if [ -n "$SYSLOG" ]; then logger -i -t $NAME -p "$SYSLOG" -- "$*" fi if [ -n "$EMAIL" ]; then echo -e "\n$*\n\n`date`" | mail -s "$NAME on `hostname`" "$EMAIL" fi echo -e $* fi } # Sanity check on binary paths if [ ! -x $SQUID ]; then log "Can't find executable squid at: $SQUID" exit 1 fi if [ ! -x $SQUIDCLIENT ]; then log "Can't find executable squidclient at: $SQUIDCLIENT" exit 1 fi # Function: test status of squid, and return one of the following: # OK - Squid is running an processing requests normally # NO - Squid process is not running / can't be found # EE - Squid is running, but not servicing requests squidstatus() { if $SQUID -k check 2>/dev/null; then if [ -n "$TESTURL" ]; then if $SQUIDCLIENT -s "$TESTURL" 2>/dev/null; then echo "OK" else echo "EE" fi else echo "OK" fi else echo "NO" fi } # Function: get a lock to protect critical sections lock() { # Loop until we get a lock: until (set -o noclobber; echo $$ >"$LCKFILE") 2>/dev/null; do set x `ls -l $LCKFILE` log "$1 waiting for lockfile (other working since $7 $8 $9)..." sleep 5 done } # Function: release a critical section lock unlock() { rm -f "$LCKFILE" } # Function: Set to running state and call any activation hooks activate() { lock "activate" if [ $STATUS != "RUNNING" ]; then # before activating, do one final test to make sure case `squidstatus` in OK) # Fire it up log "Squid is OK; calling start hook. " `start_hook` STATUS="RUNNING" ;; NO) log "Squid process is not running; not activating" STATUS="BROKEN" ;; EE) log "Squid process not responding; not activating" STATUS="BROKEN" ;; *) log "Unknown squid status; not activating" STATUS="BROKEN" ;; esac fi unlock } # Function: Set to stopped state and call any deactivation hooks deactivate() { lock "deactivate" if [ $STATUS = "RUNNING" ]; then log "Deactivating at user's request; calling stop hook. " `stop_hook` fi if [ "$1" = "STOPPED" ]; then STATUS="STOPPED" else STATUS="BROKEN" fi unlock } # Function: Clean up and exit (signal handler for TERM, QUIT, INT, etc) cleanup() { log "$0 shutting down; deactivating" # return to "STOPPED" state deactivate # clean up lockfiles rm -f "$PIDFILE" "$LCKFILE" } # Function: Start a polling daemon to keep an eye on squid polling() { # Create PID file (make sure we're the only daemon running) if (set -o noclobber; echo $$ >"$PIDFILE") 2>/dev/null; then log "Starting polling daemon" else log "Unable to get lock $PIDFILE; is $NAME already running?" sleep 15 exit 1 fi # Initialize signal handlers trap 'log "Signal to start received"; activate' $SIGSTART trap 'log "Signal to stop received"; deactivate "STOPPED"' $SIGSTOP trap 'log "Ignoring $SIGINFO; current running status is: $STATUS"' $SIGINFO # sig 0 covers all exits, both normal and interrupted trap cleanup 0 # poll forever... while true; do # don't bother checking if squid isn't supposed to be running... if [ $STATUS != "STOPPED" ]; then case `squidstatus` in OK) if [ $STATUS = "BROKEN" ]; then log "Squid has recovered; activating..." activate fi ;; NO) if [ $STATUS = "RUNNING" ]; then log "Squid process is no longer running; deactivating..." deactivate "BROKEN" fi ;; EE) if [ $STATUS = "RUNNING" ]; then log "Squid process not responding; deactivating..." deactivate "BROKEN" fi ;; *) if [ $STATUS = "RUNNING" ]; then log "Unknown squid status; deactivating..." deactivate "BROKEN" fi ;; esac fi # nap for a bit before next check sleep $DELAY & # "wait"ing on process allows signal traps to execute immediately, # rather that pausing until the sleep finishes execution wait done } # Function: send signal to running daemon signal() { echo -n "Sending signal to daemon... " if [ -r "$PIDFILE" ]; then kill -"$1" `cat "$PIDFILE"` echo "done" else echo "could not find PID of daemon in $PIDFILE; is it running?" exit 1; fi } # Function: send start signal to running daemon starting() { signal $SIGSTART } # Function: send stop signal to running daemon stopping() { signal $SIGSTOP } # Function: send info signal to running daemon info() { signal $SIGINFO } ############ ### MAIN ### ############ case "$1" in poll) polling ;; start) starting ;; stop) stopping ;; info) info ;; *) echo "Usage: $0 {poll|start|stop|info}" >&2 echo "" >&2 echo " poll starts a daemon that watches squid" >&2 echo "" >&2 echo " start and stop signal the daemon to stop/start immediately," >&2 echo " rather than waiting for the watcher to notice" >&2 echo "" >&2 echo " info sends info signal to daemon (check syslog)" >&2 echo "" >&2 exit 1 ;; esac