1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <unistd.h>
4 #include <string.h>
5 #include <signal.h>
6 #include <sys/wait.h>
7 #include <sysexits.h>
8 
9 #include <assert.h>
10 
11 volatile sig_atomic_t caught_sig = 0;
12 
signal_handler(int which)13 static void signal_handler(int which)
14 {
15     caught_sig = which;
16 }
17 
wait_for_process(pid_t pid)18 static int wait_for_process(pid_t pid)
19 {
20     int rv = EX_SOFTWARE;
21     int status = 0;
22     int i = 0;
23     struct sigaction sig_handler;
24 
25     memset(&sig_handler, 0, sizeof(struct sigaction));
26     sig_handler.sa_handler = signal_handler;
27     sig_handler.sa_flags = 0;
28 
29     sigaction(SIGALRM, &sig_handler, NULL);
30     sigaction(SIGHUP, &sig_handler, NULL);
31     sigaction(SIGINT, &sig_handler, NULL);
32     sigaction(SIGUSR1, &sig_handler, NULL);
33     sigaction(SIGTERM, &sig_handler, NULL);
34     sigaction(SIGPIPE, &sig_handler, NULL);
35 
36     /* Loop forever waiting for the process to quit */
37     for (i = 0; ;i++) {
38         pid_t p = waitpid(pid, &status, 0);
39         if (p == pid) {
40             /* child exited.  Let's get out of here */
41             rv = WIFEXITED(status) ?
42                 WEXITSTATUS(status) :
43                 (0x80 | WTERMSIG(status));
44             break;
45         } else {
46             int sig = 0;
47             /* pass along SIGHUP gracefully */
48             if (caught_sig == SIGHUP) {
49                 i = 0;
50                 int sig = caught_sig;
51                 if (kill(pid, sig) < 0) {
52                     /* Kill failed.  Must have lost the process. :/ */
53                     perror("lost child when trying to kill");
54                 }
55                 continue;
56             }
57             switch (i) {
58             case 0:
59                 /* On the first iteration, pass the signal through */
60                 sig = caught_sig > 0 ? caught_sig : SIGTERM;
61                 if (caught_sig == SIGALRM) {
62                    fprintf(stderr, "Timeout.. killing the process\n");
63                 }
64                 break;
65             case 1:
66                 sig = SIGTERM;
67                 break;
68             default:
69                 sig = SIGKILL;
70                 break;
71             }
72             if (kill(pid, sig) < 0) {
73                 /* Kill failed.  Must have lost the process. :/ */
74                 perror("lost child when trying to kill");
75             }
76             /* Wait up to 5 seconds for the pid */
77             alarm(5);
78         }
79     }
80     return rv;
81 }
82 
spawn_and_wait(char ** argv)83 static int spawn_and_wait(char **argv)
84 {
85     int rv = EX_SOFTWARE;
86     pid_t pid = fork();
87 
88     switch (pid) {
89     case -1:
90         perror("fork");
91         rv = EX_OSERR;
92         break; /* NOTREACHED */
93     case 0:
94         execvp(argv[0], argv);
95         perror("exec");
96         rv = EX_SOFTWARE;
97         break; /* NOTREACHED */
98     default:
99         rv = wait_for_process(pid);
100     }
101     return rv;
102 }
103 
usage(void)104 static void usage(void) {
105     fprintf(stderr, "./timedrun <naptime in sec> args...\n");
106     exit(-1);
107 }
108 
main(int argc,char ** argv)109 int main(int argc, char **argv)
110 {
111     int naptime = 0;
112     if (argc < 3)
113         usage();
114 
115     naptime = atoi(argv[1]);
116     assert(naptime > 0 && naptime < 1800);
117 
118     alarm(naptime);
119 
120     return spawn_and_wait(argv+2);
121 }
122