1 /*-
2 * Copyright (c) 2001, 2007 - 2010 Peter Pentchev
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
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.
13 *
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
24 * SUCH DAMAGE.
25 */
26
27
28 #include "../config.h"
29
30 /* we hope all OS's have those..*/
31 #include <sys/types.h>
32 #include <sys/signal.h>
33 #include <sys/time.h>
34 #include <sys/wait.h>
35
36 #include <signal.h>
37 #include <stdarg.h>
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <string.h>
41 #include <unistd.h>
42
43 #include <errno.h>
44
45 #ifdef HAVE_ERR
46 #include <err.h>
47 #endif /* HAVE_ERR */
48
49 #ifdef HAVE_SYSEXITS_H
50 #include <sysexits.h>
51 #else
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 */
71
72 #ifndef __unused
73 # ifdef __GNUC__
74 # if GCC_VERSION >= 3004
75 # define __unused __attribute__((unused))
76 # else
77 # define __unused
78 # endif
79 # else /* __GNUC__ */
80 # define __unused
81 # endif /* __GNUC__ */
82 #endif /* __unused */
83
84 #ifndef __dead2
85 #ifdef __GNUC__
86 #define __dead2 __attribute__((noreturn))
87 #else /* __GNUC__ */
88 #define __dead2
89 #endif /* __GNUC__ */
90 #endif /* __dead2 */
91
92 #define PARSE_CMDLINE
93
94 unsigned long warntime, warnmsec, killtime, killmsec;
95 unsigned long warnsig, killsig;
96 volatile int fdone, falarm, fsig, sigcaught;
97 int propagate, quiet;
98
99 static struct {
100 const char *name, opt, issig;
101 unsigned long *sec, *msec;
102 } envopts[] = {
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}
108 };
109
110 static struct {
111 const char *name;
112 int num;
113 } signals[] = {
114 /* We kind of assume that the POSIX-mandated signals are present */
115 {"ABRT", SIGABRT},
116 {"ALRM", SIGALRM},
117 {"BUS", SIGBUS},
118 {"CHLD", SIGCHLD},
119 {"CONT", SIGCONT},
120 {"FPE", SIGFPE},
121 {"HUP", SIGHUP},
122 {"ILL", SIGILL},
123 {"INT", SIGINT},
124 {"KILL", SIGKILL},
125 {"PIPE", SIGPIPE},
126 {"QUIT", SIGQUIT},
127 {"SEGV", SIGSEGV},
128 {"STOP", SIGSTOP},
129 {"TERM", SIGTERM},
130 {"TSTP", SIGTSTP},
131 {"TTIN", SIGTTIN},
132 {"TTOU", SIGTTOU},
133 {"USR1", SIGUSR1},
134 {"USR2", SIGUSR2},
135 {"PROF", SIGPROF},
136 {"SYS", SIGSYS},
137 {"TRAP", SIGTRAP},
138 {"URG", SIGURG},
139 {"VTALRM", SIGVTALRM},
140 {"XCPU", SIGXCPU},
141 {"XFSZ", SIGXFSZ},
142
143 /* Some more signals found on a Linux 2.6 system */
144 #ifdef SIGIO
145 {"IO", SIGIO},
146 #endif
147 #ifdef SIGIOT
148 {"IOT", SIGIOT},
149 #endif
150 #ifdef SIGLOST
151 {"LOST", SIGLOST},
152 #endif
153 #ifdef SIGPOLL
154 {"POLL", SIGPOLL},
155 #endif
156 #ifdef SIGPWR
157 {"PWR", SIGPWR},
158 #endif
159 #ifdef SIGSTKFLT
160 {"STKFLT", SIGSTKFLT},
161 #endif
162 #ifdef SIGWINCH
163 {"WINCH", SIGWINCH},
164 #endif
165
166 /* Some more signals found on a FreeBSD 8.x system */
167 #ifdef SIGEMT
168 {"EMT", SIGEMT},
169 #endif
170 #ifdef SIGINFO
171 {"INFO", SIGINFO},
172 #endif
173 #ifdef SIGLWP
174 {"LWP", SIGLWP},
175 #endif
176 #ifdef SIGTHR
177 {"THR", SIGTHR},
178 #endif
179 };
180 #define SIGNALS (sizeof(signals) / sizeof(signals[0]))
181
182 #ifndef HAVE_ERR
183 static void err(int, const char *, ...);
184 static void errx(int, const char *, ...);
185 #endif /* !HAVE_ERR */
186
187 static void usage(void);
188
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 *);
196
197 #ifndef HAVE_ERR
198 static void
err(int code,const char * fmt,...)199 err(int code, const char *fmt, ...) {
200 va_list v;
201
202 va_start(v, fmt);
203 vfprintf(stderr, fmt, v);
204 va_end(v);
205
206 fprintf(stderr, ": %s\n", strerror(errno));
207 exit(code);
208 }
209
210 static void
errx(int code,const char * fmt,...)211 errx(int code, const char *fmt, ...) {
212 va_list v;
213
214 va_start(v, fmt);
215 vfprintf(stderr, fmt, v);
216 va_end(v);
217
218 fprintf(stderr, "\n");
219 exit(code);
220 }
221
222 static void
warnx(const char * fmt,...)223 warnx(const char *fmt, ...) {
224 va_list v;
225
226 va_start(v, fmt);
227 vfprintf(stderr, fmt, v);
228 va_end(v);
229
230 fprintf(stderr, "\n");
231 }
232 #endif /* !HAVE_ERR */
233
234 static void
usage(void)235 usage(void) {
236 errx(EX_USAGE, "usage: timelimit [-pq] [-S ksig] [-s wsig] "
237 "[-T ktime] [-t wtime] command");
238 }
239
240 static void
atou_fatal(const char * s,unsigned long * sec,unsigned long * msec,int issig)241 atou_fatal(const char *s, unsigned long *sec, unsigned long *msec, int issig) {
242 unsigned long v, vm, mul;
243 const char *p;
244 size_t i;
245
246 if (s[0] < '0' || s[0] > '9') {
247 if (s[0] == '\0' || !issig)
248 usage();
249 for (i = 0; i < SIGNALS; i++)
250 if (!strcmp(signals[i].name, s))
251 break;
252 if (i == SIGNALS)
253 usage();
254 *sec = (unsigned long)signals[i].num;
255 if (msec != NULL)
256 *msec = 0;
257 return;
258 }
259
260 v = 0;
261 for (p = s; (*p >= '0') && (*p <= '9'); p++)
262 v = v * 10 + *p - '0';
263 if (*p == '\0') {
264 *sec = v;
265 if (msec != NULL)
266 *msec = 0;
267 return;
268 } else if (*p != '.' || msec == NULL) {
269 usage();
270 }
271 p++;
272
273 vm = 0;
274 mul = 1000000;
275 for (; (*p >= '0') && (*p <= '9'); p++) {
276 vm = vm * 10 + *p - '0';
277 mul = mul / 10;
278 }
279 if (*p != '\0')
280 usage();
281 else if (mul < 1)
282 errx(EX_USAGE, "no more than microsecond precision");
283 #ifndef HAVE_SETITIMER
284 if (msec != 0)
285 errx(EX_UNAVAILABLE,
286 "subsecond precision not supported on this platform");
287 #endif
288 *sec = v;
289 *msec = vm * mul;
290 }
291
292 static void
init(int argc,char * argv[])293 init(int argc, char *argv[]) {
294 #ifdef PARSE_CMDLINE
295 int ch, listsigs;
296 #endif
297 int optset;
298 unsigned i;
299 char *s;
300
301 /* defaults */
302 quiet = 0;
303 warnsig = SIGTERM;
304 killsig = SIGKILL;
305 warntime = 900;
306 warnmsec = 0;
307 killtime = 5;
308 killmsec = 0;
309
310 optset = 0;
311
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,
316 envopts[i].issig);
317 optset = 1;
318 }
319
320 #ifdef PARSE_CMDLINE
321 listsigs = 0;
322 while ((ch = getopt(argc, argv, "+lqpS:s:T:t:")) != -1) {
323 switch (ch) {
324 case 'l':
325 listsigs = 1;
326 break;
327 case 'p':
328 propagate = 1;
329 break;
330 case 'q':
331 quiet = 1;
332 break;
333 default:
334 /* check if it's a recognized option */
335 for (i = 0; envopts[i].name != NULL; i++)
336 if (ch == envopts[i].opt) {
337 atou_fatal(optarg,
338 envopts[i].sec,
339 envopts[i].msec,
340 envopts[i].issig);
341 optset = 1;
342 break;
343 }
344 if (envopts[i].name == NULL)
345 usage();
346 }
347 }
348
349 if (listsigs) {
350 for (i = 0; i < SIGNALS; i++)
351 printf("%s%c", signals[i].name,
352 i + 1 < SIGNALS? ' ': '\n');
353 exit(EX_OK);
354 }
355 #else
356 optind = 1;
357 #endif
358
359 if (!optset) /* && !quiet? */
360 warnx("using defaults: warntime=%lu, warnsig=%lu, "
361 "killtime=%lu, killsig=%lu",
362 warntime, warnsig, killtime, killsig);
363
364 argc -= optind;
365 argv += optind;
366 if (argc == 0)
367 usage();
368
369 /* sanity checks */
370 if ((warntime == 0 && warnmsec == 0) || (killtime == 0 && killmsec == 0))
371 usage();
372 }
373
374 static void
sigchld(int sig __unused)375 sigchld(int sig __unused) {
376
377 fdone = 1;
378 }
379
380 static void
sigalrm(int sig __unused)381 sigalrm(int sig __unused) {
382
383 falarm = 1;
384 }
385
386 static void
sighandler(int sig)387 sighandler(int sig) {
388
389 sigcaught = sig;
390 fsig = 1;
391 }
392
393 static void
setsig_fatal(int sig,void (* handler)(int))394 setsig_fatal(int sig, void (*handler)(int)) {
395
396 setsig_fatal_gen(sig, handler, 1, "setting");
397 }
398
399 static void
setsig_fatal_gen(int sig,void (* handler)(int),int nocld,const char * what)400 setsig_fatal_gen(int sig, void (*handler)(int), int nocld, const char *what) {
401 #ifdef HAVE_SIGACTION
402 struct sigaction act;
403
404 memset(&act, 0, sizeof(act));
405 act.sa_handler = handler;
406 act.sa_flags = 0;
407 #ifdef SA_NOCLDSTOP
408 if (nocld)
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 */
417 }
418
419 static void
settimer(const char * name,unsigned long sec,unsigned long msec)420 settimer(const char *name, unsigned long sec, unsigned long msec)
421 {
422 #ifdef HAVE_SETITIMER
423 struct itimerval tval;
424
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);
430 #else
431 alarm(sec);
432 #endif
433 }
434
435 static pid_t
doit(char * argv[])436 doit(char *argv[]) {
437 pid_t pid;
438
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);
447
448 /* fork off the child process */
449 if ((pid = fork()) < 0)
450 err(EX_OSERR, "fork");
451 if (pid == 0)
452 child(argv);
453
454 /* sleep for the allowed time */
455 settimer("warning", warntime, warnmsec);
456 while (!(fdone || falarm || fsig))
457 pause();
458 alarm(0);
459
460 /* send the warning signal */
461 if (fdone)
462 return (pid);
463 if (fsig)
464 terminated("run");
465 falarm = 0;
466 if (!quiet)
467 warnx("sending warning signal %lu", warnsig);
468 kill(pid, (int) warnsig);
469
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 */
479
480 /* sleep for the grace time */
481 settimer("kill", killtime, killmsec);
482 while (!(fdone || falarm || fsig))
483 pause();
484 alarm(0);
485
486 /* send the kill signal */
487 if (fdone)
488 return (pid);
489 if (fsig)
490 terminated("grace");
491 if (!quiet)
492 warnx("sending kill signal %lu", killsig);
493 kill(pid, (int) killsig);
494 setsig_fatal_gen(SIGCHLD, SIG_DFL, 0, "restoring");
495 return (pid);
496 }
497
498 static void
terminated(const char * period)499 terminated(const char *period) {
500
501 errx(EX_SOFTWARE, "terminated by signal %d during the %s period",
502 sigcaught, period);
503 }
504
505 static void
child(char * argv[])506 child(char *argv[]) {
507
508 execvp(argv[0], argv);
509 err(EX_OSERR, "executing %s", argv[0]);
510 }
511
512 static __dead2 void
raisesignal(int sig)513 raisesignal (int sig) {
514
515 setsig_fatal_gen(sig, SIG_DFL, 0, "restoring");
516 raise(sig);
517 while (1)
518 pause();
519 /* NOTREACHED */
520 }
521
522 int
main(int argc,char * argv[])523 main(int argc, char *argv[]) {
524 pid_t pid;
525 int status;
526
527 init(argc, argv);
528 argc -= optind;
529 argv += optind;
530 pid = doit(argv);
531
532 if (waitpid(pid, &status, 0) == -1)
533 err(EX_OSERR, "could not get the exit status for process %ld",
534 (long)pid);
535 if (WIFEXITED(status))
536 return (WEXITSTATUS(status));
537 else if (!WIFSIGNALED(status))
538 return (EX_OSERR);
539 if (propagate)
540 raisesignal(WTERMSIG(status));
541 else
542 return (WTERMSIG(status) + 128);
543 }
544