ctdb-scripts: Clean up ctdb_check_unix_socket()
[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 is used elsewhere
12 # shellcheck disable=SC2034
13 CTDB_VARDIR="/usr/local/var/lib/ctdb"
14 ctdb_rundir="/usr/local/var/run/ctdb"
15
16 CTDB="${CTDB:-/usr/local/bin/ctdb}"
17
18 # Only (and always) override these variables in test code
19
20 if [ -z "$CTDB_SCRIPT_VARDIR" ] ; then
21     CTDB_SCRIPT_VARDIR="/usr/local/var/lib/ctdb/state"
22 fi
23
24 if [ -z "$CTDB_SYS_ETCDIR" ] ; then
25     CTDB_SYS_ETCDIR="/etc"
26 fi
27
28 if [ -z "$CTDB_HELPER_BINDIR" ] ; then
29     CTDB_HELPER_BINDIR="/usr/local/libexec/ctdb"
30 fi
31
32 #######################################
33 # pull in a system config file, if any
34
35 rewrite_ctdb_options ()
36 {
37     case "$CTDB_DBDIR" in
38         tmpfs|tmpfs:*)
39             _opts_defaults="mode=700"
40             # Get any extra options specified after colon
41             if [ "$CTDB_DBDIR" = "tmpfs" ] ; then
42                 _opts=""
43             else
44                 _opts="${CTDB_DBDIR#tmpfs:}"
45             fi
46             # It is OK to repeat mount options - last value wins.
47             # CTDB_DBDIR_TMPFS_OPTIONS is used by ctdbd_wrapper
48             # shellcheck disable=SC2034
49             CTDB_DBDIR_TMPFS_OPTIONS="${_opts_defaults}${_opts:+,}${_opts}"
50
51             CTDB_DBDIR="${ctdb_rundir}/CTDB_DBDIR"
52             ;;
53         *)
54             # shellcheck disable=SC2034
55             CTDB_DBDIR_TMPFS_OPTIONS=""
56     esac
57 }
58
59 _loadconfig() {
60
61     if [ -z "$1" ] ; then
62         foo="${service_config:-${service_name}}"
63         if [ -n "$foo" ] ; then
64             loadconfig "$foo"
65             return
66         fi
67     fi
68
69     if [ "$1" != "ctdb" ] ; then
70         loadconfig "ctdb"
71     fi
72
73     if [ -z "$1" ] ; then
74         return
75     fi
76
77     if [ -f "${CTDB_SYS_ETCDIR}/sysconfig/$1" ]; then
78         . "${CTDB_SYS_ETCDIR}/sysconfig/$1"
79     elif [ -f "${CTDB_SYS_ETCDIR}/default/$1" ]; then
80         . "${CTDB_SYS_ETCDIR}/default/$1"
81     elif [ -f "${CTDB_BASE}/sysconfig/$1" ]; then
82         . "${CTDB_BASE}/sysconfig/$1"
83     fi
84
85     if [ "$1" = "ctdb" ] ; then
86         _config="${CTDBD_CONF:-${CTDB_BASE}/ctdbd.conf}"
87         if [ -r "$_config" ] ; then
88             . "$_config"
89         fi
90         rewrite_ctdb_options
91     fi
92 }
93
94 loadconfig () {
95     _loadconfig "$@"
96 }
97
98 ##############################################################
99
100 die ()
101 {
102     _msg="$1"
103     _rc="${2:-1}"
104
105     echo "$_msg" >&2
106     exit "$_rc"
107 }
108
109 # Log given message or stdin to either syslog or a CTDB log file
110 # $1 is the tag passed to logger if syslog is in use.
111 script_log ()
112 {
113     _tag="$1" ; shift
114
115     case "$CTDB_LOGGING" in
116         file:*|"")
117             if [ -n "$CTDB_LOGGING" ] ; then
118                 _file="${CTDB_LOGGING#file:}"
119             else
120                 _file="/usr/local/var/log/log.ctdb"
121             fi
122             {
123                 if [ -n "$*" ] ; then
124                     echo "$*"
125                 else
126                     cat
127                 fi
128             } >>"$_file"
129             ;;
130         *)
131             # Handle all syslog:* variants here too.  There's no tool to do
132             # the lossy things, so just use logger.
133             logger -t "ctdbd: ${_tag}" "$@"
134             ;;
135     esac
136 }
137
138 # When things are run in the background in an eventscript then logging
139 # output might get lost.  This is the "solution".  :-)
140 background_with_logging ()
141 {
142     (
143         "$@" 2>&1 </dev/null |
144         script_log "${script_name}&"
145     )&
146
147     return 0
148 }
149
150 ##############################################################
151 # check number of args for different events
152 ctdb_check_args ()
153 {
154     case "$1" in
155         takeip|releaseip)
156             if [ $# != 4 ]; then
157                 echo "ERROR: must supply interface, IP and maskbits"
158                 exit 1
159             fi
160             ;;
161         updateip)
162             if [ $# != 5 ]; then
163                 echo "ERROR: must supply old interface, new interface, IP and maskbits"
164                 exit 1
165             fi
166             ;;
167     esac
168 }
169
170 ##############################################################
171 # determine on what type of system (init style) we are running
172 detect_init_style()
173 {
174     # only do detection if not already set:
175     [ -z "$CTDB_INIT_STYLE" ] || return
176
177     if [ -x /sbin/startproc ]; then
178         CTDB_INIT_STYLE="suse"
179     elif [ -x /sbin/start-stop-daemon ]; then
180         CTDB_INIT_STYLE="debian"
181     else
182         CTDB_INIT_STYLE="redhat"
183     fi
184 }
185
186 ######################################################
187 # simulate /sbin/service on platforms that don't have it
188 # _service() makes it easier to hook the service() function for
189 # testing.
190 _service ()
191 {
192   _service_name="$1"
193   _op="$2"
194
195   # do nothing, when no service was specified
196   [ -z "$_service_name" ] && return
197
198   if [ -x /sbin/service ]; then
199       $_nice /sbin/service "$_service_name" "$_op"
200   elif [ -x /usr/sbin/service ]; then
201       $_nice /usr/sbin/service "$_service_name" "$_op"
202   elif [ -x /bin/systemctl ]; then
203       $_nice /bin/systemctl "$_op" "$_service_name"
204   elif [ -x "${CTDB_SYS_ETCDIR}/init.d/${_service_name}" ]; then
205       $_nice "${CTDB_SYS_ETCDIR}/init.d/${_service_name}" "$_op"
206   elif [ -x "${CTDB_SYS_ETCDIR}/rc.d/init.d/${_service_name}" ]; then
207       $_nice "${CTDB_SYS_ETCDIR}/rc.d/init.d/${_service_name}" "$_op"
208   fi
209 }
210
211 service()
212 {
213     _nice=""
214     _service "$@"
215 }
216
217 ######################################################
218 # simulate /sbin/service (niced) on platforms that don't have it
219 nice_service()
220 {
221     _nice="nice"
222     _service "$@"
223 }
224
225 ######################################################
226 # Cached retrieval of PNN from local node.  This never changes so why
227 # open a client connection to the server each time this is needed?
228 ctdb_get_pnn ()
229 {
230     _pnn_file="${CTDB_SCRIPT_VARDIR}/my-pnn"
231     if [ ! -f "$_pnn_file" ] ; then
232         $CTDB pnn >"$_pnn_file"
233     fi
234
235     cat "$_pnn_file"
236 }
237
238 # Cached retrieval of private IP address from local node.  This never
239 # changes.
240 ctdb_get_ip_address ()
241 {
242     _ip_addr_file="${CTDB_SCRIPT_VARDIR}/my-ip-address"
243     if [ ! -f "$_ip_addr_file" ] ; then
244         $CTDB -X nodestatus |
245             awk -F '|' 'NR == 2 { print $3 }' >"$_ip_addr_file"
246     fi
247
248     # ip_address is used by caller
249     # shellcheck disable=SC2034
250     cat "$_ip_addr_file"
251 }
252
253 ######################################################
254 # wrapper around /proc/ settings to allow them to be hooked
255 # for testing
256 # 1st arg is relative path under /proc/, 2nd arg is value to set
257 set_proc ()
258 {
259     echo "$2" >"/proc/$1"
260 }
261
262 set_proc_maybe ()
263 {
264     if [ -w "/proc/$1" ] ; then
265         set_proc "$1" "$2"
266     fi
267 }
268
269 ######################################################
270 # wrapper around getting file contents from /proc/ to allow
271 # this to be hooked for testing
272 # 1st arg is relative path under /proc/
273 get_proc ()
274 {
275     cat "/proc/$1"
276 }
277
278 ######################################################
279 # Print up to $_max kernel stack traces for processes named $_program
280 program_stack_traces ()
281 {
282     _prog="$1"
283     _max="${2:-1}"
284
285     _count=1
286     for _pid in $(pidof "$_prog") ; do
287         [ "$_count" -le "$_max" ] || break
288
289         # Do this first to avoid racing with process exit
290         _stack=$(get_proc "${_pid}/stack" 2>/dev/null)
291         if [ -n "$_stack" ] ; then
292             echo "Stack trace for ${_prog}[${_pid}]:"
293             echo "$_stack"
294             _count=$((_count + 1))
295         fi
296     done
297 }
298
299 ######################################################
300 # Ensure $service_name is set
301 assert_service_name ()
302 {
303     [ -n "$service_name" ] || die "INTERNAL ERROR: \$service_name not set"
304 }
305
306 ######################################################
307 # check a set of directories is available
308 # return 1 on a missing directory
309 # directories are read from stdin
310 ######################################################
311 ctdb_check_directories_probe()
312 {
313     while IFS="" read d ; do
314         case "$d" in
315             *%*)
316                 continue
317                 ;;
318             *)
319                 [ -d "${d}/." ] || return 1
320         esac
321     done
322 }
323
324 ######################################################
325 # check a set of directories is available
326 # directories are read from stdin
327 ######################################################
328 ctdb_check_directories()
329 {
330     ctdb_check_directories_probe || {
331         echo "ERROR: $service_name directory \"$d\" not available"
332         exit 1
333     }
334 }
335
336 ######################################################
337 # check a set of tcp ports
338 # usage: ctdb_check_tcp_ports <ports...>
339 ######################################################
340
341 # Check whether something is listening on all of the given TCP ports
342 # using the "ctdb checktcpport" command.
343 ctdb_check_tcp_ports()
344 {
345         if [ -z "$1" ] ; then
346                 echo "INTERNAL ERROR: ctdb_check_tcp_ports - no ports specified"
347                 exit 1
348         fi
349
350         for _p ; do  # process each function argument (port)
351                 _cmd="$CTDB checktcpport $_p"
352                 _out=$($_cmd 2>&1)
353                 _ret=$?
354                 case "$_ret" in
355                 0)
356                         echo "$service_name not listening on TCP port $_p"
357                         return 1
358                         ;;
359                 98)
360                         # Couldn't bind, something already listening, next port
361                         continue
362                         ;;
363                 *)
364                         echo "unexpected error (${_ret}) running \"${_cmd}\""
365                         if [ -n "$_out" ] ; then
366                                 echo "$_out"
367                         fi
368                         return $_ret
369                         ;;
370                 esac
371         done
372
373         # All ports listening
374         return 0
375 }
376
377 ######################################################
378 # check a unix socket
379 # usage: ctdb_check_unix_socket SOCKPATH
380 ######################################################
381 ctdb_check_unix_socket()
382 {
383         _sockpath="$1"
384
385         if [ -z "$_sockpath" ] ; then
386                 echo "ERROR: ctdb_check_unix_socket() requires socket path"
387                 return 1
388         fi
389
390         if ! netstat --unix -a -n | grep -q "^unix.*LISTEN.*${_sockpath}$"; then
391                 echo "ERROR: ${service_name} not listening on ${_sockpath}"
392                 return 1
393         fi
394 }
395
396 ################################################
397 # kill off any TCP connections with the given IP
398 ################################################
399 kill_tcp_connections ()
400 {
401     _iface="$1"
402     _ip="$2"
403
404     _oneway=false
405     if [ "$3" = "oneway" ] ; then
406         _oneway=true
407     fi
408
409     get_tcp_connections_for_ip "$_ip" | {
410         _killcount=0
411         _connections=""
412         _nl="
413 "
414         while read _dst _src; do
415             _destport="${_dst##*:}"
416             __oneway=$_oneway
417             case $_destport in
418                 # we only do one-way killtcp for CIFS
419                 139|445) __oneway=true ;;
420             esac
421
422             _connections="${_connections}${_nl}${_src} ${_dst}"
423             if ! $__oneway ; then
424                 _connections="${_connections}${_nl}${_dst} ${_src}"
425             fi
426
427             _killcount=$((_killcount + 1))
428         done
429
430         if [ $_killcount -eq 0 ] ; then
431             return
432         fi
433
434         echo "$_connections" | \
435                 "${CTDB_HELPER_BINDIR}/ctdb_killtcp" "$_iface" || {
436                 echo "Failed to kill TCP connections"
437                 return
438         }
439
440         _connections=$(get_tcp_connections_for_ip "$_ip")
441         if [ -z "$_connections" ] ; then
442                 _remaining=0
443         else
444                 _remaining=$(echo "$_connections" | wc -l)
445         fi
446
447         _actually_killed=$((_killcount - _remaining))
448
449         _t="${_actually_killed}/${_killcount}"
450         echo "Killed ${_t} TCP connections to released IP $_ip"
451
452         if [ -n "$_connections" ] ; then
453                 echo "Remaining connections:"
454                 echo "$_connections" | sed -e 's|^|  |'
455         fi
456     }
457 }
458
459 ##################################################################
460 # kill off the local end for any TCP connections with the given IP
461 ##################################################################
462 kill_tcp_connections_local_only ()
463 {
464     kill_tcp_connections "$@" "oneway"
465 }
466
467 ##################################################################
468 # tickle any TCP connections with the given IP
469 ##################################################################
470 tickle_tcp_connections ()
471 {
472     _ip="$1"
473
474     # Get connections, both directions
475     _conns=$(get_tcp_connections_for_ip "$_ip" | \
476                     awk '{ print $1, $2 ; print $2, $1 }')
477
478     echo "$_conns" | awk '{ print "Tickle TCP connection", $1, $2 }'
479     echo "$_conns" | ctdb tickle
480 }
481
482 get_tcp_connections_for_ip ()
483 {
484     _ip="$1"
485
486     ss -tn state established "src [$_ip]" | awk 'NR > 1 {print $3, $4}'
487 }
488
489 ########################################################
490
491 add_ip_to_iface ()
492 {
493     _iface=$1
494     _ip=$2
495     _maskbits=$3
496
497     # Ensure interface is up
498     ip link set "$_iface" up || \
499         die "Failed to bringup interface $_iface"
500
501     # Only need to define broadcast for IPv4
502     case "$_ip" in
503         *:*) _bcast=""      ;;
504         *)   _bcast="brd +" ;;
505     esac
506
507     # Intentionally unquoted multi-word value here
508     # shellcheck disable=SC2086
509     ip addr add "$_ip/$_maskbits" $_bcast dev "$_iface" || {
510         echo "Failed to add $_ip/$_maskbits on dev $_iface"
511         return 1
512     }
513
514     # Wait 5 seconds for IPv6 addresses to stop being tentative...
515     if [ -z "$_bcast" ] ; then
516         for _x in $(seq 1 10) ; do
517             ip addr show to "${_ip}/128" | grep -q "tentative" || break
518             sleep 0.5
519         done
520
521         # If the address was a duplicate then it won't be on the
522         # interface so flag an error.
523         _t=$(ip addr show to "${_ip}/128")
524         case "$_t" in
525             "")
526                 echo "Failed to add $_ip/$_maskbits on dev $_iface"
527                 return 1
528                 ;;
529             *tentative*|*dadfailed*)
530                 echo "Failed to add $_ip/$_maskbits on dev $_iface"
531                 ip addr del "$_ip/$_maskbits" dev "$_iface"
532                 return 1
533                 ;;
534         esac
535     fi
536 }
537
538 delete_ip_from_iface()
539 {
540     _iface=$1
541     _ip=$2
542     _maskbits=$3
543
544     # This could be set globally for all interfaces but it is probably
545     # better to avoid surprises, so limit it the interfaces where CTDB
546     # has public IP addresses.  There isn't anywhere else convenient
547     # to do this so just set it each time.  This is much cheaper than
548     # remembering and re-adding secondaries.
549     set_proc "sys/net/ipv4/conf/${_iface}/promote_secondaries" 1
550
551     ip addr del "$_ip/$_maskbits" dev "$_iface" || {
552         echo "Failed to del $_ip on dev $_iface"
553         return 1
554     }
555 }
556
557 # If the given IP is hosted then print 2 items: maskbits and iface
558 ip_maskbits_iface ()
559 {
560     _addr="$1"
561
562     case "$_addr" in
563         *:*) _bits=128 ;;
564         *)   _bits=32  ;;
565     esac
566     ip addr show to "${_addr}/${_bits}" 2>/dev/null | \
567         awk 'NR == 1 { iface = $2; sub(":$", "", iface) ;
568                        sub("@.*", "", iface) }
569              $1 ~ /inet/ { mask = $2; sub(".*/", "", mask);
570                            print mask, iface }'
571 }
572
573 drop_ip ()
574 {
575     _addr="${1%/*}"  # Remove optional maskbits
576
577     # Intentional word splitting here
578     # shellcheck disable=SC2046
579     set -- $(ip_maskbits_iface "$_addr")
580     if [ -n "$1" ] ; then
581         _maskbits="$1"
582         _iface="$2"
583         echo "Removing public address $_addr/$_maskbits from device $_iface"
584         delete_ip_from_iface "$_iface" "$_addr" "$_maskbits" >/dev/null 2>&1
585     fi
586 }
587
588 drop_all_public_ips ()
589 {
590         # _x is intentionally ignored
591         # shellcheck disable=SC2034
592         while read _ip _x ; do
593                 drop_ip "$_ip"
594         done <"${CTDB_PUBLIC_ADDRESSES:-/dev/null}"
595 }
596
597 flush_route_cache ()
598 {
599     set_proc_maybe sys/net/ipv4/route/flush 1
600     set_proc_maybe sys/net/ipv6/route/flush 1
601 }
602
603 ########################################################
604 # Interface monitoring
605
606 # If the interface is a virtual one (e.g. VLAN) then get the
607 # underlying interface
608 interface_get_real ()
609 {
610     # Output of "ip link show <iface>"
611     _iface_info="$1"
612
613     # Extract the full interface description to see if it is a VLAN
614     _t=$(echo "$_iface_info" |
615                 awk 'NR == 1 { iface = $2; sub(":$", "", iface) ;
616                                print iface }')
617     case "$_t" in
618         *@*)
619             # VLAN: use the underlying interface, after the '@'
620             echo "${_t##*@}"
621             ;;
622         *)
623             # Not a regular VLAN.  For backward compatibility, assume
624             # there is some other sort of VLAN that doesn't have the
625             # '@' in the output and only use what is before a '.'.  If
626             # there is no '.' then this will be the whole interface
627             # name.
628             echo "${_t%%.*}"
629     esac
630 }
631
632 # Check whether an interface is operational
633 interface_monitor ()
634 {
635     _iface="$1"
636
637     _iface_info=$(ip link show "$_iface" 2>&1) || {
638         echo "ERROR: Monitored interface ${_iface} does not exist"
639         return 1
640     }
641
642
643     # If the interface is a virtual one (e.g. VLAN) then get the
644     # underlying interface.
645     _realiface=$(interface_get_real "$_iface_info")
646
647     if _bi=$(get_proc "net/bonding/${_realiface}" 2>/dev/null) ; then
648         # This is a bond: various monitoring strategies
649         echo "$_bi" | grep -q 'Currently Active Slave: None' && {
650             echo "ERROR: No active slaves for bond device ${_realiface}"
651             return 1
652         }
653         echo "$_bi" | grep -q '^MII Status: up' || {
654             echo "ERROR: public network interface ${_realiface} is down"
655             return 1
656         }
657         echo "$_bi" | grep -q '^Bonding Mode: IEEE 802.3ad Dynamic link aggregation' && {
658             # This works around a bug in the driver where the
659             # overall bond status can be up but none of the actual
660             # physical interfaces have a link.
661             echo "$_bi" | grep 'MII Status:' | tail -n +2 | grep -q '^MII Status: up' || {
662                 echo "ERROR: No active slaves for 802.ad bond device ${_realiface}"
663                 return 1
664             }
665         }
666
667         return 0
668     else
669         # Not a bond
670         case "$_iface" in
671             lo*)
672                 # loopback is always working
673                 return 0
674                 ;;
675             ib*)
676                 # we don't know how to test ib links
677                 return 0
678                 ;;
679             *)
680                 ethtool "$_iface" | grep -q 'Link detected: yes' || {
681                     # On some systems, this is not successful when a
682                     # cable is plugged but the interface has not been
683                     # brought up previously. Bring the interface up
684                     # and try again...
685                     ip link set "$_iface" up
686                     ethtool "$_iface" | grep -q 'Link detected: yes' || {
687                         echo "ERROR: No link on the public network interface ${_iface}"
688                         return 1
689                     }
690                 }
691                 return 0
692                 ;;
693         esac
694     fi
695 }
696
697 ########################################################
698 # Simple counters
699 _ctdb_counter_common () {
700     _service_name="${1:-${service_name:-${script_name}}}"
701     _counter_file="${CTDB_SCRIPT_VARDIR}/failcount/${_service_name}"
702     mkdir -p "${_counter_file%/*}" # dirname
703 }
704 # Some code passes an argument
705 # shellcheck disable=SC2120
706 ctdb_counter_init () {
707     _ctdb_counter_common "$1"
708
709     : >"$_counter_file"
710 }
711 ctdb_counter_incr () {
712     _ctdb_counter_common "$1"
713
714     # unary counting using newlines!
715     echo >>"$_counter_file"
716 }
717 ctdb_counter_get () {
718     _ctdb_counter_common "$1"
719     # unary counting!
720     stat -c "%s" "$_counter_file" 2>/dev/null || echo 0
721 }
722
723 ########################################################
724
725 ctdb_setup_service_state_dir ()
726 {
727         _s="${1:-${service_name}}"
728
729         _service_state_dir="${CTDB_SCRIPT_VARDIR}/service_state/${_s}"
730         mkdir -p "$_service_state_dir" ||
731                 die "Error creating state dir \"${_service_state_dir}\""
732
733         echo "$_service_state_dir"
734 }
735
736 ##################################################################
737 # Reconfigure a service on demand
738
739 _ctdb_service_reconfigure_common ()
740 {
741     _d="${CTDB_SCRIPT_VARDIR}/service_status/${service_name}"
742     mkdir -p "$_d"
743     _ctdb_service_reconfigure_flag="$_d/reconfigure"
744 }
745
746 ctdb_service_needs_reconfigure ()
747 {
748     _ctdb_service_reconfigure_common
749     [ -e "$_ctdb_service_reconfigure_flag" ]
750 }
751
752 ctdb_service_set_reconfigure ()
753 {
754     _ctdb_service_reconfigure_common
755     : >"$_ctdb_service_reconfigure_flag"
756 }
757
758 ctdb_service_unset_reconfigure ()
759 {
760     _ctdb_service_reconfigure_common
761     rm -f "$_ctdb_service_reconfigure_flag"
762 }
763
764 ctdb_service_reconfigure ()
765 {
766     echo "Reconfiguring service \"${service_name}\"..."
767     ctdb_service_unset_reconfigure
768     service_reconfigure || return $?
769     # Intentionally have this use $service_name as default
770     # shellcheck disable=SC2119
771     ctdb_counter_init
772 }
773
774 # Default service_reconfigure() function does nothing.
775 service_reconfigure ()
776 {
777     :
778 }
779
780 ##################################################################
781 # Does CTDB manage this service?
782
783 ctdb_compat_managed_service ()
784 {
785     if [ "$1" = "yes" -a "$2" = "$service_name" ] ; then
786         CTDB_MANAGED_SERVICES="$CTDB_MANAGED_SERVICES $2"
787     fi
788 }
789
790 is_ctdb_managed_service ()
791 {
792     assert_service_name
793
794     # $t is used just for readability and to allow better accurate
795     # matching via leading/trailing spaces
796     t=" $CTDB_MANAGED_SERVICES "
797
798     # Return 0 if "<space>$service_name<space>" appears in $t
799     if [ "${t#* ${service_name} }" != "${t}" ] ; then
800         return 0
801     fi
802
803     # If above didn't match then update $CTDB_MANAGED_SERVICES for
804     # backward compatibility and try again.
805     ctdb_compat_managed_service "$CTDB_MANAGES_VSFTPD"   "vsftpd"
806     ctdb_compat_managed_service "$CTDB_MANAGES_SAMBA"    "samba"
807     ctdb_compat_managed_service "$CTDB_MANAGES_WINBIND"  "winbind"
808     ctdb_compat_managed_service "$CTDB_MANAGES_HTTPD"    "apache2"
809     ctdb_compat_managed_service "$CTDB_MANAGES_HTTPD"    "httpd"
810     ctdb_compat_managed_service "$CTDB_MANAGES_ISCSI"    "iscsi"
811     ctdb_compat_managed_service "$CTDB_MANAGES_CLAMD"    "clamd"
812     ctdb_compat_managed_service "$CTDB_MANAGES_NFS"      "nfs"
813
814     t=" $CTDB_MANAGED_SERVICES "
815
816     # Return 0 if "<space>$service_name<space>" appears in $t
817     [ "${t#* ${service_name} }" != "${t}" ]
818 }
819
820 # Default service_start() and service_stop() functions.
821  
822 # These may be overridden in an eventscript.
823 service_start ()
824 {
825     service "$service_name" start
826 }
827
828 service_stop ()
829 {
830     service "$service_name" stop
831 }
832
833 ##################################################################
834
835 # This exists only for backward compatibility with 3rd party scripts
836 # that call it
837 ctdb_standard_event_handler ()
838 {
839     :
840 }
841
842 iptables_wrapper ()
843 {
844     _family="$1" ; shift
845     if [ "$_family" = "inet6" ] ; then
846         _iptables_cmd="ip6tables"
847     else
848         _iptables_cmd="iptables"
849     fi
850
851     # iptables doesn't like being re-entered, so flock-wrap it.
852     flock -w 30 "${CTDB_SCRIPT_VARDIR}/iptables.flock" "$_iptables_cmd" "$@"
853 }
854
855 # AIX (and perhaps others?) doesn't have mktemp
856 # type is commonly supported and more portable than which(1)
857 # shellcheck disable=SC2039
858 if ! type mktemp >/dev/null 2>&1 ; then
859     mktemp ()
860     {
861         _dir=false
862         if [ "$1" = "-d" ] ; then
863             _dir=true
864             shift
865         fi
866         _d="${TMPDIR:-/tmp}"
867         _hex10=$(dd if=/dev/urandom count=20 2>/dev/null | \
868             md5sum | \
869             sed -e 's@\(..........\).*@\1@')
870         _t="${_d}/tmp.${_hex10}"
871         (
872             umask 077
873             if $_dir ; then
874                 mkdir "$_t"
875             else
876                 : >"$_t"
877             fi
878         )
879         echo "$_t"
880     }
881 fi
882
883 ######################################################################
884 # NFS callout handling
885
886 nfs_callout_init ()
887 {
888         _state_dir="$1"
889
890         if [ -z "$CTDB_NFS_CALLOUT" ] ; then
891                 CTDB_NFS_CALLOUT="${CTDB_BASE}/nfs-linux-kernel-callout"
892         fi
893         # Always export, for statd callout
894         export CTDB_NFS_CALLOUT
895
896         # If the callout wants to use this then it must create it
897         export CTDB_NFS_CALLOUT_STATE_DIR="${_state_dir}/callout-state"
898
899         # Export, if set, for use by clustered NFS callouts
900         if [ -n "$CTDB_NFS_STATE_FS_TYPE" ] ; then
901                 export CTDB_NFS_STATE_FS_TYPE
902         fi
903         if [ -n "$CTDB_NFS_STATE_MNT" ] ; then
904                 export CTDB_NFS_STATE_MNT
905         fi
906
907         nfs_callout_cache="${_state_dir}/nfs_callout_cache"
908         nfs_callout_cache_callout="${nfs_callout_cache}/CTDB_NFS_CALLOUT"
909         nfs_callout_cache_ops="${nfs_callout_cache}/ops"
910 }
911
912 nfs_callout_register ()
913 {
914     mkdir -p "$nfs_callout_cache_ops"
915     rm -f "$nfs_callout_cache_ops"/*
916
917     echo "$CTDB_NFS_CALLOUT" >"$nfs_callout_cache_callout"
918
919     _t=$(eval "$CTDB_NFS_CALLOUT" "register")
920     if [ -n "$_t" ] ; then
921         echo "$_t" |
922             while IFS="" read _op ; do
923                 touch "${nfs_callout_cache_ops}/${_op}"
924             done
925     else
926         touch "${nfs_callout_cache_ops}/ALL"
927     fi
928 }
929
930 nfs_callout ()
931 {
932     # Re-run registration if $CTDB_NFS_CALLOUT has changed
933     _prev=""
934     if [ -r "$nfs_callout_cache_callout" ] ; then
935         read _prev <"$nfs_callout_cache_callout"
936     fi
937     if [ "$CTDB_NFS_CALLOUT" != "$_prev" ] ; then
938         nfs_callout_register
939     fi
940
941     # Run the operation if it is registered...
942     if [ -e "${nfs_callout_cache_ops}/${1}" ] || \
943            [ -e "${nfs_callout_cache_ops}/ALL" ]; then
944         eval "$CTDB_NFS_CALLOUT" "$@"
945     fi
946 }
947
948 ########################################################
949 # tickle handling
950 ########################################################
951
952 update_tickles ()
953 {
954         _port="$1"
955
956         tickledir="${CTDB_SCRIPT_VARDIR}/tickles"
957         mkdir -p "$tickledir"
958
959         # What public IPs do I hold?
960         _pnn=$(ctdb_get_pnn)
961         _ips=$($CTDB -X ip | awk -F'|' -v pnn="$_pnn" '$3 == pnn {print $2}')
962
963         # IPs and port as ss filters
964         _ip_filter=""
965         for _ip in $_ips ; do
966             _ip_filter="${_ip_filter}${_ip_filter:+ || }src [${_ip}]"
967         done
968         _port_filter="sport == :${_port}"
969
970         # Record connections to our public IPs in a temporary file.
971         # This temporary file is in CTDB's private state directory and
972         # $$ is used to avoid a very rare race involving CTDB's script
973         # debugging.  No security issue, nothing to see here...
974         _my_connections="${tickledir}/${_port}.connections.$$"
975         # Parentheses are needed around the filters for precedence but
976         # the parentheses can't be empty!
977         ss -tn state established \
978            "${_ip_filter:+( ${_ip_filter} )}" \
979            "${_port_filter:+( ${_port_filter} )}" |
980         awk 'NR > 1 {print $4, $3}' |
981         sort >"$_my_connections"
982
983         # Record our current tickles in a temporary file
984         _my_tickles="${tickledir}/${_port}.tickles.$$"
985         for _i in $_ips ; do
986                 $CTDB -X gettickles "$_i" "$_port" |
987                 awk -F'|' 'NR > 1 { printf "%s:%s %s:%s\n", $2, $3, $4, $5 }'
988         done |
989         sort >"$_my_tickles"
990
991         # Add tickles for connections that we haven't already got tickles for
992         comm -23 "$_my_connections" "$_my_tickles" | \
993                 $CTDB addtickle
994
995         # Remove tickles for connections that are no longer there
996         comm -13 "$_my_connections" "$_my_tickles" | \
997                 $CTDB deltickle
998
999         rm -f "$_my_connections" "$_my_tickles"
1000
1001         # Remove stale files from killed scripts
1002         # Files can't have spaces in name, more portable than -print0/-0
1003         # shellcheck disable=SC2038
1004         (cd "$tickledir" && find . -type f -mmin +10 | xargs -r rm)
1005 }
1006
1007 ########################################################
1008 # load a site local config file
1009 ########################################################
1010
1011 [ -n "$CTDB_RC_LOCAL" -a -x "$CTDB_RC_LOCAL" ] && {
1012         . "$CTDB_RC_LOCAL"
1013 }
1014
1015 [ -x "${CTDB_BASE}/rc.local" ] && {
1016         . "${CTDB_BASE}/rc.local"
1017 }
1018
1019 [ -d "${CTDB_BASE}/rc.local.d" ] && {
1020         for i in "${CTDB_BASE}/rc.local.d"/* ; do
1021                 [ -x "$i" ] && . "$i"
1022         done
1023 }
1024
1025 script_name="${0##*/}"       # basename