1 # Hey Emacs, this is a -*- shell-script -*- !!!
3 # utility functions for ctdb event scripts
5 PATH=/bin:/usr/bin:/usr/sbin:/sbin:$PATH
7 [ -z "$CTDB_VARDIR" ] && {
8 export CTDB_VARDIR="/var/ctdb"
10 [ -z "$CTDB_ETCDIR" ] && {
11 export CTDB_ETCDIR="/etc"
14 #######################################
15 # pull in a system config file, if any
19 foo="${service_config:-${service_name}}"
20 if [ -n "$foo" ] ; then
23 elif [ "$1" != "ctdb" ] ; then
27 if [ -f $CTDB_ETCDIR/sysconfig/$1 ]; then
28 . $CTDB_ETCDIR/sysconfig/$1
29 elif [ -f $CTDB_ETCDIR/default/$1 ]; then
30 . $CTDB_ETCDIR/default/$1
31 elif [ -f $CTDB_BASE/sysconfig/$1 ]; then
32 . $CTDB_BASE/sysconfig/$1
40 ##############################################################
41 # determine on what type of system (init style) we are running
43 # only do detection if not already set:
44 test "x$CTDB_INIT_STYLE" != "x" && return
46 if [ -x /sbin/startproc ]; then
47 CTDB_INIT_STYLE="suse"
48 elif [ -x /sbin/start-stop-daemon ]; then
49 CTDB_INIT_STYLE="debian"
51 CTDB_INIT_STYLE="redhat"
55 ######################################################
56 # simulate /sbin/service on platforms that don't have it
57 # _service() makes it easier to hook the service() function for
64 # do nothing, when no service was specified
65 [ -z "$_service_name" ] && return
67 if [ -x /sbin/service ]; then
68 $_nice /sbin/service "$_service_name" "$_op"
69 elif [ -x $CTDB_ETCDIR/init.d/$_service_name ]; then
70 $_nice $CTDB_ETCDIR/init.d/$_service_name "$_op"
71 elif [ -x $CTDB_ETCDIR/rc.d/init.d/$_service_name ]; then
72 $_nice $CTDB_ETCDIR/rc.d/init.d/$_service_name "$_op"
82 ######################################################
83 # simulate /sbin/service (niced) on platforms that don't have it
90 ######################################################
91 # wrapper around /proc/ settings to allow them to be hooked
93 # 1st arg is relative path under /proc/, 2nd arg is value to set
99 ######################################################
100 # wrapper around getting file contents from /proc/ to allow
101 # this to be hooked for testing
102 # 1st arg is relative path under /proc/
108 ######################################################
109 # check that a rpc server is registered with portmap
110 # and responding to requests
111 # usage: ctdb_check_rpc SERVICE_NAME PROGNUM VERSION
112 ######################################################
118 ctdb_check_rpc_out=$(rpcinfo -u localhost $prognum $version 2>&1)
119 if [ $? -ne 0 ] ; then
120 ctdb_check_rpc_out="ERROR: $progname failed RPC check:
122 echo "$ctdb_check_rpc_out"
127 ######################################################
128 # check a set of directories is available
129 # return 1 on a missing directory
130 # usage: ctdb_check_directories_probe SERVICE_NAME <directories...>
131 ######################################################
132 ctdb_check_directories_probe() {
133 while IFS="" read d ; do
139 [ -d "${d}/." ] || return 1
144 ######################################################
145 # check a set of directories is available
146 # usage: ctdb_check_directories SERVICE_NAME <directories...>
147 ######################################################
148 ctdb_check_directories() {
149 n="${1:-${service_name}}"
150 ctdb_check_directories_probe || {
151 echo "ERROR: $n directory \"$d\" not available"
156 ######################################################
157 # check a set of tcp ports
158 # usage: ctdb_check_tcp_ports <ports...>
159 ######################################################
160 ctdb_check_tcp_ports() {
163 if ! netstat -a -t -n | grep -q "0\.0\.0\.0:$p .*LISTEN" ; then
164 if ! netstat -a -t -n | grep -q ":::$p .*LISTEN" ; then
165 echo "ERROR: $service_name tcp port $p is not responding"
172 ######################################################
173 # check a unix socket
174 # usage: ctdb_check_unix_socket SERVICE_NAME <socket_path>
175 ######################################################
176 ctdb_check_unix_socket() {
178 [ -z "$socket_path" ] && return
180 if ! netstat --unix -a -n | grep -q "^unix.*LISTEN.*${socket_path}$"; then
181 echo "ERROR: $service_name socket $socket_path not found"
186 ######################################################
187 # check a command returns zero status
188 # usage: ctdb_check_command SERVICE_NAME <command>
189 ######################################################
190 ctdb_check_command() {
193 [ -z "$wait_cmd" ] && return;
194 $wait_cmd > /dev/null 2>&1 || {
195 echo "ERROR: $service_name - $wait_cmd returned error"
200 ################################################
201 # kill off any TCP connections with the given IP
202 ################################################
203 kill_tcp_connections() {
208 connfile="$CTDB_VARDIR/state/connections.$_IP"
209 netstat -tn |egrep "^tcp.*[[:space:]]+$_IP:.*ESTABLISHED" | awk '{print $4" "$5}' > $connfile
210 netstat -tn |egrep "^tcp.*[[:space:]]+::ffff:$_IP:.*ESTABLISHED" | awk '{print $4" "$5}' >> $connfile
212 while read dest src; do
213 srcip=`echo $src | sed -e "s/:[^:]*$//"`
214 srcport=`echo $src | sed -e "s/^.*://"`
215 destip=`echo $dest | sed -e "s/:[^:]*$//"`
216 destport=`echo $dest | sed -e "s/^.*://"`
217 echo "Killing TCP connection $srcip:$srcport $destip:$destport"
218 ctdb killtcp $srcip:$srcport $destip:$destport >/dev/null 2>&1 || _failed=1
220 # we only do one-way killtcp for CIFS
222 # for all others we do 2-way
224 ctdb killtcp $destip:$destport $srcip:$srcport >/dev/null 2>&1 || _failed=1
227 _killcount=`expr $_killcount + 1`
231 [ $_failed = 0 ] || {
232 echo "Failed to send killtcp control"
235 [ $_killcount -gt 0 ] || {
239 while netstat -tn |egrep "^tcp.*[[:space:]]+$_IP:.*ESTABLISHED" > /dev/null; do
241 _count=`expr $_count + 1`
242 [ $_count -gt 3 ] && {
243 echo "Timed out killing tcp connections for IP $_IP"
247 echo "killed $_killcount TCP connections to released IP $_IP"
250 ##################################################################
251 # kill off the local end for any TCP connections with the given IP
252 ##################################################################
253 kill_tcp_connections_local_only() {
258 connfile="$CTDB_VARDIR/state/connections.$_IP"
259 netstat -tn |egrep "^tcp.*[[:space:]]+$_IP:.*ESTABLISHED" | awk '{print $4" "$5}' > $connfile
260 netstat -tn |egrep "^tcp.*[[:space:]]+::ffff:$_IP:.*ESTABLISHED" | awk '{print $4" "$5}' >> $connfile
262 while read dest src; do
263 srcip=`echo $src | sed -e "s/:[^:]*$//"`
264 srcport=`echo $src | sed -e "s/^.*://"`
265 destip=`echo $dest | sed -e "s/:[^:]*$//"`
266 destport=`echo $dest | sed -e "s/^.*://"`
267 echo "Killing TCP connection $srcip:$srcport $destip:$destport"
268 ctdb killtcp $srcip:$srcport $destip:$destport >/dev/null 2>&1 || _failed=1
269 _killcount=`expr $_killcount + 1`
273 [ $_failed = 0 ] || {
274 echo "Failed to send killtcp control"
277 [ $_killcount -gt 0 ] || {
281 while netstat -tn |egrep "^tcp.*[[:space:]]+$_IP:.*ESTABLISHED" > /dev/null; do
283 _count=`expr $_count + 1`
284 [ $_count -gt 3 ] && {
285 echo "Timed out killing tcp connections for IP $_IP"
289 echo "killed $_killcount TCP connections to released IP $_IP"
292 ##################################################################
293 # tickle any TCP connections with the given IP
294 ##################################################################
295 tickle_tcp_connections() {
300 connfile="$CTDB_VARDIR/state/connections.$_IP"
301 netstat -tn |egrep "^tcp.*[[:space:]]+$_IP:.*ESTABLISHED" | awk '{print $4" "$5}' > $connfile
302 netstat -tn |egrep "^tcp.*[[:space:]]+::ffff:$_IP:.*ESTABLISHED" | awk '{print $4" "$5}' >> $connfile
304 while read dest src; do
305 srcip=`echo $src | sed -e "s/:[^:]*$//"`
306 srcport=`echo $src | sed -e "s/^.*://"`
307 destip=`echo $dest | sed -e "s/:[^:]*$//"`
308 destport=`echo $dest | sed -e "s/^.*://"`
309 echo "Tickle TCP connection $srcip:$srcport $destip:$destport"
310 ctdb tickle $srcip:$srcport $destip:$destport >/dev/null 2>&1 || _failed=1
311 echo "Tickle TCP connection $destip:$destport $srcip:$srcport"
312 ctdb tickle $destip:$destport $srcip:$srcport >/dev/null 2>&1 || _failed=1
316 [ $_failed = 0 ] || {
317 echo "Failed to send tickle control"
322 ########################################################
323 # start/stop the nfs service on different platforms
324 ########################################################
327 [ -x $CTDB_ETCDIR/init.d/nfsserver ] && {
330 [ -x $CTDB_ETCDIR/init.d/nfslock ] && {
338 service nfsserver start
341 service nfsserver stop > /dev/null 2>&1
344 set_proc "fs/nfsd/threads" 0
345 service nfsserver stop > /dev/null 2>&1
347 service nfsserver start
354 service nfslock start
358 service nfs stop > /dev/null 2>&1
359 service nfslock stop > /dev/null 2>&1
362 set_proc "fs/nfsd/threads" 0
363 service nfs stop > /dev/null 2>&1
364 service nfslock stop > /dev/null 2>&1
366 service nfslock start
372 echo "Unknown platform. NFS is not supported with ctdb"
378 ########################################################
379 # start/stop the nfs lockmanager service on different platforms
380 ########################################################
381 startstop_nfslock() {
383 [ -x $CTDB_ETCDIR/init.d/nfsserver ] && {
386 [ -x $CTDB_ETCDIR/init.d/nfslock ] && {
392 # for sles there is no service for lockmanager
393 # so we instead just shutdown/restart nfs
396 service nfsserver start
399 service nfsserver stop > /dev/null 2>&1
402 service nfsserver stop
403 service nfsserver start
410 service nfslock start
413 service nfslock stop > /dev/null 2>&1
417 service nfslock start
422 echo "Unknown platform. NFS locking is not supported with ctdb"
428 # better use delete_ip_from_iface() together with add_ip_to_iface
429 # remove_ip should be removed in future
431 local _ip_maskbits=$1
433 local _ip=`echo "$_ip_maskbits" | cut -d '/' -f1`
434 local _maskbits=`echo "$_ip_maskbits" | cut -d '/' -f2`
436 delete_ip_from_iface "$_iface" "$_ip" "$_maskbits"
445 local _state_dir="$CTDB_VARDIR/state/interface_modify"
446 local _lockfile="$_state_dir/$_iface.flock"
447 local _readd_base="$_state_dir/$_iface.readd.d"
449 mkdir -p $_state_dir || {
451 echo "Failed to mkdir -p $_state_dir - $ret"
455 test -f $_lockfile || {
459 flock --timeout 30 $_lockfile $CTDB_BASE/interface_modify.sh add "$_iface" "$_ip" "$_maskbits" "$_readd_base"
463 delete_ip_from_iface()
468 local _state_dir="$CTDB_VARDIR/state/interface_modify"
469 local _lockfile="$_state_dir/$_iface.flock"
470 local _readd_base="$_state_dir/$_iface.readd.d"
472 mkdir -p $_state_dir || {
474 echo "Failed to mkdir -p $_state_dir - $ret"
478 test -f $_lockfile || {
482 flock --timeout 30 $_lockfile $CTDB_BASE/interface_modify.sh delete "$_iface" "$_ip" "$_maskbits" "$_readd_base"
486 setup_iface_ip_readd_script()
491 local _readd_script=$4
492 local _state_dir="$CTDB_VARDIR/state/interface_modify"
493 local _lockfile="$_state_dir/$_iface.flock"
494 local _readd_base="$_state_dir/$_iface.readd.d"
496 mkdir -p $_state_dir || {
498 echo "Failed to mkdir -p $_state_dir - $ret"
502 test -f $_lockfile || {
506 flock --timeout 30 $_lockfile $CTDB_BASE/interface_modify.sh readd_script "$_iface" "$_ip" "$_maskbits" "$_readd_base" "$_readd_script"
510 ########################################################
511 # some simple logic for counting events - per eventscript
512 # usage: ctdb_counter_init
514 # ctdb_check_counter_limit <limit>
515 # ctdb_check_counter_limit succeeds when count >= <limit>
516 ########################################################
517 _ctdb_counter_common () {
518 _counter_file="$ctdb_fail_dir/$service_name"
519 mkdir -p "${_counter_file%/*}" # dirname
521 ctdb_counter_init () {
526 ctdb_counter_incr () {
530 echo -n 1 >> "$_counter_file"
532 ctdb_check_counter_limit () {
535 _limit="${1:-${service_fail_limit}}"
539 _size=$(stat -c "%s" "$_counter_file" 2>/dev/null || echo 0)
540 if [ $_size -ge $_limit ] ; then
541 echo "ERROR: more than $_limit consecutive failures for $service_name, marking cluster unhealthy"
543 elif [ $_size -gt 0 -a -z "$_quiet" ] ; then
544 echo "WARNING: less than $_limit consecutive failures ($_size) for $service_name, not unhealthy yet"
547 ctdb_check_counter_equal () {
553 _size=$(stat -c "%s" "$_counter_file" 2>/dev/null || echo 0)
554 if [ $_size -eq $_limit ] ; then
560 ########################################################
562 ctdb_status_dir="$CTDB_VARDIR/status"
563 ctdb_fail_dir="$CTDB_VARDIR/failcount"
565 ########################################################
566 # Managed status history, for auto-start/stop
568 ctdb_managed_dir="$CTDB_VARDIR/managed_history"
570 _ctdb_managed_common ()
572 _service_name="${1:-${service_name}}"
573 _ctdb_managed_file="$ctdb_managed_dir/$_service_name"
576 ctdb_service_managed ()
578 _ctdb_managed_common "$@"
579 mkdir -p "$ctdb_managed_dir"
580 touch "$_ctdb_managed_file"
583 ctdb_service_unmanaged ()
585 _ctdb_managed_common "$@"
586 rm -f "$_ctdb_managed_file"
589 is_ctdb_previously_managed_service ()
591 _ctdb_managed_common "$@"
592 [ -f "$_ctdb_managed_file" ]
595 ########################################################
596 # Check and set status
600 echo "node is \"$1\", \"${script_name}\" reports problem: $(cat $2)"
605 if [ -r "$ctdb_status_dir/$script_name/unhealthy" ] ; then
606 log_status_cat "unhealthy" "$ctdb_status_dir/$script_name/unhealthy"
608 elif [ -r "$ctdb_status_dir/$script_name/banned" ] ; then
609 log_status_cat "banned" "$ctdb_status_dir/$script_name/banned"
618 d="$ctdb_status_dir/$script_name"
625 for i in "banned" "unhealthy" ; do
632 ctdb_service_needs_reconfigure ()
634 [ -e "$ctdb_status_dir/$service_name/reconfigure" ]
637 ctdb_service_set_reconfigure ()
639 d="$ctdb_status_dir/$service_name"
644 ctdb_service_unset_reconfigure ()
646 rm -f "$ctdb_status_dir/$service_name/reconfigure"
649 ctdb_service_reconfigure ()
651 echo "Reconfiguring service \"$service_name\"..."
652 if [ -n "$service_reconfigure" ] ; then
653 eval $service_reconfigure
655 service "$service_name" restart
657 ctdb_service_unset_reconfigure
661 ctdb_compat_managed_service ()
663 if [ "$1" = "yes" ] ; then
668 is_ctdb_managed_service ()
670 _service_name="${1:-${service_name}}"
672 t=" $CTDB_MANAGED_SERVICES "
674 ctdb_compat_managed_service "$CTDB_MANAGES_VSFTPD" "vsftpd"
675 ctdb_compat_managed_service "$CTDB_MANAGES_SAMBA" "samba"
676 ctdb_compat_managed_service "$CTDB_MANAGES_SCP" "scp"
677 ctdb_compat_managed_service "$CTDB_MANAGES_WINBIND" "winbind"
678 ctdb_compat_managed_service "$CTDB_MANAGES_HTTPD" "httpd"
679 ctdb_compat_managed_service "$CTDB_MANAGES_ISCSI" "iscsi"
680 ctdb_compat_managed_service "$CTDB_MANAGES_CLAMD" "clamd"
681 ctdb_compat_managed_service "$CTDB_MANAGES_NFS" "nfs"
682 ctdb_compat_managed_service "$CTDB_MANAGES_NFS" "nfs-ganesha-gpfs"
684 # Returns 0 if "<space>$_service_name<space>" appears in $t
685 [ "${t#* ${_service_name} }" != "${t}" ]
688 ctdb_start_stop_service ()
690 _service_name="${1:-${service_name}}"
692 [ "$event_name" = "monitor" ] || return 0
694 if is_ctdb_managed_service "$_service_name" ; then
695 if ! is_ctdb_previously_managed_service "$_service_name" ; then
696 echo "Starting service $_service_name"
697 ctdb_service_start || exit $?
698 ctdb_service_managed "$_service_name"
702 if is_ctdb_previously_managed_service "$_service_name" ; then
703 echo "Stopping service $_service_name"
704 ctdb_service_stop || exit $?
705 ctdb_service_unmanaged "$_service_name"
711 ctdb_service_start ()
713 if [ -n "$service_start" ] ; then
714 eval $service_start || return $?
716 service "$service_name" start || return $?
723 if [ -n "$service_stop" ] ; then
726 service "$service_name" stop
730 ctdb_standard_event_handler ()
745 ipv4_host_addr_to_net_addr()
750 local HOST0=$(echo $HOST | awk -F . '{print $4}')
751 local HOST1=$(echo $HOST | awk -F . '{print $3}')
752 local HOST2=$(echo $HOST | awk -F . '{print $2}')
753 local HOST3=$(echo $HOST | awk -F . '{print $1}')
755 local HOST_NUM=$(( $HOST0 + $HOST1 * 256 + $HOST2 * (256 ** 2) + $HOST3 * (256 ** 3) ))
757 local MASK_NUM=$(( ( (2**32 - 1) * (2**(32 - $MASKBITS)) ) & (2**32 - 1) ))
759 local NET_NUM=$(( $HOST_NUM & $MASK_NUM))
761 local NET0=$(( $NET_NUM & 255 ))
762 local NET1=$(( ($NET_NUM & (255 * 256)) / 256 ))
763 local NET2=$(( ($NET_NUM & (255 * 256**2)) / 256**2 ))
764 local NET3=$(( ($NET_NUM & (255 * 256**3)) / 256**3 ))
766 echo "$NET3.$NET2.$NET1.$NET0"
769 ipv4_maskbits_to_net_mask()
773 local MASK_NUM=$(( ( (2**32 - 1) * (2**(32 - $MASKBITS)) ) & (2**32 - 1) ))
775 local MASK0=$(( $MASK_NUM & 255 ))
776 local MASK1=$(( ($MASK_NUM & (255 * 256)) / 256 ))
777 local MASK2=$(( ($MASK_NUM & (255 * 256**2)) / 256**2 ))
778 local MASK3=$(( ($MASK_NUM & (255 * 256**3)) / 256**3 ))
780 echo "$MASK3.$MASK2.$MASK1.$MASK0"
788 local N=`echo $ADDR | sed -e 's/[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*//'`
789 test -n "$N" && fail=1
791 local ADDR0=$(echo $ADDR | awk -F . '{print $4}')
792 local ADDR1=$(echo $ADDR | awk -F . '{print $3}')
793 local ADDR2=$(echo $ADDR | awk -F . '{print $2}')
794 local ADDR3=$(echo $ADDR | awk -F . '{print $1}')
796 test "$ADDR0" -gt 255 && fail=1
797 test "$ADDR1" -gt 255 && fail=1
798 test "$ADDR2" -gt 255 && fail=1
799 test "$ADDR3" -gt 255 && fail=1
801 test x"$fail" != x"0" && {
802 #echo "IPv4: '$ADDR' is not a valid address"
809 # iptables doesn't like being re-entered, so flock-wrap it.
812 flock -w 30 $CTDB_VARDIR/iptables-ctdb.flock /sbin/iptables "$@"
815 ########################################################
817 ########################################################
819 # Temporary directory for tickles.
820 tickledir="$CTDB_VARDIR/state/tickles"
821 mkdir -p "$tickledir"
827 mkdir -p "$tickledir" # Just in case
830 _pnn=$(ctdb pnn) ; _pnn=${_pnn#PNN:}
832 # What public IPs do I hold?
833 _ips=$(ctdb -Y ip | awk -F: -v pnn=$_pnn '$3 == pnn {print $2}')
835 # IPs as a regexp choice
836 _ipschoice="($(echo $_ips | sed -e 's/ /|/g' -e 's/\./\\\\./g'))"
838 # Record connections to our public IPs in a temporary file
839 _my_connections="${tickledir}/${_port}.connections"
840 rm -f "$_my_connections"
842 awk -v destpat="^${_ipschoice}:${_port}\$" \
843 '$1 == "tcp" && $6 == "ESTABLISHED" && $4 ~ destpat {print $5, $4}' |
844 sort >"$_my_connections"
846 # Record our current tickles in a temporary file
847 _my_tickles="${tickledir}/${_port}.tickles"
850 ctdb -Y gettickles $_i $_port |
851 awk -F: 'NR > 1 { printf "%s:%s %s:%s\n", $2, $3, $4, $5 }'
855 # Add tickles for connections that we haven't already got tickles for
856 comm -23 "$_my_connections" "$_my_tickles" |
857 while read _src _dst ; do
858 ctdb addtickle $_src $_dst
861 # Remove tickles for connections that are no longer there
862 comm -13 "$_my_connections" "$_my_tickles" |
863 while read _src _dst ; do
864 ctdb deltickle $_src $_dst
867 rm -f "$_my_connections" "$_my_tickles"
870 ########################################################
871 # load a site local config file
872 ########################################################
874 [ -n "$CTDB_RC_LOCAL" -a -x "$CTDB_RC_LOCAL" ] && {
878 [ -x $CTDB_BASE/rc.local ] && {
879 . $CTDB_BASE/rc.local
882 [ -d $CTDB_BASE/rc.local.d ] && {
883 for i in $CTDB_BASE/rc.local.d/* ; do
884 [ -x "$i" ] && . "$i"
888 script_name="${0##*/}" # basename
889 service_name="$script_name" # default is just the script name