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 #include "../config.h"
30 /* we hope all OS's have those..*/
31 #include <sys/types.h>
32 #include <sys/signal.h>
49 #ifdef HAVE_SYSEXITS_H
52 #define EX_OK 0 /* successful termination */
53 #define EX__BASE 64 /* base value for error messages */
54 #define EX_USAGE 64 /* command line usage error */
55 #define EX_DATAERR 65 /* data format error */
56 #define EX_NOINPUT 66 /* cannot open input */
57 #define EX_NOUSER 67 /* addressee unknown */
58 #define EX_NOHOST 68 /* host name unknown */
59 #define EX_UNAVAILABLE 69 /* service unavailable */
60 #define EX_SOFTWARE 70 /* internal software error */
61 #define EX_OSERR 71 /* system error (e.g., can't fork) */
62 #define EX_OSFILE 72 /* critical OS file missing */
63 #define EX_CANTCREAT 73 /* can't create (user) output file */
64 #define EX_IOERR 74 /* input/output error */
65 #define EX_TEMPFAIL 75 /* temp failure; user is invited to retry */
66 #define EX_PROTOCOL 76 /* remote error in protocol */
67 #define EX_NOPERM 77 /* permission denied */
68 #define EX_CONFIG 78 /* configuration error */
69 #define EX__MAX 78 /* maximum listed value */
70 #endif /* HAVE_SYSEXITS_H */
74 # if GCC_VERSION >= 3004
75 # define __unused __attribute__((unused))
81 # endif /* __GNUC__ */
86 #define __dead2 __attribute__((noreturn))
94 unsigned long warntime, warnmsec, killtime, killmsec;
95 unsigned long warnsig, killsig;
96 volatile int fdone, falarm, fsig, sigcaught;
100 const char *name, opt, issig;
101 unsigned long *sec, *msec;
103 {"KILLSIG", 'S', 1, &killsig, NULL},
104 {"KILLTIME", 'T', 0, &killtime, &killmsec},
105 {"WARNSIG", 's', 1, &warnsig, NULL},
106 {"WARNTIME", 't', 0, &warntime, &warnmsec},
107 {NULL, 0, 0, NULL, NULL}
114 /* We kind of assume that the POSIX-mandated signals are present */
139 {"VTALRM", SIGVTALRM},
143 /* Some more signals found on a Linux 2.6 system */
160 {"STKFLT", SIGSTKFLT},
166 /* Some more signals found on a FreeBSD 8.x system */
180 #define SIGNALS (sizeof(signals) / sizeof(signals[0]))
183 static void err(int, const char *, ...);
184 static void errx(int, const char *, ...);
185 #endif /* !HAVE_ERR */
187 static void usage(void);
189 static void init(int, char *[]);
190 static pid_t doit(char *[]);
191 static void child(char *[]);
192 static void raisesignal(int) __dead2;
193 static void setsig_fatal(int, void (*)(int));
194 static void setsig_fatal_gen(int, void (*)(int), int, const char *);
195 static void terminated(const char *);
199 err(int code, const char *fmt, ...) {
203 vfprintf(stderr, fmt, v);
206 fprintf(stderr, ": %s\n", strerror(errno));
211 errx(int code, const char *fmt, ...) {
215 vfprintf(stderr, fmt, v);
218 fprintf(stderr, "\n");
223 warnx(const char *fmt, ...) {
227 vfprintf(stderr, fmt, v);
230 fprintf(stderr, "\n");
232 #endif /* !HAVE_ERR */
236 errx(EX_USAGE, "usage: timelimit [-pq] [-S ksig] [-s wsig] "
237 "[-T ktime] [-t wtime] command");
241 atou_fatal(const char *s, unsigned long *sec, unsigned long *msec, int issig) {
242 unsigned long v, vm, mul;
246 if (s[0] < '0' || s[0] > '9') {
247 if (s[0] == '\0' || !issig)
249 for (i = 0; i < SIGNALS; i++)
250 if (!strcmp(signals[i].name, s))
254 *sec = (unsigned long)signals[i].num;
261 for (p = s; (*p >= '0') && (*p <= '9'); p++)
262 v = v * 10 + *p - '0';
268 } else if (*p != '.' || msec == NULL) {
275 for (; (*p >= '0') && (*p <= '9'); p++) {
276 vm = vm * 10 + *p - '0';
282 errx(EX_USAGE, "no more than microsecond precision");
283 #ifndef HAVE_SETITIMER
286 "subsecond precision not supported on this platform");
293 init(int argc, char *argv[]) {
312 /* process environment variables first */
313 for (i = 0; envopts[i].name != NULL; i++)
314 if ((s = getenv(envopts[i].name)) != NULL) {
315 atou_fatal(s, envopts[i].sec, envopts[i].msec,
322 while ((ch = getopt(argc, argv, "+lqpS:s:T:t:")) != -1) {
334 /* check if it's a recognized option */
335 for (i = 0; envopts[i].name != NULL; i++)
336 if (ch == envopts[i].opt) {
344 if (envopts[i].name == NULL)
350 for (i = 0; i < SIGNALS; i++)
351 printf("%s%c", signals[i].name,
352 i + 1 < SIGNALS? ' ': '\n');
359 if (!optset) /* && !quiet? */
360 warnx("using defaults: warntime=%lu, warnsig=%lu, "
361 "killtime=%lu, killsig=%lu",
362 warntime, warnsig, killtime, killsig);
370 if ((warntime == 0 && warnmsec == 0) || (killtime == 0 && killmsec == 0))
375 sigchld(int sig __unused) {
381 sigalrm(int sig __unused) {
387 sighandler(int sig) {
394 setsig_fatal(int sig, void (*handler)(int)) {
396 setsig_fatal_gen(sig, handler, 1, "setting");
400 setsig_fatal_gen(int sig, void (*handler)(int), int nocld, const char *what) {
401 #ifdef HAVE_SIGACTION
402 struct sigaction act;
404 memset(&act, 0, sizeof(act));
405 act.sa_handler = handler;
409 act.sa_flags |= SA_NOCLDSTOP;
410 #endif /* SA_NOCLDSTOP */
411 if (sigaction(sig, &act, NULL) < 0)
412 err(EX_OSERR, "%s signal handler for %d", what, sig);
413 #else /* HAVE_SIGACTION */
414 if (signal(sig, handler) == SIG_ERR)
415 err(EX_OSERR, "%s signal handler for %d", what, sig);
416 #endif /* HAVE_SIGACTION */
420 settimer(const char *name, unsigned long sec, unsigned long msec)
422 #ifdef HAVE_SETITIMER
423 struct itimerval tval;
425 tval.it_interval.tv_sec = tval.it_interval.tv_usec = 0;
426 tval.it_value.tv_sec = sec;
427 tval.it_value.tv_usec = msec;
428 if (setitimer(ITIMER_REAL, &tval, NULL) == -1)
429 err(EX_OSERR, "could not set the %s timer", name);
439 /* install signal handlers */
440 fdone = falarm = fsig = sigcaught = 0;
441 setsig_fatal(SIGALRM, sigalrm);
442 setsig_fatal(SIGCHLD, sigchld);
443 setsig_fatal(SIGTERM, sighandler);
444 setsig_fatal(SIGHUP, sighandler);
445 setsig_fatal(SIGINT, sighandler);
446 setsig_fatal(SIGQUIT, sighandler);
448 /* fork off the child process */
449 if ((pid = fork()) < 0)
450 err(EX_OSERR, "fork");
454 /* sleep for the allowed time */
455 settimer("warning", warntime, warnmsec);
456 while (!(fdone || falarm || fsig))
460 /* send the warning signal */
467 warnx("sending warning signal %lu", warnsig);
468 kill(pid, (int) warnsig);
470 #ifndef HAVE_SIGACTION
471 /* reset our signal handlers, just in case */
472 setsig_fatal(SIGALRM, sigalrm);
473 setsig_fatal(SIGCHLD, sigchld);
474 setsig_fatal(SIGTERM, sighandler);
475 setsig_fatal(SIGHUP, sighandler);
476 setsig_fatal(SIGINT, sighandler);
477 setsig_fatal(SIGQUIT, sighandler);
478 #endif /* HAVE_SIGACTION */
480 /* sleep for the grace time */
481 settimer("kill", killtime, killmsec);
482 while (!(fdone || falarm || fsig))
486 /* send the kill signal */
492 warnx("sending kill signal %lu", killsig);
493 kill(pid, (int) killsig);
494 setsig_fatal_gen(SIGCHLD, SIG_DFL, 0, "restoring");
499 terminated(const char *period) {
501 errx(EX_SOFTWARE, "terminated by signal %d during the %s period",
506 child(char *argv[]) {
508 execvp(argv[0], argv);
509 err(EX_OSERR, "executing %s", argv[0]);
513 raisesignal (int sig) {
515 setsig_fatal_gen(sig, SIG_DFL, 0, "restoring");
523 main(int argc, char *argv[]) {
532 if (waitpid(pid, &status, 0) == -1)
533 err(EX_OSERR, "could not get the exit status for process %ld",
535 if (WIFEXITED(status))
536 return (WEXITSTATUS(status));
537 else if (!WIFSIGNALED(status))
540 raisesignal(WTERMSIG(status));
542 return (WTERMSIG(status) + 128);