-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathlib-process.sh
More file actions
executable file
·117 lines (103 loc) · 3.14 KB
/
Copy pathlib-process.sh
File metadata and controls
executable file
·117 lines (103 loc) · 3.14 KB
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
#!/usr/bin/env bash
# Maestro bounded process module. Sourced, not executed.
[ "${_MAESTRO_PROCESS_LOADED-0}" = 1 ] && return 0
_MAESTRO_PROCESS_LOADED=1
# Interface:
# process_run_bounded TIMEOUT LABEL TICK_FN STDOUT_FILE STDERR_FILE -- COMMAND [ARGS...]
# process_interrupt SIGNAL EVIDENCE_FILE
# The module owns the spawned process group; callers own output and evidence files.
_MAESTRO_PROCESS_PID=""
_MAESTRO_PROCESS_COMPLETION=""
progress_init() {
if ! { true >&3; } 2>/dev/null; then exec 3>&1; fi
}
progress() { printf '%s\n' "$*" >&3; }
_process_group_stop() { # pid
local pid="$1" grace=0
case "$pid" in ''|*[!0-9]*) return 0 ;; esac
kill -TERM -"$pid" 2>/dev/null || :
while kill -0 -"$pid" 2>/dev/null && [ "$grace" -lt 5 ]; do
sleep 1
grace=$((grace + 1))
done
kill -KILL -"$pid" 2>/dev/null || :
return 0
}
process_run_bounded() { # timeout label tick stdout stderr -- command [args...]
local timeout="${1-}" label="${2-}" tick="${3-}"
local stdout_file="${4-}" stderr_file="${5-}"
local pid start elapsed timed_out=0 ticks=0 rc completion
shift 5 2>/dev/null || return 3
[ "${1-}" = "--" ] || return 3
shift
case "$timeout" in
''|*[!0-9]*) return 3 ;;
*) [ "$timeout" -ge 1 ] 2>/dev/null || return 3 ;;
esac
if [ "$tick" != : ] && ! declare -F "$tick" >/dev/null 2>&1; then
return 3
fi
[ -n "$stdout_file" ] && [ -n "$stderr_file" ] && [ "$stdout_file" != "$stderr_file" ] || return 3
[ "$#" -gt 0 ] || return 3
: > "$stdout_file" || return 3
: > "$stderr_file" || return 3
completion=$(mktemp "${TMPDIR:-/tmp}/maestro-process-completion.XXXXXX") ||
return 3
_MAESTRO_PROCESS_COMPLETION=$completion
set -m
(
"$@" > "$stdout_file" 2> "$stderr_file"
rc=$?
printf '%s\n' "$rc" > "$completion"
exit "$rc"
) &
pid=$!
_MAESTRO_PROCESS_PID=$pid
set +m
start=$SECONDS
while [ ! -s "$completion" ]; do
elapsed=$((SECONDS - start))
if [ "$elapsed" -ge "$timeout" ]; then
timed_out=1
break
fi
sleep 0.1
ticks=$((ticks + 1))
if [ "$ticks" -ge 10 ]; then
"$tick" || :
ticks=0
fi
done
if [ "$timed_out" -eq 1 ]; then
_process_group_stop "$pid"
fi
wait "$pid" 2>/dev/null
rc=$?
# A successful leader may leave descendants in its process group. Do not
# return while any process from this bounded call is still alive.
if kill -0 -"$pid" 2>/dev/null; then
_process_group_stop "$pid"
fi
rm -f "$completion"
_MAESTRO_PROCESS_COMPLETION=""
_MAESTRO_PROCESS_PID=""
if [ "$timed_out" -eq 1 ]; then
progress "$label: timed out after ${timeout}s"
return 125
fi
return "$rc"
}
process_interrupt() { # HUP|INT|TERM evidence-file
local signal="${1-}" evidence="${2-}" pid="${_MAESTRO_PROCESS_PID-}"
case "$signal" in HUP|INT|TERM) ;; *) return 3 ;; esac
[ -n "$pid" ] || return 0
_process_group_stop "$pid"
wait "$pid" 2>/dev/null || :
rm -f "${_MAESTRO_PROCESS_COMPLETION:-}"
_MAESTRO_PROCESS_COMPLETION=""
_MAESTRO_PROCESS_PID=""
if [ -n "$evidence" ]; then
printf 'MAESTRO_PROCESS: interrupted active process group with %s\n' "$signal" >> "$evidence" 2>/dev/null || :
fi
return 0
}