bugfixes, features, documentation, examples, new tool
[hband-tools.git] / user-tools / pvalve
blob5af2d4067fa246ab2556333c7a5ae06f7598c4bf
1 #!/bin/bash
3 true <<EOF
5 =pod
7 =head1 NAME
9 pvalve - Control how much a given command should run by an other command's exit code
11 =head1 SYNOPSIS
13 pvalve [<B<CONTROL COMMAND>>] -- [<B<LONG RUNNING COMMAND>>]
15 Controls when B<LONG RUNNING COMMAND> should run, by pause and unpause it
16 according to the B<CONTROL COMMAND>'s exit status.
18 =head1 DESCRIPTION
20 Pause B<LONG RUNNING COMMAND> process group with STOP signal(7) if B<CONTROL COMMAND> exits non-zero.
21 Unpause B<LONG RUNNING COMMAND> process group with CONT signal(7) if B<CONTROL COMMAND> exits zero.
23 Pvalve takes the last line from B<CONTROL COMMAND>'s stdout, and if looks like a time interval
24 (ie. positive number with optional fraction followed by optional "s", "m", or "h" suffix)
25 then the next checking of B<CONTROL COMMAND> will start after that much time.
26 Otherwise it takes B<PVALVE_INTERVAL> environment variable,
27 or start next check immediately if it's not set.
29 Pvalve won't bombard B<LONG RUNNING COMMAND> with more consecutive STOP or CONT signals.
31 =head1 USEFULNESS
33 It's useful eg. for basic load control. Start a CPU-intensive program in B<LONG RUNNING COMMAND>
34 and check hardware temperature in B<CONTROL COMMAND>. Make it exit 0 when temperature is below
35 a certain value, and exit 1 if above an other, higher value.
37 =head1 ENVIRONMENT
39 =over 4
41 =item B<PVALVE_INTERVAL>
43 Default interval between two B<CONTROL COMMAND> runs.
45 =item B<PVALVE_STATUS>
47 B<PVALVE_STATUS> describes whether B<LONG RUNNING COMMAND> should be in running or in paused state.
48 Possible values: RUN, STOP.
49 This environment is available by B<CONTROL COMMAND>.
51 =item B<PVALVE_PID>
53 PID of B<LONG RUNNING COMMAND>.
55 =back
57 =head1 CAVEATS
59 Further process groups which are created by B<LONG RUNNING COMMAND> will not be affected.
61 =cut
63 EOF
67 toolname=pvalve
68 declare -a controller_cmd
69 declare -a controlled_cmd
71 while [ $# -gt 0 ]
73 case "$1" in
74 --help|-h)
75 pod2text "$0"
76 exit
78 esac
80 if [ "$1" = -- ]
81 then
82 shift
83 break
85 controller_cmd+=("$1")
86 shift
87 done
89 while [ $# -gt 0 ]
91 controlled_cmd+=("$1")
92 shift
93 done
95 quit()
97 kill -TERM -$PVALVE_PID
98 if [ "$PVALVE_STATUS" = STOP ]
99 then
100 kill -CONT -$PVALVE_PID
102 exit 130
105 exists()
107 local pid=$1
108 declare -g ancestor_valid
109 if [ "$ancestor_valid" != no ]
110 then
111 kill -0 $pid 2>/dev/null && return 0 || ancestor_valid=no
113 kill -0 -$pid 2>/dev/null
117 trap quit TERM INT
119 setpgrp "${controlled_cmd[@]}" &
120 PVALVE_PID=$!
121 export PVALVE_PID
122 PVALVE_STATUS=RUN
123 export PVALVE_STATUS
125 echo "$toolname: RUN $PVALVE_PID" >&2
127 stdout_file=`readlink /proc/self/fd/1`
128 exec {stdout_copy_fd}> "$stdout_file"
130 while exists $PVALVE_PID
132 delay=`command "${controller_cmd[@]}" | tee /dev/fd/$stdout_copy_fd | tail -n1; exit ${PIPESTATUS[0]}`
133 code=$?
135 if [ $code = 0 -a $PVALVE_STATUS != RUN ]
136 then
137 echo "$toolname: CONT -$PVALVE_PID" >&2
138 kill -CONT -$PVALVE_PID
139 PVALVE_STATUS=RUN
140 elif [ $code != 0 -a $PVALVE_STATUS = RUN ]
141 then
142 echo "$toolname: STOP -$PVALVE_PID" >&2
143 kill -STOP -$PVALVE_PID
144 PVALVE_STATUS=STOP
147 if [[ $delay =~ ^[0-9]+([.,][0-9]+)?[smh]?$ ]]
148 then
149 true
150 else
151 delay=${PVALVE_INTERVAL:-0}
154 echo "$toolname: next check in $delay sec" >&2
155 sleep $delay
156 done
158 wait