new stack trace, linode fixes, minor improvements
[automated-distro-installer] / fai / config / files / boot / bash-trace / DEFAULT
1 #!/bin/bash
2 # Copyright (C) 2019 Ian Kelling
3 # SPDX-License-Identifier: AGPL-3.0-or-later
4
5 # meant to be sourced. copy/pasted from https://iankelling.org/git/?p=errhandle;a=summary
6
7 # Commentary: Bash stack trace and error handling functions. This file
8 # is meant to be sourced. It loads some functions which you may want to
9 # call manually (see the comments at the start of each one), and then
10 # runs err-catch. See the README file for a slightly longer explanation.
11
12
13 #######################################
14 # Print stack trace
15 #
16 # usage: err-bash-trace [MESSAGE]
17 #
18 # This function is called by the other functions which print stack
19 # traces.
20 #
21 # It does not show function args unless you first run:
22 # shopt -s extdebug
23 # which err-catch & err-print do for you.
24 #
25 # MESSAGE Message to print just before the stack trace.
26 #
27 # _frame_start Optional variable to set before calling. The frame to
28 # start printing on. default=1. Useful when printing from
29 # an ERR trap function to avoid printing that function.
30 #######################################
31 err-bash-trace() {
32 local -i argc_index=0 frame i start=${_frame_start:-1}
33 local source
34 if [[ $1 ]]; then
35 printf "%s\n" "$1"
36 fi
37 for ((frame=0; frame < ${#FUNCNAME[@]}; frame++)); do
38 argc=${BASH_ARGC[frame]}
39 argc_index+=$argc
40 ((frame < start)) && continue
41 if (( ${#BASH_SOURCE[@]} > 1 )); then
42 source="${BASH_SOURCE[frame]}:${BASH_LINENO[frame-1]}:"
43 fi
44 printf " from %sin \`%s" "$source" "${FUNCNAME[frame]}"
45 if shopt extdebug >/dev/null; then
46 for ((i=argc_index-1; i >= argc_index-argc; i--)); do
47 printf " %s" "${BASH_ARGV[i]}"
48 done
49 fi
50 echo \'
51 done
52 return 0
53 }
54
55 #######################################
56 # On error print stack trace and exit
57 #
58 # Globals:
59 # errcatch-cleanup If set, this command will run just before exiting.
60 #######################################
61 err-catch() {
62 set -E; shopt -s extdebug
63 _err-trap() {
64 err=$?
65 exec >&2
66 set +x
67 local msg="${BASH_SOURCE[1]}:${BASH_LINENO[0]}: \`$BASH_COMMAND' returned $err"
68 if (( ${#FUNCNAME[@]} > 2 )); then
69 local _frame_start=2
70 err-bash-trace "$msg"
71 else
72 echo "$msg"
73 fi
74 set -e # err trap does not work within an error trap
75 if type -t errcatch-cleanup >/dev/null; then
76 errcatch-cleanup
77 fi
78 echo "$0: exiting with status $err"
79 exit $err
80 }
81 trap _err-trap ERR
82 set -o pipefail
83 }
84
85
86 #######################################
87 # For interactive shells: on error, print stack trace and return
88 #
89 # Globals:
90 # err_catch_ignore Array containing glob patterns to test against filenames to ignore
91 # errors from. Initialized to ignore bash-completion scripts on debian
92 # based systems.
93 # _err_func_last Used internally.
94 # _err_catch_err Used internally.
95 # _err_catch_i Used internally.
96 # _err_catch_ignore Used internally.
97 #
98 # misc: All shellcheck disables for this function are false positives.
99 #######################################
100 # shellcheck disable=SC2120
101 err-catch-interactive() {
102 err_catch_ignore=(
103 '/etc/bash_completion.d/*'
104 )
105 # shellcheck disable=SC2034
106 declare -i _err_func_last=0
107 set -E; shopt -s extdebug
108 # shellcheck disable=SC2154
109 trap '_err_catch_err=$? _trap_bc="$BASH_COMMAND"
110 _err_catch_ignore=false
111 for _err_catch_i in "${err_catch_ignore[@]}"; do
112 if [[ ${BASH_SOURCE[0]} == $_err_catch_i ]]; then
113 _err_catch_ignore=true
114 break
115 fi
116 done
117 if ! $_err_catch_ignore; then
118 if (( ${#FUNCNAME[@]} > _err_func_last )); then
119 echo ERR: \`$_trap_bc'"\'"' returned $_err_catch_err
120 fi
121 _err_func_last=${#FUNCNAME[@]}
122 if (( _err_func_last )); then
123 printf " from %s:%s:in \`%s" "${BASH_SOURCE[0]}" "$(declare -F "${FUNCNAME[0]}"|awk "{print \$2}")" "${FUNCNAME[0]}"
124 if shopt extdebug >/dev/null; then
125 for ((_err_catch_i=${BASH_ARGC[0]}-1; _err_catch_i >= 0; _err_catch_i--)); do
126 printf " %s" "${BASH_ARGV[_err_catch_i]}"
127 done
128 fi
129 echo '"\'"'
130 return $_err_catch_err
131 fi
132 fi' ERR
133 set -o pipefail
134 }
135
136
137 #######################################
138 # Undoes err-catch/err-catch-interactive
139 #######################################
140 err-allow() {
141 shopt -u extdebug
142 set +E +o pipefail
143 trap ERR
144 }
145
146 #######################################
147 # On error, print stack trace
148 #######################################
149 err-print() {
150 # help: on errors: print stack trace
151 #
152 # This function depends on err-bash-trace.
153
154 set -E; shopt -s extdebug
155 _err-trap() {
156 err=$?
157 exec >&2
158 set +x
159 echo "${BASH_SOURCE[1]}:${BASH_LINENO[0]}: \`$BASH_COMMAND' returned $err"
160 err-bash-trace 2
161 }
162 trap _err-trap ERR
163 set -o pipefail
164 }
165
166
167 #######################################
168 # Print stack trace and exit
169 #
170 # Use this instead of the exit command to be more informative.
171 #
172 # usage: err-exit [EXIT_CODE] [MESSAGE]
173 #
174 # EXIT_CODE Default is 1.
175 # MESSAGE Print MESSAGE to stderr. If only one of EXIT_CODE
176 # and MESSAGE is given, we consider it to be an
177 # exit code if it is a number.
178 #######################################
179 err-exit() {
180 exec >&2
181 code=1
182 if [[ "$*" ]]; then
183 if [[ ${1/[^0-9]/} == "$1" ]]; then
184 code=$1
185 if [[ $2 ]]; then
186 printf '%s\n' "$2" >&2
187 fi
188 else
189 printf '%s\n' "$0: $1" >&2
190 fi
191 fi
192 echo "${BASH_SOURCE[1]}:${BASH_LINENO[0]}"
193 err-bash-trace 2
194 echo "$0: exiting with code $code"
195 exit $err
196 }
197
198 # We want this more often than not, so run it now.
199 if [[ $- == *i* ]]; then
200 err-catch-interactive
201 else
202 err-catch
203 fi