ctdb-scripts: Dump stack traces of smbd processes after shutdown
[samba.git] / ctdb / config / events.d / 50.samba
index 117b45926575ead20c1eaf96922ab2415b775ca0..4ed892cdc5905e3efaa45e9441109a59e781c2f7 100755 (executable)
@@ -2,9 +2,9 @@
 # ctdb event script for Samba
 
 [ -n "$CTDB_BASE" ] || \
-    export CTDB_BASE=$(cd -P $(dirname "$0") ; dirname "$PWD")
+    CTDB_BASE=$(d=$(dirname "$0") ; cd -P "$d" ; dirname "$PWD")
 
-. $CTDB_BASE/functions
+. "${CTDB_BASE}/functions"
 
 detect_init_style
 
@@ -24,11 +24,13 @@ case $CTDB_INIT_STYLE in
                ;;
 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 ()
 {
@@ -63,79 +65,73 @@ service_start ()
 service_stop ()
 {
     service "$CTDB_SERVICE_SMB" stop
+    program_stack_traces "smbd" 5
     if [ -n "$CTDB_SERVICE_NMB" ] ; then
        service "$CTDB_SERVICE_NMB" stop
     fi
 }
 
-# we keep a cached copy of smb.conf here
-smbconf_cache="$service_state_dir/smb.conf.cache"
+######################################################################
+# Show the testparm output using a cached smb.conf to avoid delays due
+# to registry access.
 
+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"
+
+    testparm_foreground_update "$_timeout" >/dev/null 2>&1 </dev/null &
 }
 
-##################################################
-# show the testparm output using a cached smb.conf 
-# to avoid registry access
-testparm_cat() {
-    [ -f $smbconf_cache ] || {
-       testparm_foreground_update
-    }
-    testparm -v -s "$smbconf_cache" "$@" 2>/dev/null
+testparm_cat ()
+{
+    testparm -s "$smbconf_cache" "$@" 2>/dev/null
 }
 
 list_samba_shares ()
@@ -145,6 +141,11 @@ list_samba_shares ()
     sed -e 's/"//g'
 }
 
+list_samba_ports ()
+{
+    testparm_cat --parameter-name="smb ports" |
+    sed -e 's@,@ @g'
+}
 
 ###########################
 
@@ -155,40 +156,34 @@ is_ctdb_managed_service || exit 0
 ###########################
 
 case "$1" in
-     startup)
+startup)
        ctdb_service_start
        ;;
 
-     shutdown)
+shutdown)
        ctdb_service_stop
        ;;
 
-     monitor)
-       if [ "$CTDB_SAMBA_SKIP_SHARE_CHECK" != "yes" ] ; then
-           testparm_background_update
-
-           testparm_cat | egrep '^WARNING|^ERROR|^Unknown' && {
-               testparm_foreground_update
-               testparm_cat | egrep '^WARNING|^ERROR|^Unknown' && \
-                   die "ERROR: testparm shows smb.conf is not clean"
-           }
-
-           list_samba_shares | ctdb_check_directories_probe || {
-               testparm_foreground_update
-               list_samba_shares |
-               ctdb_check_directories
-           } || exit $?
-       fi
+monitor)
+       testparm_foreground_update 10
+       ret=$?
 
        smb_ports="$CTDB_SAMBA_CHECK_PORTS"
        if [ -z "$smb_ports" ] ; then
-           smb_ports=`testparm_cat --parameter-name="smb ports"`
+           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 $?
-       ;;
 
-    *)
-       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