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