aboutsummaryrefslogtreecommitdiff
path: root/toys/other/timeout.c
diff options
context:
space:
mode:
Diffstat (limited to 'toys/other/timeout.c')
-rw-r--r--toys/other/timeout.c104
1 files changed, 64 insertions, 40 deletions
diff --git a/toys/other/timeout.c b/toys/other/timeout.c
index e93a806c..ed4956a4 100644
--- a/toys/other/timeout.c
+++ b/toys/other/timeout.c
@@ -4,13 +4,13 @@
*
* No standard
-USE_TIMEOUT(NEWTOY(timeout, "<2^(foreground)(preserve-status)vk:s(signal):", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_ARGFAIL(125)))
+USE_TIMEOUT(NEWTOY(timeout, "<2^(foreground)(preserve-status)vk:s(signal):i", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_ARGFAIL(125)))
config TIMEOUT
bool "timeout"
default y
help
- usage: timeout [-k DURATION] [-s SIGNAL] DURATION COMMAND...
+ usage: timeout [-i] [-k DURATION] [-s SIGNAL] DURATION COMMAND...
Run command line as a child process, sending child a signal if the
command doesn't exit soon enough.
@@ -18,8 +18,9 @@ config TIMEOUT
DURATION can be a decimal fraction. An optional suffix can be "m"
(minutes), "h" (hours), "d" (days), or "s" (seconds, the default).
- -s Send specified signal (default TERM)
+ -i Only kill for inactivity (restart timeout when command produces output)
-k Send KILL signal if child still running this long after first signal
+ -s Send specified signal (default TERM)
-v Verbose
--foreground Don't create new process group
--preserve-status Exit with the child's exit status
@@ -31,54 +32,77 @@ config TIMEOUT
GLOBALS(
char *s, *k;
- int nextsig;
- pid_t pid;
- struct timespec kts;
- struct itimerspec its;
- timer_t timer;
+ struct pollfd pfd;
+ sigjmp_buf sj;
+ int fds[2], pid;
)
-static void handler(int i)
+static void handler(int sig)
{
- if (FLAG(v))
- fprintf(stderr, "timeout pid %d signal %d\n", TT.pid, TT.nextsig);
-
- toys.exitval = (TT.nextsig==9) ? 137 : 124;
- kill(TT.pid, TT.nextsig);
- if (TT.k) {
- TT.k = 0;
- TT.nextsig = SIGKILL;
- xsignal(SIGALRM, handler);
- TT.its.it_value = TT.kts;
- if (timer_settime(TT.timer, 0, &TT.its, 0)) perror_exit("timer_settime");
- }
+ siglongjmp(TT.sj, 1);
+}
+
+static long nantomil(struct timespec *ts)
+{
+ return ts->tv_sec*1000+ts->tv_nsec/1000000;
+}
+
+static void callback(char *argv[])
+{
+ xsignal(SIGCHLD, SIG_DFL);
+ if (!FLAG(foreground)) setpgid(0, 0);
}
void timeout_main(void)
{
- struct sigevent se = { .sigev_notify = SIGEV_SIGNAL, .sigev_signo = SIGALRM };
+ int ii, ms, nextsig = SIGTERM;
+ struct timespec tts, kts;
// Use same ARGFAIL value for any remaining parsing errors
toys.exitval = 125;
- xparsetimespec(*toys.optargs, &TT.its.it_value);
- if (TT.k) xparsetimespec(TT.k, &TT.kts);
-
- TT.nextsig = SIGTERM;
- if (TT.s && -1 == (TT.nextsig = sig_to_num(TT.s)))
- error_exit("bad -s: '%s'", TT.s);
-
- if (!FLAG(foreground)) setpgid(0, 0);
+ xparsetimespec(*toys.optargs, &tts);
+ if (TT.k) xparsetimespec(TT.k, &kts);
+ if (TT.s && -1==(nextsig = sig_to_num(TT.s))) error_exit("bad -s: '%s'",TT.s);
toys.exitval = 0;
- if (!(TT.pid = XVFORK())) xexec(toys.optargs+1);
- else {
- int status;
-
- xsignal(SIGALRM, handler);
- if (timer_create(CLOCK_MONOTONIC, &se, &TT.timer)) perror_exit("timer");
- if (timer_settime(TT.timer, 0, &TT.its, 0)) perror_exit("timer_settime");
-
- status = xwaitpid(TT.pid);
- if (FLAG(preserve_status) || !toys.exitval) toys.exitval = status;
+ TT.pfd.events = POLLIN;
+ TT.fds[1] = -1;
+ if (sigsetjmp(TT.sj, 1)) goto done;
+ xsignal_flags(SIGCHLD, handler, SA_NOCLDSTOP);
+
+ TT.pid = xpopen_setup(toys.optargs+1, FLAG(i) ? TT.fds : 0, callback);
+ xsignal(SIGTTIN, SIG_IGN);
+ xsignal(SIGTTOU, SIG_IGN);
+ xsignal(SIGTSTP, SIG_IGN);
+ if (!FLAG(i)) xpipe(TT.fds);
+ TT.pfd.fd = TT.fds[1];
+ ms = nantomil(&tts);
+ for (;;) {
+ if (1 != xpoll(&TT.pfd, 1, ms)) {
+ if (FLAG(v))
+ perror_msg("sending signal %s to command %s", num_to_sig(nextsig),
+ toys.optargs[1]);
+ toys.exitval = (nextsig==9) ? 137 : 124;
+ kill(FLAG(foreground) ? TT.pid : -TT.pid, nextsig);
+ if (!TT.k || nextsig==SIGKILL) break;
+ nextsig = SIGKILL;
+ ms = nantomil(&kts);
+
+ continue;
+ }
+ if (TT.pfd.revents&POLLIN) {
+ errno = 0;
+ if (1>(ii = read(TT.fds[1], toybuf, sizeof(toybuf)))) {
+ if (errno==EINTR) continue;
+ break;
+ }
+ writeall(1, toybuf, ii);
+ }
+ if (TT.pfd.revents&POLLHUP) break;
}
+done:
+ xsignal(SIGCHLD, SIG_DFL);
+ ii = xpclose_both(TT.pid, TT.fds);
+
+ if (FLAG(preserve_status) || !toys.exitval) toys.exitval = ii;
}