]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/tools/timelimit.c
Backport from Bacula Enterprise
[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 #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
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
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
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
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
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
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
375 sigchld(int sig __unused) {
376
377         fdone = 1;
378 }
379
380 static void
381 sigalrm(int sig __unused) {
382
383         falarm = 1;
384 }
385
386 static void
387 sighandler(int sig) {
388
389         sigcaught = sig;
390         fsig = 1;
391 }
392
393 static void
394 setsig_fatal(int sig, void (*handler)(int)) {
395         
396         setsig_fatal_gen(sig, handler, 1, "setting");
397 }
398
399 static void
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
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
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
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
506 child(char *argv[]) {
507
508         execvp(argv[0], argv);
509         err(EX_OSERR, "executing %s", argv[0]);
510 }
511
512 static __dead2 void
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
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 }