1 # Hey Emacs, this is a -*- shell-script -*- !!!
3 # utility functions for ctdb event scripts
5 PATH=/bin:/usr/bin:/usr/sbin:/sbin:$PATH
7 [ -z "$CTDB_VARDIR" ] && {
8 if [ -d "/var/lib/ctdb" ] ; then
9 export CTDB_VARDIR="/var/lib/ctdb"
11 export CTDB_VARDIR="/var/ctdb"
14 [ -z "$CTDB_ETCDIR" ] && {
15 export CTDB_ETCDIR="/etc"
18 #######################################
19 # pull in a system config file, if any
23 foo="${service_config:-${service_name}}"
24 if [ -n "$foo" ] ; then
27 elif [ "$1" != "ctdb" ] ; then
31 if [ -f $CTDB_ETCDIR/sysconfig/$1 ]; then
32 . $CTDB_ETCDIR/sysconfig/$1
33 elif [ -f $CTDB_ETCDIR/default/$1 ]; then
34 . $CTDB_ETCDIR/default/$1
35 elif [ -f $CTDB_BASE/sysconfig/$1 ]; then
36 . $CTDB_BASE/sysconfig/$1
44 ##############################################################
45 # make sure CTDB_CURRENT_DEBUGLEVEL is set to the desired debug level
48 # If it is already set then do nothing, since it might have been set
49 # via a file in rc.local.d/. If it is not set then set it by sourcing
50 # /var/ctdb/eventscript_debuglevel. If this file does not exist then
51 # create it using output from "ctdb getdebug". If the option 1st arg
52 # is "create" then don't source an existing file but create a new one
53 # instead - this is useful for creating the file just once in each
54 # event run in 00.ctdb. If there's a problem getting the debug level
55 # from ctdb then it is silently set to 0 - no use spamming logs if our
56 # debug code is broken...
57 ctdb_set_current_debuglevel ()
59 [ -z "$CTDB_CURRENT_DEBUGLEVEL" ] || return 0
61 _f="$CTDB_VARDIR/eventscript_debuglevel"
63 if [ "$1" = "create" -o ! -r "$_f" ] ; then
64 _t=$(ctdb getdebug -Y 2>/dev/null)
65 # get last field of output
69 echo "export CTDB_CURRENT_DEBUGLEVEL=\"${_t:-0}\"" >"$_f"
77 if [ $CTDB_CURRENT_DEBUGLEVEL -ge 4 ] ; then
78 # If there are arguments then echo them. Otherwise expect to
79 # use stdin, which allows us to pass lots of debug using a
98 # When things are run in the background in an eventscript then logging
99 # output might get lost. This is the "solution". :-)
100 background_with_logging ()
103 if [ "$CTDB_SYSLOG" = "yes" -o -z "$CTDB_LOGFILE" ] ; then
106 case "$CTDB_OPTIONS" in
107 *--syslog*) _using_syslog=true ;;
111 "$@" 2>&1 </dev/null |
112 if $_using_syslog ; then
113 logger -t "ctdbd: ${script_name}&"
115 cat >>"$CTDB_LOGFILE"
122 ##############################################################
123 # check number of args for different events
129 echo "ERROR: must supply interface, IP and maskbits"
135 echo "ERROR: must supply old interface, new interface, IP and maskbits"
142 ##############################################################
143 # determine on what type of system (init style) we are running
144 detect_init_style() {
145 # only do detection if not already set:
146 test "x$CTDB_INIT_STYLE" != "x" && return
148 if [ -x /sbin/startproc ]; then
149 CTDB_INIT_STYLE="suse"
150 elif [ -x /sbin/start-stop-daemon ]; then
151 CTDB_INIT_STYLE="debian"
153 CTDB_INIT_STYLE="redhat"
157 ######################################################
158 # simulate /sbin/service on platforms that don't have it
159 # _service() makes it easier to hook the service() function for
166 # do nothing, when no service was specified
167 [ -z "$_service_name" ] && return
169 if [ -x /sbin/service ]; then
170 $_nice /sbin/service "$_service_name" "$_op"
171 elif [ -x $CTDB_ETCDIR/init.d/$_service_name ]; then
172 $_nice $CTDB_ETCDIR/init.d/$_service_name "$_op"
173 elif [ -x $CTDB_ETCDIR/rc.d/init.d/$_service_name ]; then
174 $_nice $CTDB_ETCDIR/rc.d/init.d/$_service_name "$_op"
184 ######################################################
185 # simulate /sbin/service (niced) on platforms that don't have it
192 ######################################################
193 # wrapper around /proc/ settings to allow them to be hooked
195 # 1st arg is relative path under /proc/, 2nd arg is value to set
198 echo "$2" >"/proc/$1"
201 ######################################################
202 # wrapper around getting file contents from /proc/ to allow
203 # this to be hooked for testing
204 # 1st arg is relative path under /proc/
210 ######################################################
211 # Check that an RPC service is healthy -
212 # this includes allowing a certain number of failures
213 # before marking the NFS service unhealthy.
215 # usage: nfs_check_rpc_service SERVICE_NAME [ triple ...]
217 # each triple is a set of 3 arguments: an operator, a
218 # fail count limit and an action string.
222 # nfs_check_rpc_service "lockd" \
223 # -ge 15 "verbose restart unhealthy" \
224 # -eq 10 "restart:bs"
226 # says that if lockd is down for 15 iterations then do
227 # a verbose restart of lockd and mark the node unhealthy.
228 # Before this, after 10 iterations of failure, the
229 # service is restarted silently in the background.
230 # Order is important: the number of failures need to be
231 # specified in reverse order because processing stops
232 # after the first condition that is true.
233 ######################################################
234 nfs_check_rpc_service ()
236 _prog_name="$1" ; shift
241 *) _v="$1" ; shift ;;
245 _rpc_prog="$_prog_name"
248 case "$_prog_name" in
252 _restart="echo 'Trying to restart NFS service'"
253 _restart="${_restart}; startstop_nfs restart"
258 _restart="echo 'Trying to restart Ganesha NFS service'"
259 _restart="${_restart}; startstop_ganesha restart"
262 _opts="${MOUNTD_PORT:+ -p }${MOUNTD_PORT}"
265 _opts="${RQUOTAD_PORT:+ -p }${RQUOTAD_PORT}"
270 _restart="echo 'Trying to restart lock manager service'"
271 _restart="${_restart}; startstop_nfslock restart"
275 _opts="${STATD_HOSTNAME:+ -n }${STATD_HOSTNAME}"
276 _opts="${_opts}${STATD_PORT:+ -p }${STATD_PORT}"
277 _opts="${_opts}${STATD_OUTGOING_PORT:+ -o }${STATD_OUTGOING_PORT}"
280 echo "Internal error: unknown RPC program \"$_prog_name\"."
284 _service_name="nfs_${_prog_name}"
286 if ctdb_check_rpc "$_rpc_prog" $_version >/dev/null ; then
287 ctdb_counter_init "$_service_name"
291 ctdb_counter_incr "$_service_name"
293 while [ -n "$3" ] ; do
294 ctdb_check_counter "quiet" "$1" "$2" "$_service_name" || {
295 for _action in $3 ; do
298 echo "$ctdb_check_rpc_out"
301 # No explicit command specified, construct rpc command.
302 if [ -z "$_restart" ] ; then
303 _p="rpc.${_prog_name}"
304 _restart="echo 'Trying to restart $_prog_name [${_p}${_opts}]'"
305 _restart="${_restart}; killall -q -9 $_p"
306 _restart="${_restart}; $_p $_opts"
309 # Process restart flags...
310 _flags="${_action#restart:}"
311 # There may not have been a colon...
312 [ "$_flags" != "$_action" ] || _flags=""
313 # q=quiet - everything to /dev/null
314 if [ "${_flags#*q}" != "$_flags" ] ; then
315 _restart="{ ${_restart} ; } >/dev/null 2>&1"
317 # s=stealthy - last command to /dev/null
318 if [ "${_flags#*s}" != "$_flags" ] ; then
319 _restart="${_restart} >/dev/null 2>&1"
321 # b=background - the whole thing, easy and reliable
322 if [ "${_flags#*b}" != "$_flags" ] ; then
323 _restart="{ ${_restart} ; } &"
333 echo "Internal error: unknown action \"$_action\"."
338 # Only process the first action group.
345 ######################################################
346 # check that a rpc server is registered with portmap
347 # and responding to requests
348 # usage: ctdb_check_rpc SERVICE_NAME VERSION
349 ######################################################
355 if ! ctdb_check_rpc_out=$(rpcinfo -u localhost $progname $version 2>&1) ; then
356 ctdb_check_rpc_out="ERROR: $progname failed RPC check:
358 echo "$ctdb_check_rpc_out"
363 ######################################################
364 # check a set of directories is available
365 # return 1 on a missing directory
366 # usage: ctdb_check_directories_probe SERVICE_NAME <directories...>
367 ######################################################
368 ctdb_check_directories_probe() {
369 while IFS="" read d ; do
375 [ -d "${d}/." ] || return 1
380 ######################################################
381 # check a set of directories is available
382 # usage: ctdb_check_directories SERVICE_NAME <directories...>
383 ######################################################
384 ctdb_check_directories() {
385 n="${1:-${service_name}}"
386 ctdb_check_directories_probe || {
387 echo "ERROR: $n directory \"$d\" not available"
392 ######################################################
393 # check a set of tcp ports
394 # usage: ctdb_check_tcp_ports <ports...>
395 ######################################################
397 # This flag file is created when a service is initially started. It
398 # is deleted the first time TCP port checks for that service succeed.
399 # Until then ctdb_check_tcp_ports() prints a more subtle "error"
400 # message if a port check fails.
401 _ctdb_check_tcp_common ()
403 _ctdb_service_started_file="$ctdb_fail_dir/$service_name.started"
406 ctdb_check_tcp_init ()
408 _ctdb_check_tcp_common
409 mkdir -p "${_ctdb_service_started_file%/*}" # dirname
410 touch "$_ctdb_service_started_file"
413 ctdb_check_tcp_ports()
415 if [ -z "$1" ] ; then
416 echo "INTERNAL ERROR: ctdb_check_tcp_ports - no ports specified"
420 # Set default value for CTDB_TCP_PORT_CHECKS if unset.
421 # If any of these defaults are unsupported then this variable can
422 # be overridden in /etc/sysconfig/ctdb or via a file in
423 # /etc/ctdb/rc.local.d/.
424 : ${CTDB_TCP_PORT_CHECKERS:=ctdb nmap netstat}
426 for _c in $CTDB_TCP_PORT_CHECKERS ; do
427 ctdb_check_tcp_ports_$_c "$@"
430 _ctdb_check_tcp_common
431 rm -f "$_ctdb_service_started_file"
435 _ctdb_check_tcp_common
436 if [ ! -f "$_ctdb_service_started_file" ] ; then
437 echo "ERROR: $service_name tcp port $_p is not responding"
439 $ctdb_check_tcp_ports_debug
442 echo "INFO: $service_name tcp port $_p is not responding"
449 ctdb_check_ports - checker $_c not implemented
450 output from checker was:
451 $ctdb_check_tcp_ports_debug
459 echo "INTERNAL ERROR: ctdb_check_ports - no working checkers in CTDB_TCP_PORT_CHECKERS=\"$CTDB_TCP_PORT_CHECKERS\""
464 ctdb_check_tcp_ports_netstat ()
466 _cmd='netstat -l -t -n'
468 if [ $? -eq 127 ] ; then
469 # netstat probably not installed - unlikely?
470 ctdb_check_tcp_ports_debug="$_ns"
474 for _p ; do # process each function argument (port)
475 for _a in '0\.0\.0\.0' '::' ; do
476 _pat="[[:space:]]${_a}:${_p}[[:space:]]+[^[:space:]]+[[:space:]]+LISTEN"
477 if echo "$_ns" | grep -E -q "$_pat" ; then
478 # We matched the port, so process next port
483 # We didn't match the port, so flag an error.
484 ctdb_check_tcp_ports_debug="$_cmd shows this output:
492 ctdb_check_tcp_ports_nmap ()
494 # nmap wants a comma-separated list of ports
497 _ports="${_ports}${_ports:+,}${_p}"
500 _cmd="nmap -n -oG - -PS 127.0.0.1 -p $_ports"
502 _nmap_out=$($_cmd 2>&1)
503 if [ $? -eq 127 ] ; then
504 # nmap probably not installed
505 ctdb_check_tcp_ports_debug="$_nmap_out"
509 # get the port-related output
510 _port_info=$(echo "$_nmap_out" | sed -n -r -e 's@^.*Ports:[[:space:]]@@p')
513 # looking for something like this:
514 # 445/open/tcp//microsoft-ds///
515 # possibly followed by a comma
517 case "$_port_info" in
518 # The info we're after must be either at the beginning of
519 # the string or it must follow a space.
522 # Nope, flag an error...
523 ctdb_check_tcp_ports_debug="$_cmd shows this output:
532 # Use the new "ctdb checktcpport" command to check the port.
533 # This is very cheap.
534 ctdb_check_tcp_ports_ctdb ()
536 for _p ; do # process each function argument (port)
537 _cmd="ctdb checktcpport $_p"
542 ctdb_check_tcp_ports_debug="\"$_cmd\" was able to bind to port"
546 # Couldn't bind, something already listening, next port...
550 ctdb_check_tcp_ports_debug="$_cmd (exited with $_ret) with output:
552 # assume not implemented
560 ######################################################
561 # check a unix socket
562 # usage: ctdb_check_unix_socket SERVICE_NAME <socket_path>
563 ######################################################
564 ctdb_check_unix_socket() {
566 [ -z "$socket_path" ] && return
568 if ! netstat --unix -a -n | grep -q "^unix.*LISTEN.*${socket_path}$"; then
569 echo "ERROR: $service_name socket $socket_path not found"
574 ######################################################
575 # check a command returns zero status
576 # usage: ctdb_check_command SERVICE_NAME <command>
577 ######################################################
578 ctdb_check_command() {
581 [ -z "$wait_cmd" ] && return;
582 $wait_cmd > /dev/null 2>&1 || {
583 echo "ERROR: $service_name - $wait_cmd returned error"
588 ################################################
589 # kill off any TCP connections with the given IP
590 ################################################
591 kill_tcp_connections() {
596 connfile="$CTDB_VARDIR/state/connections.$_IP"
597 netstat -tn |egrep "^tcp.*[[:space:]]+$_IP:.*ESTABLISHED" | awk '{print $4" "$5}' > $connfile
598 netstat -tn |egrep "^tcp.*[[:space:]]+::ffff:$_IP:.*ESTABLISHED" | awk '{print $4" "$5}' >> $connfile
600 while read dest src; do
601 srcip=`echo $src | sed -e "s/:[^:]*$//"`
602 srcport=`echo $src | sed -e "s/^.*://"`
603 destip=`echo $dest | sed -e "s/:[^:]*$//"`
604 destport=`echo $dest | sed -e "s/^.*://"`
605 echo "Killing TCP connection $srcip:$srcport $destip:$destport"
606 ctdb killtcp $srcip:$srcport $destip:$destport >/dev/null 2>&1 || _failed=1
608 # we only do one-way killtcp for CIFS
610 # for all others we do 2-way
612 ctdb killtcp $destip:$destport $srcip:$srcport >/dev/null 2>&1 || _failed=1
615 _killcount=`expr $_killcount + 1`
619 [ $_failed = 0 ] || {
620 echo "Failed to send killtcp control"
623 [ $_killcount -gt 0 ] || {
627 while netstat -tn |egrep "^tcp.*[[:space:]]+$_IP:.*ESTABLISHED" > /dev/null; do
629 _count=`expr $_count + 1`
630 [ $_count -gt 3 ] && {
631 echo "Timed out killing tcp connections for IP $_IP"
635 echo "killed $_killcount TCP connections to released IP $_IP"
638 ##################################################################
639 # kill off the local end for any TCP connections with the given IP
640 ##################################################################
641 kill_tcp_connections_local_only() {
646 connfile="$CTDB_VARDIR/state/connections.$_IP"
647 netstat -tn |egrep "^tcp.*[[:space:]]+$_IP:.*ESTABLISHED" | awk '{print $4" "$5}' > $connfile
648 netstat -tn |egrep "^tcp.*[[:space:]]+::ffff:$_IP:.*ESTABLISHED" | awk '{print $4" "$5}' >> $connfile
650 while read dest src; do
651 srcip=`echo $src | sed -e "s/:[^:]*$//"`
652 srcport=`echo $src | sed -e "s/^.*://"`
653 destip=`echo $dest | sed -e "s/:[^:]*$//"`
654 destport=`echo $dest | sed -e "s/^.*://"`
655 echo "Killing TCP connection $srcip:$srcport $destip:$destport"
656 ctdb killtcp $srcip:$srcport $destip:$destport >/dev/null 2>&1 || _failed=1
657 _killcount=`expr $_killcount + 1`
661 [ $_failed = 0 ] || {
662 echo "Failed to send killtcp control"
665 [ $_killcount -gt 0 ] || {
669 while netstat -tn |egrep "^tcp.*[[:space:]]+$_IP:.*ESTABLISHED" > /dev/null; do
671 _count=`expr $_count + 1`
672 [ $_count -gt 3 ] && {
673 echo "Timed out killing tcp connections for IP $_IP"
677 echo "killed $_killcount TCP connections to released IP $_IP"
680 ##################################################################
681 # tickle any TCP connections with the given IP
682 ##################################################################
683 tickle_tcp_connections() {
688 connfile="$CTDB_VARDIR/state/connections.$_IP"
689 netstat -tn |egrep "^tcp.*[[:space:]]+$_IP:.*ESTABLISHED" | awk '{print $4" "$5}' > $connfile
690 netstat -tn |egrep "^tcp.*[[:space:]]+::ffff:$_IP:.*ESTABLISHED" | awk '{print $4" "$5}' >> $connfile
692 while read dest src; do
693 srcip=`echo $src | sed -e "s/:[^:]*$//"`
694 srcport=`echo $src | sed -e "s/^.*://"`
695 destip=`echo $dest | sed -e "s/:[^:]*$//"`
696 destport=`echo $dest | sed -e "s/^.*://"`
697 echo "Tickle TCP connection $srcip:$srcport $destip:$destport"
698 ctdb tickle $srcip:$srcport $destip:$destport >/dev/null 2>&1 || _failed=1
699 echo "Tickle TCP connection $destip:$destport $srcip:$srcport"
700 ctdb tickle $destip:$destport $srcip:$srcport >/dev/null 2>&1 || _failed=1
704 [ $_failed = 0 ] || {
705 echo "Failed to send tickle control"
710 ########################################################
711 # start/stop the Ganesha nfs service
712 ########################################################
715 _ganesha_fsal_list="gpfs"
716 for _fsal in $_ganesha_fsal_list ; do
717 _service_name="nfs-ganesha-${_fsal}"
718 if [ -x /etc/init.d/$_service_name ] ; then
724 service "$_service_name" start
727 service "$_service_name" stop
730 service "$_service_name" restart
735 ########################################################
736 # start/stop the nfs service on different platforms
737 ########################################################
740 [ -x $CTDB_ETCDIR/init.d/nfsserver ] && {
743 [ -x $CTDB_ETCDIR/init.d/nfslock ] && {
751 service nfsserver start
754 service nfsserver stop > /dev/null 2>&1
757 set_proc "fs/nfsd/threads" 0
758 service nfsserver stop > /dev/null 2>&1
760 service nfsserver start
767 service nfslock start
775 set_proc "fs/nfsd/threads" 0
776 service nfs stop > /dev/null 2>&1
777 service nfslock stop > /dev/null 2>&1
779 service nfslock start
785 echo "Unknown platform. NFS is not supported with ctdb"
791 ########################################################
792 # start/stop the nfs lockmanager service on different platforms
793 ########################################################
794 startstop_nfslock() {
796 [ -x $CTDB_ETCDIR/init.d/nfsserver ] && {
799 [ -x $CTDB_ETCDIR/init.d/nfslock ] && {
805 # for sles there is no service for lockmanager
806 # so we instead just shutdown/restart nfs
809 service nfsserver start
812 service nfsserver stop > /dev/null 2>&1
815 service nfsserver stop
816 service nfsserver start
823 service nfslock start
826 service nfslock stop > /dev/null 2>&1
830 service nfslock start
835 echo "Unknown platform. NFS locking is not supported with ctdb"
847 _lockfile="${CTDB_VARDIR}/state/interface_modify_${_iface}.flock"
848 [ -f "$_lockfile" ] || touch "$_lockfile"
851 # Note: use of return/exit/die() below only gets us out of the
852 # sub-shell, which is actually what we want. That is, the
853 # function should just return non-zero.
855 flock --timeout 30 0 || \
856 die "add_ip_to_iface: unable to get lock for ${_iface}"
858 # Ensure interface is up
859 ip link set "$_iface" up || \
860 die "Failed to bringup interface $_iface"
862 ip addr add "$_ip/$_maskbits" brd + dev "$_iface" || \
863 die "Failed to add $_ip/$_maskbits on dev $_iface"
866 # Do nothing here - return above only gets us out of the subshell
867 # and doing anything here will affect the return code.
870 delete_ip_from_iface()
876 _lockfile="${CTDB_VARDIR}/state/interface_modify_${_iface}.flock"
877 [ -f "$_lockfile" ] || touch "$_lockfile"
880 # Note: use of return/exit/die() below only gets us out of the
881 # sub-shell, which is actually what we want. That is, the
882 # function should just return non-zero.
884 flock --timeout 30 0 || \
885 die "delete_ip_from_iface: unable to get lock for ${_iface}"
887 _im="$_ip/$_maskbits" # shorthand for readability
889 # "ip addr del" will delete all secondary IPs if this is the
890 # primary. To work around this _very_ annoying behaviour we
891 # have to keep a record of the secondaries and re-add them
895 if ip addr list dev "$_iface" primary | grep -Fq "inet $_im " ; then
896 _secondaries=$(ip addr list dev "$_iface" secondary | \
897 awk '$1 == "inet" { print $2 }')
901 ip addr del "$_im" dev "$_iface" || {
902 echo "Failed to del $_ip on dev $_iface"
906 if [ -n "$_secondaries" ] ; then
907 for _i in $_secondaries; do
908 if ip addr list dev "$_iface" | grep -Fq "inet $_i" ; then
909 echo "Kept secondary $_i on dev $_iface"
911 echo "Re-adding secondary address $_i to dev $_iface"
912 ip addr add $_i brd + dev $_iface || {
913 echo "Failed to re-add address $_i to dev $_iface"
923 # Do nothing here - return above only gets us out of the subshell
924 # and doing anything here will affect the return code.
927 ########################################################
928 # some simple logic for counting events - per eventscript
929 # usage: ctdb_counter_init
931 # ctdb_check_counter_limit <limit>
932 # ctdb_check_counter_limit succeeds when count >= <limit>
933 ########################################################
934 _ctdb_counter_common () {
935 _service_name="${1:-${service_name}}"
936 _counter_file="$ctdb_fail_dir/$_service_name"
937 mkdir -p "${_counter_file%/*}" # dirname
939 ctdb_counter_init () {
940 _ctdb_counter_common "$1"
944 ctdb_counter_incr () {
945 _ctdb_counter_common "$1"
948 echo -n 1 >> "$_counter_file"
950 ctdb_check_counter_limit () {
953 _limit="${1:-${service_fail_limit}}"
957 _size=$(stat -c "%s" "$_counter_file" 2>/dev/null || echo 0)
958 if [ $_size -ge $_limit ] ; then
959 echo "ERROR: more than $_limit consecutive failures for $service_name, marking cluster unhealthy"
961 elif [ $_size -gt 0 -a -z "$_quiet" ] ; then
962 echo "WARNING: less than $_limit consecutive failures ($_size) for $service_name, not unhealthy yet"
965 ctdb_check_counter_equal () {
971 _size=$(stat -c "%s" "$_counter_file" 2>/dev/null || echo 0)
972 if [ $_size -eq $_limit ] ; then
977 ctdb_check_counter () {
978 _msg="${1:-error}" # "error" - anything else is silent on fail
979 _op="${2:--ge}" # an integer operator supported by test
980 _limit="${3:-${service_fail_limit}}"
982 _ctdb_counter_common "$1"
985 _size=$(stat -c "%s" "$_counter_file" 2>/dev/null || echo 0)
986 if [ $_size $_op $_limit ] ; then
987 if [ "$_msg" = "error" ] ; then
988 echo "ERROR: $_limit consecutive failures for $_service_name, marking node unhealthy"
996 ########################################################
998 ctdb_status_dir="$CTDB_VARDIR/status"
999 ctdb_fail_dir="$CTDB_VARDIR/failcount"
1001 ctdb_setup_service_state_dir ()
1003 service_state_dir="$CTDB_VARDIR/state/${1:-${service_name}}"
1004 mkdir -p "$service_state_dir" || {
1005 echo "Error creating state dir \"$service_state_dir\""
1010 ########################################################
1011 # Managed status history, for auto-start/stop
1013 ctdb_managed_dir="$CTDB_VARDIR/managed_history"
1015 _ctdb_managed_common ()
1017 _service_name="${1:-${service_name}}"
1018 _ctdb_managed_file="$ctdb_managed_dir/$_service_name"
1021 ctdb_service_managed ()
1023 _ctdb_managed_common "$@"
1024 mkdir -p "$ctdb_managed_dir"
1025 touch "$_ctdb_managed_file"
1028 ctdb_service_unmanaged ()
1030 _ctdb_managed_common "$@"
1031 rm -f "$_ctdb_managed_file"
1034 is_ctdb_previously_managed_service ()
1036 _ctdb_managed_common "$@"
1037 [ -f "$_ctdb_managed_file" ]
1040 ########################################################
1041 # Check and set status
1045 echo "node is \"$1\", \"${script_name}\" reports problem: $(cat $2)"
1050 if [ -r "$ctdb_status_dir/$script_name/unhealthy" ] ; then
1051 log_status_cat "unhealthy" "$ctdb_status_dir/$script_name/unhealthy"
1053 elif [ -r "$ctdb_status_dir/$script_name/banned" ] ; then
1054 log_status_cat "banned" "$ctdb_status_dir/$script_name/banned"
1063 d="$ctdb_status_dir/$script_name"
1070 for i in "banned" "unhealthy" ; do
1077 ##################################################################
1078 # Reconfigure a service on demand
1080 _ctdb_service_reconfigure_common ()
1082 _d="$ctdb_status_dir/${1:-${service_name}}"
1084 _ctdb_service_reconfigure_flag="$_d/reconfigure"
1087 ctdb_service_needs_reconfigure ()
1089 _ctdb_service_reconfigure_common "$@"
1090 [ -e "$_ctdb_service_reconfigure_flag" ]
1093 ctdb_service_set_reconfigure ()
1095 _ctdb_service_reconfigure_common "$@"
1096 >"$_ctdb_service_reconfigure_flag"
1099 ctdb_service_unset_reconfigure ()
1101 _ctdb_service_reconfigure_common "$@"
1102 rm -f "$_ctdb_service_reconfigure_flag"
1105 ctdb_service_reconfigure ()
1107 echo "Reconfiguring service \"$@\"..."
1108 ctdb_service_unset_reconfigure "$@"
1109 service_reconfigure "$@" || return $?
1110 ctdb_counter_init "$@"
1113 # Default service_reconfigure() function.
1114 service_reconfigure ()
1116 service "${1:-$service_name}" restart
1119 ctdb_reconfigure_try_lock ()
1122 _ctdb_service_reconfigure_common "$@"
1123 _lock="${_d}/reconfigure_lock"
1128 # This is overkill but will work if we need to extend this to
1129 # allow certain events to run multiple times in parallel
1130 # (e.g. takeip) and write multiple PIDs to the file.
1132 if [ -n "$_locker_event" ] ; then
1133 while read _pid ; do
1134 if [ -n "$_pid" -a "$_pid" != $$ ] && \
1135 kill -0 "$_pid" 2>/dev/null ; then
1141 printf "%s\n%s\n" "$event_name" $$ >"$_lock"
1146 ctdb_replay_monitor_status ()
1148 echo "Replaying previous status for this script due to reconfigure..."
1149 # Leading colon (':') is missing in some versions...
1150 _out=$(ctdb scriptstatus -Y | grep -E "^:?monitor:${script_name}:")
1151 # Output looks like this:
1152 # :monitor:60.nfs:1:ERROR:1314764004.030861:1314764004.035514:foo bar:
1153 # This is the cheapest way of getting fields in the middle.
1154 set -- $(IFS=":" ; echo $_out)
1157 # The error output field can include colons so we'll try to
1158 # preserve them. The weak checking at the beginning tries to make
1159 # this work for both broken (no leading ':') and fixed output.
1161 _err_out="${_out#*monitor:${script_name}:*:*:*:*:}"
1163 OK) : ;; # Do nothing special.
1165 # Recast this as an error, since we can't exit with the
1166 # correct negative number.
1168 _err_out="[Replay of TIMEDOUT scriptstatus - note incorrect return code.] ${_err_out}"
1171 # Recast this as an OK, since we can't exit with the
1172 # correct negative number.
1174 _err_out="[Replay of DISABLED scriptstatus - note incorrect return code.] ${_err_out}"
1176 *) : ;; # Must be ERROR, do nothing special.
1182 ctdb_service_check_reconfigure ()
1184 [ -n "$1" ] || set -- "$service_name"
1186 # We only care about some events in this function. For others we
1188 case "$event_name" in
1189 monitor|ipreallocated|reconfigure) : ;;
1193 if ctdb_reconfigure_try_lock "$@" ; then
1194 # No events covered by this function are running, so proceed
1196 case "$event_name" in
1198 (ctdb_service_reconfigure "$@")
1202 if ctdb_service_needs_reconfigure "$@" ; then
1203 ctdb_service_reconfigure "$@"
1207 if ctdb_service_needs_reconfigure "$@" ; then
1208 ctdb_service_reconfigure "$@"
1209 # Given that the reconfigure might not have
1210 # resulted in the service being stable yet, we
1211 # replay the previous status since that's the best
1212 # information we have.
1213 ctdb_replay_monitor_status
1218 # Somebody else is running an event we don't want to collide
1219 # with. We proceed with caution.
1220 case "$event_name" in
1222 # Tell whoever called us to retry.
1226 # Defer any scheduled reconfigure and just run the
1227 # rest of the ipreallocated event, as per the
1228 # eventscript. There's an assumption here that the
1229 # event doesn't depend on any scheduled reconfigure.
1230 # This is true in the current code.
1234 # There is most likely a reconfigure in progress so
1235 # the service is possibly unstable. As above, we
1236 # defer any scheduled reconfigured. We also replay
1237 # the previous monitor status since that's the best
1238 # information we have.
1239 ctdb_replay_monitor_status
1245 ##################################################################
1246 # Does CTDB manage this service? - and associated auto-start/stop
1248 ctdb_compat_managed_service ()
1250 if [ "$1" = "yes" -a "$2" = "$_service_name" ] ; then
1251 CTDB_MANAGED_SERVICES="$CTDB_MANAGED_SERVICES $2"
1255 is_ctdb_managed_service ()
1257 _service_name="${1:-${service_name}}"
1259 # $t is used just for readability and to allow better accurate
1260 # matching via leading/trailing spaces
1261 t=" $CTDB_MANAGED_SERVICES "
1263 # Return 0 if "<space>$_service_name<space>" appears in $t
1264 if [ "${t#* ${_service_name} }" != "${t}" ] ; then
1268 # If above didn't match then update $CTDB_MANAGED_SERVICES for
1269 # backward compatibility and try again.
1270 ctdb_compat_managed_service "$CTDB_MANAGES_VSFTPD" "vsftpd"
1271 ctdb_compat_managed_service "$CTDB_MANAGES_SAMBA" "samba"
1272 ctdb_compat_managed_service "$CTDB_MANAGES_SCP" "scp"
1273 ctdb_compat_managed_service "$CTDB_MANAGES_WINBIND" "winbind"
1274 ctdb_compat_managed_service "$CTDB_MANAGES_HTTPD" "apache2"
1275 ctdb_compat_managed_service "$CTDB_MANAGES_HTTPD" "httpd"
1276 ctdb_compat_managed_service "$CTDB_MANAGES_ISCSI" "iscsi"
1277 ctdb_compat_managed_service "$CTDB_MANAGES_CLAMD" "clamd"
1278 ctdb_compat_managed_service "$CTDB_MANAGES_NFS" "nfs"
1279 ctdb_compat_managed_service "$CTDB_MANAGES_NFS" "nfs-ganesha-gpfs"
1281 t=" $CTDB_MANAGED_SERVICES "
1283 # Return 0 if "<space>$_service_name<space>" appears in $t
1284 [ "${t#* ${_service_name} }" != "${t}" ]
1287 ctdb_start_stop_service ()
1289 # Do nothing unless configured to...
1290 [ "$CTDB_SERVICE_AUTOSTARTSTOP" = "yes" ] || return 0
1292 _service_name="${1:-${service_name}}"
1294 [ "$event_name" = "monitor" ] || return 0
1296 if is_ctdb_managed_service "$_service_name" ; then
1297 if ! is_ctdb_previously_managed_service "$_service_name" ; then
1298 echo "Starting service \"$_service_name\" - now managed"
1299 background_with_logging ctdb_service_start "$_service_name"
1303 if is_ctdb_previously_managed_service "$_service_name" ; then
1304 echo "Stopping service \"$_service_name\" - no longer managed"
1305 background_with_logging ctdb_service_stop "$_service_name"
1311 ctdb_service_start ()
1313 # The service is marked managed if we've ever tried to start it.
1314 ctdb_service_managed "$@"
1316 # Here we only want $1. If no argument is passed then
1317 # service_start needs to know.
1318 service_start "$@" || return $?
1320 ctdb_counter_init "$@"
1324 ctdb_service_stop ()
1326 ctdb_service_unmanaged "$@"
1330 # Default service_start() and service_stop() functions.
1332 # These may be overridden in an eventscript. When overriding, the
1333 # following convention must be followed. If these functions are
1334 # called with no arguments then they may use internal logic to
1335 # determine whether the service is managed and, therefore, whether
1336 # they should take any action. However, if the service name is
1337 # specified as an argument then an attempt must be made to start or
1338 # stop the service. This is because the auto-start/stop code calls
1339 # them with the service name as an argument.
1342 service "${1:-${service_name}}" start
1347 service "${1:-${service_name}}" stop
1350 ##################################################################
1352 ctdb_standard_event_handler ()
1367 # iptables doesn't like being re-entered, so flock-wrap it.
1370 flock -w 30 $CTDB_VARDIR/iptables-ctdb.flock /sbin/iptables "$@"
1373 ########################################################
1375 ########################################################
1377 # Temporary directory for tickles.
1378 tickledir="$CTDB_VARDIR/state/tickles"
1379 mkdir -p "$tickledir"
1385 mkdir -p "$tickledir" # Just in case
1388 _pnn=$(ctdb pnn) ; _pnn=${_pnn#PNN:}
1390 # What public IPs do I hold?
1391 _ips=$(ctdb -Y ip | awk -F: -v pnn=$_pnn '$3 == pnn {print $2}')
1393 # IPs as a regexp choice
1394 _ipschoice="($(echo $_ips | sed -e 's/ /|/g' -e 's/\./\\\\./g'))"
1396 # Record connections to our public IPs in a temporary file
1397 _my_connections="${tickledir}/${_port}.connections"
1398 rm -f "$_my_connections"
1400 awk -v destpat="^${_ipschoice}:${_port}\$" \
1401 '$1 == "tcp" && $6 == "ESTABLISHED" && $4 ~ destpat {print $5, $4}' |
1402 sort >"$_my_connections"
1404 # Record our current tickles in a temporary file
1405 _my_tickles="${tickledir}/${_port}.tickles"
1406 rm -f "$_my_tickles"
1407 for _i in $_ips ; do
1408 ctdb -Y gettickles $_i $_port |
1409 awk -F: 'NR > 1 { printf "%s:%s %s:%s\n", $2, $3, $4, $5 }'
1411 sort >"$_my_tickles"
1413 # Add tickles for connections that we haven't already got tickles for
1414 comm -23 "$_my_connections" "$_my_tickles" |
1415 while read _src _dst ; do
1416 ctdb addtickle $_src $_dst
1419 # Remove tickles for connections that are no longer there
1420 comm -13 "$_my_connections" "$_my_tickles" |
1421 while read _src _dst ; do
1422 ctdb deltickle $_src $_dst
1425 rm -f "$_my_connections" "$_my_tickles"
1428 ########################################################
1429 # load a site local config file
1430 ########################################################
1432 [ -n "$CTDB_RC_LOCAL" -a -x "$CTDB_RC_LOCAL" ] && {
1436 [ -x $CTDB_BASE/rc.local ] && {
1437 . $CTDB_BASE/rc.local
1440 [ -d $CTDB_BASE/rc.local.d ] && {
1441 for i in $CTDB_BASE/rc.local.d/* ; do
1442 [ -x "$i" ] && . "$i"
1446 # We'll call this here to ensure $CTDB_CURRENT_DEBUGLEVEL is set.
1447 # This gives us a chance to override the debug level using a file in
1448 # $CTDB_BASE/rc.local.d/.
1449 ctdb_set_current_debuglevel
1451 script_name="${0##*/}" # basename
1452 service_name="$script_name" # default is just the script name
1453 service_fail_limit=1