xref: /aosp_15_r20/external/toybox/toys/other/timeout.c (revision cf5a6c84e2b8763fc1a7db14496fd4742913b199)
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