1#!/bin/bash 2# 3# Alarm tool. 4# 5# Usage: 6# ./alarm.sh <function name> 7 8# You can source this file and use the alarm-status function. 9 10set -o nounset 11set -o pipefail 12set -o errexit 13 14# Run a command with a timeout, and print its status to a directory. 15# 16# Usage: 17# alarm-status job_dir/STATUS 10 \ 18# flaky_command ... 19 20alarm-status() { 21 set +o errexit 22 local status_file=$1 23 shift # everything except the status file goes to perl 24 25 # NOTE: It would be nice to setpgrp() before exec? And then can the signal 26 # be delivered to the entire group, like kill -SIGALRM -PID? 27 28 # NOTE: If we did this in Python, the error message would also be clearer. 29 perl -e 'alarm shift; exec @ARGV or die "ERROR: after exec @ARGV"' "$@" 30 local exit_code=$? 31 32 set -o errexit 33 34 local result='' 35 case $exit_code in 36 0) 37 # Would be nice to show elapsed time? 38 result='OK' 39 ;; 40 9) 41 # decode_assoc.R will exit 9 if there are no reports AFTER 42 # --remove-bad-rows. A task can also be marked SKIPPED before running 43 # the child process (see backfill.sh). 44 result='SKIPPED by child process' 45 ;; 46 # exit code 142 means SIGALARM. 128 + 14 = 142. See 'kill -l'. 47 142) 48 local seconds=$1 49 result="TIMEOUT after $seconds seconds" 50 ;; 51 *) 52 result="FAIL with status $exit_code" 53 ;; 54 esac 55 echo "$result" 56 echo "$result" > $status_file 57} 58 59_work() { 60 local n=10 # 2 seconds 61 for i in $(seq $n); do 62 echo $i - "$@" 63 sleep 0.2 64 done 65} 66 67_succeed() { 68 _work "$@" 69 exit 0 70} 71 72_fail() { 73 _work "$@" 74 exit 1 75} 76 77_skip() { 78 exit 9 79} 80 81# http://perldoc.perl.org/functions/alarm.html 82# 83# Delivers alarm. But how to get the process to have a distinct exit code? 84 85demo() { 86 mkdir -p _tmp 87 88 # timeout 89 alarm-status _tmp/A 1 $0 _succeed foo 90 echo 91 92 # ok 93 alarm-status _tmp/B 3 $0 _succeed bar 94 echo 95 96 # fail 97 alarm-status _tmp/C 3 $0 _fail baz 98 echo 99 100 # skip 101 alarm-status _tmp/D 3 $0 _skip baz 102 echo 103 104 head _tmp/{A,B,C,D} 105} 106 107test-simple() { 108 alarm-status _tmp/status.txt 1 sleep 2 109} 110 111test-bad-command() { 112 alarm-status _tmp/status.txt 1 nonexistent_sleep 2 113} 114 115# BUG 116test-perl() { 117 set +o errexit 118 perl -e 'alarm shift; exec @ARGV or die "ERROR after exec @ARGV"' 1 _sleep 2 119 echo $? 120} 121 122if test $(basename $0) = 'alarm-lib.sh'; then 123 "$@" 124fi 125