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