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