ctdb-scripts: Fix CTDB_DBDIR=tmpfs support
[samba.git] / ctdb / config / functions
1 # Hey Emacs, this is a -*- shell-script -*- !!!
2
3 # utility functions for ctdb event scripts
4
5 if [ -z "$CTDB_BASE" ] ; then
6     echo 'CTDB_BASE unset in CTDB functions file'
7     exit 1
8 fi
9
10 CTDB_VARDIR="/usr/local/var/lib/ctdb"
11 ctdb_rundir="/usr/local/var/run/ctdb"
12
13 # Only (and always) override these variables in test code
14
15 if [ -z "$CTDB_SCRIPT_VARDIR" ] ; then
16     CTDB_SCRIPT_VARDIR="/usr/local/var/lib/ctdb/state"
17 fi
18
19 if [ -z "$CTDB_SYS_ETCDIR" ] ; then
20     CTDB_SYS_ETCDIR="/etc"
21 fi
22
23 #######################################
24 # pull in a system config file, if any
25
26 rewrite_ctdb_options ()
27 {
28     case "$CTDB_DBDIR" in
29         tmpfs|tmpfs:*)
30             _opts_defaults="mode=700"
31             # Get any extra options specified after colon
32             if [ "$CTDB_DBDIR" = "tmpfs" ] ; then
33                 _opts=""
34             else
35                 _opts="${CTDB_DBDIR#tmpfs:}"
36             fi
37             # This is an internal variable, only used by ctdbd_wrapper.
38             # It is OK to repeat mount options - last value wins
39             CTDB_DBDIR_TMPFS_OPTIONS="${_opts_defaults}${_opts:+,}${_opts}"
40
41             CTDB_DBDIR="${ctdb_rundir}/CTDB_DBDIR"
42             ;;
43         *)
44             CTDB_DBDIR_TMPFS_OPTIONS=""
45     esac
46 }
47
48 _loadconfig() {
49
50     if [ -z "$1" ] ; then
51         foo="${service_config:-${service_name}}"
52         if [ -n "$foo" ] ; then
53             loadconfig "$foo"
54             return
55         fi
56     fi
57
58     if [ "$1" != "ctdb" ] ; then
59         loadconfig "ctdb"
60     fi
61
62     if [ -z "$1" ] ; then
63         return
64     fi
65
66     if [ -f $CTDB_SYS_ETCDIR/sysconfig/$1 ]; then
67         . $CTDB_SYS_ETCDIR/sysconfig/$1
68     elif [ -f $CTDB_SYS_ETCDIR/default/$1 ]; then
69         . $CTDB_SYS_ETCDIR/default/$1
70     elif [ -f $CTDB_BASE/sysconfig/$1 ]; then
71         . $CTDB_BASE/sysconfig/$1
72     fi
73
74     if [ "$1" = "ctdb" ] ; then
75         _config="${CTDB_BASE}/ctdbd.conf"
76         if [ -r "$_config" ] ; then
77             . "$_config"
78         fi
79         rewrite_ctdb_options
80     fi
81 }
82
83 loadconfig () {
84     _loadconfig "$@"
85 }
86
87 ##############################################################
88
89 # CTDB_SCRIPT_DEBUGLEVEL can be overwritten by setting it in a
90 # configuration file.
91 debug ()
92 {
93     if [ ${CTDB_SCRIPT_DEBUGLEVEL:-2} -ge 4 ] ; then
94         # If there are arguments then echo them.  Otherwise expect to
95         # use stdin, which allows us to pass lots of debug using a
96         # here document.
97         if [ -n "$1" ] ; then
98             echo "DEBUG: $*"
99         else
100             sed -e 's@^@DEBUG: @'
101         fi
102     else
103         if [ -z "$1" ] ; then
104             cat >/dev/null
105         fi
106     fi
107 }
108
109 die ()
110 {
111     _msg="$1"
112     _rc="${2:-1}"
113
114     echo "$_msg"
115     exit $_rc
116 }
117
118 # Log given message or stdin to either syslog or a CTDB log file
119 # $1 is the tag passed to logger if syslog is in use.
120 script_log ()
121 {
122     _tag="$1" ; shift
123
124     case "$CTDB_LOGGING" in
125         file:*|"")
126             if [ -n "$CTDB_LOGGING" ] ; then
127                 _file="${CTDB_LOGGING#file:}"
128             else
129                 _file="/usr/local/var/log/log.ctdb"
130             fi
131             {
132                 if [ -n "$*" ] ; then
133                     echo "$*"
134                 else
135                     cat
136                 fi
137             } >>"$_file"
138             ;;
139         *)
140             # Handle all syslog:* variants here too.  There's no tool to do
141             # the lossy things, so just use logger.
142             logger -t "ctdbd: ${_tag}" $*
143             ;;
144     esac
145 }
146
147 # When things are run in the background in an eventscript then logging
148 # output might get lost.  This is the "solution".  :-)
149 background_with_logging ()
150 {
151     (
152         "$@" 2>&1 </dev/null |
153         script_log "${script_name}&"
154     )&
155
156     return 0
157 }
158
159 ##############################################################
160 # check number of args for different events
161 ctdb_check_args ()
162 {
163     case "$1" in
164         takeip|releaseip)
165             if [ $# != 4 ]; then
166                 echo "ERROR: must supply interface, IP and maskbits"
167                 exit 1
168             fi
169             ;;
170         updateip)
171             if [ $# != 5 ]; then
172                 echo "ERROR: must supply old interface, new interface, IP and maskbits"
173                 exit 1
174             fi
175             ;;
176     esac
177 }
178
179 ##############################################################
180 # determine on what type of system (init style) we are running
181 detect_init_style()
182 {
183     # only do detection if not already set:
184     [ -z "$CTDB_INIT_STYLE" ] || return
185
186     if [ -x /sbin/startproc ]; then
187         CTDB_INIT_STYLE="suse"
188     elif [ -x /sbin/start-stop-daemon ]; then
189         CTDB_INIT_STYLE="debian"
190     else
191         CTDB_INIT_STYLE="redhat"
192     fi
193 }
194
195 ######################################################
196 # simulate /sbin/service on platforms that don't have it
197 # _service() makes it easier to hook the service() function for
198 # testing.
199 _service ()
200 {
201   _service_name="$1"
202   _op="$2"
203
204   # do nothing, when no service was specified
205   [ -z "$_service_name" ] && return
206
207   if [ -x /sbin/service ]; then
208       $_nice /sbin/service "$_service_name" "$_op"
209   elif [ -x /usr/sbin/service ]; then
210       $_nice /usr/sbin/service "$_service_name" "$_op"
211   elif [ -x $CTDB_SYS_ETCDIR/init.d/$_service_name ]; then
212       $_nice $CTDB_SYS_ETCDIR/init.d/$_service_name "$_op"
213   elif [ -x $CTDB_SYS_ETCDIR/rc.d/init.d/$_service_name ]; then
214       $_nice $CTDB_SYS_ETCDIR/rc.d/init.d/$_service_name "$_op"
215   fi
216 }
217
218 service()
219 {
220     _nice=""
221     _service "$@"
222 }
223
224 ######################################################
225 # simulate /sbin/service (niced) on platforms that don't have it
226 nice_service()
227 {
228     _nice="nice"
229     _service "$@"
230 }
231
232 ######################################################
233 # Cached retrieval of PNN from local node.  This never changes so why
234 # open a client connection to the server each time this is needed?
235 # This sets $pnn - this avoid an unnecessary subprocess.
236 ctdb_get_pnn ()
237 {
238     _pnn_file="${CTDB_SCRIPT_VARDIR}/my-pnn"
239     if [ ! -f "$_pnn_file" ] ; then
240         ctdb pnn | sed -e 's@.*:@@' >"$_pnn_file"
241     fi
242
243     read pnn <"$_pnn_file"
244 }
245
246 ######################################################
247 # wrapper around /proc/ settings to allow them to be hooked
248 # for testing
249 # 1st arg is relative path under /proc/, 2nd arg is value to set
250 set_proc ()
251 {
252     echo "$2" >"/proc/$1"
253 }
254
255 set_proc_maybe ()
256 {
257     if [ -w "/proc/$1" ] ; then
258         set_proc "$1" "$2"
259     fi
260 }
261
262 ######################################################
263 # wrapper around getting file contents from /proc/ to allow
264 # this to be hooked for testing
265 # 1st arg is relative path under /proc/
266 get_proc ()
267 {
268     cat "/proc/$1"
269 }
270
271 ######################################################
272 # Print up to $_max kernel stack traces for processes named $_program
273 program_stack_traces ()
274 {
275     _prog="$1"
276     _max="${2:-1}"
277
278     _count=1
279     for _pid in $(pidof "$_prog") ; do
280         [ $_count -le $_max ] || break
281
282         # Do this first to avoid racing with process exit
283         _stack=$(get_proc "${_pid}/stack" 2>/dev/null)
284         if [ -n "$_stack" ] ; then
285             echo "Stack trace for ${_prog}[${_pid}]:"
286             echo "$_stack"
287             _count=$(($_count + 1))
288         fi
289     done
290 }
291
292 ######################################################
293 # Ensure $service_name is set
294 assert_service_name ()
295 {
296     [ -n "$service_name" ] || die "INTERNAL ERROR: \$service_name not set"
297 }
298
299 ######################################################
300 # check a set of directories is available
301 # return 1 on a missing directory
302 # directories are read from stdin
303 ######################################################
304 ctdb_check_directories_probe()
305 {
306     while IFS="" read d ; do
307         case "$d" in
308             *%*)
309                 continue
310                 ;;
311             *)
312                 [ -d "${d}/." ] || return 1
313         esac
314     done
315 }
316
317 ######################################################
318 # check a set of directories is available
319 # directories are read from stdin
320 ######################################################
321 ctdb_check_directories()
322 {
323     ctdb_check_directories_probe || {
324         echo "ERROR: $service_name directory \"$d\" not available"
325         exit 1
326     }
327 }
328
329 ######################################################
330 # check a set of tcp ports
331 # usage: ctdb_check_tcp_ports <ports...>
332 ######################################################
333
334 # This flag file is created when a service is initially started.  It
335 # is deleted the first time TCP port checks for that service succeed.
336 # Until then ctdb_check_tcp_ports() prints a more subtle "error"
337 # message if a port check fails.
338 _ctdb_check_tcp_common ()
339 {
340     assert_service_name
341     _d="${CTDB_SCRIPT_VARDIR}/failcount"
342     _ctdb_service_started_file="${_d}/${service_name}.started"
343 }
344
345 ctdb_check_tcp_init ()
346 {
347     _ctdb_check_tcp_common
348     mkdir -p "${_ctdb_service_started_file%/*}" # dirname
349     touch "$_ctdb_service_started_file"
350 }
351
352 # Check whether something is listening on all of the given TCP ports
353 # using the "ctdb checktcpport" command.
354 ctdb_check_tcp_ports()
355 {
356     if [ -z "$1" ] ; then
357         echo "INTERNAL ERROR: ctdb_check_tcp_ports - no ports specified"
358         exit 1
359     fi
360
361     for _p ; do  # process each function argument (port)
362         _cmd="ctdb checktcpport $_p"
363         _out=$($_cmd 2>&1)
364         _ret=$?
365         case "$_ret" in
366             0)
367                 _ctdb_check_tcp_common
368                 if [ ! -f "$_ctdb_service_started_file" ] ; then
369                     echo "ERROR: $service_name tcp port $_p is not responding"
370                     debug "\"ctdb checktcpport $_p\" was able to bind to port"
371                 else
372                     echo "INFO: $service_name tcp port $_p is not responding"
373                 fi
374
375                 return 1
376                 ;;
377             98)
378                 # Couldn't bind, something already listening, next port...
379                 continue
380                 ;;
381             *)
382                 echo "ERROR: unexpected error running \"ctdb checktcpport\""
383                 debug <<EOF
384 ctdb checktcpport (exited with $_ret) with output:
385 $_out"
386 EOF
387                 return $_ret
388         esac
389     done
390
391     # All ports listening
392     _ctdb_check_tcp_common
393     rm -f "$_ctdb_service_started_file"
394     return 0
395 }
396
397 ######################################################
398 # check a unix socket
399 # usage: ctdb_check_unix_socket SERVICE_NAME <socket_path>
400 ######################################################
401 ctdb_check_unix_socket() {
402     socket_path="$1"
403     [ -z "$socket_path" ] && return
404
405     if ! netstat --unix -a -n | grep -q "^unix.*LISTEN.*${socket_path}$"; then
406         echo "ERROR: $service_name socket $socket_path not found"
407         return 1
408     fi
409 }
410
411 ######################################################
412 # check a command returns zero status
413 # usage: ctdb_check_command <command>
414 ######################################################
415 ctdb_check_command ()
416 {
417     _out=$("$@" 2>&1) || {
418         echo "ERROR: $* returned error"
419         echo "$_out" | debug
420         exit 1
421     }
422 }
423
424 ################################################
425 # kill off any TCP connections with the given IP
426 ################################################
427 kill_tcp_connections ()
428 {
429     _ip="$1"
430
431     _oneway=false
432     if [ "$2" = "oneway" ] ; then
433         _oneway=true
434     fi
435
436     get_tcp_connections_for_ip "$_ip" | {
437         _killcount=0
438         _connections=""
439         _nl="
440 "
441         while read _dst _src; do
442             _destport="${_dst##*:}"
443             __oneway=$_oneway
444             case $_destport in
445                 # we only do one-way killtcp for CIFS
446                 139|445) __oneway=true ;;
447             esac
448
449             echo "Killing TCP connection $_src $_dst"
450             _connections="${_connections}${_nl}${_src} ${_dst}"
451             if ! $__oneway ; then
452                 _connections="${_connections}${_nl}${_dst} ${_src}"
453             fi
454
455             _killcount=$(($_killcount + 1))
456         done
457
458         if [ $_killcount -eq 0 ] ; then
459             return
460         fi
461
462         echo "$_connections" | ctdb killtcp || {
463             echo "Failed to send killtcp control"
464             return
465         }
466
467         _count=0
468         while : ; do
469             _remaining=$(get_tcp_connections_for_ip $_ip | wc -l)
470
471             if [ $_remaining -eq 0 ] ; then
472                 echo "Killed $_killcount TCP connections to released IP $_ip"
473                 return
474             fi
475
476             _count=$(($_count + 1))
477             if [ $_count -gt 3 ] ; then
478                 echo "Timed out killing tcp connections for IP $_ip ($_remaining remaining)"
479                 return
480             fi
481
482             echo "Waiting for $_remaining connections to be killed for IP $_ip"
483             sleep 1
484         done
485     }
486 }
487
488 ##################################################################
489 # kill off the local end for any TCP connections with the given IP
490 ##################################################################
491 kill_tcp_connections_local_only ()
492 {
493     kill_tcp_connections "$1" "oneway"
494 }
495
496 ##################################################################
497 # tickle any TCP connections with the given IP
498 ##################################################################
499 tickle_tcp_connections ()
500 {
501     _ip="$1"
502
503     get_tcp_connections_for_ip "$_ip" |
504     {
505         _failed=false
506
507         while read dest src; do
508             echo "Tickle TCP connection $src $dest"
509             ctdb tickle $src $dest >/dev/null 2>&1 || _failed=true
510             echo "Tickle TCP connection $dest $src"
511             ctdb tickle $dest $src >/dev/null 2>&1 || _failed=true
512         done
513
514         if $_failed ; then
515             echo "Failed to send tickle control"
516         fi
517     }
518 }
519
520 get_tcp_connections_for_ip ()
521 {
522     _ip="$1"
523
524     netstat -tn | awk -v ip=$_ip \
525         'index($1, "tcp") == 1 && \
526          (index($4, ip ":") == 1 || index($4, "::ffff:" ip ":") == 1) \
527          && $6 == "ESTABLISHED" \
528          {print $4" "$5}'
529 }
530
531 ########################################################
532
533 add_ip_to_iface ()
534 {
535     _iface=$1
536     _ip=$2
537     _maskbits=$3
538
539     # Ensure interface is up
540     ip link set "$_iface" up || \
541         die "Failed to bringup interface $_iface"
542
543     # Only need to define broadcast for IPv4
544     case "$ip" in
545         *:*) _bcast=""      ;;
546         *)   _bcast="brd +" ;;
547     esac
548
549     ip addr add "$_ip/$_maskbits" $_bcast dev "$_iface" || {
550         echo "Failed to add $_ip/$_maskbits on dev $_iface"
551         return 1
552     }
553
554     # Wait 5 seconds for IPv6 addresses to stop being tentative...
555     if [ -z "$_bcast" ] ; then
556         for _x in $(seq 1 10) ; do
557             ip addr show to "${_ip}/128" | grep -q "tentative" || break
558             sleep 0.5
559         done
560
561         # If the address was a duplicate then it won't be on the
562         # interface so flag an error.
563         _t=$(ip addr show to "${_ip}/128")
564         case "$_t" in
565             "")
566                 echo "Failed to add $_ip/$_maskbits on dev $_iface"
567                 return 1
568                 ;;
569             *tentative*|*dadfailed*)
570                 echo "Failed to add $_ip/$_maskbits on dev $_iface"
571                 ip addr del "$_ip/$_maskbits" dev "$_iface"
572                 return 1
573                 ;;
574         esac
575     fi
576 }
577
578 delete_ip_from_iface()
579 {
580     _iface=$1
581     _ip=$2
582     _maskbits=$3
583
584     # This could be set globally for all interfaces but it is probably
585     # better to avoid surprises, so limit it the interfaces where CTDB
586     # has public IP addresses.  There isn't anywhere else convenient
587     # to do this so just set it each time.  This is much cheaper than
588     # remembering and re-adding secondaries.
589     set_proc "sys/net/ipv4/conf/${_iface}/promote_secondaries" 1
590
591     ip addr del "$_ip/$_maskbits" dev "$_iface" || {
592         echo "Failed to del $_ip on dev $_iface"
593         return 1
594     }
595 }
596
597 # If the given IP is hosted then print 2 items: maskbits and iface
598 ip_maskbits_iface ()
599 {
600     _addr="$1"
601
602     case "$_addr" in
603         *:*) _family="inet6" ; _bits=128 ;;
604         *)   _family="inet"  ; _bits=32  ;;
605     esac
606
607     ip addr show to "${_addr}/${_bits}" 2>/dev/null | \
608         awk -v family="${_family}" \
609             'NR == 1 { iface = $2; sub(":$", "", iface) ; \
610                        sub("@.*", "", iface) } \
611              $1 ~ /inet/ { mask = $2; sub(".*/", "", mask); \
612                            print mask, iface, family }'
613 }
614
615 drop_ip ()
616 {
617     _addr="${1%/*}"  # Remove optional maskbits
618
619     set -- $(ip_maskbits_iface $_addr)
620     if [ -n "$1" ] ; then
621         _maskbits="$1"
622         _iface="$2"
623         echo "Removing public address $_addr/$_maskbits from device $_iface"
624         delete_ip_from_iface $_iface $_addr $_maskbits >/dev/null 2>&1
625     fi
626 }
627
628 drop_all_public_ips ()
629 {
630     while read _ip _x ; do
631         drop_ip "$_ip"
632     done <"${CTDB_PUBLIC_ADDRESSES:-/dev/null}"
633 }
634
635 flush_route_cache ()
636 {
637     set_proc_maybe sys/net/ipv4/route/flush 1
638     set_proc_maybe sys/net/ipv6/route/flush 1
639 }
640
641 ########################################################
642 # Simple counters
643 _ctdb_counter_common () {
644     _service_name="${1:-${service_name:-${script_name}}}"
645     _counter_file="${CTDB_SCRIPT_VARDIR}/failcount/${_service_name}"
646     mkdir -p "${_counter_file%/*}" # dirname
647 }
648 ctdb_counter_init () {
649     _ctdb_counter_common "$1"
650
651     >"$_counter_file"
652 }
653 ctdb_counter_incr () {
654     _ctdb_counter_common "$1"
655
656     # unary counting!
657     echo -n 1 >> "$_counter_file"
658 }
659 ctdb_counter_get () {
660     _ctdb_counter_common "$1"
661     # unary counting!
662     stat -c "%s" "$_counter_file" 2>/dev/null || echo 0
663 }
664 ctdb_check_counter () {
665     _msg="${1:-error}"  # "error"  - anything else is silent on fail
666     _op="${2:--ge}"  # an integer operator supported by test
667     _limit="${3:-${service_fail_limit}}"
668     shift 3
669
670     _size=$(ctdb_counter_get "$1")
671
672     _hit=false
673     if [ "$_op" != "%" ] ; then
674         if [ $_size $_op $_limit ] ; then
675             _hit=true
676         fi
677     else
678         if [ $(($_size $_op $_limit)) -eq 0 ] ; then
679             _hit=true
680         fi
681     fi
682     if $_hit ; then
683         if [ "$_msg" = "error" ] ; then
684             echo "ERROR: $_size consecutive failures for $_service_name, marking node unhealthy"
685             exit 1              
686         else
687             return 1
688         fi
689     fi
690 }
691
692 ########################################################
693
694 ctdb_setup_service_state_dir ()
695 {
696     service_state_dir="${CTDB_SCRIPT_VARDIR}/service_state/${1:-${service_name}}"
697     mkdir -p "$service_state_dir" || {
698         echo "Error creating state dir \"$service_state_dir\""
699         exit 1
700     }
701 }
702
703 ########################################################
704 # Managed status history, for auto-start/stop
705
706 _ctdb_managed_common ()
707 {
708     _ctdb_managed_file="${CTDB_SCRIPT_VARDIR}/managed_history/${service_name}"
709 }
710
711 ctdb_service_managed ()
712 {
713     _ctdb_managed_common
714     mkdir -p "${_ctdb_managed_file%/*}" # dirname
715     touch "$_ctdb_managed_file"
716 }
717
718 ctdb_service_unmanaged ()
719 {
720     _ctdb_managed_common
721     rm -f "$_ctdb_managed_file"
722 }
723
724 is_ctdb_previously_managed_service ()
725 {
726     _ctdb_managed_common
727     [ -f "$_ctdb_managed_file" ]
728 }
729
730 ##################################################################
731 # Reconfigure a service on demand
732
733 _ctdb_service_reconfigure_common ()
734 {
735     _d="${CTDB_SCRIPT_VARDIR}/service_status/${service_name}"
736     mkdir -p "$_d"
737     _ctdb_service_reconfigure_flag="$_d/reconfigure"
738 }
739
740 ctdb_service_needs_reconfigure ()
741 {
742     _ctdb_service_reconfigure_common
743     [ -e "$_ctdb_service_reconfigure_flag" ]
744 }
745
746 ctdb_service_set_reconfigure ()
747 {
748     _ctdb_service_reconfigure_common
749     >"$_ctdb_service_reconfigure_flag"
750 }
751
752 ctdb_service_unset_reconfigure ()
753 {
754     _ctdb_service_reconfigure_common
755     rm -f "$_ctdb_service_reconfigure_flag"
756 }
757
758 ctdb_service_reconfigure ()
759 {
760     echo "Reconfiguring service \"${service_name}\"..."
761     ctdb_service_unset_reconfigure
762     service_reconfigure || return $?
763     ctdb_counter_init
764 }
765
766 # Default service_reconfigure() function does nothing.
767 service_reconfigure ()
768 {
769     :
770 }
771
772 ctdb_reconfigure_take_lock ()
773 {
774     _ctdb_service_reconfigure_common
775     _lock="${_d}/reconfigure_lock"
776     mkdir -p "${_lock%/*}" # dirname
777     touch "$_lock"
778
779     (
780         flock 0
781         # This is overkill but will work if we need to extend this to
782         # allow certain events to run multiple times in parallel
783         # (e.g. takeip) and write multiple PIDs to the file.
784         read _locker_event 
785         if [ -n "$_locker_event" ] ; then
786             while read _pid ; do
787                 if [ -n "$_pid" -a "$_pid" != $$ ] && \
788                     kill -0 "$_pid" 2>/dev/null ; then
789                     exit 1
790                 fi
791             done
792         fi
793
794         printf "%s\n%s\n" "$event_name" $$ >"$_lock"
795         exit 0
796     ) <"$_lock"
797 }
798
799 ctdb_reconfigure_release_lock ()
800 {
801     _ctdb_service_reconfigure_common
802     _lock="${_d}/reconfigure_lock"
803
804     rm -f "$_lock"
805 }
806
807 ctdb_replay_monitor_status ()
808 {
809     echo "Replaying previous status for this script due to reconfigure..."
810     # Leading separator ('|') is missing in some versions...
811     _out=$(ctdb scriptstatus -X | grep -E "^\|?monitor\|${script_name}\|")
812     # Output looks like this:
813     # |monitor|60.nfs|1|ERROR|1314764004.030861|1314764004.035514|foo bar|
814     # This is the cheapest way of getting fields in the middle.
815     set -- $(IFS="|" ; echo $_out)
816     _code="$3"
817     _status="$4"
818     # The error output field can include colons so we'll try to
819     # preserve them.  The weak checking at the beginning tries to make
820     # this work for both broken (no leading '|') and fixed output.
821     _out="${_out%|}"
822     _err_out="${_out#*monitor|${script_name}|*|*|*|*|}"
823     case "$_status" in
824         OK) : ;;  # Do nothing special.
825         TIMEDOUT)
826             # Recast this as an error, since we can't exit with the
827             # correct negative number.
828             _code=1
829             _err_out="[Replay of TIMEDOUT scriptstatus - note incorrect return code.] ${_err_out}"
830             ;;
831         DISABLED)
832             # Recast this as an OK, since we can't exit with the
833             # correct negative number.
834             _code=0
835             _err_out="[Replay of DISABLED scriptstatus - note incorrect return code.] ${_err_out}"
836             ;;
837         *) : ;;  # Must be ERROR, do nothing special.
838     esac
839     if [ -n "$_err_out" ] ; then
840         echo "$_err_out"
841     fi
842     exit $_code
843 }
844
845 ctdb_service_check_reconfigure ()
846 {
847     assert_service_name
848
849     # We only care about some events in this function.  For others we
850     # return now.
851     case "$event_name" in
852         monitor|ipreallocated|reconfigure) : ;;
853         *) return 0 ;;
854     esac
855
856     if ctdb_reconfigure_take_lock ; then
857         # No events covered by this function are running, so proceed
858         # with gay abandon.
859         case "$event_name" in
860             reconfigure)
861                 (ctdb_service_reconfigure)
862                 exit $?
863                 ;;
864             ipreallocated)
865                 if ctdb_service_needs_reconfigure ; then
866                     ctdb_service_reconfigure
867                 fi
868                 ;;
869         esac
870
871         ctdb_reconfigure_release_lock
872     else
873         # Somebody else is running an event we don't want to collide
874         # with.  We proceed with caution.
875         case "$event_name" in
876             reconfigure)
877                 # Tell whoever called us to retry.
878                 exit 2
879                 ;;
880             ipreallocated)
881                 # Defer any scheduled reconfigure and just run the
882                 # rest of the ipreallocated event, as per the
883                 # eventscript.  There's an assumption here that the
884                 # event doesn't depend on any scheduled reconfigure.
885                 # This is true in the current code.
886                 return 0
887                 ;;
888             monitor)
889                 # There is most likely a reconfigure in progress so
890                 # the service is possibly unstable.  As above, we
891                 # defer any scheduled reconfigured.  We also replay
892                 # the previous monitor status since that's the best
893                 # information we have.
894                 ctdb_replay_monitor_status
895                 ;;
896         esac
897     fi
898 }
899
900 ##################################################################
901 # Does CTDB manage this service? - and associated auto-start/stop
902
903 ctdb_compat_managed_service ()
904 {
905     if [ "$1" = "yes" -a "$2" = "$service_name" ] ; then
906         CTDB_MANAGED_SERVICES="$CTDB_MANAGED_SERVICES $2"
907     fi
908 }
909
910 is_ctdb_managed_service ()
911 {
912     assert_service_name
913
914     # $t is used just for readability and to allow better accurate
915     # matching via leading/trailing spaces
916     t=" $CTDB_MANAGED_SERVICES "
917
918     # Return 0 if "<space>$service_name<space>" appears in $t
919     if [ "${t#* ${service_name} }" != "${t}" ] ; then
920         return 0
921     fi
922
923     # If above didn't match then update $CTDB_MANAGED_SERVICES for
924     # backward compatibility and try again.
925     ctdb_compat_managed_service "$CTDB_MANAGES_VSFTPD"   "vsftpd"
926     ctdb_compat_managed_service "$CTDB_MANAGES_SAMBA"    "samba"
927     ctdb_compat_managed_service "$CTDB_MANAGES_WINBIND"  "winbind"
928     ctdb_compat_managed_service "$CTDB_MANAGES_HTTPD"    "apache2"
929     ctdb_compat_managed_service "$CTDB_MANAGES_HTTPD"    "httpd"
930     ctdb_compat_managed_service "$CTDB_MANAGES_ISCSI"    "iscsi"
931     ctdb_compat_managed_service "$CTDB_MANAGES_CLAMD"    "clamd"
932     ctdb_compat_managed_service "$CTDB_MANAGES_NFS"      "nfs"
933
934     t=" $CTDB_MANAGED_SERVICES "
935
936     # Return 0 if "<space>$service_name<space>" appears in $t
937     [ "${t#* ${service_name} }" != "${t}" ]
938 }
939
940 ctdb_start_stop_service ()
941 {
942     assert_service_name
943
944     # Allow service-start/service-stop pseudo-events to start/stop
945     # services when we're not auto-starting/stopping and we're not
946     # monitoring.
947     case "$event_name" in
948         service-start)
949             if is_ctdb_managed_service ; then
950                 die 'service-start event not permitted when service is managed'
951             fi
952             if [ "$CTDB_SERVICE_AUTOSTARTSTOP" = "yes" ] ; then
953                 die 'service-start event not permitted with $CTDB_SERVICE_AUTOSTARTSTOP = yes'
954             fi
955             ctdb_service_start
956             exit $?
957             ;;
958         service-stop)
959             if is_ctdb_managed_service ; then
960                 die 'service-stop event not permitted when service is managed'
961             fi
962             if [ "$CTDB_SERVICE_AUTOSTARTSTOP" = "yes" ] ; then
963                 die 'service-stop event not permitted with $CTDB_SERVICE_AUTOSTARTSTOP = yes'
964             fi
965             ctdb_service_stop
966             exit $?
967             ;;
968     esac
969
970     # Do nothing unless configured to...
971     [ "$CTDB_SERVICE_AUTOSTARTSTOP" = "yes" ] || return 0
972
973     [ "$event_name" = "monitor" ] || return 0
974
975     if is_ctdb_managed_service ; then
976         if ! is_ctdb_previously_managed_service ; then
977             echo "Starting service \"$service_name\" - now managed"
978             background_with_logging ctdb_service_start
979             exit $?
980         fi
981     else
982         if is_ctdb_previously_managed_service ; then
983             echo "Stopping service \"$service_name\" - no longer managed"
984             background_with_logging ctdb_service_stop
985             exit $?
986         fi
987     fi
988 }
989
990 ctdb_service_start ()
991 {
992     # The service is marked managed if we've ever tried to start it.
993     ctdb_service_managed
994
995     service_start || return $?
996
997     ctdb_counter_init
998     ctdb_check_tcp_init
999 }
1000
1001 ctdb_service_stop ()
1002 {
1003     ctdb_service_unmanaged
1004     service_stop
1005 }
1006
1007 # Default service_start() and service_stop() functions.
1008  
1009 # These may be overridden in an eventscript.
1010 service_start ()
1011 {
1012     service "$service_name" start
1013 }
1014
1015 service_stop ()
1016 {
1017     service "$service_name" stop
1018 }
1019
1020 ##################################################################
1021
1022 ctdb_standard_event_handler ()
1023 {
1024     :
1025 }
1026
1027 iptables_wrapper ()
1028 {
1029     _family="$1" ; shift
1030     if [ "$_family" = "inet6" ] ; then
1031         _iptables_cmd="ip6tables"
1032     else
1033         _iptables_cmd="iptables"
1034     fi
1035
1036     # iptables doesn't like being re-entered, so flock-wrap it.
1037     flock -w 30 "${CTDB_SCRIPT_VARDIR}/iptables.flock" "$_iptables_cmd" "$@"
1038 }
1039
1040 # AIX (and perhaps others?) doesn't have mktemp
1041 if ! type mktemp >/dev/null 2>&1 ; then
1042     mktemp ()
1043     {
1044         _dir=false
1045         if [ "$1" = "-d" ] ; then
1046             _dir=true
1047             shift
1048         fi
1049         _d="${TMPDIR:-/tmp}"
1050         _hex10=$(dd if=/dev/urandom count=20 2>/dev/null | \
1051             md5sum | \
1052             sed -e 's@\(..........\).*@\1@')
1053         _t="${_d}/tmp.${_hex10}"
1054         (
1055             umask 077
1056             if $_dir ; then
1057                 mkdir "$_t"
1058             else
1059                 >"$_t"
1060             fi
1061         )
1062         echo "$_t"
1063     }
1064 fi
1065
1066 ########################################################
1067 # tickle handling
1068 ########################################################
1069
1070 update_tickles ()
1071 {
1072         _port="$1"
1073
1074         tickledir="${CTDB_SCRIPT_VARDIR}/tickles"
1075         mkdir -p "$tickledir"
1076
1077         ctdb_get_pnn
1078
1079         # What public IPs do I hold?
1080         _ips=$(ctdb -X ip | awk -F'|' -v pnn=$pnn '$3 == pnn {print $2}')
1081
1082         # IPs as a regexp choice
1083         _ipschoice="($(echo $_ips | sed -e 's/ /|/g' -e 's/\./\\\\./g'))"
1084
1085         # Record connections to our public IPs in a temporary file
1086         _my_connections="${tickledir}/${_port}.connections"
1087         rm -f "$_my_connections"
1088         netstat -tn |
1089         awk -v destpat="^${_ipschoice}:${_port}\$" \
1090           '$1 == "tcp" && $6 == "ESTABLISHED" && $4 ~ destpat {print $5, $4}' |
1091         sort >"$_my_connections"
1092
1093         # Record our current tickles in a temporary file
1094         _my_tickles="${tickledir}/${_port}.tickles"
1095         rm -f "$_my_tickles"
1096         for _i in $_ips ; do
1097                 ctdb -X gettickles $_i $_port |
1098                 awk -F'|' 'NR > 1 { printf "%s:%s %s:%s\n", $2, $3, $4, $5 }'
1099         done |
1100         sort >"$_my_tickles"
1101
1102         # Add tickles for connections that we haven't already got tickles for
1103         comm -23 "$_my_connections" "$_my_tickles" |
1104         while read _src _dst ; do
1105                 ctdb addtickle $_src $_dst
1106         done
1107
1108         # Remove tickles for connections that are no longer there
1109         comm -13 "$_my_connections" "$_my_tickles" |
1110         while read _src _dst ; do
1111                 ctdb deltickle $_src $_dst
1112         done
1113
1114         rm -f "$_my_connections" "$_my_tickles" 
1115 }
1116
1117 ########################################################
1118 # load a site local config file
1119 ########################################################
1120
1121 [ -n "$CTDB_RC_LOCAL" -a -x "$CTDB_RC_LOCAL" ] && {
1122         . "$CTDB_RC_LOCAL"
1123 }
1124
1125 [ -x $CTDB_BASE/rc.local ] && {
1126         . $CTDB_BASE/rc.local
1127 }
1128
1129 [ -d $CTDB_BASE/rc.local.d ] && {
1130         for i in $CTDB_BASE/rc.local.d/* ; do
1131                 [ -x "$i" ] && . "$i"
1132         done
1133 }
1134
1135 script_name="${0##*/}"       # basename
1136 service_fail_limit=1
1137 event_name="$1"