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"
564 ctdb_active_dir="$CTDB_VARDIR/active"
568 echo "node is \"$1\", \"${script_name}\" reports problem: $(cat $2)"
573 if [ -r "$ctdb_status_dir/$script_name/unhealthy" ] ; then
574 log_status_cat "unhealthy" "$ctdb_status_dir/$script_name/unhealthy"
576 elif [ -r "$ctdb_status_dir/$script_name/banned" ] ; then
577 log_status_cat "banned" "$ctdb_status_dir/$script_name/banned"
586 d="$ctdb_status_dir/$script_name"
593 for i in "banned" "unhealthy" ; do
600 ctdb_service_needs_reconfigure ()
602 [ -e "$ctdb_status_dir/$service_name/reconfigure" ]
605 ctdb_service_set_reconfigure ()
607 d="$ctdb_status_dir/$service_name"
612 ctdb_service_unset_reconfigure ()
614 rm -f "$ctdb_status_dir/$service_name/reconfigure"
617 ctdb_service_reconfigure ()
619 echo "Reconfiguring service \"$service_name\"..."
620 if [ -n "$service_reconfigure" ] ; then
621 eval $service_reconfigure
623 service "$service_name" restart
625 ctdb_service_unset_reconfigure
629 ctdb_compat_managed_service ()
631 if [ "$1" = "yes" ] ; then
636 is_ctdb_managed_service ()
638 _service_name="${1:-${service_name}}"
640 t=" $CTDB_MANAGED_SERVICES "
642 ctdb_compat_managed_service "$CTDB_MANAGES_VSFTPD" "vsftpd"
643 ctdb_compat_managed_service "$CTDB_MANAGES_SAMBA" "samba"
644 ctdb_compat_managed_service "$CTDB_MANAGES_SCP" "scp"
645 ctdb_compat_managed_service "$CTDB_MANAGES_WINBIND" "winbind"
646 ctdb_compat_managed_service "$CTDB_MANAGES_HTTPD" "httpd"
647 ctdb_compat_managed_service "$CTDB_MANAGES_ISCSI" "iscsi"
648 ctdb_compat_managed_service "$CTDB_MANAGES_CLAMD" "clamd"
649 ctdb_compat_managed_service "$CTDB_MANAGES_NFS" "nfs"
650 ctdb_compat_managed_service "$CTDB_MANAGES_NFS" "nfs-ganesha-gpfs"
652 # Returns 0 if "<space>$_service_name<space>" appears in $t
653 [ "${t#* ${_service_name} }" != "${t}" ]
656 ctdb_start_stop_service ()
658 _service_name="${1:-${service_name}}"
660 [ "$event_name" = "monitor" ] || return 0
662 _active="$ctdb_active_dir/$_service_name"
663 if is_ctdb_managed_service "$_service_name"; then
664 if ! [ -e "$_active" ] ; then
665 echo "Starting service $_service_name"
666 ctdb_service_start || exit $?
667 mkdir -p "$ctdb_active_dir"
672 if [ -e "$_active" ] ; then
673 echo "Stopping service $_service_name"
674 ctdb_service_stop || exit $?
681 ctdb_service_start ()
683 if [ -n "$service_start" ] ; then
684 eval $service_start || return $?
686 service "$service_name" start || return $?
693 if [ -n "$service_stop" ] ; then
696 service "$service_name" stop
700 ctdb_standard_event_handler ()
715 ipv4_host_addr_to_net_addr()
720 local HOST0=$(echo $HOST | awk -F . '{print $4}')
721 local HOST1=$(echo $HOST | awk -F . '{print $3}')
722 local HOST2=$(echo $HOST | awk -F . '{print $2}')
723 local HOST3=$(echo $HOST | awk -F . '{print $1}')
725 local HOST_NUM=$(( $HOST0 + $HOST1 * 256 + $HOST2 * (256 ** 2) + $HOST3 * (256 ** 3) ))
727 local MASK_NUM=$(( ( (2**32 - 1) * (2**(32 - $MASKBITS)) ) & (2**32 - 1) ))
729 local NET_NUM=$(( $HOST_NUM & $MASK_NUM))
731 local NET0=$(( $NET_NUM & 255 ))
732 local NET1=$(( ($NET_NUM & (255 * 256)) / 256 ))
733 local NET2=$(( ($NET_NUM & (255 * 256**2)) / 256**2 ))
734 local NET3=$(( ($NET_NUM & (255 * 256**3)) / 256**3 ))
736 echo "$NET3.$NET2.$NET1.$NET0"
739 ipv4_maskbits_to_net_mask()
743 local MASK_NUM=$(( ( (2**32 - 1) * (2**(32 - $MASKBITS)) ) & (2**32 - 1) ))
745 local MASK0=$(( $MASK_NUM & 255 ))
746 local MASK1=$(( ($MASK_NUM & (255 * 256)) / 256 ))
747 local MASK2=$(( ($MASK_NUM & (255 * 256**2)) / 256**2 ))
748 local MASK3=$(( ($MASK_NUM & (255 * 256**3)) / 256**3 ))
750 echo "$MASK3.$MASK2.$MASK1.$MASK0"
758 local N=`echo $ADDR | sed -e 's/[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*//'`
759 test -n "$N" && fail=1
761 local ADDR0=$(echo $ADDR | awk -F . '{print $4}')
762 local ADDR1=$(echo $ADDR | awk -F . '{print $3}')
763 local ADDR2=$(echo $ADDR | awk -F . '{print $2}')
764 local ADDR3=$(echo $ADDR | awk -F . '{print $1}')
766 test "$ADDR0" -gt 255 && fail=1
767 test "$ADDR1" -gt 255 && fail=1
768 test "$ADDR2" -gt 255 && fail=1
769 test "$ADDR3" -gt 255 && fail=1
771 test x"$fail" != x"0" && {
772 #echo "IPv4: '$ADDR' is not a valid address"
779 # iptables doesn't like being re-entered, so flock-wrap it.
782 flock -w 30 $CTDB_VARDIR/iptables-ctdb.flock /sbin/iptables "$@"
785 ########################################################
787 ########################################################
789 # Temporary directory for tickles.
790 tickledir="$CTDB_VARDIR/state/tickles"
791 mkdir -p "$tickledir"
797 mkdir -p "$tickledir" # Just in case
800 _pnn=$(ctdb pnn) ; _pnn=${_pnn#PNN:}
802 # What public IPs do I hold?
803 _ips=$(ctdb -Y ip | awk -F: -v pnn=$_pnn '$3 == pnn {print $2}')
805 # IPs as a regexp choice
806 _ipschoice="($(echo $_ips | sed -e 's/ /|/g' -e 's/\./\\\\./g'))"
808 # Record connections to our public IPs in a temporary file
809 _my_connections="${tickledir}/${_port}.connections"
810 rm -f "$_my_connections"
812 awk -v destpat="^${_ipschoice}:${_port}\$" \
813 '$1 == "tcp" && $6 == "ESTABLISHED" && $4 ~ destpat {print $5, $4}' |
814 sort >"$_my_connections"
816 # Record our current tickles in a temporary file
817 _my_tickles="${tickledir}/${_port}.tickles"
820 ctdb -Y gettickles $_i $_port |
821 awk -F: 'NR > 1 { printf "%s:%s %s:%s\n", $2, $3, $4, $5 }'
825 # Add tickles for connections that we haven't already got tickles for
826 comm -23 "$_my_connections" "$_my_tickles" |
827 while read _src _dst ; do
828 ctdb addtickle $_src $_dst
831 # Remove tickles for connections that are no longer there
832 comm -13 "$_my_connections" "$_my_tickles" |
833 while read _src _dst ; do
834 ctdb deltickle $_src $_dst
837 rm -f "$_my_connections" "$_my_tickles"
840 ########################################################
841 # load a site local config file
842 ########################################################
844 [ -x $CTDB_BASE/rc.local ] && {
845 . $CTDB_BASE/rc.local
848 [ -d $CTDB_BASE/rc.local.d ] && {
849 for i in $CTDB_BASE/rc.local.d/* ; do
850 [ -x "$i" ] && . "$i"
854 script_name="${0##*/}" # basename
855 service_name="$script_name" # default is just the script name