Eventscript functions: new functions to remember/check if service managed.
[ctdb.git] / config / functions
1 # Hey Emacs, this is a -*- shell-script -*- !!!
2
3 # utility functions for ctdb event scripts
4
5 PATH=/bin:/usr/bin:/usr/sbin:/sbin:$PATH
6
7 [ -z "$CTDB_VARDIR" ] && {
8     export CTDB_VARDIR="/var/ctdb"
9 }
10 [ -z "$CTDB_ETCDIR" ] && {
11     export CTDB_ETCDIR="/etc"
12 }
13
14 #######################################
15 # pull in a system config file, if any
16 _loadconfig() {
17
18     if [ -z "$1" ] ; then
19         foo="${service_config:-${service_name}}"
20         if [ -n "$foo" ] ; then
21             loadconfig "$foo"
22         fi
23     elif [ "$1" != "ctdb" ] ; then
24         loadconfig "ctdb"
25     fi
26
27     if [ -f $CTDB_ETCDIR/sysconfig/$1 ]; then
28         . $CTDB_ETCDIR/sysconfig/$1
29     elif [ -f $CTDB_ETCDIR/default/$1 ]; then
30         . $CTDB_ETCDIR/default/$1
31     elif [ -f $CTDB_BASE/sysconfig/$1 ]; then
32         . $CTDB_BASE/sysconfig/$1
33     fi
34 }
35
36 loadconfig () {
37     _loadconfig "$@"
38 }
39
40 ##############################################################
41 # determine on what type of system (init style) we are running
42 detect_init_style() {
43     # only do detection if not already set:
44     test "x$CTDB_INIT_STYLE" != "x" && return
45
46     if [ -x /sbin/startproc ]; then
47         CTDB_INIT_STYLE="suse"
48     elif [ -x /sbin/start-stop-daemon ]; then
49         CTDB_INIT_STYLE="debian"
50     else
51         CTDB_INIT_STYLE="redhat"
52     fi
53 }
54
55 ######################################################
56 # simulate /sbin/service on platforms that don't have it
57 # _service() makes it easier to hook the service() function for
58 # testing.
59 _service ()
60 {
61   _service_name="$1"
62   _op="$2"
63
64   # do nothing, when no service was specified
65   [ -z "$_service_name" ] && return
66
67   if [ -x /sbin/service ]; then
68       $_nice /sbin/service "$_service_name" "$_op"
69   elif [ -x $CTDB_ETCDIR/init.d/$_service_name ]; then
70       $_nice $CTDB_ETCDIR/init.d/$_service_name "$_op"
71   elif [ -x $CTDB_ETCDIR/rc.d/init.d/$_service_name ]; then
72       $_nice $CTDB_ETCDIR/rc.d/init.d/$_service_name "$_op"
73   fi
74 }
75
76 service()
77 {
78     _nice=""
79     _service "$@"
80 }
81
82 ######################################################
83 # simulate /sbin/service (niced) on platforms that don't have it
84 nice_service()
85 {
86     _nice="nice"
87     _service "$@"
88 }
89
90 ######################################################
91 # wrapper around /proc/ settings to allow them to be hooked
92 # for testing
93 # 1st arg is relative path under /proc/, 2nd arg is value to set
94 set_proc ()
95 {
96     echo "$2" >"/proc/$1"
97 }
98
99 ######################################################
100 # wrapper around getting file contents from /proc/ to allow
101 # this to be hooked for testing
102 # 1st arg is relative path under /proc/
103 get_proc ()
104 {
105     cat "/proc/$1"
106 }
107
108 ######################################################
109 # check that a rpc server is registered with portmap
110 # and responding to requests
111 # usage: ctdb_check_rpc SERVICE_NAME PROGNUM VERSION
112 ######################################################
113 ctdb_check_rpc() {
114     progname="$1"
115     prognum="$2"
116     version="$3"
117
118     ctdb_check_rpc_out=$(rpcinfo -u localhost $prognum $version 2>&1)
119     if [ $? -ne 0 ] ; then
120         ctdb_check_rpc_out="ERROR: $progname failed RPC check:
121 $ctdb_check_rpc_out"
122         echo "$ctdb_check_rpc_out"
123         return 1
124     fi
125 }
126
127 ######################################################
128 # check a set of directories is available
129 # return 1 on a missing directory
130 # usage: ctdb_check_directories_probe SERVICE_NAME <directories...>
131 ######################################################
132 ctdb_check_directories_probe() {
133     while IFS="" read d ; do
134         case "$d" in
135             *%*)
136                 continue
137                 ;;
138             *)
139                 [ -d "${d}/." ] || return 1
140         esac
141     done
142 }
143
144 ######################################################
145 # check a set of directories is available
146 # usage: ctdb_check_directories SERVICE_NAME <directories...>
147 ######################################################
148 ctdb_check_directories() {
149     n="${1:-${service_name}}"
150     ctdb_check_directories_probe || {
151         echo "ERROR: $n directory \"$d\" not available"
152         exit 1
153     }
154 }
155
156 ######################################################
157 # check a set of tcp ports
158 # usage: ctdb_check_tcp_ports <ports...>
159 ######################################################
160 ctdb_check_tcp_ports() {
161
162     for p ; do
163         if ! netstat -a -t -n | grep -q "0\.0\.0\.0:$p .*LISTEN" ; then
164             if ! netstat -a -t -n | grep -q ":::$p .*LISTEN" ; then
165                 echo "ERROR: $service_name tcp port $p is not responding"
166                 return 1
167             fi
168         fi
169     done
170 }
171
172 ######################################################
173 # check a unix socket
174 # usage: ctdb_check_unix_socket SERVICE_NAME <socket_path>
175 ######################################################
176 ctdb_check_unix_socket() {
177     socket_path="$1"
178     [ -z "$socket_path" ] && return
179
180     if ! netstat --unix -a -n | grep -q "^unix.*LISTEN.*${socket_path}$"; then
181         echo "ERROR: $service_name socket $socket_path not found"
182         return 1
183     fi
184 }
185
186 ######################################################
187 # check a command returns zero status
188 # usage: ctdb_check_command SERVICE_NAME <command>
189 ######################################################
190 ctdb_check_command() {
191   service_name="$1"
192   wait_cmd="$2"
193   [ -z "$wait_cmd" ] && return;
194   $wait_cmd > /dev/null 2>&1 || {
195       echo "ERROR: $service_name - $wait_cmd returned error"
196       exit 1
197   }
198 }
199
200 ################################################
201 # kill off any TCP connections with the given IP
202 ################################################
203 kill_tcp_connections() {
204     _IP="$1"    
205     _failed=0
206
207     _killcount=0
208     connfile="$CTDB_VARDIR/state/connections.$_IP"
209     netstat -tn |egrep "^tcp.*[[:space:]]+$_IP:.*ESTABLISHED" | awk '{print $4" "$5}' > $connfile
210     netstat -tn |egrep "^tcp.*[[:space:]]+::ffff:$_IP:.*ESTABLISHED" | awk '{print $4" "$5}' >> $connfile
211
212     while read dest src; do
213         srcip=`echo $src | sed -e "s/:[^:]*$//"`
214         srcport=`echo $src | sed -e "s/^.*://"`
215         destip=`echo $dest | sed -e "s/:[^:]*$//"`
216         destport=`echo $dest | sed -e "s/^.*://"`
217         echo "Killing TCP connection $srcip:$srcport $destip:$destport"
218         ctdb killtcp $srcip:$srcport $destip:$destport >/dev/null 2>&1 || _failed=1
219         case $destport in
220           # we only do one-way killtcp for CIFS
221           139|445) : ;;
222           # for all others we do 2-way
223           *) 
224                 ctdb killtcp $destip:$destport $srcip:$srcport >/dev/null 2>&1 || _failed=1
225                 ;;
226         esac
227         _killcount=`expr $_killcount + 1`
228      done < $connfile
229     rm -f $connfile
230
231     [ $_failed = 0 ] || {
232         echo "Failed to send killtcp control"
233         return;
234     }
235     [ $_killcount -gt 0 ] || {
236         return;
237     }
238     _count=0
239     while netstat -tn |egrep "^tcp.*[[:space:]]+$_IP:.*ESTABLISHED" > /dev/null; do
240         sleep 1
241         _count=`expr $_count + 1`
242         [ $_count -gt 3 ] && {
243             echo "Timed out killing tcp connections for IP $_IP"
244             return;
245         }
246     done
247     echo "killed $_killcount TCP connections to released IP $_IP"
248 }
249
250 ##################################################################
251 # kill off the local end for any TCP connections with the given IP
252 ##################################################################
253 kill_tcp_connections_local_only() {
254     _IP="$1"    
255     _failed=0
256
257     _killcount=0
258     connfile="$CTDB_VARDIR/state/connections.$_IP"
259     netstat -tn |egrep "^tcp.*[[:space:]]+$_IP:.*ESTABLISHED" | awk '{print $4" "$5}' > $connfile
260     netstat -tn |egrep "^tcp.*[[:space:]]+::ffff:$_IP:.*ESTABLISHED" | awk '{print $4" "$5}' >> $connfile
261
262     while read dest src; do
263         srcip=`echo $src | sed -e "s/:[^:]*$//"`
264         srcport=`echo $src | sed -e "s/^.*://"`
265         destip=`echo $dest | sed -e "s/:[^:]*$//"`
266         destport=`echo $dest | sed -e "s/^.*://"`
267         echo "Killing TCP connection $srcip:$srcport $destip:$destport"
268         ctdb killtcp $srcip:$srcport $destip:$destport >/dev/null 2>&1 || _failed=1
269         _killcount=`expr $_killcount + 1`
270      done < $connfile
271     rm -f $connfile
272
273     [ $_failed = 0 ] || {
274         echo "Failed to send killtcp control"
275         return;
276     }
277     [ $_killcount -gt 0 ] || {
278         return;
279     }
280     _count=0
281     while netstat -tn |egrep "^tcp.*[[:space:]]+$_IP:.*ESTABLISHED" > /dev/null; do
282         sleep 1
283         _count=`expr $_count + 1`
284         [ $_count -gt 3 ] && {
285             echo "Timed out killing tcp connections for IP $_IP"
286             return;
287         }
288     done
289     echo "killed $_killcount TCP connections to released IP $_IP"
290 }
291
292 ##################################################################
293 # tickle any TCP connections with the given IP
294 ##################################################################
295 tickle_tcp_connections() {
296     _IP="$1"
297     _failed=0
298
299     _killcount=0
300     connfile="$CTDB_VARDIR/state/connections.$_IP"
301     netstat -tn |egrep "^tcp.*[[:space:]]+$_IP:.*ESTABLISHED" | awk '{print $4" "$5}' > $connfile
302     netstat -tn |egrep "^tcp.*[[:space:]]+::ffff:$_IP:.*ESTABLISHED" | awk '{print $4" "$5}' >> $connfile
303
304     while read dest src; do
305         srcip=`echo $src | sed -e "s/:[^:]*$//"`
306         srcport=`echo $src | sed -e "s/^.*://"`
307         destip=`echo $dest | sed -e "s/:[^:]*$//"`
308         destport=`echo $dest | sed -e "s/^.*://"`
309         echo "Tickle TCP connection $srcip:$srcport $destip:$destport"
310         ctdb tickle $srcip:$srcport $destip:$destport >/dev/null 2>&1 || _failed=1
311         echo "Tickle TCP connection $destip:$destport $srcip:$srcport"
312         ctdb tickle $destip:$destport $srcip:$srcport >/dev/null 2>&1 || _failed=1
313      done < $connfile
314     rm -f $connfile
315
316     [ $_failed = 0 ] || {
317         echo "Failed to send tickle control"
318         return;
319     }
320 }
321
322 ########################################################
323 # start/stop the nfs service on different platforms
324 ########################################################
325 startstop_nfs() {
326         PLATFORM="unknown"
327         [ -x $CTDB_ETCDIR/init.d/nfsserver ] && {
328                 PLATFORM="sles"
329         }
330         [ -x $CTDB_ETCDIR/init.d/nfslock ] && {
331                 PLATFORM="rhel"
332         }
333
334         case $PLATFORM in
335         sles)
336                 case $1 in
337                 start)
338                         service nfsserver start
339                         ;;
340                 stop)
341                         service nfsserver stop > /dev/null 2>&1
342                         ;;
343                 restart)
344                         set_proc "fs/nfsd/threads" 0
345                         service nfsserver stop > /dev/null 2>&1
346                         pkill -9 nfsd
347                         service nfsserver start
348                         ;;
349                 esac
350                 ;;
351         rhel)
352                 case $1 in
353                 start)
354                         service nfslock start
355                         service nfs start
356                         ;;
357                 stop)
358                         service nfs stop > /dev/null 2>&1
359                         service nfslock stop > /dev/null 2>&1
360                         ;;
361                 restart)
362                         set_proc "fs/nfsd/threads" 0
363                         service nfs stop > /dev/null 2>&1
364                         service nfslock stop > /dev/null 2>&1
365                         pkill -9 nfsd
366                         service nfslock start
367                         service nfs start
368                         ;;
369                 esac
370                 ;;
371         *)
372                 echo "Unknown platform. NFS is not supported with ctdb"
373                 exit 1
374                 ;;
375         esac
376 }
377
378 ########################################################
379 # start/stop the nfs lockmanager service on different platforms
380 ########################################################
381 startstop_nfslock() {
382         PLATFORM="unknown"
383         [ -x $CTDB_ETCDIR/init.d/nfsserver ] && {
384                 PLATFORM="sles"
385         }
386         [ -x $CTDB_ETCDIR/init.d/nfslock ] && {
387                 PLATFORM="rhel"
388         }
389
390         case $PLATFORM in
391         sles)
392                 # for sles there is no service for lockmanager
393                 # so we instead just shutdown/restart nfs
394                 case $1 in
395                 start)
396                         service nfsserver start
397                         ;;
398                 stop)
399                         service nfsserver stop > /dev/null 2>&1
400                         ;;
401                 restart)
402                         service nfsserver stop
403                         service nfsserver start
404                         ;;
405                 esac
406                 ;;
407         rhel)
408                 case $1 in
409                 start)
410                         service nfslock start
411                         ;;
412                 stop)
413                         service nfslock stop > /dev/null 2>&1
414                         ;;
415                 restart)
416                         service nfslock stop
417                         service nfslock start
418                         ;;
419                 esac
420                 ;;
421         *)
422                 echo "Unknown platform. NFS locking is not supported with ctdb"
423                 exit 1
424                 ;;
425         esac
426 }
427
428 # better use delete_ip_from_iface() together with add_ip_to_iface
429 # remove_ip should be removed in future
430 remove_ip() {
431         local _ip_maskbits=$1
432         local _iface=$2
433         local _ip=`echo "$_ip_maskbits" | cut -d '/' -f1`
434         local _maskbits=`echo "$_ip_maskbits" | cut -d '/' -f2`
435
436         delete_ip_from_iface "$_iface" "$_ip" "$_maskbits"
437         return $?
438 }
439
440 add_ip_to_iface()
441 {
442         local _iface=$1
443         local _ip=$2
444         local _maskbits=$3
445         local _state_dir="$CTDB_VARDIR/state/interface_modify"
446         local _lockfile="$_state_dir/$_iface.flock"
447         local _readd_base="$_state_dir/$_iface.readd.d"
448
449         mkdir -p $_state_dir || {
450                 ret=$?
451                 echo "Failed to mkdir -p $_state_dir - $ret"
452                 return $ret
453         }
454
455         test -f $_lockfile || {
456                 touch $_lockfile
457         }
458
459         flock --timeout 30 $_lockfile $CTDB_BASE/interface_modify.sh add "$_iface" "$_ip" "$_maskbits" "$_readd_base"
460         return $?
461 }
462
463 delete_ip_from_iface()
464 {
465         local _iface=$1
466         local _ip=$2
467         local _maskbits=$3
468         local _state_dir="$CTDB_VARDIR/state/interface_modify"
469         local _lockfile="$_state_dir/$_iface.flock"
470         local _readd_base="$_state_dir/$_iface.readd.d"
471
472         mkdir -p $_state_dir || {
473                 ret=$?
474                 echo "Failed to mkdir -p $_state_dir - $ret"
475                 return $ret
476         }
477
478         test -f $_lockfile || {
479                 touch $_lockfile
480         }
481
482         flock --timeout 30 $_lockfile $CTDB_BASE/interface_modify.sh delete "$_iface" "$_ip" "$_maskbits" "$_readd_base"
483         return $?
484 }
485
486 setup_iface_ip_readd_script()
487 {
488         local _iface=$1
489         local _ip=$2
490         local _maskbits=$3
491         local _readd_script=$4
492         local _state_dir="$CTDB_VARDIR/state/interface_modify"
493         local _lockfile="$_state_dir/$_iface.flock"
494         local _readd_base="$_state_dir/$_iface.readd.d"
495
496         mkdir -p $_state_dir || {
497                 ret=$?
498                 echo "Failed to mkdir -p $_state_dir - $ret"
499                 return $ret
500         }
501
502         test -f $_lockfile || {
503                 touch $_lockfile
504         }
505
506         flock --timeout 30 $_lockfile $CTDB_BASE/interface_modify.sh readd_script "$_iface" "$_ip" "$_maskbits" "$_readd_base" "$_readd_script"
507         return $?
508 }
509
510 ########################################################
511 # some simple logic for counting events - per eventscript
512 # usage: ctdb_counter_init
513 #        ctdb_counter_incr
514 #        ctdb_check_counter_limit <limit>
515 # ctdb_check_counter_limit succeeds when count >= <limit>
516 ########################################################
517 _ctdb_counter_common () {
518     _counter_file="$ctdb_fail_dir/$service_name"
519     mkdir -p "${_counter_file%/*}" # dirname
520 }
521 ctdb_counter_init () {
522     _ctdb_counter_common
523
524     >"$_counter_file"
525 }
526 ctdb_counter_incr () {
527     _ctdb_counter_common
528
529     # unary counting!
530     echo -n 1 >> "$_counter_file"
531 }
532 ctdb_check_counter_limit () {
533     _ctdb_counter_common
534
535     _limit="${1:-${service_fail_limit}}"
536     _quiet="$2"
537
538     # unary counting!
539     _size=$(stat -c "%s" "$_counter_file" 2>/dev/null || echo 0)
540     if [ $_size -ge $_limit ] ; then
541         echo "ERROR: more than $_limit consecutive failures for $service_name, marking cluster unhealthy"
542         exit 1
543     elif [ $_size -gt 0 -a -z "$_quiet" ] ; then
544         echo "WARNING: less than $_limit consecutive failures ($_size) for $service_name, not unhealthy yet"
545     fi
546 }
547 ctdb_check_counter_equal () {
548     _ctdb_counter_common
549
550     _limit=$1
551
552     # unary counting!
553     _size=$(stat -c "%s" "$_counter_file" 2>/dev/null || echo 0)
554     if [ $_size -eq $_limit ] ; then
555         return 1
556     fi
557     return 0
558 }
559
560 ########################################################
561
562 ctdb_status_dir="$CTDB_VARDIR/status"
563 ctdb_fail_dir="$CTDB_VARDIR/failcount"
564
565 ########################################################
566 # Managed status history, for auto-start/stop
567
568 ctdb_managed_dir="$CTDB_VARDIR/managed_history"
569
570 _ctdb_managed_common ()
571 {
572     _service_name="${1:-${service_name}}"
573     _ctdb_managed_file="$ctdb_managed_dir/$_service_name"
574 }
575
576 ctdb_service_managed ()
577 {
578     _ctdb_managed_common "$@"
579     mkdir -p "$ctdb_managed_dir"
580     touch "$_ctdb_managed_file"
581 }
582
583 ctdb_service_unmanaged ()
584 {
585     _ctdb_managed_common "$@"
586     rm -f "$_ctdb_managed_file"
587 }
588
589 is_ctdb_previously_managed_service ()
590 {
591     _ctdb_managed_common "$@"
592     [ -f "$_ctdb_managed_file" ]
593 }
594
595 ########################################################
596 # Check and set status
597
598 log_status_cat ()
599 {
600     echo "node is \"$1\", \"${script_name}\" reports problem: $(cat $2)"
601 }
602
603 ctdb_checkstatus ()
604 {
605     if [ -r "$ctdb_status_dir/$script_name/unhealthy" ] ; then
606         log_status_cat "unhealthy" "$ctdb_status_dir/$script_name/unhealthy"
607         return 1
608     elif [ -r "$ctdb_status_dir/$script_name/banned" ] ; then
609         log_status_cat "banned" "$ctdb_status_dir/$script_name/banned"
610         return 2
611     else
612         return 0
613     fi
614 }
615
616 ctdb_setstatus ()
617 {
618     d="$ctdb_status_dir/$script_name"
619     case "$1" in
620         unhealthy|banned)
621             mkdir -p "$d"
622             cat "$2" >"$d/$1"
623             ;;
624         *)
625             for i in "banned" "unhealthy" ; do
626                 rm -f "$d/$i"
627             done
628             ;;
629     esac
630 }
631
632 ctdb_service_needs_reconfigure ()
633 {
634     [ -e "$ctdb_status_dir/$service_name/reconfigure" ]
635 }
636
637 ctdb_service_set_reconfigure ()
638 {
639     d="$ctdb_status_dir/$service_name"
640     mkdir -p "$d"
641     >"$d/reconfigure"
642 }
643
644 ctdb_service_unset_reconfigure ()
645 {
646     rm -f "$ctdb_status_dir/$service_name/reconfigure"
647 }
648
649 ctdb_service_reconfigure ()
650 {
651     echo "Reconfiguring service \"$service_name\"..."
652     if [ -n "$service_reconfigure" ] ; then
653         eval $service_reconfigure
654     else
655         service "$service_name" restart
656     fi
657     ctdb_service_unset_reconfigure
658     ctdb_counter_init
659 }
660
661 ctdb_compat_managed_service ()
662 {
663     if [ "$1" = "yes" ] ; then
664         t="$t $2 "
665     fi
666 }
667
668 is_ctdb_managed_service ()
669 {
670     _service_name="${1:-${service_name}}"
671
672     t=" $CTDB_MANAGED_SERVICES "
673
674     ctdb_compat_managed_service "$CTDB_MANAGES_VSFTPD"   "vsftpd"
675     ctdb_compat_managed_service "$CTDB_MANAGES_SAMBA"    "samba"
676     ctdb_compat_managed_service "$CTDB_MANAGES_SCP"      "scp"
677     ctdb_compat_managed_service "$CTDB_MANAGES_WINBIND"  "winbind"
678     ctdb_compat_managed_service "$CTDB_MANAGES_HTTPD"    "httpd"
679     ctdb_compat_managed_service "$CTDB_MANAGES_ISCSI"    "iscsi"
680     ctdb_compat_managed_service "$CTDB_MANAGES_CLAMD"    "clamd"
681     ctdb_compat_managed_service "$CTDB_MANAGES_NFS"      "nfs"
682     ctdb_compat_managed_service "$CTDB_MANAGES_NFS"      "nfs-ganesha-gpfs"
683
684     # Returns 0 if "<space>$_service_name<space>" appears in $t
685     [ "${t#* ${_service_name} }" != "${t}" ]
686 }
687
688 ctdb_start_stop_service ()
689 {
690     _service_name="${1:-${service_name}}"
691
692     [ "$event_name" = "monitor" ] || return 0
693
694     if is_ctdb_managed_service "$_service_name" ; then
695         if ! is_ctdb_previously_managed_service "$_service_name" ; then
696             echo "Starting service $_service_name"
697             ctdb_service_start || exit $?
698             ctdb_service_managed "$_service_name"
699             exit 0
700         fi
701     else
702         if is_ctdb_previously_managed_service "$_service_name" ; then
703             echo "Stopping service $_service_name"
704             ctdb_service_stop || exit $?
705             ctdb_service_unmanaged "$_service_name"
706             exit 0
707         fi
708     fi
709 }
710
711 ctdb_service_start ()
712 {
713     if [ -n "$service_start" ] ; then
714         eval $service_start || return $?
715     else
716         service "$service_name" start || return $?
717     fi
718     ctdb_counter_init
719 }
720
721 ctdb_service_stop ()
722 {
723     if [ -n "$service_stop" ] ; then
724         eval $service_stop
725     else
726         service "$service_name" stop
727     fi
728 }
729
730 ctdb_standard_event_handler ()
731 {
732     case "$1" in
733         status)
734             ctdb_checkstatus
735             exit
736             ;;
737         setstatus)
738             shift
739             ctdb_setstatus "$@"
740             exit
741             ;;
742     esac
743 }
744
745 ipv4_host_addr_to_net_addr()
746 {
747         local HOST=$1
748         local MASKBITS=$2
749
750         local HOST0=$(echo $HOST | awk -F . '{print $4}')
751         local HOST1=$(echo $HOST | awk -F . '{print $3}')
752         local HOST2=$(echo $HOST | awk -F . '{print $2}')
753         local HOST3=$(echo $HOST | awk -F . '{print $1}')
754
755         local HOST_NUM=$(( $HOST0 + $HOST1 * 256 + $HOST2 * (256 ** 2) + $HOST3 * (256 ** 3) ))
756
757         local MASK_NUM=$(( ( (2**32 - 1) * (2**(32 - $MASKBITS)) ) & (2**32 - 1) ))
758
759         local NET_NUM=$(( $HOST_NUM & $MASK_NUM))
760
761         local NET0=$(( $NET_NUM & 255 ))
762         local NET1=$(( ($NET_NUM & (255 * 256)) / 256 ))
763         local NET2=$(( ($NET_NUM & (255 * 256**2)) / 256**2 ))
764         local NET3=$(( ($NET_NUM & (255 * 256**3)) / 256**3 ))
765
766         echo "$NET3.$NET2.$NET1.$NET0"
767 }
768
769 ipv4_maskbits_to_net_mask()
770 {
771         local MASKBITS=$1
772
773         local MASK_NUM=$(( ( (2**32 - 1) * (2**(32 - $MASKBITS)) ) & (2**32 - 1) ))
774
775         local MASK0=$(( $MASK_NUM & 255 ))
776         local MASK1=$(( ($MASK_NUM & (255 * 256)) / 256 ))
777         local MASK2=$(( ($MASK_NUM & (255 * 256**2)) / 256**2 ))
778         local MASK3=$(( ($MASK_NUM & (255 * 256**3)) / 256**3 ))
779
780         echo "$MASK3.$MASK2.$MASK1.$MASK0"
781 }
782
783 ipv4_is_valid_addr()
784 {
785         local ADDR=$1
786         local fail=0
787
788         local N=`echo $ADDR | sed -e 's/[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*//'`
789         test -n "$N" && fail=1
790
791         local ADDR0=$(echo $ADDR | awk -F . '{print $4}')
792         local ADDR1=$(echo $ADDR | awk -F . '{print $3}')
793         local ADDR2=$(echo $ADDR | awk -F . '{print $2}')
794         local ADDR3=$(echo $ADDR | awk -F . '{print $1}')
795
796         test "$ADDR0" -gt 255 && fail=1
797         test "$ADDR1" -gt 255 && fail=1
798         test "$ADDR2" -gt 255 && fail=1
799         test "$ADDR3" -gt 255 && fail=1
800
801         test x"$fail" != x"0" && {
802                 #echo "IPv4: '$ADDR' is not a valid address"
803                 return 1;
804         }
805
806         return 0;
807 }
808
809 # iptables doesn't like being re-entered, so flock-wrap it.
810 iptables()
811 {
812         flock -w 30 $CTDB_VARDIR/iptables-ctdb.flock /sbin/iptables "$@"
813 }
814
815 ########################################################
816 # tickle handling
817 ########################################################
818
819 # Temporary directory for tickles.
820 tickledir="$CTDB_VARDIR/state/tickles"
821 mkdir -p "$tickledir"
822
823 update_tickles ()
824 {
825         _port="$1"
826
827         mkdir -p "$tickledir" # Just in case
828
829         # Who am I?
830         _pnn=$(ctdb pnn) ; _pnn=${_pnn#PNN:}
831
832         # What public IPs do I hold?
833         _ips=$(ctdb -Y ip | awk -F: -v pnn=$_pnn '$3 == pnn {print $2}')
834
835         # IPs as a regexp choice
836         _ipschoice="($(echo $_ips | sed -e 's/ /|/g' -e 's/\./\\\\./g'))"
837
838         # Record connections to our public IPs in a temporary file
839         _my_connections="${tickledir}/${_port}.connections"
840         rm -f "$_my_connections"
841         netstat -tn |
842         awk -v destpat="^${_ipschoice}:${_port}\$" \
843           '$1 == "tcp" && $6 == "ESTABLISHED" && $4 ~ destpat {print $5, $4}' |
844         sort >"$_my_connections"
845
846         # Record our current tickles in a temporary file
847         _my_tickles="${tickledir}/${_port}.tickles"
848         rm -f "$_my_tickles"
849         for _i in $_ips ; do
850                 ctdb -Y gettickles $_i $_port | 
851                 awk -F: 'NR > 1 { printf "%s:%s %s:%s\n", $2, $3, $4, $5 }'
852         done |
853         sort >"$_my_tickles"
854
855         # Add tickles for connections that we haven't already got tickles for
856         comm -23 "$_my_connections" "$_my_tickles" |
857         while read _src _dst ; do
858                 ctdb addtickle $_src $_dst
859         done
860
861         # Remove tickles for connections that are no longer there
862         comm -13 "$_my_connections" "$_my_tickles" |
863         while read _src _dst ; do
864                 ctdb deltickle $_src $_dst
865         done
866
867         rm -f "$_my_connections" "$_my_tickles" 
868 }
869
870 ########################################################
871 # load a site local config file
872 ########################################################
873
874 [ -n "$CTDB_RC_LOCAL" -a -x "$CTDB_RC_LOCAL" ] && {
875         . "$CTDB_RC_LOCAL"
876 }
877
878 [ -x $CTDB_BASE/rc.local ] && {
879         . $CTDB_BASE/rc.local
880 }
881
882 [ -d $CTDB_BASE/rc.local.d ] && {
883         for i in $CTDB_BASE/rc.local.d/* ; do
884                 [ -x "$i" ] && . "$i"
885         done
886 }
887
888 script_name="${0##*/}"       # basename
889 service_name="$script_name"  # default is just the script name
890 service_fail_limit=1
891 event_name="$1"