2 * Copyright (c) 2001, 2007 - 2010 Peter Pentchev
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 /* we hope all OS's have those..*/
29 #include <sys/types.h>
30 #include <sys/signal.h>
47 #ifdef HAVE_SYSEXITS_H
50 #define EX_OK 0 /* successful termination */
51 #define EX__BASE 64 /* base value for error messages */
52 #define EX_USAGE 64 /* command line usage error */
53 #define EX_DATAERR 65 /* data format error */
54 #define EX_NOINPUT 66 /* cannot open input */
55 #define EX_NOUSER 67 /* addressee unknown */
56 #define EX_NOHOST 68 /* host name unknown */
57 #define EX_UNAVAILABLE 69 /* service unavailable */
58 #define EX_SOFTWARE 70 /* internal software error */
59 #define EX_OSERR 71 /* system error (e.g., can't fork) */
60 #define EX_OSFILE 72 /* critical OS file missing */
61 #define EX_CANTCREAT 73 /* can't create (user) output file */
62 #define EX_IOERR 74 /* input/output error */
63 #define EX_TEMPFAIL 75 /* temp failure; user is invited to retry */
64 #define EX_PROTOCOL 76 /* remote error in protocol */
65 #define EX_NOPERM 77 /* permission denied */
66 #define EX_CONFIG 78 /* configuration error */
67 #define EX__MAX 78 /* maximum listed value */
68 #endif /* HAVE_SYSEXITS_H */
72 #define __unused __attribute__((unused))
80 #define __dead2 __attribute__((noreturn))
88 unsigned long warntime, warnmsec, killtime, killmsec;
89 unsigned long warnsig, killsig;
90 volatile int fdone, falarm, fsig, sigcaught;
94 const char *name, opt, issig;
95 unsigned long *sec, *msec;
97 {"KILLSIG", 'S', 1, &killsig, NULL},
98 {"KILLTIME", 'T', 0, &killtime, &killmsec},
99 {"WARNSIG", 's', 1, &warnsig, NULL},
100 {"WARNTIME", 't', 0, &warntime, &warnmsec},
101 {NULL, 0, 0, NULL, NULL}
108 /* We kind of assume that the POSIX-mandated signals are present */
133 {"VTALRM", SIGVTALRM},
137 /* Some more signals found on a Linux 2.6 system */
154 {"STKFLT", SIGSTKFLT},
160 /* Some more signals found on a FreeBSD 8.x system */
174 #define SIGNALS (sizeof(signals) / sizeof(signals[0]))
177 static void err(int, const char *, ...);
178 static void errx(int, const char *, ...);
179 #endif /* !HAVE_ERR */
181 static void usage(void);
183 static void init(int, char *[]);
184 static pid_t doit(char *[]);
185 static void child(char *[]);
186 static void raisesignal(int) __dead2;
187 static void setsig_fatal(int, void (*)(int));
188 static void setsig_fatal_gen(int, void (*)(int), int, const char *);
189 static void terminated(const char *);
193 err(int code, const char *fmt, ...) {
197 vfprintf(stderr, fmt, v);
200 fprintf(stderr, ": %s\n", strerror(errno));
205 errx(int code, const char *fmt, ...) {
209 vfprintf(stderr, fmt, v);
212 fprintf(stderr, "\n");
217 warnx(const char *fmt, ...) {
221 vfprintf(stderr, fmt, v);
224 fprintf(stderr, "\n");
226 #endif /* !HAVE_ERR */
230 errx(EX_USAGE, "usage: timelimit [-pq] [-S ksig] [-s wsig] "
231 "[-T ktime] [-t wtime] command");
235 atou_fatal(const char *s, unsigned long *sec, unsigned long *msec, int issig) {
236 unsigned long v, vm, mul;
240 if (s[0] < '0' || s[0] > '9') {
241 if (s[0] == '\0' || !issig)
243 for (i = 0; i < SIGNALS; i++)
244 if (!strcmp(signals[i].name, s))
248 *sec = (unsigned long)signals[i].num;
255 for (p = s; (*p >= '0') && (*p <= '9'); p++)
256 v = v * 10 + *p - '0';
262 } else if (*p != '.' || msec == NULL) {
269 for (; (*p >= '0') && (*p <= '9'); p++) {
270 vm = vm * 10 + *p - '0';
276 errx(EX_USAGE, "no more than microsecond precision");
277 #ifndef HAVE_SETITIMER
280 "subsecond precision not supported on this platform");
287 init(int argc, char *argv[]) {
306 /* process environment variables first */
307 for (i = 0; envopts[i].name != NULL; i++)
308 if ((s = getenv(envopts[i].name)) != NULL) {
309 atou_fatal(s, envopts[i].sec, envopts[i].msec,
316 while ((ch = getopt(argc, argv, "+lqpS:s:T:t:")) != -1) {
328 /* check if it's a recognized option */
329 for (i = 0; envopts[i].name != NULL; i++)
330 if (ch == envopts[i].opt) {
338 if (envopts[i].name == NULL)
344 for (i = 0; i < SIGNALS; i++)
345 printf("%s%c", signals[i].name,
346 i + 1 < SIGNALS? ' ': '\n');
353 if (!optset) /* && !quiet? */
354 warnx("using defaults: warntime=%lu, warnsig=%lu, "
355 "killtime=%lu, killsig=%lu",
356 warntime, warnsig, killtime, killsig);
364 if ((warntime == 0 && warnmsec == 0) || (killtime == 0 && killmsec == 0))
369 sigchld(int sig __unused) {
375 sigalrm(int sig __unused) {
381 sighandler(int sig) {
388 setsig_fatal(int sig, void (*handler)(int)) {
390 setsig_fatal_gen(sig, handler, 1, "setting");
394 setsig_fatal_gen(int sig, void (*handler)(int), int nocld, const char *what) {
395 #ifdef HAVE_SIGACTION
396 struct sigaction act;
398 memset(&act, 0, sizeof(act));
399 act.sa_handler = handler;
403 act.sa_flags |= SA_NOCLDSTOP;
404 #endif /* SA_NOCLDSTOP */
405 if (sigaction(sig, &act, NULL) < 0)
406 err(EX_OSERR, "%s signal handler for %d", what, sig);
407 #else /* HAVE_SIGACTION */
408 if (signal(sig, handler) == SIG_ERR)
409 err(EX_OSERR, "%s signal handler for %d", what, sig);
410 #endif /* HAVE_SIGACTION */
414 settimer(const char *name, unsigned long sec, unsigned long msec)
416 #ifdef HAVE_SETITIMER
417 struct itimerval tval;
419 tval.it_interval.tv_sec = tval.it_interval.tv_usec = 0;
420 tval.it_value.tv_sec = sec;
421 tval.it_value.tv_usec = msec;
422 if (setitimer(ITIMER_REAL, &tval, NULL) == -1)
423 err(EX_OSERR, "could not set the %s timer", name);
433 /* install signal handlers */
434 fdone = falarm = fsig = sigcaught = 0;
435 setsig_fatal(SIGALRM, sigalrm);
436 setsig_fatal(SIGCHLD, sigchld);
437 setsig_fatal(SIGTERM, sighandler);
438 setsig_fatal(SIGHUP, sighandler);
439 setsig_fatal(SIGINT, sighandler);
440 setsig_fatal(SIGQUIT, sighandler);
442 /* fork off the child process */
443 if ((pid = fork()) < 0)
444 err(EX_OSERR, "fork");
448 /* sleep for the allowed time */
449 settimer("warning", warntime, warnmsec);
450 while (!(fdone || falarm || fsig))
454 /* send the warning signal */
461 warnx("sending warning signal %lu", warnsig);
462 kill(pid, (int) warnsig);
464 #ifndef HAVE_SIGACTION
465 /* reset our signal handlers, just in case */
466 setsig_fatal(SIGALRM, sigalrm);
467 setsig_fatal(SIGCHLD, sigchld);
468 setsig_fatal(SIGTERM, sighandler);
469 setsig_fatal(SIGHUP, sighandler);
470 setsig_fatal(SIGINT, sighandler);
471 setsig_fatal(SIGQUIT, sighandler);
472 #endif /* HAVE_SIGACTION */
474 /* sleep for the grace time */
475 settimer("kill", killtime, killmsec);
476 while (!(fdone || falarm || fsig))
480 /* send the kill signal */
486 warnx("sending kill signal %lu", killsig);
487 kill(pid, (int) killsig);
488 setsig_fatal_gen(SIGCHLD, SIG_DFL, 0, "restoring");
493 terminated(const char *period) {
495 errx(EX_SOFTWARE, "terminated by signal %d during the %s period",
500 child(char *argv[]) {
502 execvp(argv[0], argv);
503 err(EX_OSERR, "executing %s", argv[0]);
507 raisesignal (int sig) {
509 setsig_fatal_gen(sig, SIG_DFL, 0, "restoring");
517 main(int argc, char *argv[]) {
526 if (waitpid(pid, &status, 0) == -1)
527 err(EX_OSERR, "could not get the exit status for process %ld",
529 if (WIFEXITED(status))
530 return (WEXITSTATUS(status));
531 else if (!WIFSIGNALED(status))
534 raisesignal(WTERMSIG(status));
536 return (WTERMSIG(status) + 128);