Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/libexec/rc/debug.sh
34821 views
1
:
2
# NAME:
3
# debug.sh - selectively debug scripts
4
#
5
# SYNOPSIS:
6
# $_DEBUG_SH . debug.sh
7
# DebugOn [-eo] "tag" ...
8
# DebugOff [-eo] [rc="rc"] "tag" ...
9
# Debugging
10
# DebugAdd "tag"
11
# DebugEcho ...
12
# DebugLog ...
13
# DebugShell "tag" ...
14
# DebugTrace ...
15
# Debug "tag" ...
16
#
17
# $DEBUG_SKIP echo skipped when Debug "tag" is true.
18
# $DEBUG_DO echo only done when Debug "tag" is true.
19
#
20
# DESCRIPTION:
21
# debug.sh provides the following functions to facilitate
22
# flexible run-time tracing of complicated shell scripts.
23
#
24
# DebugOn turns tracing on if any "tag" is found in "DEBUG_SH".
25
# It turns tracing off if "!tag" is found in "DEBUG_SH".
26
# It also sets "DEBUG_ON" to the "tag" that caused tracing to be
27
# enabled, or "DEBUG_OFF" if we matched "!tag".
28
# If '-e' option given returns 1 if no "tag" matched.
29
# If the '-o' flag is given, tracing is turned off unless there
30
# was a matched "tag", useful for functions too noisy to tace.
31
#
32
# Further; when we set "DEBUG_ON" if we find
33
# "$DEBUG_ON:debug_add:tag" in "DEBUG_SH" we will
34
# add the new "tag" to "DEBUG_SH" so it only has effect after that
35
# point.
36
#
37
# DebugOff turns tracing on if any "tag" matches "DEBUG_OFF" or
38
# off if any "tag" matches "DEBUG_ON". This allows nested
39
# functions to not interfere with each other.
40
#
41
# DebugOff accepts but ignores the '-e' and '-o' options.
42
# The optional "rc" value will be returned rather than the
43
# default of 0. Thus if DebugOff is the last operation in a
44
# function, "rc" will be the return code of that function.
45
#
46
# DebugAdd allows adding a "tag" to "DEBUG_SH" to influence
47
# later events, possibly in a child process.
48
#
49
# DebugEcho is just shorthand for:
50
#.nf
51
# $DEBUG_DO echo "$@"
52
#.fi
53
#
54
# Debugging returns true if tracing is enabled.
55
# It is useful for bounding complex debug actions, rather than
56
# using lots of "DEBUG_DO" lines.
57
#
58
# DebugShell runs an interactive shell if any "tag" is found in
59
# "DEBUG_INTERACTIVE", and there is a tty available.
60
# The shell used is defined by "DEBUG_SHELL" or "SHELL" and
61
# defaults to '/bin/sh'.
62
#
63
# Debug calls DebugOn and if that does not turn tracing on, it
64
# calls DebugOff to turn it off.
65
#
66
# The variables "DEBUG_SKIP" and "DEBUG_DO" are set so as to
67
# enable/disable code that should be skipped/run when debugging
68
# is turned on. "DEBUGGING" is the same as "DEBUG_SKIP" for
69
# backwards compatability.
70
#
71
# The use of $_DEBUG_SH is to prevent multiple inclusion, though
72
# it does no harm in this case.
73
#
74
# BUGS:
75
# Does not work with some versions of ksh.
76
# If a function turns tracing on, ksh turns it off when the
77
# function returns - useless.
78
# PD ksh works ok ;-)
79
#
80
# AUTHOR:
81
# Simon J. Gerraty <[email protected]>
82
83
# RCSid:
84
# $Id: debug.sh,v 1.47 2025/08/07 21:59:54 sjg Exp $
85
#
86
# @(#) Copyright (c) 1994-2024 Simon J. Gerraty
87
#
88
# SPDX-License-Identifier: BSD-2-Clause
89
#
90
# Please send copies of changes and bug-fixes to:
91
# [email protected]
92
#
93
94
_DEBUG_SH=:
95
96
Myname=${Myname:-`basename $0 .sh`}
97
98
DEBUGGING=
99
DEBUG_DO=:
100
DEBUG_SKIP=
101
export DEBUGGING DEBUG_DO DEBUG_SKIP
102
103
# have is handy
104
if test -z "$_HAVE_SH"; then
105
_HAVE_SH=:
106
107
##
108
# have that does not rely on return code of type
109
#
110
have() {
111
case `(type "$1") 2>&1` in
112
*" found") return 1;;
113
esac
114
return 0
115
}
116
fi
117
118
# does local *actually* work?
119
local_works() {
120
local _fu
121
}
122
123
if local_works > /dev/null 2>&1; then
124
_local=local
125
else
126
_local=:
127
fi
128
# for backwards compatability
129
local=$_local
130
131
if test -z "$isPOSIX_SHELL"; then
132
if (echo ${PATH%:*}) > /dev/null 2>&1; then
133
# true should be a builtin, : certainly is
134
isPOSIX_SHELL=:
135
else
136
isPOSIX_SHELL=false
137
false() {
138
return 1
139
}
140
fi
141
fi
142
143
is_posix_shell() {
144
$isPOSIX_SHELL
145
return
146
}
147
148
149
##
150
# _debugAdd match
151
#
152
# Called from _debugOn when $match also appears in $DEBUG_SH with
153
# a suffix of :debug_add:tag we will add tag to DEBUG_SH
154
#
155
_debugAdd() {
156
eval $_local tag
157
158
for tag in `IFS=,; echo $DEBUG_SH`
159
do
160
: tag=$tag
161
case "$tag" in
162
$1:debug_add:*)
163
if is_posix_shell; then
164
tag=${tag#$1:debug_add:}
165
else
166
tag=`expr $tag : '.*:debug_add:\(.*\)'`
167
fi
168
case ",$DEBUG_SH," in
169
*,$tag,*) ;;
170
*) set -x
171
: _debugAdd $1
172
DEBUG_SH=$DEBUG_SH,$tag
173
set +x
174
;;
175
esac
176
;;
177
esac
178
done
179
export DEBUG_SH
180
}
181
182
183
##
184
# _debugOn match first
185
#
186
# Actually turn on tracing, set $DEBUG_ON=$match
187
#
188
# Check if $DEBUG_SH contains $match:debug_add:* and call _debugAdd
189
# to add the suffix to DEBUG_SH. This useful when we only want
190
# to trace some script when run under specific circumstances.
191
#
192
# If we have included hooks.sh $_HOOKS_SH will be set
193
# and if $first (the first arg to DebugOn) is suitable as a variable
194
# name we will run ${first}_debugOn_hooks.
195
#
196
# We disable tracing for hooks_run itself but functions can trace
197
# if they want based on DEBUG_DO
198
#
199
_debugOn() {
200
DEBUG_OFF=
201
DEBUG_DO=
202
DEBUG_SKIP=:
203
DEBUG_X=-x
204
# do this firt to reduce noise
205
case ",$DEBUG_SH," in
206
*,$1:debug_add:*) _debugAdd $1;;
207
*,$2:debug_add:*) _debugAdd $2;;
208
esac
209
set -x
210
DEBUG_ON=$1
211
case "$_HOOKS_SH,$2" in
212
,*|:,|:,*[${CASE_CLASS_NEG:-!}A-Za-z0-9_]*) ;;
213
*) # avoid noise from hooks_run
214
set +x
215
hooks_run ${2}_debugOn_hooks
216
set -x
217
;;
218
esac
219
}
220
221
##
222
# _debugOff match $DEBUG_ON $first
223
#
224
# Actually turn off tracing, set $DEBUG_OFF=$match
225
#
226
# If we have included hooks.sh $_HOOKS_SH will be set
227
# and if $first (the first arg to DebugOff) is suitable as a variable
228
# name we will run ${first}_debugOff_hooks.
229
#
230
# We do hooks_run after turning off tracing, but before resetting
231
# DEBUG_DO so functions can trace if they want
232
#
233
_debugOff() {
234
DEBUG_OFF=$1
235
set +x
236
case "$_HOOKS_SH,$3" in
237
,*|:,|:,*[${CASE_CLASS_NEG:-!}A-Za-z0-9_]*) ;;
238
*) hooks_run ${3}_debugOff_hooks;;
239
esac
240
set +x # just to be sure
241
DEBUG_ON=$2
242
DEBUG_DO=:
243
DEBUG_SKIP=
244
DEBUG_X=
245
}
246
247
##
248
# DebugAdd tag
249
#
250
# Add tag to DEBUG_SH
251
#
252
DebugAdd() {
253
DEBUG_SH=${DEBUG_SH:+$DEBUG_SH,}$1
254
export DEBUG_SH
255
}
256
257
##
258
# DebugEcho message
259
#
260
# Output message if we are debugging
261
#
262
DebugEcho() {
263
$DEBUG_DO echo "$@"
264
}
265
266
##
267
# Debugging
268
#
269
# return 0 if we are debugging.
270
#
271
Debugging() {
272
test "$DEBUG_SKIP"
273
}
274
275
##
276
# DebugLog message
277
#
278
# Outout message with timestamp if we are debugging
279
#
280
DebugLog() {
281
$DEBUG_SKIP return 0
282
echo `date '+@ %s [%Y-%m-%d %H:%M:%S %Z]'` "$@"
283
}
284
285
##
286
# DebugTrace message
287
#
288
# Something hard to miss when wading through huge -x output
289
#
290
DebugTrace() {
291
$DEBUG_SKIP return 0
292
set +x
293
echo "@ ==================== [ $DEBUG_ON ] ===================="
294
DebugLog "$@"
295
echo "@ ==================== [ $DEBUG_ON ] ===================="
296
set -x
297
}
298
299
##
300
# DebugOn [-e] [-o] match ...
301
#
302
# Turn on debugging if any $match is found in $DEBUG_SH.
303
#
304
DebugOn() {
305
eval ${local:-:} _e _match _off _rc
306
_rc=0 # avoid problems with set -e
307
_off=:
308
while :
309
do
310
case "$1" in
311
-e) _rc=1; shift;; # caller ok with return 1
312
-o) _off=; shift;; # off unless we have a match
313
*) break;;
314
esac
315
done
316
case ",${DEBUG_SH:-$DEBUG}," in
317
,,) return $_rc;;
318
*,[Dd]ebug,*) ;;
319
*) $DEBUG_DO set +x;; # reduce the noise
320
esac
321
_match=
322
# if debugging is off because of a !e
323
# don't add 'all' to the On list.
324
case "$_off$DEBUG_OFF" in
325
:) _e=all;;
326
*) _e=;;
327
esac
328
for _e in ${*:-$Myname} $_e
329
do
330
: $_e in ,${DEBUG_SH:-$DEBUG},
331
case ",${DEBUG_SH:-$DEBUG}," in
332
*,!$_e,*|*,!$Myname:$_e,*)
333
# only turn it off if it was on
334
_rc=0
335
$DEBUG_DO _debugOff $_e $DEBUG_ON $1
336
break
337
;;
338
*,$_e,*|*,$Myname:$_e,*)
339
# only turn it on if it was off
340
_rc=0
341
_match=$_e
342
$DEBUG_SKIP _debugOn $_e $1
343
break
344
;;
345
esac
346
done
347
if test -z "$_off$_match"; then
348
# off unless explicit match, but
349
# only turn it off if it was on
350
$DEBUG_DO _debugOff $_e $DEBUG_ON $1
351
fi
352
DEBUGGING=$DEBUG_SKIP # backwards compatability
353
$DEBUG_DO set -x # back on if needed
354
$DEBUG_DO set -x # make sure we see it in trace
355
return $_rc
356
}
357
358
##
359
# DebugOff [-e] [-o] [rc=$?] match ...
360
#
361
# Only turn debugging off if one of our args was the reason it
362
# was turned on.
363
#
364
# We normally return 0, but caller can pass rc=$? as first arg
365
# so that we preserve the status of last statement.
366
#
367
# The options '-e' and '-o' are ignored, they just make it easier to
368
# keep DebugOn and DebugOff lines in sync.
369
#
370
DebugOff() {
371
eval ${local:-:} _e _rc
372
case ",${DEBUG_SH:-$DEBUG}," in
373
*,[Dd]ebug,*) ;;
374
*) $DEBUG_DO set +x;; # reduce the noise
375
esac
376
_rc=0 # always happy
377
while :
378
do
379
case "$1" in
380
-[eo]) shift;; # ignore it
381
rc=*) eval "_$1"; shift;;
382
*) break;;
383
esac
384
done
385
for _e in $*
386
do
387
: $_e==$DEBUG_OFF DEBUG_OFF
388
case "$DEBUG_OFF" in
389
"") break;;
390
$_e) _debugOn $DEBUG_ON $1; return $_rc;;
391
esac
392
done
393
for _e in $*
394
do
395
: $_e==$DEBUG_ON DEBUG_ON
396
case "$DEBUG_ON" in
397
"") break;;
398
$_e) _debugOff "" "" $1; return $_rc;;
399
esac
400
done
401
DEBUGGING=$DEBUG_SKIP # backwards compatability
402
$DEBUG_DO set -x # back on if needed
403
$DEBUG_DO set -x # make sure we see it in trace
404
return $_rc
405
}
406
407
_TTY=${_TTY:-`test -t 0 && tty`}; export _TTY
408
409
# override this if you like
410
_debugShell() {
411
test "x$_TTY" != x || return 0
412
{
413
echo DebugShell "$@"
414
echo "Type 'exit' to continue..."
415
} > $_TTY
416
${DEBUG_SHELL:-${SHELL:-/bin/sh}} < $_TTY > $_TTY 2>&1
417
}
418
419
# Run an interactive shell if appropriate
420
# Note: you can use $DEBUG_SKIP DebugShell ... to skip unless debugOn
421
DebugShell() {
422
eval ${local:-:} _e
423
case "$_TTY%${DEBUG_INTERACTIVE}" in
424
*%|%*) return 0;; # no tty or no spec
425
esac
426
for _e in ${*:-$Myname} all
427
do
428
case ",${DEBUG_INTERACTIVE}," in
429
*,!$_e,*|*,!$Myname:$_e,*)
430
return 0
431
;;
432
*,$_e,*|*,$Myname:$_e,*)
433
# Provide clues as to why/where
434
_debugShell "$_e: $@"
435
return $?
436
;;
437
esac
438
done
439
return 0
440
}
441
442
# For backwards compatability
443
Debug() {
444
case "${DEBUG_SH:-$DEBUG}" in
445
"") ;;
446
*) DEBUG_ON=${DEBUG_ON:-_Debug}
447
DebugOn -e $* || DebugOff $DEBUG_LAST
448
DEBUGGING=$DEBUG_SKIP
449
;;
450
esac
451
}
452
453