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