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