Eventscript functions: new function ctdb_check_counter().
[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 ctdb_check_counter () {
549     _msg="${1:-error}"  # "error"  - anything else is silent on fail
550     _op="${2:--ge}"  # an integer operator supported by test
551     _limit="${3:-${service_fail_limit}}"
552     shift 3
553     _ctdb_counter_common "$1"
554
555     # unary counting!
556     _size=$(stat -c "%s" "$_counter_file" 2>/dev/null || echo 0)
557     if [ $_size $_op $_limit ] ; then
558         if [ "$_msg" = "error" ] ; then
559             echo "ERROR: $_limit consecutive failures for $_service_name, marking node unhealthy"
560             exit 1              
561         else
562             return 1
563         fi
564     fi
565 }
566
567 ########################################################
568
569 ctdb_status_dir="$CTDB_VARDIR/status"
570 ctdb_fail_dir="$CTDB_VARDIR/failcount"
571
572 ctdb_setup_service_state_dir ()
573 {
574     service_state_dir="$CTDB_VARDIR/state/${1:-${service_name}}"
575     mkdir -p "$service_state_dir" || {
576         echo "Error creating state dir \"$service_state_dir\""
577         exit 1
578     }
579 }
580
581 ########################################################
582 # Managed status history, for auto-start/stop
583
584 ctdb_managed_dir="$CTDB_VARDIR/managed_history"
585
586 _ctdb_managed_common ()
587 {
588     _service_name="${1:-${service_name}}"
589     _ctdb_managed_file="$ctdb_managed_dir/$_service_name"
590 }
591
592 ctdb_service_managed ()
593 {
594     _ctdb_managed_common "$@"
595     mkdir -p "$ctdb_managed_dir"
596     touch "$_ctdb_managed_file"
597 }
598
599 ctdb_service_unmanaged ()
600 {
601     _ctdb_managed_common "$@"
602     rm -f "$_ctdb_managed_file"
603 }
604
605 is_ctdb_previously_managed_service ()
606 {
607     _ctdb_managed_common "$@"
608     [ -f "$_ctdb_managed_file" ]
609 }
610
611 ########################################################
612 # Check and set status
613
614 log_status_cat ()
615 {
616     echo "node is \"$1\", \"${script_name}\" reports problem: $(cat $2)"
617 }
618
619 ctdb_checkstatus ()
620 {
621     if [ -r "$ctdb_status_dir/$script_name/unhealthy" ] ; then
622         log_status_cat "unhealthy" "$ctdb_status_dir/$script_name/unhealthy"
623         return 1
624     elif [ -r "$ctdb_status_dir/$script_name/banned" ] ; then
625         log_status_cat "banned" "$ctdb_status_dir/$script_name/banned"
626         return 2
627     else
628         return 0
629     fi
630 }
631
632 ctdb_setstatus ()
633 {
634     d="$ctdb_status_dir/$script_name"
635     case "$1" in
636         unhealthy|banned)
637             mkdir -p "$d"
638             cat "$2" >"$d/$1"
639             ;;
640         *)
641             for i in "banned" "unhealthy" ; do
642                 rm -f "$d/$i"
643             done
644             ;;
645     esac
646 }
647
648 ##################################################################
649 # Reconfigure a service on demand
650
651 _ctdb_service_reconfigure_common ()
652 {
653     _d="$ctdb_status_dir/${1:-${service_name}}"
654     mkdir -p "$_d"
655     _ctdb_service_reconfigure_flag="$_d/reconfigure"
656 }
657
658 ctdb_service_needs_reconfigure ()
659 {
660     _ctdb_service_reconfigure_common "$@"
661     [ -e "$_ctdb_service_reconfigure_flag" ]
662 }
663
664 ctdb_service_set_reconfigure ()
665 {
666     _ctdb_service_reconfigure_common "$@"
667     >"$_ctdb_service_reconfigure_flag"
668 }
669
670 ctdb_service_unset_reconfigure ()
671 {
672     _ctdb_service_reconfigure_common "$@"
673     rm -f "$_ctdb_service_reconfigure_flag"
674 }
675
676 ctdb_service_reconfigure ()
677 {
678     echo "Reconfiguring service \"$service_name\"..."
679     ctdb_service_unset_reconfigure "$@"
680     service_reconfigure "$@" || return $?
681     ctdb_counter_init "$@"
682 }
683
684 # Default service_reconfigure() function.
685 service_reconfigure ()
686 {
687     service "${1:-$service_name}" restart
688 }
689
690 ctdb_service_check_reconfigure ()
691 {
692     [ "$event_name" = "monitor" ] || return 0
693
694     if ctdb_service_needs_reconfigure "$@" ; then
695         ctdb_service_reconfigure "$@"
696         exit 0
697     fi
698 }
699
700 ##################################################################
701 # Does CTDB manage this service? - and associated auto-start/stop
702
703 ctdb_compat_managed_service ()
704 {
705     if [ "$1" = "yes" -a "$2" = "$_service_name" ] ; then
706         CTDB_MANAGED_SERVICES="$CTDB_MANAGED_SERVICES $2"
707     fi
708 }
709
710 is_ctdb_managed_service ()
711 {
712     _service_name="${1:-${service_name}}"
713
714     # $t is used just for readability and to allow better accurate
715     # matching via leading/trailing spaces
716     t=" $CTDB_MANAGED_SERVICES "
717
718     # Return 0 if "<space>$_service_name<space>" appears in $t
719     if [ "${t#* ${_service_name} }" != "${t}" ] ; then
720         return 0
721     fi
722
723     # If above didn't match then update $CTDB_MANAGED_SERVICES for
724     # backward compatibility and try again.
725     ctdb_compat_managed_service "$CTDB_MANAGES_VSFTPD"   "vsftpd"
726     ctdb_compat_managed_service "$CTDB_MANAGES_SAMBA"    "samba"
727     ctdb_compat_managed_service "$CTDB_MANAGES_SCP"      "scp"
728     ctdb_compat_managed_service "$CTDB_MANAGES_WINBIND"  "winbind"
729     ctdb_compat_managed_service "$CTDB_MANAGES_HTTPD"    "httpd"
730     ctdb_compat_managed_service "$CTDB_MANAGES_ISCSI"    "iscsi"
731     ctdb_compat_managed_service "$CTDB_MANAGES_CLAMD"    "clamd"
732     ctdb_compat_managed_service "$CTDB_MANAGES_NFS"      "nfs"
733     ctdb_compat_managed_service "$CTDB_MANAGES_NFS"      "nfs-ganesha-gpfs"
734
735     t=" $CTDB_MANAGED_SERVICES "
736
737     # Return 0 if "<space>$_service_name<space>" appears in $t
738     [ "${t#* ${_service_name} }" != "${t}" ]
739 }
740
741 ctdb_start_stop_service ()
742 {
743     _service_name="${1:-${service_name}}"
744
745     [ "$event_name" = "monitor" ] || return 0
746
747     if is_ctdb_managed_service "$_service_name" ; then
748         if ! is_ctdb_previously_managed_service "$_service_name" ; then
749             echo "Starting service \"$_service_name\" - now managed"
750             ctdb_service_start "$_service_name"
751             exit $?
752         fi
753     else
754         if is_ctdb_previously_managed_service "$_service_name" ; then
755             echo "Stopping service \"$_service_name\" - no longer managed"
756             ctdb_service_stop "$_service_name"
757             exit $?
758         fi
759     fi
760 }
761
762 ctdb_service_start ()
763 {
764     # The service is marked managed if we've ever tried to start it.
765     ctdb_service_managed "$@"
766
767     # Here we only want $1.  If no argument is passed then
768     # service_start needs to know.
769     service_start "$@" || return $?
770
771     ctdb_counter_init "$@"
772 }
773
774 ctdb_service_stop ()
775 {
776     ctdb_service_unmanaged "$@"
777     service_stop "$@"
778 }
779
780 # Default service_start() and service_stop() functions.
781  
782 # These may be overridden in an eventscript.  When overriding, the
783 # following convention must be followed.  If these functions are
784 # called with no arguments then they may use internal logic to
785 # determine whether the service is managed and, therefore, whether
786 # they should take any action.  However, if the service name is
787 # specified as an argument then an attempt must be made to start or
788 # stop the service.  This is because the auto-start/stop code calls
789 # them with the service name as an argument.
790 service_start ()
791 {
792     service "${1:-${service_name}}" start
793 }
794
795 service_stop ()
796 {
797     service "${1:-${service_name}}" stop
798 }
799
800 ##################################################################
801
802 ctdb_standard_event_handler ()
803 {
804     case "$1" in
805         status)
806             ctdb_checkstatus
807             exit
808             ;;
809         setstatus)
810             shift
811             ctdb_setstatus "$@"
812             exit
813             ;;
814     esac
815 }
816
817 ipv4_host_addr_to_net_addr()
818 {
819         local HOST=$1
820         local MASKBITS=$2
821
822         local HOST0=$(echo $HOST | awk -F . '{print $4}')
823         local HOST1=$(echo $HOST | awk -F . '{print $3}')
824         local HOST2=$(echo $HOST | awk -F . '{print $2}')
825         local HOST3=$(echo $HOST | awk -F . '{print $1}')
826
827         local HOST_NUM=$(( $HOST0 + $HOST1 * 256 + $HOST2 * (256 ** 2) + $HOST3 * (256 ** 3) ))
828
829         local MASK_NUM=$(( ( (2**32 - 1) * (2**(32 - $MASKBITS)) ) & (2**32 - 1) ))
830
831         local NET_NUM=$(( $HOST_NUM & $MASK_NUM))
832
833         local NET0=$(( $NET_NUM & 255 ))
834         local NET1=$(( ($NET_NUM & (255 * 256)) / 256 ))
835         local NET2=$(( ($NET_NUM & (255 * 256**2)) / 256**2 ))
836         local NET3=$(( ($NET_NUM & (255 * 256**3)) / 256**3 ))
837
838         echo "$NET3.$NET2.$NET1.$NET0"
839 }
840
841 ipv4_maskbits_to_net_mask()
842 {
843         local MASKBITS=$1
844
845         local MASK_NUM=$(( ( (2**32 - 1) * (2**(32 - $MASKBITS)) ) & (2**32 - 1) ))
846
847         local MASK0=$(( $MASK_NUM & 255 ))
848         local MASK1=$(( ($MASK_NUM & (255 * 256)) / 256 ))
849         local MASK2=$(( ($MASK_NUM & (255 * 256**2)) / 256**2 ))
850         local MASK3=$(( ($MASK_NUM & (255 * 256**3)) / 256**3 ))
851
852         echo "$MASK3.$MASK2.$MASK1.$MASK0"
853 }
854
855 ipv4_is_valid_addr()
856 {
857         local ADDR=$1
858         local fail=0
859
860         local N=`echo $ADDR | sed -e 's/[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*//'`
861         test -n "$N" && fail=1
862
863         local ADDR0=$(echo $ADDR | awk -F . '{print $4}')
864         local ADDR1=$(echo $ADDR | awk -F . '{print $3}')
865         local ADDR2=$(echo $ADDR | awk -F . '{print $2}')
866         local ADDR3=$(echo $ADDR | awk -F . '{print $1}')
867
868         test "$ADDR0" -gt 255 && fail=1
869         test "$ADDR1" -gt 255 && fail=1
870         test "$ADDR2" -gt 255 && fail=1
871         test "$ADDR3" -gt 255 && fail=1
872
873         test x"$fail" != x"0" && {
874                 #echo "IPv4: '$ADDR' is not a valid address"
875                 return 1;
876         }
877
878         return 0;
879 }
880
881 # iptables doesn't like being re-entered, so flock-wrap it.
882 iptables()
883 {
884         flock -w 30 $CTDB_VARDIR/iptables-ctdb.flock /sbin/iptables "$@"
885 }
886
887 ########################################################
888 # tickle handling
889 ########################################################
890
891 # Temporary directory for tickles.
892 tickledir="$CTDB_VARDIR/state/tickles"
893 mkdir -p "$tickledir"
894
895 update_tickles ()
896 {
897         _port="$1"
898
899         mkdir -p "$tickledir" # Just in case
900
901         # Who am I?
902         _pnn=$(ctdb pnn) ; _pnn=${_pnn#PNN:}
903
904         # What public IPs do I hold?
905         _ips=$(ctdb -Y ip | awk -F: -v pnn=$_pnn '$3 == pnn {print $2}')
906
907         # IPs as a regexp choice
908         _ipschoice="($(echo $_ips | sed -e 's/ /|/g' -e 's/\./\\\\./g'))"
909
910         # Record connections to our public IPs in a temporary file
911         _my_connections="${tickledir}/${_port}.connections"
912         rm -f "$_my_connections"
913         netstat -tn |
914         awk -v destpat="^${_ipschoice}:${_port}\$" \
915           '$1 == "tcp" && $6 == "ESTABLISHED" && $4 ~ destpat {print $5, $4}' |
916         sort >"$_my_connections"
917
918         # Record our current tickles in a temporary file
919         _my_tickles="${tickledir}/${_port}.tickles"
920         rm -f "$_my_tickles"
921         for _i in $_ips ; do
922                 ctdb -Y gettickles $_i $_port | 
923                 awk -F: 'NR > 1 { printf "%s:%s %s:%s\n", $2, $3, $4, $5 }'
924         done |
925         sort >"$_my_tickles"
926
927         # Add tickles for connections that we haven't already got tickles for
928         comm -23 "$_my_connections" "$_my_tickles" |
929         while read _src _dst ; do
930                 ctdb addtickle $_src $_dst
931         done
932
933         # Remove tickles for connections that are no longer there
934         comm -13 "$_my_connections" "$_my_tickles" |
935         while read _src _dst ; do
936                 ctdb deltickle $_src $_dst
937         done
938
939         rm -f "$_my_connections" "$_my_tickles" 
940 }
941
942 ########################################################
943 # load a site local config file
944 ########################################################
945
946 [ -n "$CTDB_RC_LOCAL" -a -x "$CTDB_RC_LOCAL" ] && {
947         . "$CTDB_RC_LOCAL"
948 }
949
950 [ -x $CTDB_BASE/rc.local ] && {
951         . $CTDB_BASE/rc.local
952 }
953
954 [ -d $CTDB_BASE/rc.local.d ] && {
955         for i in $CTDB_BASE/rc.local.d/* ; do
956                 [ -x "$i" ] && . "$i"
957         done
958 }
959
960 script_name="${0##*/}"       # basename
961 service_name="$script_name"  # default is just the script name
962 service_fail_limit=1
963 event_name="$1"