Eventscripts: remove unused remove_ip() function.
[sahlberg/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
359                         service nfslock stop
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 add_ip_to_iface()
429 {
430         local _iface=$1
431         local _ip=$2
432         local _maskbits=$3
433         local _state_dir="$CTDB_VARDIR/state/interface_modify"
434         local _lockfile="$_state_dir/$_iface.flock"
435         local _readd_base="$_state_dir/$_iface.readd.d"
436
437         mkdir -p $_state_dir || {
438                 ret=$?
439                 echo "Failed to mkdir -p $_state_dir - $ret"
440                 return $ret
441         }
442
443         test -f $_lockfile || {
444                 touch $_lockfile
445         }
446
447         flock --timeout 30 $_lockfile $CTDB_BASE/interface_modify.sh add "$_iface" "$_ip" "$_maskbits" "$_readd_base"
448         return $?
449 }
450
451 delete_ip_from_iface()
452 {
453         local _iface=$1
454         local _ip=$2
455         local _maskbits=$3
456         local _state_dir="$CTDB_VARDIR/state/interface_modify"
457         local _lockfile="$_state_dir/$_iface.flock"
458         local _readd_base="$_state_dir/$_iface.readd.d"
459
460         mkdir -p $_state_dir || {
461                 ret=$?
462                 echo "Failed to mkdir -p $_state_dir - $ret"
463                 return $ret
464         }
465
466         test -f $_lockfile || {
467                 touch $_lockfile
468         }
469
470         flock --timeout 30 $_lockfile $CTDB_BASE/interface_modify.sh delete "$_iface" "$_ip" "$_maskbits" "$_readd_base"
471         return $?
472 }
473
474 setup_iface_ip_readd_script()
475 {
476         local _iface=$1
477         local _ip=$2
478         local _maskbits=$3
479         local _readd_script=$4
480         local _state_dir="$CTDB_VARDIR/state/interface_modify"
481         local _lockfile="$_state_dir/$_iface.flock"
482         local _readd_base="$_state_dir/$_iface.readd.d"
483
484         mkdir -p $_state_dir || {
485                 ret=$?
486                 echo "Failed to mkdir -p $_state_dir - $ret"
487                 return $ret
488         }
489
490         test -f $_lockfile || {
491                 touch $_lockfile
492         }
493
494         flock --timeout 30 $_lockfile $CTDB_BASE/interface_modify.sh readd_script "$_iface" "$_ip" "$_maskbits" "$_readd_base" "$_readd_script"
495         return $?
496 }
497
498 ########################################################
499 # some simple logic for counting events - per eventscript
500 # usage: ctdb_counter_init
501 #        ctdb_counter_incr
502 #        ctdb_check_counter_limit <limit>
503 # ctdb_check_counter_limit succeeds when count >= <limit>
504 ########################################################
505 _ctdb_counter_common () {
506     _service_name="${1:-${service_name}}"
507     _counter_file="$ctdb_fail_dir/$_service_name"
508     mkdir -p "${_counter_file%/*}" # dirname
509 }
510 ctdb_counter_init () {
511     _ctdb_counter_common "$1"
512
513     >"$_counter_file"
514 }
515 ctdb_counter_incr () {
516     _ctdb_counter_common "$1"
517
518     # unary counting!
519     echo -n 1 >> "$_counter_file"
520 }
521 ctdb_check_counter_limit () {
522     _ctdb_counter_common
523
524     _limit="${1:-${service_fail_limit}}"
525     _quiet="$2"
526
527     # unary counting!
528     _size=$(stat -c "%s" "$_counter_file" 2>/dev/null || echo 0)
529     if [ $_size -ge $_limit ] ; then
530         echo "ERROR: more than $_limit consecutive failures for $service_name, marking cluster unhealthy"
531         exit 1
532     elif [ $_size -gt 0 -a -z "$_quiet" ] ; then
533         echo "WARNING: less than $_limit consecutive failures ($_size) for $service_name, not unhealthy yet"
534     fi
535 }
536 ctdb_check_counter_equal () {
537     _ctdb_counter_common
538
539     _limit=$1
540
541     # unary counting!
542     _size=$(stat -c "%s" "$_counter_file" 2>/dev/null || echo 0)
543     if [ $_size -eq $_limit ] ; then
544         return 1
545     fi
546     return 0
547 }
548
549 ########################################################
550
551 ctdb_status_dir="$CTDB_VARDIR/status"
552 ctdb_fail_dir="$CTDB_VARDIR/failcount"
553
554 ctdb_setup_service_state_dir ()
555 {
556     service_state_dir="$CTDB_VARDIR/state/${1:-${service_name}}"
557     mkdir -p "$service_state_dir" || {
558         echo "Error creating state dir \"$service_state_dir\""
559         exit 1
560     }
561 }
562
563 ########################################################
564 # Managed status history, for auto-start/stop
565
566 ctdb_managed_dir="$CTDB_VARDIR/managed_history"
567
568 _ctdb_managed_common ()
569 {
570     _service_name="${1:-${service_name}}"
571     _ctdb_managed_file="$ctdb_managed_dir/$_service_name"
572 }
573
574 ctdb_service_managed ()
575 {
576     _ctdb_managed_common "$@"
577     mkdir -p "$ctdb_managed_dir"
578     touch "$_ctdb_managed_file"
579 }
580
581 ctdb_service_unmanaged ()
582 {
583     _ctdb_managed_common "$@"
584     rm -f "$_ctdb_managed_file"
585 }
586
587 is_ctdb_previously_managed_service ()
588 {
589     _ctdb_managed_common "$@"
590     [ -f "$_ctdb_managed_file" ]
591 }
592
593 ########################################################
594 # Check and set status
595
596 log_status_cat ()
597 {
598     echo "node is \"$1\", \"${script_name}\" reports problem: $(cat $2)"
599 }
600
601 ctdb_checkstatus ()
602 {
603     if [ -r "$ctdb_status_dir/$script_name/unhealthy" ] ; then
604         log_status_cat "unhealthy" "$ctdb_status_dir/$script_name/unhealthy"
605         return 1
606     elif [ -r "$ctdb_status_dir/$script_name/banned" ] ; then
607         log_status_cat "banned" "$ctdb_status_dir/$script_name/banned"
608         return 2
609     else
610         return 0
611     fi
612 }
613
614 ctdb_setstatus ()
615 {
616     d="$ctdb_status_dir/$script_name"
617     case "$1" in
618         unhealthy|banned)
619             mkdir -p "$d"
620             cat "$2" >"$d/$1"
621             ;;
622         *)
623             for i in "banned" "unhealthy" ; do
624                 rm -f "$d/$i"
625             done
626             ;;
627     esac
628 }
629
630 ##################################################################
631 # Reconfigure a service on demand
632
633 _ctdb_service_reconfigure_common ()
634 {
635     _d="$ctdb_status_dir/${1:-${service_name}}"
636     mkdir -p "$_d"
637     _ctdb_service_reconfigure_flag="$_d/reconfigure"
638 }
639
640 ctdb_service_needs_reconfigure ()
641 {
642     _ctdb_service_reconfigure_common "$@"
643     [ -e "$_ctdb_service_reconfigure_flag" ]
644 }
645
646 ctdb_service_set_reconfigure ()
647 {
648     _ctdb_service_reconfigure_common "$@"
649     >"$_ctdb_service_reconfigure_flag"
650 }
651
652 ctdb_service_unset_reconfigure ()
653 {
654     _ctdb_service_reconfigure_common "$@"
655     rm -f "$_ctdb_service_reconfigure_flag"
656 }
657
658 ctdb_service_reconfigure ()
659 {
660     echo "Reconfiguring service \"$service_name\"..."
661     ctdb_service_unset_reconfigure "$@"
662     service_reconfigure "$@" || return $?
663     ctdb_counter_init "$@"
664 }
665
666 # Default service_reconfigure() function.
667 service_reconfigure ()
668 {
669     service "${1:-$service_name}" restart
670 }
671
672 ctdb_service_check_reconfigure ()
673 {
674     [ "$event_name" = "monitor" ] || return 0
675
676     if ctdb_service_needs_reconfigure "$@" ; then
677         ctdb_service_reconfigure "$@"
678         exit 0
679     fi
680 }
681
682 ##################################################################
683 # Does CTDB manage this service? - and associated auto-start/stop
684
685 ctdb_compat_managed_service ()
686 {
687     if [ "$1" = "yes" -a "$2" = "$_service_name" ] ; then
688         CTDB_MANAGED_SERVICES="$CTDB_MANAGED_SERVICES $2"
689     fi
690 }
691
692 is_ctdb_managed_service ()
693 {
694     _service_name="${1:-${service_name}}"
695
696     # $t is used just for readability and to allow better accurate
697     # matching via leading/trailing spaces
698     t=" $CTDB_MANAGED_SERVICES "
699
700     # Return 0 if "<space>$_service_name<space>" appears in $t
701     if [ "${t#* ${_service_name} }" != "${t}" ] ; then
702         return 0
703     fi
704
705     # If above didn't match then update $CTDB_MANAGED_SERVICES for
706     # backward compatibility and try again.
707     ctdb_compat_managed_service "$CTDB_MANAGES_VSFTPD"   "vsftpd"
708     ctdb_compat_managed_service "$CTDB_MANAGES_SAMBA"    "samba"
709     ctdb_compat_managed_service "$CTDB_MANAGES_SCP"      "scp"
710     ctdb_compat_managed_service "$CTDB_MANAGES_WINBIND"  "winbind"
711     ctdb_compat_managed_service "$CTDB_MANAGES_HTTPD"    "httpd"
712     ctdb_compat_managed_service "$CTDB_MANAGES_ISCSI"    "iscsi"
713     ctdb_compat_managed_service "$CTDB_MANAGES_CLAMD"    "clamd"
714     ctdb_compat_managed_service "$CTDB_MANAGES_NFS"      "nfs"
715     ctdb_compat_managed_service "$CTDB_MANAGES_NFS"      "nfs-ganesha-gpfs"
716
717     t=" $CTDB_MANAGED_SERVICES "
718
719     # Return 0 if "<space>$_service_name<space>" appears in $t
720     [ "${t#* ${_service_name} }" != "${t}" ]
721 }
722
723 ctdb_start_stop_service ()
724 {
725     _service_name="${1:-${service_name}}"
726
727     [ "$event_name" = "monitor" ] || return 0
728
729     if is_ctdb_managed_service "$_service_name" ; then
730         if ! is_ctdb_previously_managed_service "$_service_name" ; then
731             echo "Starting service \"$_service_name\" - now managed"
732             ctdb_service_start "$_service_name"
733             exit $?
734         fi
735     else
736         if is_ctdb_previously_managed_service "$_service_name" ; then
737             echo "Stopping service \"$_service_name\" - no longer managed"
738             ctdb_service_stop "$_service_name"
739             exit $?
740         fi
741     fi
742 }
743
744 ctdb_service_start ()
745 {
746     # The service is marked managed if we've ever tried to start it.
747     ctdb_service_managed "$@"
748
749     # Here we only want $1.  If no argument is passed then
750     # service_start needs to know.
751     service_start "$@" || return $?
752
753     ctdb_counter_init "$@"
754 }
755
756 ctdb_service_stop ()
757 {
758     ctdb_service_unmanaged "$@"
759     service_stop "$@"
760 }
761
762 # Default service_start() and service_stop() functions.
763  
764 # These may be overridden in an eventscript.  When overriding, the
765 # following convention must be followed.  If these functions are
766 # called with no arguments then they may use internal logic to
767 # determine whether the service is managed and, therefore, whether
768 # they should take any action.  However, if the service name is
769 # specified as an argument then an attempt must be made to start or
770 # stop the service.  This is because the auto-start/stop code calls
771 # them with the service name as an argument.
772 service_start ()
773 {
774     service "${1:-${service_name}}" start
775 }
776
777 service_stop ()
778 {
779     service "${1:-${service_name}}" stop
780 }
781
782 ##################################################################
783
784 ctdb_standard_event_handler ()
785 {
786     case "$1" in
787         status)
788             ctdb_checkstatus
789             exit
790             ;;
791         setstatus)
792             shift
793             ctdb_setstatus "$@"
794             exit
795             ;;
796     esac
797 }
798
799 ipv4_host_addr_to_net_addr()
800 {
801         local HOST=$1
802         local MASKBITS=$2
803
804         local HOST0=$(echo $HOST | awk -F . '{print $4}')
805         local HOST1=$(echo $HOST | awk -F . '{print $3}')
806         local HOST2=$(echo $HOST | awk -F . '{print $2}')
807         local HOST3=$(echo $HOST | awk -F . '{print $1}')
808
809         local HOST_NUM=$(( $HOST0 + $HOST1 * 256 + $HOST2 * (256 ** 2) + $HOST3 * (256 ** 3) ))
810
811         local MASK_NUM=$(( ( (2**32 - 1) * (2**(32 - $MASKBITS)) ) & (2**32 - 1) ))
812
813         local NET_NUM=$(( $HOST_NUM & $MASK_NUM))
814
815         local NET0=$(( $NET_NUM & 255 ))
816         local NET1=$(( ($NET_NUM & (255 * 256)) / 256 ))
817         local NET2=$(( ($NET_NUM & (255 * 256**2)) / 256**2 ))
818         local NET3=$(( ($NET_NUM & (255 * 256**3)) / 256**3 ))
819
820         echo "$NET3.$NET2.$NET1.$NET0"
821 }
822
823 ipv4_maskbits_to_net_mask()
824 {
825         local MASKBITS=$1
826
827         local MASK_NUM=$(( ( (2**32 - 1) * (2**(32 - $MASKBITS)) ) & (2**32 - 1) ))
828
829         local MASK0=$(( $MASK_NUM & 255 ))
830         local MASK1=$(( ($MASK_NUM & (255 * 256)) / 256 ))
831         local MASK2=$(( ($MASK_NUM & (255 * 256**2)) / 256**2 ))
832         local MASK3=$(( ($MASK_NUM & (255 * 256**3)) / 256**3 ))
833
834         echo "$MASK3.$MASK2.$MASK1.$MASK0"
835 }
836
837 ipv4_is_valid_addr()
838 {
839         local ADDR=$1
840         local fail=0
841
842         local N=`echo $ADDR | sed -e 's/[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*//'`
843         test -n "$N" && fail=1
844
845         local ADDR0=$(echo $ADDR | awk -F . '{print $4}')
846         local ADDR1=$(echo $ADDR | awk -F . '{print $3}')
847         local ADDR2=$(echo $ADDR | awk -F . '{print $2}')
848         local ADDR3=$(echo $ADDR | awk -F . '{print $1}')
849
850         test "$ADDR0" -gt 255 && fail=1
851         test "$ADDR1" -gt 255 && fail=1
852         test "$ADDR2" -gt 255 && fail=1
853         test "$ADDR3" -gt 255 && fail=1
854
855         test x"$fail" != x"0" && {
856                 #echo "IPv4: '$ADDR' is not a valid address"
857                 return 1;
858         }
859
860         return 0;
861 }
862
863 # iptables doesn't like being re-entered, so flock-wrap it.
864 iptables()
865 {
866         flock -w 30 $CTDB_VARDIR/iptables-ctdb.flock /sbin/iptables "$@"
867 }
868
869 ########################################################
870 # tickle handling
871 ########################################################
872
873 # Temporary directory for tickles.
874 tickledir="$CTDB_VARDIR/state/tickles"
875 mkdir -p "$tickledir"
876
877 update_tickles ()
878 {
879         _port="$1"
880
881         mkdir -p "$tickledir" # Just in case
882
883         # Who am I?
884         _pnn=$(ctdb pnn) ; _pnn=${_pnn#PNN:}
885
886         # What public IPs do I hold?
887         _ips=$(ctdb -Y ip | awk -F: -v pnn=$_pnn '$3 == pnn {print $2}')
888
889         # IPs as a regexp choice
890         _ipschoice="($(echo $_ips | sed -e 's/ /|/g' -e 's/\./\\\\./g'))"
891
892         # Record connections to our public IPs in a temporary file
893         _my_connections="${tickledir}/${_port}.connections"
894         rm -f "$_my_connections"
895         netstat -tn |
896         awk -v destpat="^${_ipschoice}:${_port}\$" \
897           '$1 == "tcp" && $6 == "ESTABLISHED" && $4 ~ destpat {print $5, $4}' |
898         sort >"$_my_connections"
899
900         # Record our current tickles in a temporary file
901         _my_tickles="${tickledir}/${_port}.tickles"
902         rm -f "$_my_tickles"
903         for _i in $_ips ; do
904                 ctdb -Y gettickles $_i $_port | 
905                 awk -F: 'NR > 1 { printf "%s:%s %s:%s\n", $2, $3, $4, $5 }'
906         done |
907         sort >"$_my_tickles"
908
909         # Add tickles for connections that we haven't already got tickles for
910         comm -23 "$_my_connections" "$_my_tickles" |
911         while read _src _dst ; do
912                 ctdb addtickle $_src $_dst
913         done
914
915         # Remove tickles for connections that are no longer there
916         comm -13 "$_my_connections" "$_my_tickles" |
917         while read _src _dst ; do
918                 ctdb deltickle $_src $_dst
919         done
920
921         rm -f "$_my_connections" "$_my_tickles" 
922 }
923
924 ########################################################
925 # load a site local config file
926 ########################################################
927
928 [ -n "$CTDB_RC_LOCAL" -a -x "$CTDB_RC_LOCAL" ] && {
929         . "$CTDB_RC_LOCAL"
930 }
931
932 [ -x $CTDB_BASE/rc.local ] && {
933         . $CTDB_BASE/rc.local
934 }
935
936 [ -d $CTDB_BASE/rc.local.d ] && {
937         for i in $CTDB_BASE/rc.local.d/* ; do
938                 [ -x "$i" ] && . "$i"
939         done
940 }
941
942 script_name="${0##*/}"       # basename
943 service_name="$script_name"  # default is just the script name
944 service_fail_limit=1
945 event_name="$1"