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