X-Git-Url: https://iankelling.org/git/?a=blobdiff_plain;f=fai%2Fconfig%2Ffiles%2Fboot%2Fbash-trace%2FDEFAULT;h=2a4077f851c1c6ef468ac58db2cee12eac3626ff;hb=d9993568d38dd7d2d18ced6b5007e9cc07d1e576;hp=dc1a218786f9b2109c6b9acc2de849b8e3a7ff8a;hpb=fe81034ee9664d8e131bac218b40d99a58a31649;p=automated-distro-installer diff --git a/fai/config/files/boot/bash-trace/DEFAULT b/fai/config/files/boot/bash-trace/DEFAULT index dc1a218..2a4077f 100644 --- a/fai/config/files/boot/bash-trace/DEFAULT +++ b/fai/config/files/boot/bash-trace/DEFAULT @@ -1,10 +1,47 @@ #!/bin/bash -# Copyright (C) 2019 Ian Kelling +# Bash Error Handler +# Copyright (C) 2020 Ian Kelling # SPDX-License-Identifier: GPL-3.0-or-later +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + + +# This is a single file library, just source this file. When an error +# happens, we print a stack trace then exit. In an interactive shell, we +# return from functions instead of exiting. If err-cleanup is a command, +# it runs before the stack trace. Functions are documented inline below +# for additional use cases. +# +# Note: occasionally the line numbers are off a bit (at least in Bash +# 5.0). This appears to be a bash bug. I plan to report it next time it +# happens to me. +# +# Please email me if you use this or have anything to contribute. I'm +# not aware of any users yet Ian Kelling . +# +# Tested on bash 4.4.20(1)-release (x86_64-pc-linux-gnu) and +# 5.0.17(1)-release (x86_64-pc-linux-gnu). +# +# Related: see my bash script template repo at https://iankelling.org/git. + + +# TODO: investigate to see if we can format output betting in case of +# subshell failure. Right now, we get independent trace from inside and +# outside of the subshell. Note, errexit + inherit_errexit doesn't have +# any smarts around this either. -# Commentary: Print stack trace and exit/return on errors, or use -# functions below for for more details and manual error handling. See -# end of file for credits etc. +if ! test "$BASH_VERSION"; then echo "error: shell is not bash" >&2; exit 1; fi ####################################### # err-catch: Setup trap on ERR to print stack trace and exit (or return @@ -17,6 +54,10 @@ # Note: In interactive shell, stack calling line number is not # available, so we print function definition lines. # +# Note: This works like set -e, which has one unintuitive feature: If +# you use a function as part of a conditional, eg: func && come_cmd, a +# failed command within func won't trigger an error. +# # Globals # # err_catch_ignore Array containing glob patterns to test against @@ -39,9 +80,11 @@ err-catch() { ) fi declare -i _err_func_last=0 - shopt -s extdebug + if [[ $- != *c* ]]; then + shopt -s extdebug + fi # shellcheck disable=SC2154 - trap '_err-bash-trace-interactive $? "$BASH_COMMAND" ${BASH_ARGC[0]} "${BASH_ARGV[@]}" || return $?' ERR + trap '_err-bash-trace-interactive $? "${PIPESTATUS[*]}" "$BASH_COMMAND" ${BASH_ARGC[0]} "${BASH_ARGV[@]}" || return $?' ERR else # Man bash on exdebug: "If set at shell invocation, arrange to # execute the debugger". We want to avoid that, but I want this file @@ -86,27 +129,67 @@ err-allow() { # ####################################### err-exit() { - local err=$? + # vars have _ prefix so that we can inspect existing set vars without + # too much overwriting of them. + local _err=$? _pipestatus="${_pipestatus[*]}" + # This has to come before most things or vars get changed - local msg="${BASH_SOURCE[1]}:${BASH_LINENO[0]}: \`$BASH_COMMAND' returned $err" + local _msg="${BASH_SOURCE[1]}:${BASH_LINENO[0]}: \`$BASH_COMMAND' returned $_err" + local _cmdr="$BASH_COMMAND" # command right. we chop of the left, keep the right. + + if [[ $_pipestatus != "$_err" ]]; then + _msg+=", PIPESTATUS: $_pipestatus" + fi set +x if [[ $1 == -* ]]; then - err=${1#-} + _err=${1#-} shift - elif (( ! err )); then - err=1 + elif (( ! _err )); then + _err=1 fi if [[ $1 ]]; then - msg="$1" + _msg="$1" + fi + + ## Begin printing vars from within BASH_COMMAND ## + local _var _chars _l + local -A _vars + while [[ $_cmdr ]]; do + _chars="${#_cmdr}" + _cmdr="${_cmdr#*$}" + _cmdr="${_cmdr#{}" + if (( _chars == ${#_cmdr} )); then + break + fi + _var="${_cmdr%%[^a-zA-Z0-9_]*}" + if [[ ! $_var || $_var == [0-9]* ]]; then + continue + fi + _vars[${_var}]=t + done + #echo "iank ${_vars[*]}" + #set |& grep ^password + # in my small test, this took 50% longer than piping to grep. + # That seems a small enough penalty to stay in bash here. + if (( ${#_vars[@]} )); then + set |& while read -r _l; do + for _var in "${!_vars[@]}"; do + case $_l in + ${_var}=*) printf "%s\n" "$_l" >&2 ;; + esac + done + done fi - printf "%s\n" "$msg" >&2 + ## End printing vars from within BASH_COMMAND ## + + printf "%s\n" "$_msg" >&2 err-bash-trace 2 set -e # err trap does not work within an error trap if type -t err-cleanup >/dev/null; then err-cleanup fi - printf "%s: exiting with status %s\n" "$0" "$err" >&2 - exit $err + printf "%s: exiting with status %s\n" "$0" "$_err" >&2 + exit $_err } ####################################### @@ -176,14 +259,20 @@ _err-bash-trace-interactive() { # We have these passed to us because they are lost inside the # function. ret=$1 - bash_command="$2" - argc=$(( $3 - 1 )) - shift 3 + pipestatus="$2" + bash_command="$3" + argc=$(( $4 - 1 )) + shift 4 argv=("$@") # The trap returns a nonzero, then gets called again. This condition - # tells us if we are the first. - if (( _err_func_last > last )); then - printf "ERR: \`%s\' returned %s\n" "$bash_command" $ret >&2 + # tells us if is that has happened by checking if we've gone down a + # stack level. + if (( _err_func_last >= last )); then + printf "ERR: \`%s\' returned %s" "$bash_command" $ret >&2 + if [[ $pipestatus != "$ret" ]]; then + printf ", PIPESTATUS: %s" "$pipestatus" >&2 + fi + echo >&2 fi printf " from \`%s" "${FUNCNAME[1]}" >&2 if shopt extdebug >/dev/null; then @@ -207,14 +296,3 @@ _err-bash-trace-interactive() { return 0 fi } - -# Credits etc: -# -# Related: see my bash script template repo at https://iankelling.org/git. -# -# -# Please email me if you have a patches, bugs, feedback, or if you use -# it or republish it since I'm not aware of any users yet -# Ian Kelling . -# -# Tested on bash 4.4.20(1)-release (x86_64-pc-linux-gnu). If you test