mirror of
https://github.com/linuxboot/heads.git
synced 2024-12-24 07:06:42 +00:00
functions: Add visibility to DO_WITH_DEBUG without affecting command
DO_WITH_DEBUG traces command exit status (if failed), stdout/stderr (if not empty), and PATH (if command was not found). The caller still observes the exit status, and stdout/stderr still go to the caller as well. This way, DO_WITH_DEBUG can be inserted anywhere with minimal spam in the logs and without affecting the script. Signed-off-by: Jonathon Hall <jonathon.hall@puri.sm>
This commit is contained in:
parent
ae5f9c5416
commit
015af7e6c7
@ -12,35 +12,106 @@ mask_param() {
|
||||
fi
|
||||
}
|
||||
|
||||
# Trace a command with DEBUG, then execute it.
|
||||
# Pipe input to this to sink it to the debug log, with a name prefix.
|
||||
# If the input is empty, no output is produced, so actual output is
|
||||
# readily visible in logs.
|
||||
#
|
||||
# For example:
|
||||
# ls /boot/vmlinux* | SINK_DEBUG "/boot kernels"
|
||||
SINK_DEBUG() {
|
||||
local name="$1"
|
||||
local line haveblank
|
||||
# If the input doesn't end with a line break, read won't give us the
|
||||
# last (unterminated) line. Add a line break with echo to ensure we
|
||||
# don't lose any input. Buffer up to one blank line so we can avoid
|
||||
# emitting a final (or only) blank line.
|
||||
(cat; echo) | while IFS= read -r line; do
|
||||
[[ -n "$haveblank" ]] && DEBUG "$name: " # Emit buffered blank line
|
||||
if [[ -z "$line" ]]; then
|
||||
haveblank=y
|
||||
else
|
||||
haveblank=
|
||||
DEBUG "$name: $line"
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
# Trace a command with DEBUG, then execute it. Trace failed exit status, stdout
|
||||
# and stderr, etc.
|
||||
#
|
||||
# DO_WITH_DEBUG is designed so it can be dropped in to most command invocations
|
||||
# without side effects - it adds visibility without actually affecting the
|
||||
# execution of the script. Exit statuses, stdout, and stderr are traced, but
|
||||
# they are still returned/written to the caller.
|
||||
#
|
||||
# A password parameter can be masked by passing --mask-position N before the
|
||||
# command to execute, the debug trace will just indicate whether the password
|
||||
# was empty or nonempty (which is important when use of a password is optional).
|
||||
# N=0 is the name of the command to be executed, N=1 is its first parameter,
|
||||
# etc.
|
||||
#
|
||||
# DO_WITH_DEBUG() can be added in most places where a command is executed to
|
||||
# add visibility in the debug log. For example:
|
||||
#
|
||||
# [DO_WITH_DEBUG] mount "$BLOCK" "$MOUNTPOINT"
|
||||
# ^-- adding DO_WITH_DEBUG will show the block device, mountpoint, and whether
|
||||
# the mount fails
|
||||
#
|
||||
# [DO_WITH_DEBUG --mask-position 7] tpmr seal "$KEY" "$IDX" "$pcrs" "$pcrf" "$size" "$PASSWORD"
|
||||
# ^-- trace the resulting invocation, but mask the password in the log
|
||||
#
|
||||
# if ! [DO_WITH_DEBUG] umount "$MOUNTPOINT"; then [...]
|
||||
# ^-- it can be used when the exit status is checked, like the condition of `if`
|
||||
#
|
||||
# hotp_token_info="$([DO_WITH_DEBUG] hotp_verification info)"
|
||||
# ^-- output of hotp_verification info becomes visible in debug log while
|
||||
# still being captured by script
|
||||
#
|
||||
# [DO_WITH_DEBUG] umount "$MOUNTPOINT" &>/dev/null || true
|
||||
# ^-- if the command's stdout/stderr/failure are ignored, this still works the
|
||||
# same way with DO_WITH_DEBUG
|
||||
DO_WITH_DEBUG() {
|
||||
local exit_status
|
||||
local exit_status=0
|
||||
local cmd_output
|
||||
DEBUG "PATH: $PATH"
|
||||
local cmd=("$@")
|
||||
if [[ "$1" == "--mask-position" ]]; then
|
||||
local mask_position="$2"
|
||||
shift
|
||||
shift
|
||||
cmd=("$@")
|
||||
cmd[$mask_position]="$(mask_param "${cmd[$mask_position]}")"
|
||||
local show_args=("$@")
|
||||
show_args[$mask_position]="$(mask_param "${show_args[$mask_position]}")"
|
||||
DEBUG "${show_args[@]}"
|
||||
else
|
||||
DEBUG "$@"
|
||||
fi
|
||||
if [[ ${#cmd[@]} -eq 1 ]]; then
|
||||
# If there's only one argument, try to split it into multiple arguments
|
||||
read -a cmd <<< "${cmd[0]}"
|
||||
|
||||
# Execute the command and capture the exit status. Tee stdout/stderr to
|
||||
# debug sinks, so they're visible but still can be used by the caller
|
||||
#
|
||||
# This is tricky when set -e / set -o pipefail may or may not be in
|
||||
# effect.
|
||||
# - Putting the command in an `if` ensures set -e won't terminate us,
|
||||
# and also does not overwrite $? (like `|| true` would).
|
||||
# - We capture PIPESTATUS[0] whether the command succeeds or fails,
|
||||
# since we don't know whether the pipeline status will be that of the
|
||||
# command or 'tee' (depends on set -o pipefail).
|
||||
if ! "$@" 2> >(tee /dev/stderr | SINK_DEBUG "$1 err") | tee >(SINK_DEBUG "$1 out"); then
|
||||
exit_status="${PIPESTATUS[0]}"
|
||||
else
|
||||
exit_status="${PIPESTATUS[0]}"
|
||||
fi
|
||||
DEBUG "Executing command with cmd: ${cmd[*]}"
|
||||
# Sanitize the command output by removing special characters
|
||||
cmd_output=$("${cmd[@]}" 2>&1 | sed 's/[&;|`$(){}<>]//g')
|
||||
exit_status=$?
|
||||
DEBUG "Command output: $cmd_output"
|
||||
DEBUG "Command exited with status: $exit_status"
|
||||
return $exit_status
|
||||
if [[ "$exit_status" -ne 0 ]]; then
|
||||
# Trace unsuccessful exit status, but only at DEBUG because this
|
||||
# may be expected. Include the command name in case the command
|
||||
# also invoked a DO_WITH_DEBUG (it could be a script).
|
||||
DEBUG "$1: exited with status $exit_status"
|
||||
fi
|
||||
# If the command was (probably) not found, trace PATH in case it
|
||||
# prevented the command from being found
|
||||
if [[ "$exit_status" -eq 127 ]]; then
|
||||
DEBUG "$1: PATH=$PATH"
|
||||
fi
|
||||
|
||||
return "$exit_status"
|
||||
}
|
||||
|
||||
# Trace the current script and function.
|
||||
|
Loading…
Reference in New Issue
Block a user