make getpeername() return the original socket address which before it was intercepted
[hband-tools.git] / user-tools / trackrun
blob790f45a038b415a33a39462f4e0d71f4667d4f63
1 #!/bin/bash
3 set -e -o pipefail
4 set -u
7 true <<'EOF'
9 =pod
11 =head1 NAME
13 trackrun - Record when the given command was started and ended and expose to it in environment variables
15 =head1 SYNOPSIS
17 trackrun [I<OPTIONS>] [--] I<COMMAND> [I<ARGS>]
19 =head1 DESCRIPTION
21 It records when it starts COMMAND and when it ends, identifying COMMAND either by one of 4 options:
23 =over 4
25 =item Full command line including I<ARGS>.
27 =item Only the command name, I<COMMAND>.
29 =item By the name given by the user in I<NAME>.
31 =item By the environment variable value given by name I<ENV>.
33 =back
35 Set B<TRACKRUN_LAST_STARTED> and B<TRACKRUN_LAST_ENDED> environments for COMMAND to the ISO 8601 representation
36 of the date and time when COMMAND was last started and ended respectively.
37 Set B<TRACKRUN_LAST_STATUS> to the status COMMAND last exited with.
38 Those are left empty if no data yet.
40 On every run, a UUID is generated, so you can connect events of concurrent runs in the track report.
41 It is exposed in B<TRACKRUN_UUID> env.
43 =head1 OPTIONS
45 =over 4
47 =item -f, --full-command
49 =item -b, --command-basename (default)
51 =item -n, --name I<NAME>
53 =item -e, --env-var I<ENV>
55 =item -h, --show-hash
57 Show the hash generated from either of those options above before run the I<COMMAND>.
58 This hash is used for the filename in which command-related events are stored.
60 =item -u, --show-uuid
62 Show the current run's UUID before actually start the command.
64 =item -U, --write-uuid I<FILE>
66 Write current run's UUID in the given file before start the command.
68 =item -R, --report
70 Do not run I<COMMAND>, instead display its tracked history.
72 =back
74 =head1 FILES
76 Store tracking data in F<~/.trackrun> directory.
78 =head1 ENVIRONMENT
80 =over 4
82 =item TRACKRUN_LAST_STARTED
84 =item TRACKRUN_LAST_ENDED
86 =item TRACKRUN_LAST_STATUS
88 =item TRACKRUN_LAST_UUID
90 =item TRACKRUN_LAST_SUCCESSFUL_STARTED
92 =item TRACKRUN_LAST_SUCCESSFUL_ENDED
94 =item TRACKRUN_LAST_SUCCESSFUL_UUID
96 The last successful run's UUID, date-time when started and ended.
98 =item TRACKRUN_UUID
100 The current run's UUID
102 =back
104 =head1 LIMITATIONS
106 Trackrun does not do locking. You may take care of it if you need using flock(1), cronrun(1), or similar.
108 =cut
113 cmd_id=''
114 cmd_id_by=basename
115 do_report=''
116 uuid_outfile=''
117 do_show_hash=''
118 do_show_uuid=''
121 while [ $# -gt 0 ]
123 case "$1" in
124 (--help)
125 pod2text "$0"
126 exit 0;;
127 (-f|--full-command)
128 cmd_id_by=full
130 (-b|--command-basename)
131 cmd_id_by=basename
133 (-n|--name)
134 cmd_id_by=name
135 shift
136 cmd_id=$1
138 (-e|--env-var)
139 cmd_id_by=env
140 shift
141 cmd_id=${!1}
143 (-R|--report)
144 do_report=1
146 (-h|--show-hash)
147 do_show_hash=1
149 (-u|--show-uuid)
150 do_show_uuid=1
152 (-U|--write-uuid)
153 shift
154 uuid_outfile=$1
156 (--)
157 shift
158 break;;
159 (-*)
160 echo "$0: unknown option: $1" >&1
161 exit -1;;
163 break;;
164 esac
165 shift
166 done
169 declare -a cmd_args=()
171 if [ -z "$cmd_id" ]
172 then
173 case "$cmd_id_by" in
174 (full|basename)
175 if [ $# = 0 ]
176 then
177 pod2text "$0" >&2
178 exit -1
180 cmd_args=("$@")
182 esac
183 case "$cmd_id_by" in
184 (full)
185 cmd_id=${cmd_args[*]}
187 (basename)
188 cmd_id=`basename "${cmd_args[0]}"`
190 (name)
191 echo "$0: empty --name" >&2
192 exit 1
194 (env)
195 echo "$0: value empty in --env-var" >&2
196 exit 1
198 esac
202 track_dir=~/.trackrun
203 cmd_id_hash=`printf %s "$cmd_id" | md5sum | cut -c 1-32`
204 track_file=$track_dir/$cmd_id_hash
207 if [ $do_show_hash ]
208 then
209 echo $cmd_id_hash
210 exit
212 if [ $do_report ]
213 then
214 cat "$track_file" | tac | td-add-headers UUID EVENT DATETIME STATUS COMMAND
215 exit
219 lookup()
221 # lookup A B [A B [A B]]
222 # search a line in STDIN of which field A's value is B for all pair of A and B,
223 # if A is not a number but "return", then return the B field's value.
224 # Fields are counted form zero.
225 perl -e 'while(<STDIN>) {
226 chomp;
227 @field = split /\t/;
228 @Filters = @ARGV;
229 while(@Filters)
231 $filter_field = shift @Filters;
232 $filter_value = shift @Filters;
233 if($filter_field eq "return") { print $filter_value eq "record" ? $_ : $field[$filter_value]; exit; }
234 elsif($field[$filter_field] ne $filter_value) { last; }
236 }' -- "$@"
239 mkdir -p "$track_dir"
240 set +e +o pipefail
241 last_record_end=`tac "$track_file" 2>/dev/null | lookup 1 end return record`
242 export TRACKRUN_LAST_UUID=`echo "$last_record_end" | lookup return 0`
243 export TRACKRUN_LAST_STARTED=`tac "$track_file" 2>/dev/null | lookup 0 "$TRACKRUN_LAST_UUID" 1 start return 2`
244 export TRACKRUN_LAST_ENDED=`echo "$last_record_end" | lookup return 2`
245 export TRACKRUN_LAST_STATUS=`echo "$last_record_end" | lookup return 3`
246 last_successful_record_end=`tac "$track_file" 2>/dev/null | lookup 1 end 3 0 return record`
247 export TRACKRUN_LAST_SUCCESSFUL_UUID=`echo "$last_successful_record_end" | lookup return 0`
248 export TRACKRUN_LAST_SUCCESSFUL_STARTED=`tac "$track_file" 2>/dev/null | lookup 0 "$TRACKRUN_LAST_SUCCESSFUL_UUID" 1 start return 2`
249 export TRACKRUN_LAST_SUCCESSFUL_ENDED=`echo "$last_successful_record_end" | lookup return 2`
250 set -e -o pipefail
252 if [ -z "${cmd_args[0]:-}" ]
253 then
254 cmd_args=("$@")
257 type uuidgen >/dev/null
258 export TRACKRUN_UUID=`uuidgen`
260 if [ $do_show_uuid ]
261 then
262 echo "${0##*/}: command run uuid: $TRACKRUN_UUID" >&2
264 if [ -n "$uuid_outfile" ]
265 then
266 echo "$TRACKRUN_UUID" > "$uuid_outfile"
269 record()
271 printf "%s\t%s\t%(%FT%T%z)T\t%s\t%s\n" "$TRACKRUN_UUID" "$1" -1 "${2:-}" "${cmd_args[*]}" >> "$track_file"
274 record start
277 exec -- setsid "${cmd_args[@]}"
278 exit 127
281 child_pid=$!
282 signal_handler() { interrupted=yes; }
283 trap signal_handler INT
284 while true
286 interrupted=''
287 set +e
288 wait $child_pid
289 exit_code=$?
290 set -e
291 if [ $interrupted ]
292 then
293 exit_code=''
294 kill -s INT $child_pid || true
295 else
296 break
298 done
300 record end $exit_code
302 exit $exit_code