aboutsummaryrefslogtreecommitdiff
path: root/libexec/rc/debug.sh
diff options
context:
space:
mode:
Diffstat (limited to 'libexec/rc/debug.sh')
-rwxr-xr-xlibexec/rc/debug.sh451
1 files changed, 451 insertions, 0 deletions
diff --git a/libexec/rc/debug.sh b/libexec/rc/debug.sh
new file mode 100755
index 000000000000..739c81a709f6
--- /dev/null
+++ b/libexec/rc/debug.sh
@@ -0,0 +1,451 @@
+:
+# NAME:
+# debug.sh - selectively debug scripts
+#
+# SYNOPSIS:
+# $_DEBUG_SH . debug.sh
+# DebugOn [-eo] "tag" ...
+# DebugOff [-eo] [rc="rc"] "tag" ...
+# Debugging
+# DebugAdd "tag"
+# DebugEcho ...
+# DebugLog ...
+# DebugShell "tag" ...
+# DebugTrace ...
+# Debug "tag" ...
+#
+# $DEBUG_SKIP echo skipped when Debug "tag" is true.
+# $DEBUG_DO echo only done when Debug "tag" is true.
+#
+# DESCRIPTION:
+# debug.sh provides the following functions to facilitate
+# flexible run-time tracing of complicated shell scripts.
+#
+# DebugOn turns tracing on if any "tag" is found in "DEBUG_SH".
+# It turns tracing off if "!tag" is found in "DEBUG_SH".
+# It also sets "DEBUG_ON" to the "tag" that caused tracing to be
+# enabled, or "DEBUG_OFF" if we matched "!tag".
+# If '-e' option given returns 1 if no "tag" matched.
+# If the '-o' flag is given, tracing is turned off unless there
+# was a matched "tag", useful for functions too noisy to tace.
+#
+# Further; when we set "DEBUG_ON" if we find
+# "$DEBUG_ON:debug_add:tag" in "DEBUG_SH" we will
+# add the new "tag" to "DEBUG_SH" so it only has effect after that
+# point.
+#
+# DebugOff turns tracing on if any "tag" matches "DEBUG_OFF" or
+# off if any "tag" matches "DEBUG_ON". This allows nested
+# functions to not interfere with each other.
+#
+# DebugOff accepts but ignores the '-e' and '-o' options.
+# The optional "rc" value will be returned rather than the
+# default of 0. Thus if DebugOff is the last operation in a
+# function, "rc" will be the return code of that function.
+#
+# DebugAdd allows adding a "tag" to "DEBUG_SH" to influence
+# later events, possibly in a child process.
+#
+# DebugEcho is just shorthand for:
+#.nf
+# $DEBUG_DO echo "$@"
+#.fi
+#
+# Debugging returns true if tracing is enabled.
+# It is useful for bounding complex debug actions, rather than
+# using lots of "DEBUG_DO" lines.
+#
+# DebugShell runs an interactive shell if any "tag" is found in
+# "DEBUG_INTERACTIVE", and there is a tty available.
+# The shell used is defined by "DEBUG_SHELL" or "SHELL" and
+# defaults to '/bin/sh'.
+#
+# Debug calls DebugOn and if that does not turn tracing on, it
+# calls DebugOff to turn it off.
+#
+# The variables "DEBUG_SKIP" and "DEBUG_DO" are set so as to
+# enable/disable code that should be skipped/run when debugging
+# is turned on. "DEBUGGING" is the same as "DEBUG_SKIP" for
+# backwards compatability.
+#
+# The use of $_DEBUG_SH is to prevent multiple inclusion, though
+# it does no harm in this case.
+#
+# BUGS:
+# Does not work with some versions of ksh.
+# If a function turns tracing on, ksh turns it off when the
+# function returns - useless.
+# PD ksh works ok ;-)
+#
+# AUTHOR:
+# Simon J. Gerraty <sjg@crufty.net>
+
+# RCSid:
+# $Id: debug.sh,v 1.47 2025/08/07 21:59:54 sjg Exp $
+#
+# @(#) Copyright (c) 1994-2024 Simon J. Gerraty
+#
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Please send copies of changes and bug-fixes to:
+# sjg@crufty.net
+#
+
+_DEBUG_SH=:
+
+Myname=${Myname:-`basename $0 .sh`}
+
+DEBUGGING=
+DEBUG_DO=:
+DEBUG_SKIP=
+export DEBUGGING DEBUG_DO DEBUG_SKIP
+
+# have is handy
+if test -z "$_HAVE_SH"; then
+ _HAVE_SH=:
+
+ ##
+ # have that does not rely on return code of type
+ #
+ have() {
+ case `(type "$1") 2>&1` in
+ *" found") return 1;;
+ esac
+ return 0
+ }
+fi
+
+# does local *actually* work?
+local_works() {
+ local _fu
+}
+
+if local_works > /dev/null 2>&1; then
+ _local=local
+else
+ _local=:
+fi
+# for backwards compatability
+local=$_local
+
+if test -z "$isPOSIX_SHELL"; then
+ if (echo ${PATH%:*}) > /dev/null 2>&1; then
+ # true should be a builtin, : certainly is
+ isPOSIX_SHELL=:
+ else
+ isPOSIX_SHELL=false
+ false() {
+ return 1
+ }
+ fi
+fi
+
+is_posix_shell() {
+ $isPOSIX_SHELL
+ return
+}
+
+
+##
+# _debugAdd match
+#
+# Called from _debugOn when $match also appears in $DEBUG_SH with
+# a suffix of :debug_add:tag we will add tag to DEBUG_SH
+#
+_debugAdd() {
+ eval $_local tag
+
+ for tag in `IFS=,; echo $DEBUG_SH`
+ do
+ : tag=$tag
+ case "$tag" in
+ $1:debug_add:*)
+ if is_posix_shell; then
+ tag=${tag#$1:debug_add:}
+ else
+ tag=`expr $tag : '.*:debug_add:\(.*\)'`
+ fi
+ case ",$DEBUG_SH," in
+ *,$tag,*) ;;
+ *) set -x
+ : _debugAdd $1
+ DEBUG_SH=$DEBUG_SH,$tag
+ set +x
+ ;;
+ esac
+ ;;
+ esac
+ done
+ export DEBUG_SH
+}
+
+
+##
+# _debugOn match first
+#
+# Actually turn on tracing, set $DEBUG_ON=$match
+#
+# Check if $DEBUG_SH contains $match:debug_add:* and call _debugAdd
+# to add the suffix to DEBUG_SH. This useful when we only want
+# to trace some script when run under specific circumstances.
+#
+# If we have included hooks.sh $_HOOKS_SH will be set
+# and if $first (the first arg to DebugOn) is suitable as a variable
+# name we will run ${first}_debugOn_hooks.
+#
+# We disable tracing for hooks_run itself but functions can trace
+# if they want based on DEBUG_DO
+#
+_debugOn() {
+ DEBUG_OFF=
+ DEBUG_DO=
+ DEBUG_SKIP=:
+ DEBUG_X=-x
+ # do this firt to reduce noise
+ case ",$DEBUG_SH," in
+ *,$1:debug_add:*) _debugAdd $1;;
+ *,$2:debug_add:*) _debugAdd $2;;
+ esac
+ set -x
+ DEBUG_ON=$1
+ case "$_HOOKS_SH,$2" in
+ ,*|:,|:,*[${CASE_CLASS_NEG:-!}A-Za-z0-9_]*) ;;
+ *) # avoid noise from hooks_run
+ set +x
+ hooks_run ${2}_debugOn_hooks
+ set -x
+ ;;
+ esac
+}
+
+##
+# _debugOff match $DEBUG_ON $first
+#
+# Actually turn off tracing, set $DEBUG_OFF=$match
+#
+# If we have included hooks.sh $_HOOKS_SH will be set
+# and if $first (the first arg to DebugOff) is suitable as a variable
+# name we will run ${first}_debugOff_hooks.
+#
+# We do hooks_run after turning off tracing, but before resetting
+# DEBUG_DO so functions can trace if they want
+#
+_debugOff() {
+ DEBUG_OFF=$1
+ set +x
+ case "$_HOOKS_SH,$3" in
+ ,*|:,|:,*[${CASE_CLASS_NEG:-!}A-Za-z0-9_]*) ;;
+ *) hooks_run ${3}_debugOff_hooks;;
+ esac
+ set +x # just to be sure
+ DEBUG_ON=$2
+ DEBUG_DO=:
+ DEBUG_SKIP=
+ DEBUG_X=
+}
+
+##
+# DebugAdd tag
+#
+# Add tag to DEBUG_SH
+#
+DebugAdd() {
+ DEBUG_SH=${DEBUG_SH:+$DEBUG_SH,}$1
+ export DEBUG_SH
+}
+
+##
+# DebugEcho message
+#
+# Output message if we are debugging
+#
+DebugEcho() {
+ $DEBUG_DO echo "$@"
+}
+
+##
+# Debugging
+#
+# return 0 if we are debugging.
+#
+Debugging() {
+ test "$DEBUG_SKIP"
+}
+
+##
+# DebugLog message
+#
+# Outout message with timestamp if we are debugging
+#
+DebugLog() {
+ $DEBUG_SKIP return 0
+ echo `date '+@ %s [%Y-%m-%d %H:%M:%S %Z]'` "$@"
+}
+
+##
+# DebugTrace message
+#
+# Something hard to miss when wading through huge -x output
+#
+DebugTrace() {
+ $DEBUG_SKIP return 0
+ set +x
+ echo "@ ==================== [ $DEBUG_ON ] ===================="
+ DebugLog "$@"
+ echo "@ ==================== [ $DEBUG_ON ] ===================="
+ set -x
+}
+
+##
+# DebugOn [-e] [-o] match ...
+#
+# Turn on debugging if any $match is found in $DEBUG_SH.
+#
+DebugOn() {
+ eval ${local:-:} _e _match _off _rc
+ _rc=0 # avoid problems with set -e
+ _off=:
+ while :
+ do
+ case "$1" in
+ -e) _rc=1; shift;; # caller ok with return 1
+ -o) _off=; shift;; # off unless we have a match
+ *) break;;
+ esac
+ done
+ case ",${DEBUG_SH:-$DEBUG}," in
+ ,,) return $_rc;;
+ *,[Dd]ebug,*) ;;
+ *) $DEBUG_DO set +x;; # reduce the noise
+ esac
+ _match=
+ # if debugging is off because of a !e
+ # don't add 'all' to the On list.
+ case "$_off$DEBUG_OFF" in
+ :) _e=all;;
+ *) _e=;;
+ esac
+ for _e in ${*:-$Myname} $_e
+ do
+ : $_e in ,${DEBUG_SH:-$DEBUG},
+ case ",${DEBUG_SH:-$DEBUG}," in
+ *,!$_e,*|*,!$Myname:$_e,*)
+ # only turn it off if it was on
+ _rc=0
+ $DEBUG_DO _debugOff $_e $DEBUG_ON $1
+ break
+ ;;
+ *,$_e,*|*,$Myname:$_e,*)
+ # only turn it on if it was off
+ _rc=0
+ _match=$_e
+ $DEBUG_SKIP _debugOn $_e $1
+ break
+ ;;
+ esac
+ done
+ if test -z "$_off$_match"; then
+ # off unless explicit match, but
+ # only turn it off if it was on
+ $DEBUG_DO _debugOff $_e $DEBUG_ON $1
+ fi
+ DEBUGGING=$DEBUG_SKIP # backwards compatability
+ $DEBUG_DO set -x # back on if needed
+ $DEBUG_DO set -x # make sure we see it in trace
+ return $_rc
+}
+
+##
+# DebugOff [-e] [-o] [rc=$?] match ...
+#
+# Only turn debugging off if one of our args was the reason it
+# was turned on.
+#
+# We normally return 0, but caller can pass rc=$? as first arg
+# so that we preserve the status of last statement.
+#
+# The options '-e' and '-o' are ignored, they just make it easier to
+# keep DebugOn and DebugOff lines in sync.
+#
+DebugOff() {
+ eval ${local:-:} _e _rc
+ case ",${DEBUG_SH:-$DEBUG}," in
+ *,[Dd]ebug,*) ;;
+ *) $DEBUG_DO set +x;; # reduce the noise
+ esac
+ _rc=0 # always happy
+ while :
+ do
+ case "$1" in
+ -[eo]) shift;; # ignore it
+ rc=*) eval "_$1"; shift;;
+ *) break;;
+ esac
+ done
+ for _e in $*
+ do
+ : $_e==$DEBUG_OFF DEBUG_OFF
+ case "$DEBUG_OFF" in
+ "") break;;
+ $_e) _debugOn $DEBUG_ON $1; return $_rc;;
+ esac
+ done
+ for _e in $*
+ do
+ : $_e==$DEBUG_ON DEBUG_ON
+ case "$DEBUG_ON" in
+ "") break;;
+ $_e) _debugOff "" "" $1; return $_rc;;
+ esac
+ done
+ DEBUGGING=$DEBUG_SKIP # backwards compatability
+ $DEBUG_DO set -x # back on if needed
+ $DEBUG_DO set -x # make sure we see it in trace
+ return $_rc
+}
+
+_TTY=${_TTY:-`test -t 0 && tty`}; export _TTY
+
+# override this if you like
+_debugShell() {
+ test "x$_TTY" != x || return 0
+ {
+ echo DebugShell "$@"
+ echo "Type 'exit' to continue..."
+ } > $_TTY
+ ${DEBUG_SHELL:-${SHELL:-/bin/sh}} < $_TTY > $_TTY 2>&1
+}
+
+# Run an interactive shell if appropriate
+# Note: you can use $DEBUG_SKIP DebugShell ... to skip unless debugOn
+DebugShell() {
+ eval ${local:-:} _e
+ case "$_TTY%${DEBUG_INTERACTIVE}" in
+ *%|%*) return 0;; # no tty or no spec
+ esac
+ for _e in ${*:-$Myname} all
+ do
+ case ",${DEBUG_INTERACTIVE}," in
+ *,!$_e,*|*,!$Myname:$_e,*)
+ return 0
+ ;;
+ *,$_e,*|*,$Myname:$_e,*)
+ # Provide clues as to why/where
+ _debugShell "$_e: $@"
+ return $?
+ ;;
+ esac
+ done
+ return 0
+}
+
+# For backwards compatability
+Debug() {
+ case "${DEBUG_SH:-$DEBUG}" in
+ "") ;;
+ *) DEBUG_ON=${DEBUG_ON:-_Debug}
+ DebugOn -e $* || DebugOff $DEBUG_LAST
+ DEBUGGING=$DEBUG_SKIP
+ ;;
+ esac
+}