tools/ctdb: "ctdb runstate" now accepts optional expected run state arguments
[obnox/ctdb.git] / config / ctdb.init
index 9b30b860045fca6781495fafc794dbbfbb6cfdd0..70dcfa642169408d32bf773942764e59ecb8c110 100755 (executable)
@@ -6,7 +6,7 @@
 # chkconfig:           - 90 01
 #
 # description:                 Starts and stops the clustered tdb daemon
-# pidfile:             /var/run/ctdbd/ctdbd.pid
+# pidfile:             /var/run/ctdb/ctdbd.pid
 #
 
 ### BEGIN INIT INFO
@@ -32,6 +32,10 @@ fi
     LC_ALL=en_US.UTF-8
 }
 
+if [ -f /lib/lsb/init-functions ] ; then
+    . /lib/lsb/init-functions
+fi
+
 # Avoid using root's TMPDIR
 unset TMPDIR
 
@@ -50,6 +54,7 @@ detect_init_style
 export CTDB_INIT_STYLE
 
 ctdbd=${CTDBD:-/usr/sbin/ctdbd}
+pidfile="/var/run/ctdb/ctdbd.pid"
 
 if [ "$CTDB_VALGRIND" = "yes" ]; then
     init_style="valgrind"
@@ -84,6 +89,9 @@ build_ctdb_options () {
     }
     maybe_set "--reclock"                "$CTDB_RECOVERY_LOCK"
 
+    mkdir -p $(dirname "$pidfile")
+    maybe_set "--pidfile"                "$pidfile"
+
     # build up CTDB_OPTIONS variable from optional parameters
     maybe_set "--logfile"                "$CTDB_LOGFILE"
     maybe_set "--nlist"                  "$CTDB_NODES"
@@ -107,93 +115,101 @@ build_ctdb_options () {
     maybe_set "--max-persistent-check-errors" "$CTDB_MAX_PERSISTENT_CHECK_ERRORS"
 }
 
-check_tdb () {
-       local PDBASE=$1
-
-       test x"$TDBTOOL_HAS_CHECK" = x"1" && {
-               #
-               # Note tdbtool always exits with 0
-               #
-               local OK=`tdbtool $PDBASE check | grep "Database integrity is OK" | wc -l`
-               test x"$OK" = x"1" || {
-                       return 1;
-               }
-
-               return 0;
-       }
-
-       tdbdump $PDBASE >/dev/null 2>/dev/null || {
-               return $?;
-       }
-
-       return 0;
+export_debug_variables ()
+{
+    export CTDB_DEBUG_HUNG_SCRIPT CTDB_EXTERNAL_TRACE
 }
 
-check_persistent_databases () {
-    PERSISTENT_DB_DIR="${CTDB_DBDIR:-/var/ctdb}/persistent"
-    mkdir -p $PERSISTENT_DB_DIR 2>/dev/null
-    local ERRCOUNT=$CTDB_MAX_PERSISTENT_CHECK_ERRORS
-
-    test -z "$ERRCOUNT" && {
-       ERRCOUNT="0"
-    }
-    test x"$ERRCOUNT" != x"0" && {
-       return 0;
-    }
+# Log given message or stdin to either syslog or a CTDB log file
+do_log ()
+{
+    script_log "ctdb.init" "$@"
+}
 
-    if test -x /usr/bin/tdbtool ; then
-        HAVE_TDBTOOL=1
+select_tdb_checker ()
+{
+    # Find the best TDB consistency check available.
+    use_tdb_tool_check=false
+    if which tdbtool >/dev/null 2>&1 && \
+       echo "help" | tdbtool | grep -q check ; then
+
+       use_tdb_tool_check=true
+    elif which tdbtool >/dev/null 2>&1 && which tdbdump >/dev/null 2>&1 ; then
+           do_log <<EOF
+WARNING: The installed 'tdbtool' does not offer the 'check' subcommand.
+ Using 'tdbdump' for database checks.
+ Consider updating 'tdbtool' for better checks!
+EOF
+    elif which tdbdump >/dev/null 2>&1 ; then
+       do_log <<EOF
+WARNING: 'tdbtool' is not available.
+ Using 'tdbdump' to check the databases.
+ Consider installing a recent 'tdbtool' for better checks!
+EOF
     else
-        HAVE_TDBTOOL=0
+       do_log <<EOF
+WARNING: Cannot check databases since neither
+ 'tdbdump' nor 'tdbtool check' is available.
+ Consider installing tdbtool or at least tdbdump!
+EOF
+        return 1
     fi
+}
 
-    if test x"$HAVE_TDBTOOL" = x"1" ; then
-        TDBTOOL_HAS_CHECK=`echo "help" | /usr/bin/tdbtool | grep check | wc -l`
-    else
-        TDBTOOL_HAS_CHECK=0
-    fi
+check_tdb ()
+{
+    _db="$1"
 
-    if test -x /usr/bin/tdbdump ; then
-        HAVE_TDBDUMP=1
+    if $use_tdb_tool_check ; then
+       # tdbtool always exits with 0  :-(
+       if tdbtool "$_db" check 2>/dev/null |
+           grep -q "Database integrity is OK" ; then
+           return 0
+       else
+           return 1
+       fi
     else
-        HAVE_TDBDUMP=0
+       tdbdump "$_db" >/dev/null 2>/dev/null
+       return $?
     fi
+}
 
-    if test x"$HAVE_TDBDUMP" = x"0" -a x"$TDBTOOL_HAS_CHECK" = x"0" ; then
-        echo "WARNING: Cannot check persistent databases since"
-        echo "neither 'tdbdump' nor 'tdbtool check' is available."
-        echo "Consider installing tdbtool or at least tdbdump!"
-        return 0
-    fi
+check_persistent_databases ()
+{
+    _dir="${CTDB_DBDIR_PERSISTENT:-${CTDB_DBDIR:-/var/ctdb}/persistent}"
+    mkdir -p "$_dir" 2>/dev/null
 
-    if test x"$HAVE_TDBDUMP" = x"1" -a x"$TDBTOOL_HAS_CHECK" = x"0" ; then
-        if test x"$HAVE_TDBTOOL" = x"0"; then
-            echo "WARNING: 'tdbtool' is not available. Using 'tdbdump' to"
-            echo "check the persistent databases."
-            echo "Consider installing a recent 'tdbtool' for better checks!"
-        else
-            echo "WARNING: The installed 'tdbtool' does not offer the 'check'"
-            echo "subcommand. Using 'tdbdump' for persistent database checks."
-            echo "Consider updating 'tdbtool' for better checks!"
-        fi
-    fi
+    [ "${CTDB_MAX_PERSISTENT_CHECK_ERRORS:-0}" = "0" ] || return 0
 
-    for PDBASE in `ls $PERSISTENT_DB_DIR/*.tdb.[0-9] 2>/dev/null`; do
-       check_tdb $PDBASE || {
-           echo "Persistent database $PDBASE is corrupted! CTDB will not start."
+    for _db in $(ls "$_dir/"*.tdb.*[0-9] 2>/dev/null) ; do
+       check_tdb $_db || {
+           do_log "Persistent database $_db is corrupted! CTDB will not start."
            return 1
        }
     done
 }
 
-set_ctdb_variables () {
-    # set any tunables from the config file
-    set | grep ^CTDB_SET_ | cut -d_ -f3- | 
-    while read v; do
-       varname=`echo $v | cut -d= -f1`
-       value=`echo $v | cut -d= -f2`
-       ctdb setvar $varname $value || RETVAL=1
-    done || exit 1
+check_non_persistent_databases ()
+{
+    _dir="${CTDB_DBDIR:-/var/ctdb}"
+    mkdir -p "$_dir" 2>/dev/null
+
+    for _db in $(ls "${_dir}/"*.tdb.*[0-9] 2>/dev/null) ; do
+       check_tdb $_db || {
+           _backup="${_db}.$(date +'%Y%m%d.%H%M%S.%N').corrupt"
+           do_log <<EOF
+WARNING: database ${_db} is corrupted.
+ Moving to backup ${_backup} for later analysis.
+EOF
+           mv "$_db" "$_backup"
+
+           # Now remove excess backups
+           ls -td "${_db}."*".corrupt" |
+           tail -n +$((${CTDB_MAX_CORRUPT_DB_BACKUPS:-10} + 1)) |
+           xargs rm -f
+           
+       }
+    done
 }
 
 set_retval() {
@@ -204,7 +220,7 @@ wait_until_ready () {
     _timeout="${1:-10}" # default is 10 seconds
 
     _count=0
-    while ! ctdb ping >/dev/null 2>&1 ; do
+    while ! ctdb runstate startup running >/dev/null 2>&1 ; do
        if [ $_count -ge $_timeout ] ; then
            return 1
        fi
@@ -213,18 +229,6 @@ wait_until_ready () {
     done
 }
 
-ctdbd=${CTDBD:-/usr/sbin/ctdbd}
-
-drop_all_public_ips() {
-    [ -z "$CTDB_PUBLIC_ADDRESSES" ] && {
-       return
-    }
-
-    cat $CTDB_PUBLIC_ADDRESSES | while read IP IFACE REST; do
-       ip addr del $IP dev $IFACE >/dev/null 2>/dev/null
-    done
-}
-
 start() {
     echo -n $"Starting ctdbd service: "
 
@@ -233,13 +237,24 @@ start() {
        return 0
     }
 
+    # About to start new $ctdbd.  The ping above has failed and any
+    # new $ctdbd will destroy the Unix domain socket, so any processes
+    # that aren't yet completely useless soon will be...  so kill
+    # them.
+    pkill -9 -f "$ctdbd"
+
     build_ctdb_options
 
+    export_debug_variables
+
     # make sure we drop any ips that might still be held if previous
     # instance of ctdb got killed with -9 or similar
-    drop_all_public_ips
+    drop_all_public_ips "ctdb.init"
 
-    check_persistent_databases || return $?
+    if select_tdb_checker ; then
+       check_persistent_databases || return $?
+       check_non_persistent_databases
+    fi
 
     if [ "$CTDB_SUPPRESS_COREFILE" = "yes" ]; then
        ulimit -c 0
@@ -271,10 +286,9 @@ start() {
     esac
 
     if [ $RETVAL -eq 0 ] ; then
-       if wait_until_ready ; then
-           set_ctdb_variables
-       else
+       if ! wait_until_ready ; then
            RETVAL=1
+           echo "Timed out waiting for initialisation - killing CTDB"
            pkill -9 -f $ctdbd >/dev/null 2>&1
        fi
     fi
@@ -313,7 +327,7 @@ stop() {
     while pkill -0 -f $ctdbd ; do
        sleep 1
        count=$(($count + 1))
-       [ $count -gt 10 ] && {
+       [ $count -gt 30 ] && {
            echo -n $"killing ctdbd "
            pkill -9 -f $ctdbd
            pkill -9 -f $CTDB_BASE/events.d/
@@ -322,6 +336,8 @@ stop() {
     # make sure all ips are dropped, pfkill -9 might leave them hanging around
     drop_all_public_ips
 
+    rm -f "$pidfile"
+
     case $init_style in
        suse)
            # re-set the return code to the recorded RETVAL in order
@@ -344,34 +360,51 @@ restart() {
     start
 }
 
-status() {
-    echo -n $"Checking for ctdbd service: "
-    ctdb ping >/dev/null 2>&1 || {
-       RETVAL=$?
-       echo -n "  ctdbd not running. "
-       case $init_style in
-           suse)
-               set_retval $RETVAL
-               rc_status -v
-               ;;
-           redhat)
-               if [ -f /var/lock/subsys/ctdb ]; then
-                       echo $"ctdb dead but subsys locked"
-                       RETVAL=2
-               else
-                       echo $"ctdb is stopped"
-                       RETVAL=3
-               fi
-               ;;
-       esac
-       return $RETVAL
-    }
-    echo ""
-    ctdb status
+# Given that CTDB_VALGRIND is a debug option we don't support the pid
+# file.  We just do a quick and dirty hack instead.  Otherwise we just
+# end up re-implementing each distro's pidfile support...
+check_status_valgrind ()
+{
+    if pkill -0 -f "valgrind.*${ctdbd}" ; then
+       echo "ctdbd is running under valgrind..."
+       return 0
+    else
+       echo "ctdbd is not running"
+       return 1
+    fi
 }
 
+check_status ()
+{
+    # Backward compatibility.  When we arrange to pass --pidfile to
+    # ctdbd we also create the directory that will contain it.  If
+    # that directory is missing then we don't use the pidfile to check
+    # status.
+    if [ -d $(dirname "$pidfile") ] ; then
+       _pf_opt="-p $pidfile"
+    else
+       _pf_opt=""
+    fi
+
+    case "$init_style" in
+       valgrind)
+           check_status_valgrind
+           ;;
+       suse)
+           checkproc $_pf_opt "$ctdbd"
+           rc_status -v
+           ;;
+       redhat)
+           status $_pf_opt -l "ctdb" "$ctdbd"
+           ;;
+       debian)
+           status_of_proc $_pf_opt "$ctdbd" "ctdb"
+           ;;
+    esac
+}
 
-[ -f "$CTDB_BASE/rc.ctdb" ] && "$CTDB_BASE/rc.ctdb" $1
+
+[ -x "$CTDB_BASE/rc.ctdb" ] && "$CTDB_BASE/rc.ctdb" $1
 
 case "$1" in
     start)
@@ -384,14 +417,16 @@ case "$1" in
        restart
        ;;
     status)
-       status
+       check_status
        ;;
     condrestart|try-restart)
-       ctdb status > /dev/null && restart || :
+       if check_status >/dev/null ; then
+           restart
+       fi
        ;;
     cron)
        # used from cron to auto-restart ctdb
-       ctdb status > /dev/null || restart
+       check_status >/dev/null || restart
        ;;
     *)
        echo $"Usage: $0 {start|stop|restart|reload|force-reload|status|cron|condrestart|try-restart}"