diff options
Diffstat (limited to 'libexec/rc/debug.sh')
-rwxr-xr-x | libexec/rc/debug.sh | 451 |
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 +} |