X-Git-Url: https://iankelling.org/git/?p=automated-distro-installer;a=blobdiff_plain;f=fai%2Fconfig%2Ffiles%2Fboot%2Fbash-trace%2FDEFAULT;h=246042d28c1241dd91ea03ddd3415f8ab20d3bbc;hp=61f8ae52edb9958c799001e43a1f91b8f8994f74;hb=78a1427fc167ccee73d448054a9c40c19d737ed3;hpb=19fcc3931853969ed5aa97897795557d324cae39 diff --git a/fai/config/files/boot/bash-trace/DEFAULT b/fai/config/files/boot/bash-trace/DEFAULT index 61f8ae5..246042d 100644 --- a/fai/config/files/boot/bash-trace/DEFAULT +++ b/fai/config/files/boot/bash-trace/DEFAULT @@ -1,48 +1,203 @@ +#!/bin/bash +# Copyright (C) 2019 Ian Kelling +# SPDX-License-Identifier: AGPL-3.0-or-later + # meant to be sourced. copy/pasted from https://iankelling.org/git/?p=errhandle;a=summary -bash-trace() { - local -i argc_index=0 arg frame i start=${1:-1} max_indent=8 indent - local source - local extdebug=false - if [[ $(shopt -p extdebug) == *-s* ]]; then - extdebug=true - fi - - for ((frame=0; frame < ${#FUNCNAME[@]}-1; frame++)); do - argc=${BASH_ARGC[frame]} - argc_index+=$argc - ((frame < start)) && continue - if (( ${#BASH_SOURCE[@]} > 1 )); then - source="${BASH_SOURCE[frame+1]}:${BASH_LINENO[frame]}:" - fi - indent=$((frame-start+1)) - indent=$((indent < max_indent ? indent : max_indent)) - printf "%${indent}s↳%sin \`%s" '' "$source" "${FUNCNAME[frame]}" - if $extdebug; then - for ((i=argc_index-1; i >= argc_index-argc; i--)); do - printf " %s" "${BASH_ARGV[i]}" - done - fi - echo \' - done +# Commentary: Bash stack trace and error handling functions. This file +# is meant to be sourced. It loads some functions which you may want to +# call manually (see the comments at the start of each one), and then +# runs err-catch. See the README file for a slightly longer explanation. + + +####################################### +# Print stack trace +# +# usage: err-bash-trace [MESSAGE] +# +# This function is called by the other functions which print stack +# traces. +# +# It does not show function args unless you first run: +# shopt -s extdebug +# which err-catch & err-print do for you. +# +# MESSAGE Message to print just before the stack trace. +# +# _frame_start Optional variable to set before calling. The frame to +# start printing on. default=1. Useful when printing from +# an ERR trap function to avoid printing that function. +####################################### +err-bash-trace() { + local -i argc_index=0 frame i start=${_frame_start:-1} + local source + if [[ $1 ]]; then + printf "%s\n" "$1" + fi + for ((frame=0; frame < ${#FUNCNAME[@]}; frame++)); do + argc=${BASH_ARGC[frame]} + argc_index+=$argc + ((frame < start)) && continue + if (( ${#BASH_SOURCE[@]} > 1 )); then + source="${BASH_SOURCE[frame]}:${BASH_LINENO[frame-1]}:" + fi + printf " from %sin \`%s" "$source" "${FUNCNAME[frame]}" + if shopt extdebug >/dev/null; then + for ((i=argc_index-1; i >= argc_index-argc; i--)); do + printf " %s" "${BASH_ARGV[i]}" + done + fi + echo \' + done + return 0 +} + +####################################### +# On error print stack trace and exit +# +# Globals: +# errcatch-cleanup If set, this command will run just before exiting. +####################################### +err-catch() { + set -E; shopt -s extdebug + _err-trap() { + err=$? + exec >&2 + set +x + local msg="${BASH_SOURCE[1]}:${BASH_LINENO[0]}: \`$BASH_COMMAND' returned $err" + if (( ${#FUNCNAME[@]} > 2 )); then + local _frame_start=2 + err-bash-trace "$msg" + else + echo "$msg" + fi + set -e # err trap does not work within an error trap + if type -t errcatch-cleanup >/dev/null; then + errcatch-cleanup + fi + echo "$0: exiting with status $err" + exit $err + } + trap _err-trap ERR + set -o pipefail +} + + +####################################### +# For interactive shells: on error, print stack trace and return +# +# Globals: +# err_catch_ignore Array containing glob patterns to test against filenames to ignore +# errors from. Initialized to ignore bash-completion scripts on debian +# based systems. +# _err_func_last Used internally. +# _err_catch_err Used internally. +# _err_catch_i Used internally. +# _err_catch_ignore Used internally. +# +# misc: All shellcheck disables for this function are false positives. +####################################### +# shellcheck disable=SC2120 +err-catch-interactive() { + err_catch_ignore=( + '/etc/bash_completion.d/*' + ) + # shellcheck disable=SC2034 + declare -i _err_func_last=0 + set -E; shopt -s extdebug + # shellcheck disable=SC2154 + trap '_err_catch_err=$? _trap_bc="$BASH_COMMAND" + _err_catch_ignore=false + for _err_catch_i in "${err_catch_ignore[@]}"; do + if [[ ${BASH_SOURCE[0]} == $_err_catch_i ]]; then + _err_catch_ignore=true + break + fi + done + if ! $_err_catch_ignore; then + if (( ${#FUNCNAME[@]} > _err_func_last )); then + echo ERR: \`$_trap_bc'"\'"' returned $_err_catch_err + fi + _err_func_last=${#FUNCNAME[@]} + if (( _err_func_last )); then + printf " from %s:%s:in \`%s" "${BASH_SOURCE[0]}" "$(declare -F "${FUNCNAME[0]}"|awk "{print \$2}")" "${FUNCNAME[0]}" + if shopt extdebug >/dev/null; then + for ((_err_catch_i=${BASH_ARGC[0]}-1; _err_catch_i >= 0; _err_catch_i--)); do + printf " %s" "${BASH_ARGV[_err_catch_i]}" + done + fi + echo '"\'"' + return $_err_catch_err + fi + fi' ERR + set -o pipefail } -errcatch() { - set -E; shopt -s extdebug - _err-trap() { - err=$? - exec >&2 - set +x - echo "${BASH_SOURCE[1]}:${BASH_LINENO[0]}:in \`$BASH_COMMAND' returned $err" - bash-trace 2 - set -e - "${_errcatch_cleanup[@]}" - echo "$0: exiting with code $err" - exit $err - } - trap _err-trap ERR - set -o pipefail +####################################### +# Undoes err-catch/err-catch-interactive +####################################### +err-allow() { + shopt -u extdebug + set +E +o pipefail + trap ERR +} + +####################################### +# On error, print stack trace +####################################### +err-print() { + # help: on errors: print stack trace + # + # This function depends on err-bash-trace. + + set -E; shopt -s extdebug + _err-trap() { + err=$? + exec >&2 + set +x + echo "${BASH_SOURCE[1]}:${BASH_LINENO[0]}: \`$BASH_COMMAND' returned $err" + err-bash-trace 2 + } + trap _err-trap ERR + set -o pipefail +} + + +####################################### +# Print stack trace and exit +# +# Use this instead of the exit command to be more informative. +# +# usage: err-exit [EXIT_CODE] [MESSAGE] +# +# EXIT_CODE Default is 1. +# MESSAGE Print MESSAGE to stderr. If only one of EXIT_CODE +# and MESSAGE is given, we consider it to be an +# exit code if it is a number. +####################################### +err-exit() { + exec >&2 + code=1 + if [[ "$*" ]]; then + if [[ ${1/[^0-9]/} == "$1" ]]; then + code=$1 + if [[ $2 ]]; then + printf '%s\n' "$2" >&2 + fi + else + printf '%s\n' "$0: $1" >&2 + fi + fi + echo "${BASH_SOURCE[1]}:${BASH_LINENO[0]}" + err-bash-trace 2 + echo "$0: exiting with code $code" + exit $err } -errcatch +# We want this more often than not, so run it now. +if [[ $- == *i* ]]; then + err-catch-interactive +else + err-catch +fi