aboutsummaryrefslogtreecommitdiffstats
path: root/examples/scripts/timeout3
blob: de650be13467cbfdfcf3c4951655a6758740bcb2 (plain)
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
#!/bin/bash
#
# The Bash script executes a command with a time-out.
# Based on the Bash documentation example.
#
# Upon time-out expiration SIGTERM (15) is sent to the process.  If the signal
# is blocked, then the subsequent SIGKILL (9) terminates it.
# Dmitry V Golovashkin (E-mail: dvg@ieee.org)
#
script_name="${0##*/}"

# Default values.
readonly param_timeout=5
readonly param_interval=1
readonly param_delay=1

declare -i timeout=param_timeout
declare -i interval=param_interval
declare -i delay=param_delay

blue="$(tput setaf 4)"
bold_red="$(tput bold; tput setaf 1)"
off="$(tput sgr0)"

function print_usage() {
cat <<EOF

Synopsis:  $script_name [-t timeout] [-i interval] [-d delay] command

Executes the command with a time-out.  Upon time-out expiration SIGTERM (15) is
sent to the process.  If SIGTERM signal is blocked, then the subsequent SIGKILL
(9) terminates it.

$blue-t timeout$off
    Number of seconds to wait for command completion.
    Default value: $param_timeout seconds.  In some practical situations
    this value ${bold_red}must$off be increased (for instance -t 180) to allow
    the command to complete.

$blue-i interval$off
    Interval between checks if the process is still alive.
    Positive integer, default value: $param_interval seconds.
    Default value is OK for most situations.

$blue-d delay$off
    Delay between posting the SIGTERM signal and destroying the process by
    SIGKILL.  Default value: $param_delay seconds.
    Default value is OK for most situations.

As of today, Bash does not support floating point arithmetic (sleep does),
therefore all time values must be integers.
Dmitry Golovashkin (E-mail: dvg@ieee.org)
EOF
exit 1 # No useful work was done.
}

# Options.
while getopts ":t:i:d:" option; do
    case "$option" in
        t) timeout=$OPTARG ;;
        i) interval=$OPTARG ;;
        d) delay=$OPTARG ;;
        *) print_usage ;;
    esac
done
shift $((OPTIND - 1))

# $# should be at least 1 (the command to execute), however it may be strictly
# greater than 1 if the command itself has options.
if (($# == 0 || interval <= 0)); then
    print_usage
fi

# kill -0 pid   Exit code indicates if a signal may be sent to "pid" process.
(
    ((t = timeout))

    while ((t > 0)); do
        sleep $interval
        kill -0 $$ || exit 0
        ((t -= interval))
    done

    # Be nice, post SIGTERM first.
    # The 'exit 0' below will be executed if any preceeding command fails.
    kill -s SIGTERM $$ && kill -0 $$ || exit 0
    sleep $delay
    kill -s SIGKILL $$
) 2> /dev/null &

exec "$@"