02c111ad43a542a80fcd1bafaa1e5dc2c31ebf34
[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 ctdb_active_dir="$CTDB_VARDIR/active"
565
566 log_status_cat ()
567 {
568     echo "node is \"$1\", \"${script_name}\" reports problem: $(cat $2)"
569 }
570
571 ctdb_checkstatus ()
572 {
573     if [ -r "$ctdb_status_dir/$script_name/unhealthy" ] ; then
574         log_status_cat "unhealthy" "$ctdb_status_dir/$script_name/unhealthy"
575         return 1
576     elif [ -r "$ctdb_status_dir/$script_name/banned" ] ; then
577         log_status_cat "banned" "$ctdb_status_dir/$script_name/banned"
578         return 2
579     else
580         return 0
581     fi
582 }
583
584 ctdb_setstatus ()
585 {
586     d="$ctdb_status_dir/$script_name"
587     case "$1" in
588         unhealthy|banned)
589             mkdir -p "$d"
590             cat "$2" >"$d/$1"
591             ;;
592         *)
593             for i in "banned" "unhealthy" ; do
594                 rm -f "$d/$i"
595             done
596             ;;
597     esac
598 }
599
600 ctdb_service_needs_reconfigure ()
601 {
602     [ -e "$ctdb_status_dir/$service_name/reconfigure" ]
603 }
604
605 ctdb_service_set_reconfigure ()
606 {
607     d="$ctdb_status_dir/$service_name"
608     mkdir -p "$d"
609     >"$d/reconfigure"
610 }
611
612 ctdb_service_unset_reconfigure ()
613 {
614     rm -f "$ctdb_status_dir/$service_name/reconfigure"
615 }
616
617 ctdb_service_reconfigure ()
618 {
619     echo "Reconfiguring service \"$service_name\"..."
620     if [ -n "$service_reconfigure" ] ; then
621         eval $service_reconfigure
622     else
623         service "$service_name" restart
624     fi
625     ctdb_service_unset_reconfigure
626     ctdb_counter_init
627 }
628
629 ctdb_compat_managed_service ()
630 {
631     if [ "$1" = "yes" ] ; then
632         t="$t $2 "
633     fi
634 }
635
636 is_ctdb_managed_service ()
637 {
638     _service_name="${1:-${service_name}}"
639
640     t=" $CTDB_MANAGED_SERVICES "
641
642     ctdb_compat_managed_service "$CTDB_MANAGES_VSFTPD"   "vsftpd"
643     ctdb_compat_managed_service "$CTDB_MANAGES_SAMBA"    "samba"
644     ctdb_compat_managed_service "$CTDB_MANAGES_SCP"      "scp"
645     ctdb_compat_managed_service "$CTDB_MANAGES_WINBIND"  "winbind"
646     ctdb_compat_managed_service "$CTDB_MANAGES_HTTPD"    "httpd"
647     ctdb_compat_managed_service "$CTDB_MANAGES_ISCSI"    "iscsi"
648     ctdb_compat_managed_service "$CTDB_MANAGES_CLAMD"    "clamd"
649     ctdb_compat_managed_service "$CTDB_MANAGES_NFS"      "nfs"
650     ctdb_compat_managed_service "$CTDB_MANAGES_NFS"      "nfs-ganesha-gpfs"
651
652     # Returns 0 if "<space>$_service_name<space>" appears in $t
653     [ "${t#* ${_service_name} }" != "${t}" ]
654 }
655
656 ctdb_start_stop_service ()
657 {
658     _service_name="${1:-${service_name}}"
659
660     [ "$event_name" = "monitor" ] || return 0
661
662     _active="$ctdb_active_dir/$_service_name"
663     if is_ctdb_managed_service "$_service_name"; 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     else
672         if [ -e "$_active" ] ; then
673             echo "Stopping service $_service_name"
674             ctdb_service_stop || exit $?
675             rm -f "$_active"
676             exit 0
677         fi
678     fi
679 }
680
681 ctdb_service_start ()
682 {
683     if [ -n "$service_start" ] ; then
684         eval $service_start || return $?
685     else
686         service "$service_name" start || return $?
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 $CTDB_VARDIR/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
857 event_name="$1"