1*cf5a6c84SAndroid Build Coastguard Worker /* timeout.c - Run command line with a timeout
2*cf5a6c84SAndroid Build Coastguard Worker *
3*cf5a6c84SAndroid Build Coastguard Worker * Copyright 2013 Rob Landley <[email protected]>
4*cf5a6c84SAndroid Build Coastguard Worker *
5*cf5a6c84SAndroid Build Coastguard Worker * No standard
6*cf5a6c84SAndroid Build Coastguard Worker
7*cf5a6c84SAndroid Build Coastguard Worker USE_TIMEOUT(NEWTOY(timeout, "<2^(foreground)(preserve-status)vk:s(signal):i", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_ARGFAIL(125)))
8*cf5a6c84SAndroid Build Coastguard Worker
9*cf5a6c84SAndroid Build Coastguard Worker config TIMEOUT
10*cf5a6c84SAndroid Build Coastguard Worker bool "timeout"
11*cf5a6c84SAndroid Build Coastguard Worker default y
12*cf5a6c84SAndroid Build Coastguard Worker help
13*cf5a6c84SAndroid Build Coastguard Worker usage: timeout [-iv] [-k DURATION] [-s SIGNAL] DURATION COMMAND...
14*cf5a6c84SAndroid Build Coastguard Worker
15*cf5a6c84SAndroid Build Coastguard Worker Run command line as a child process, sending child a signal if the
16*cf5a6c84SAndroid Build Coastguard Worker command doesn't exit soon enough.
17*cf5a6c84SAndroid Build Coastguard Worker
18*cf5a6c84SAndroid Build Coastguard Worker DURATION can be a decimal fraction. An optional suffix can be "m"
19*cf5a6c84SAndroid Build Coastguard Worker (minutes), "h" (hours), "d" (days), or "s" (seconds, the default).
20*cf5a6c84SAndroid Build Coastguard Worker
21*cf5a6c84SAndroid Build Coastguard Worker -i Only kill for inactivity (restart timeout when command produces output)
22*cf5a6c84SAndroid Build Coastguard Worker -k Send KILL signal if child still running this long after first signal
23*cf5a6c84SAndroid Build Coastguard Worker -s Send specified signal (default TERM)
24*cf5a6c84SAndroid Build Coastguard Worker -v Verbose
25*cf5a6c84SAndroid Build Coastguard Worker --foreground Don't create new process group
26*cf5a6c84SAndroid Build Coastguard Worker --preserve-status Exit with the child's exit status
27*cf5a6c84SAndroid Build Coastguard Worker */
28*cf5a6c84SAndroid Build Coastguard Worker
29*cf5a6c84SAndroid Build Coastguard Worker #define FOR_timeout
30*cf5a6c84SAndroid Build Coastguard Worker #include "toys.h"
31*cf5a6c84SAndroid Build Coastguard Worker
32*cf5a6c84SAndroid Build Coastguard Worker GLOBALS(
33*cf5a6c84SAndroid Build Coastguard Worker char *s, *k;
34*cf5a6c84SAndroid Build Coastguard Worker
35*cf5a6c84SAndroid Build Coastguard Worker struct pollfd pfd;
36*cf5a6c84SAndroid Build Coastguard Worker sigjmp_buf sj;
37*cf5a6c84SAndroid Build Coastguard Worker int fds[2], pid, rc;
38*cf5a6c84SAndroid Build Coastguard Worker )
39*cf5a6c84SAndroid Build Coastguard Worker
handler(int sig,siginfo_t * si)40*cf5a6c84SAndroid Build Coastguard Worker static void handler(int sig, siginfo_t *si)
41*cf5a6c84SAndroid Build Coastguard Worker {
42*cf5a6c84SAndroid Build Coastguard Worker TT.rc = si->si_status + ((si->si_code!=CLD_EXITED)<<7);
43*cf5a6c84SAndroid Build Coastguard Worker siglongjmp(TT.sj, 1);
44*cf5a6c84SAndroid Build Coastguard Worker }
45*cf5a6c84SAndroid Build Coastguard Worker
nantomil(struct timespec * ts)46*cf5a6c84SAndroid Build Coastguard Worker static long nantomil(struct timespec *ts)
47*cf5a6c84SAndroid Build Coastguard Worker {
48*cf5a6c84SAndroid Build Coastguard Worker return ts->tv_sec*1000+ts->tv_nsec/1000000;
49*cf5a6c84SAndroid Build Coastguard Worker }
50*cf5a6c84SAndroid Build Coastguard Worker
callback(char * argv[])51*cf5a6c84SAndroid Build Coastguard Worker static void callback(char *argv[])
52*cf5a6c84SAndroid Build Coastguard Worker {
53*cf5a6c84SAndroid Build Coastguard Worker if (!FLAG(foreground)) setpgid(0, 0);
54*cf5a6c84SAndroid Build Coastguard Worker }
55*cf5a6c84SAndroid Build Coastguard Worker
timeout_main(void)56*cf5a6c84SAndroid Build Coastguard Worker void timeout_main(void)
57*cf5a6c84SAndroid Build Coastguard Worker {
58*cf5a6c84SAndroid Build Coastguard Worker int ii, ms, nextsig = SIGTERM;
59*cf5a6c84SAndroid Build Coastguard Worker struct timespec tts, kts;
60*cf5a6c84SAndroid Build Coastguard Worker
61*cf5a6c84SAndroid Build Coastguard Worker // Use same ARGFAIL value for any remaining parsing errors
62*cf5a6c84SAndroid Build Coastguard Worker toys.exitval = 125;
63*cf5a6c84SAndroid Build Coastguard Worker xparsetimespec(*toys.optargs, &tts);
64*cf5a6c84SAndroid Build Coastguard Worker if (TT.k) xparsetimespec(TT.k, &kts);
65*cf5a6c84SAndroid Build Coastguard Worker if (TT.s && -1==(nextsig = sig_to_num(TT.s))) error_exit("bad -s: '%s'",TT.s);
66*cf5a6c84SAndroid Build Coastguard Worker
67*cf5a6c84SAndroid Build Coastguard Worker toys.exitval = 0;
68*cf5a6c84SAndroid Build Coastguard Worker TT.pfd.events = POLLIN;
69*cf5a6c84SAndroid Build Coastguard Worker TT.fds[1] = -1;
70*cf5a6c84SAndroid Build Coastguard Worker if (sigsetjmp(TT.sj, 1)) goto done;
71*cf5a6c84SAndroid Build Coastguard Worker xsignal_flags(SIGCHLD, handler, SA_NOCLDSTOP|SA_SIGINFO);
72*cf5a6c84SAndroid Build Coastguard Worker
73*cf5a6c84SAndroid Build Coastguard Worker TT.pid = xpopen_setup(toys.optargs+1, FLAG(i) ? TT.fds : 0, callback);
74*cf5a6c84SAndroid Build Coastguard Worker xsignal(SIGTTIN, SIG_IGN);
75*cf5a6c84SAndroid Build Coastguard Worker xsignal(SIGTTOU, SIG_IGN);
76*cf5a6c84SAndroid Build Coastguard Worker xsignal(SIGTSTP, SIG_IGN);
77*cf5a6c84SAndroid Build Coastguard Worker if (!FLAG(i)) xpipe(TT.fds);
78*cf5a6c84SAndroid Build Coastguard Worker TT.pfd.fd = TT.fds[1];
79*cf5a6c84SAndroid Build Coastguard Worker ms = nantomil(&tts);
80*cf5a6c84SAndroid Build Coastguard Worker for (;;) {
81*cf5a6c84SAndroid Build Coastguard Worker if (1 != xpoll(&TT.pfd, 1, ms)) {
82*cf5a6c84SAndroid Build Coastguard Worker if (FLAG(v))
83*cf5a6c84SAndroid Build Coastguard Worker perror_msg("sending signal %s to command %s", num_to_sig(nextsig),
84*cf5a6c84SAndroid Build Coastguard Worker toys.optargs[1]);
85*cf5a6c84SAndroid Build Coastguard Worker toys.exitval = (nextsig==9) ? 137 : 124;
86*cf5a6c84SAndroid Build Coastguard Worker kill(FLAG(foreground) ? TT.pid : -TT.pid, nextsig);
87*cf5a6c84SAndroid Build Coastguard Worker if (!TT.k || nextsig==SIGKILL) break;
88*cf5a6c84SAndroid Build Coastguard Worker nextsig = SIGKILL;
89*cf5a6c84SAndroid Build Coastguard Worker ms = nantomil(&kts);
90*cf5a6c84SAndroid Build Coastguard Worker
91*cf5a6c84SAndroid Build Coastguard Worker continue;
92*cf5a6c84SAndroid Build Coastguard Worker }
93*cf5a6c84SAndroid Build Coastguard Worker if (TT.pfd.revents&POLLIN) {
94*cf5a6c84SAndroid Build Coastguard Worker errno = 0;
95*cf5a6c84SAndroid Build Coastguard Worker if (1>(ii = read(TT.fds[1], toybuf, sizeof(toybuf)))) {
96*cf5a6c84SAndroid Build Coastguard Worker if (errno==EINTR) continue;
97*cf5a6c84SAndroid Build Coastguard Worker break;
98*cf5a6c84SAndroid Build Coastguard Worker }
99*cf5a6c84SAndroid Build Coastguard Worker writeall(1, toybuf, ii);
100*cf5a6c84SAndroid Build Coastguard Worker }
101*cf5a6c84SAndroid Build Coastguard Worker if (TT.pfd.revents&POLLHUP) break;
102*cf5a6c84SAndroid Build Coastguard Worker }
103*cf5a6c84SAndroid Build Coastguard Worker done:
104*cf5a6c84SAndroid Build Coastguard Worker xpclose_both(TT.pid, TT.fds);
105*cf5a6c84SAndroid Build Coastguard Worker
106*cf5a6c84SAndroid Build Coastguard Worker if (FLAG(preserve_status) || !toys.exitval) toys.exitval = TT.rc;
107*cf5a6c84SAndroid Build Coastguard Worker }
108