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