ctdb-scripts: Dump stack traces of smbd processes after shutdown
[samba.git] / ctdb / config / events.d / 50.samba
index 1c6822714f590e2fea5174546b56a3e83e9afa4b..4ed892cdc5905e3efaa45e9441109a59e781c2f7 100755 (executable)
@@ -1,7 +1,10 @@
 #!/bin/sh
 # ctdb event script for Samba
 
-. $CTDB_BASE/functions
+[ -n "$CTDB_BASE" ] || \
+    CTDB_BASE=$(d=$(dirname "$0") ; cd -P "$d" ; dirname "$PWD")
+
+. "${CTDB_BASE}/functions"
 
 detect_init_style
 
@@ -9,218 +12,126 @@ case $CTDB_INIT_STYLE in
        suse)
                CTDB_SERVICE_SMB=${CTDB_SERVICE_SMB:-smb}
                CTDB_SERVICE_NMB=${CTDB_SERVICE_NMB:-nmb}
-               CTDB_SERVICE_WINBIND=${CTDB_SERVICE_WINBIND:-winbind}
                ;;
        debian)
                CTDB_SERVICE_SMB=${CTDB_SERVICE_SMB:-samba}
                CTDB_SERVICE_NMB=${CTDB_SERVICE_NMB:-""}
-               CTDB_SERVICE_WINBIND=${CTDB_SERVICE_WINBIND:-winbind}
                ;;
        *)
-               # should not happen, but for now use redhat style as default:
+               # Use redhat style as default:
                CTDB_SERVICE_SMB=${CTDB_SERVICE_SMB:-smb}
                CTDB_SERVICE_NMB=${CTDB_SERVICE_NMB:-""}
-               CTDB_SERVICE_WINBIND=${CTDB_SERVICE_WINBIND:-winbind}
                ;;
 esac
 
+# service_name is used by various functions
+# shellcheck disable=SC2034
 service_name="samba"
 
 loadconfig
 
-ctdb_setup_service_state_dir
+service_state_dir=$(ctdb_setup_service_state_dir) || exit $?
 
 service_start ()
 {
-       # If set then we force-start the relevant service.
-       _service_name="$1"
-
-       # make sure samba is not already started
-       if [ "$_service_name" = "samba" ] || \
-           is_ctdb_managed_service "samba" ; then
-               service "$CTDB_SERVICE_SMB" stop > /dev/null 2>&1
-               if [ -n "$CTDB_SERVICE_NMB" ] ; then
-                   service "$CTDB_SERVICE_NMB" stop > /dev/null 2>&1
-               fi
-               killall -0 -q smbd && {
-                   sleep 1
-                   # make absolutely sure samba is dead
-                   killall -q -9 smbd
-               }
-
-               killall -0 -q nmbd && {
-                   sleep 1
-                   # make absolutely sure samba is dead
-                   killall -q -9 nmbd
-               }
-       fi
-
-       # make sure winbind is not already started
-       if [ "$_service_name" = "winbind" ] || \
-           check_ctdb_manages_winbind ; then
-               service "$CTDB_SERVICE_WINBIND" stop > /dev/null 2>&1
-               killall -0 -q winbindd && {
-                   sleep 1
-                   # make absolutely sure winbindd is dead
-                   killall -q -9 winbindd
-               }
+    # make sure samba is not already started
+    service "$CTDB_SERVICE_SMB" stop > /dev/null 2>&1
+    if [ -n "$CTDB_SERVICE_NMB" ] ; then
+       service "$CTDB_SERVICE_NMB" stop > /dev/null 2>&1
+    fi
+    killall -0 -q smbd && {
+       sleep 1
+       # make absolutely sure samba is dead
+       killall -q -9 smbd
+    }
+    killall -0 -q nmbd && {
+       sleep 1
+       # make absolutely sure samba is dead
+       killall -q -9 nmbd
+    }
 
-       fi
+    # start Samba service. Start it reniced, as under very heavy load
+    # the number of smbd processes will mean that it leaves few cycles
+    # for anything else
+    net serverid wipe
 
-       # start the winbind service
-       if [ "$_service_name" = "winbind" ] || \
-           check_ctdb_manages_winbind ; then
-               service "$CTDB_SERVICE_WINBIND" start || {
-                   echo failed to start winbind
-                   exit 1
-               }
-       fi
+    if [ -n "$CTDB_SERVICE_NMB" ] ; then
+       nice_service "$CTDB_SERVICE_NMB" start || die "Failed to start nmbd"
+    fi
 
-       # start Samba service. Start it reniced, as under very heavy load 
-       # the number of smbd processes will mean that it leaves few cycles for
-       # anything else
-       if [ "$_service_name" = "samba" ] || \
-           is_ctdb_managed_service "samba" ; then
-               net serverid wipe
-
-               if [ -n "$CTDB_SERVICE_NMB" ] ; then
-                   nice_service "$CTDB_SERVICE_NMB" start || {
-                       echo failed to start nmbd
-                       exit 1
-                   }
-               fi
-               nice_service "$CTDB_SERVICE_SMB" start || {
-                       echo failed to start samba
-                       exit 1
-               }
-       fi
+    nice_service "$CTDB_SERVICE_SMB" start || die "Failed to start samba"
 }
 
 service_stop ()
 {
-       # If set then we force-stop the relevant service.
-       _service_name="$1"
-
-       # shutdown Samba when ctdb goes down
-       if [ "$_service_name" = "samba" ] || \
-           is_ctdb_managed_service "samba" ; then
-               service "$CTDB_SERVICE_SMB" stop
-               if [ -n "$CTDB_SERVICE_NMB" ] ; then
-                   service "$CTDB_SERVICE_NMB" stop
-               fi
-       fi
-
-       # stop the winbind service
-       if [ "$_service_name" = "winbind" ] || \
-           check_ctdb_manages_winbind ; then
-               service "$CTDB_SERVICE_WINBIND" stop
-       fi
-
-       return 0
+    service "$CTDB_SERVICE_SMB" stop
+    program_stack_traces "smbd" 5
+    if [ -n "$CTDB_SERVICE_NMB" ] ; then
+       service "$CTDB_SERVICE_NMB" stop
+    fi
 }
 
-service_reconfigure ()
-{
-    # Samba automatically reloads config - no restart needed.
-    :
-}
+######################################################################
+# Show the testparm output using a cached smb.conf to avoid delays due
+# to registry access.
 
-# set default samba cleanup period - in minutes
-[ -z "$SAMBA_CLEANUP_PERIOD" ] && {
-    SAMBA_CLEANUP_PERIOD=10
-}
-
-# we keep a cached copy of smb.conf here
 smbconf_cache="$service_state_dir/smb.conf.cache"
 
-
-#############################################
-# update the smb.conf cache in the foreground
-testparm_foreground_update() {
-    testparm -s 2> /dev/null | egrep -v 'registry.shares.=|include.=' > "$smbconf_cache"
+testparm_foreground_update ()
+{
+    _timeout="$1"
+
+    # No need to remove these temporary files, since there are only 2
+    # of them.
+    _out="${smbconf_cache}.out"
+    _err="${smbconf_cache}.err"
+
+    timeout "$_timeout" testparm -v -s >"$_out" 2>"$_err"
+    case $? in
+       0) : ;;
+       124)
+           if [ -f "$smbconf_cache" ] ; then
+               echo "WARNING: smb.conf cache update timed out - using old cache file"
+               return 1
+           else
+               echo "ERROR: smb.conf cache create failed - testparm command timed out"
+               exit 1
+           fi
+           ;;
+       *)
+           if [ -f "$smbconf_cache" ] ; then
+               echo "WARNING: smb.conf cache update failed - using old cache file"
+               cat "$_err"
+               return 1
+           else
+               echo "ERROR: smb.conf cache create failed - testparm failed with:"
+               cat "$_err"
+               exit 1
+           fi
+    esac
+
+    # Only using $$ here to avoid a collision.  This is written into
+    # CTDB's own state directory so there is no real need for a secure
+    # temporary file.
+    _tmpfile="${smbconf_cache}.$$"
+    # Patterns to exclude...
+    _pat='^[[:space:]]+(registry[[:space:]]+shares|include|copy|winbind[[:space:]]+separator)[[:space:]]+='
+    grep -Ev "$_pat" <"$_out" >"$_tmpfile"
+    mv "$_tmpfile" "$smbconf_cache" # atomic
+
+    return 0
 }
 
-#############################################
-# update the smb.conf cache in the background
-testparm_background_update() {
-    # if the cache doesn't exist, then update in the foreground
-    [ -f $smbconf_cache ] || {
-       testparm_foreground_update
-    }
-    # otherwise do a background update
-    (
-       tmpfile="${smbconf_cache}.$$"
-       testparm -s > $tmpfile 2> /dev/null &
-       # remember the pid of the teamparm process
-       pid="$!"
-       # give it 10 seconds to run
-       timeleft=10
-       while [ $timeleft -gt 0 ]; do
-           timeleft=$(($timeleft - 1))
-           # see if the process still exists
-           kill -0 $pid > /dev/null 2>&1 || {
-               # it doesn't exist, grab its exit status
-               wait $pid
-               [ $? = 0 ] || {
-                   echo "50.samba: smb.conf background update exited with status $?"
-                   rm -f "${tmpfile}"
-                   exit 1
-               }               
-               # put the new smb.conf contents in the cache (atomic rename)
-               # make sure we remove references to the registry while doing 
-               # this to ensure that running testparm on the cache does
-               # not use the registry
-               egrep -v 'registry.shares.=|include.=' < "$tmpfile" > "${tmpfile}.2"
-               rm -f "$tmpfile"
-               mv -f "${tmpfile}.2" "$smbconf_cache" || {
-                   echo "50.samba: failed to update background cache"
-                   rm -f "${tmpfile}.2"
-                   exit 1
-               }
-               exit 0
-           }
-           # keep waiting for testparm to finish
-           sleep 1
-       done
-       # it took more than 10 seconds - kill it off
-       rm -f "${tmpfile}"
-       kill -9 "$pid" > /dev/null 2>&1
-       echo "50.samba: timed out updating smbconf cache in background"
-       exit 1
-    ) &
-}
+testparm_background_update ()
+{
+    _timeout="$1"
 
-##################################################
-# show the testparm output using a cached smb.conf 
-# to avoid registry access
-testparm_cat() {
-    [ -f $smbconf_cache ] || {
-       testparm_foreground_update
-    }
-    testparm -s "$smbconf_cache" "$@" 2>/dev/null
+    testparm_foreground_update "$_timeout" >/dev/null 2>&1 </dev/null &
 }
 
-# function to see if ctdb manages winbind - this overrides with extra
-# logic if $CTDB_MANAGES_WINBIND is not set or null.
-check_ctdb_manages_winbind() {
-    if is_ctdb_managed_service "winbind" ; then
-       return 0
-    elif [ -n "$CTDB_MANAGES_WINBIND" ] ; then
-       # If this variable is set we want to respect it.  We return
-       # false here because we know it is not set to "yes" - if it
-       # were then the 1st "if" above would have succeeded.
-       return 1
-    else
-       _secmode=`testparm_cat --parameter-name=security`
-       case "$_secmode" in
-           ADS|DOMAIN)
-               return 0
-               ;;
-           *)
-               return 1
-               ;;
-       esac
-    fi
+testparm_cat ()
+{
+    testparm -s "$smbconf_cache" "$@" 2>/dev/null
 }
 
 list_samba_shares ()
@@ -230,89 +141,49 @@ list_samba_shares ()
     sed -e 's/"//g'
 }
 
-
-###########################
-# periodic cleanup function
-periodic_cleanup() {
-    # running smbstatus scrubs any dead entries from the connections
-    # and sessionid database
-    # echo "Running periodic cleanup of samba databases"
-    smbstatus -np > /dev/null 2>&1 &
+list_samba_ports ()
+{
+    testparm_cat --parameter-name="smb ports" |
+    sed -e 's@,@ @g'
 }
 
 ###########################
 
 ctdb_start_stop_service
-ctdb_start_stop_service "winbind"
 
-is_ctdb_managed_service || is_ctdb_managed_service "winbind" || exit 0
+is_ctdb_managed_service || exit 0
 
 ###########################
 
-case "$1" in 
-     startup)
+case "$1" in
+startup)
        ctdb_service_start
        ;;
-       
-     shutdown)
+
+shutdown)
        ctdb_service_stop
        ;;
 
-     monitor)
-       # Create a dummy file to track when we need to do periodic cleanup
-       # of samba databases
-       periodic_cleanup_file="$service_state_dir/periodic_cleanup"
-       [ -f "$periodic_cleanup_file" ] || {
-               touch "$periodic_cleanup_file"
-       }
-       [ `find "$periodic_cleanup_file" -mmin +$SAMBA_CLEANUP_PERIOD | wc -l` -eq 1 ] && {
-               # Cleanup the databases
-               periodic_cleanup
-               touch "$periodic_cleanup_file"
-       }
-
-       is_ctdb_managed_service "samba" && {
-               [ "$CTDB_SAMBA_SKIP_SHARE_CHECK" = "yes" ] || {
-                       testparm_background_update
-
-                       testparm_cat | egrep '^WARNING|^ERROR|^Unknown' && {
-                           testparm_foreground_update
-                           testparm_cat | egrep '^WARNING|^ERROR|^Unknown' && {
-                               echo "ERROR: testparm shows smb.conf is not clean"
-                               exit 1
-                           }
-                       }
-                       
-                       list_samba_shares |
-                       ctdb_check_directories_probe || {
-                           testparm_foreground_update
-                           list_samba_shares |
-                           ctdb_check_directories
-                       } || exit $?
-               }
-
-               smb_ports="$CTDB_SAMBA_CHECK_PORTS"
-               [ -z "$smb_ports" ] && {
-                       smb_ports=`testparm_cat --parameter-name="smb ports"`
-               }
-               ctdb_check_tcp_ports $smb_ports || exit $?
-       }
-
-       # check winbind is OK
-       check_ctdb_manages_winbind && {
-               ctdb_check_command "winbind" "wbinfo -p"
-       }
-       ;;
+monitor)
+       testparm_foreground_update 10
+       ret=$?
 
-     takeip|releaseip)
-       iface=$2
-       ip=$3
-       maskbits=$4
+       smb_ports="$CTDB_SAMBA_CHECK_PORTS"
+       if [ -z "$smb_ports" ] ; then
+           smb_ports=$(list_samba_ports)
+           [ -n "$smb_ports" ] || die "Failed to set smb ports"
+       fi
+       # Intentionally unquoted multi-word value here
+       # shellcheck disable=SC2086
+       ctdb_check_tcp_ports $smb_ports || exit $?
 
-       smbcontrol winbindd ip-dropped $ip >/dev/null 2>/dev/null
-       ;;
-    *)
-       ctdb_standard_event_handler "$@"
+       if [ "$CTDB_SAMBA_SKIP_SHARE_CHECK" != "yes" ] ; then
+           list_samba_shares | ctdb_check_directories || exit $?
+       fi
+
+       if [ $ret -ne 0 ] ; then
+           testparm_background_update 10
+       fi
        ;;
 esac