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