xref: /linux/tools/testing/selftests/net/lib/sh/defer.sh (revision ccde82e909467abdf098a8ee6f63e1ecf9a47ce5)
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