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