1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131
|
#!/bin/bash
# SPDX-License-Identifier: GPL-2.0
# Whether to pause and allow debugging when an executed deferred command has a
# non-zero exit code.
: "${DEFER_PAUSE_ON_FAIL:=no}"
# map[(scope_id,track,cleanup_id) -> cleanup_command]
# track={d=default | p=priority}
declare -A __DEFER__JOBS
# map[(scope_id,track) -> # cleanup_commands]
declare -A __DEFER__NJOBS
# scope_id of the topmost scope.
__DEFER__SCOPE_ID=0
__defer__ndefer_key()
{
local track=$1; shift
echo $__DEFER__SCOPE_ID,$track
}
__defer__defer_key()
{
local track=$1; shift
local defer_ix=$1; shift
echo $__DEFER__SCOPE_ID,$track,$defer_ix
}
__defer__ndefers()
{
local track=$1; shift
echo ${__DEFER__NJOBS[$(__defer__ndefer_key $track)]}
}
__defer__run()
{
local track=$1; shift
local defer_ix=$1; shift
local defer_key=$(__defer__defer_key $track $defer_ix)
local ret
eval ${__DEFER__JOBS[$defer_key]}
ret=$?
if [[ "$DEFER_PAUSE_ON_FAIL" == yes && "$ret" -ne 0 ]]; then
echo "Deferred command (track $track index $defer_ix):"
echo " ${__DEFER__JOBS[$defer_key]}"
echo "... ended with an exit status of $ret"
echo "Hit enter to continue, 'q' to quit"
read a
[[ "$a" == q ]] && exit 1
fi
unset __DEFER__JOBS[$defer_key]
}
__defer__schedule()
{
local track=$1; shift
local ndefers=$(__defer__ndefers $track)
local ndefers_key=$(__defer__ndefer_key $track)
local defer_key=$(__defer__defer_key $track $ndefers)
local defer="${@@Q}"
__DEFER__JOBS[$defer_key]="$defer"
__DEFER__NJOBS[$ndefers_key]=$((ndefers + 1))
}
__defer__scope_wipe()
{
__DEFER__NJOBS[$(__defer__ndefer_key d)]=0
__DEFER__NJOBS[$(__defer__ndefer_key p)]=0
}
defer_scope_push()
{
((__DEFER__SCOPE_ID++))
__defer__scope_wipe
}
defer_scope_pop()
{
local defer_ix
for ((defer_ix=$(__defer__ndefers p); defer_ix-->0; )); do
__defer__run p $defer_ix
done
for ((defer_ix=$(__defer__ndefers d); defer_ix-->0; )); do
__defer__run d $defer_ix
done
__defer__scope_wipe
((__DEFER__SCOPE_ID--))
}
defer()
{
__defer__schedule d "$@"
}
defer_prio()
{
__defer__schedule p "$@"
}
defer_scopes_cleanup()
{
while ((__DEFER__SCOPE_ID >= 0)); do
defer_scope_pop
done
}
in_defer_scope()
{
local ret
defer_scope_push
"$@"
ret=$?
defer_scope_pop
return $ret
}
__defer__scope_wipe
|