Merge.
[ccan] / junkcode / fork0@users.sf.net-timeout / timeout.c
1 /* execute a program with a timeout by alarm(2) */
2 #define _GNU_SOURCE
3 #include <unistd.h>
4 #include <sys/types.h>
5 #include <sys/wait.h>
6 #include <signal.h>
7 #include <stdio.h>
8 #include <string.h>
9 #include <stdlib.h>
10 #include <errno.h>
11
12 static const char *argv0;
13 static char **prgargv;
14 static pid_t pid;
15 static int signo;
16 static unsigned int timeout;
17
18 static void timedout(int sig)
19 {
20         fprintf(stderr, "%s[%d]: %s[%d] timed out after %u sec\n",
21                 argv0, getpid(), *prgargv, pid, timeout);
22         if (pid)
23                 kill(-pid, signo);
24 }
25
26 static void interrupted(int sig)
27 {
28         alarm(0);
29         if (pid)
30                 kill(-pid, sig);
31 }
32
33 static void usage()
34 {
35         fprintf(stderr, "%s <timeout-seconds> [-<signal>] program ...\n"
36                 "Where <signal> is a signal number (see kill -l).\n"
37                 "Some symbolic names recognized. KILL used by default\n",
38                 argv0);
39         exit(1);
40 }
41
42 static struct {
43         const char *name;
44         int signo;
45 } known[] = {
46         {"HUP",     SIGHUP},
47         {"INT",     SIGINT},
48         {"QUIT",    SIGQUIT},
49         {"ILL",     SIGILL},
50         {"TRAP",    SIGTRAP},
51         {"ABRT",    SIGABRT},
52         {"BUS",     SIGBUS},
53         {"FPE",     SIGFPE},
54         {"KILL",    SIGKILL},
55         {"USR1",    SIGUSR1},
56         {"SEGV",    SIGSEGV},
57         {"USR2",    SIGUSR2},
58         {"PIPE",    SIGPIPE},
59         {"ALRM",    SIGALRM},
60         {"TERM",    SIGTERM},
61         {"STKFLT",  SIGSTKFLT},
62         {"CHLD",    SIGCHLD},
63         {"CONT",    SIGCONT},
64         {"STOP",    SIGSTOP},
65         {"TSTP",    SIGTSTP},
66         {"TTIN",    SIGTTIN},
67         {"TTOU",    SIGTTOU},
68         {"URG",     SIGURG},
69         {"XCPU",    SIGXCPU},
70         {"XFSZ",    SIGXFSZ},
71         {"VTALRM",  SIGVTALRM},
72         {"PROF",    SIGPROF},
73         {"WINCH",   SIGWINCH},
74         {"IO",      SIGIO},
75         {"PWR",     SIGPWR},
76         {"SYS",     SIGSYS},
77 };
78
79 static int signo_arg(const char *arg)
80 {
81         if (*arg == '-') {
82                 char *p;
83                 int s = strtol(++arg, &p, 10);
84                 if (!*p && p > arg && s > 0 && s < _NSIG) {
85                         signo = s;
86                         return 1;
87                 }
88                 if (!strncasecmp(arg, "SIG", 3))
89                         arg += 3;
90                 for (s = 0; s < sizeof(known)/sizeof(*known); ++s)
91                         if (!strcasecmp(arg, known[s].name)) {
92                                 signo = known[s].signo;
93                                 return 1;
94                         }
95         }
96         return 0;
97 }
98
99 int main(int argc, char** argv)
100 {
101         argv0 = strrchr(*argv, '/');
102         if (argv0)
103                 ++argv0;
104         else
105                 argv0 = *argv;
106
107         signal(SIGALRM, timedout);
108         signal(SIGINT, interrupted);
109         signal(SIGHUP, interrupted);
110
111         ++argv;
112
113         if (!*argv)
114                 usage();
115
116         if (signo_arg(*argv))
117                 ++argv;
118         if (sscanf(*argv, "%u", &timeout) == 1)
119                 ++argv;
120         else
121                 usage();
122         if (!signo && signo_arg(*argv))
123                 ++argv;
124         if (!signo)
125                 signo = SIGKILL;
126
127         if (!*argv)
128                 usage();
129
130         prgargv = argv;
131         alarm(timeout);
132         pid = fork();
133
134         if (!pid) {
135                 signal(SIGALRM, SIG_DFL);
136                 signal(SIGCHLD, SIG_DFL);
137                 signal(SIGINT, SIG_DFL);
138                 signal(SIGHUP, SIG_DFL);
139                 setpgid(0, 0);
140                 execvp(*prgargv, prgargv);
141                 fprintf(stderr, "%s: %s: %s\n",
142                         argv0, *prgargv, strerror(errno));
143                 _exit(2);
144         } else if (pid < 0) {
145                 fprintf(stderr, "%s: %s\n", argv0, strerror(errno));
146         } else {
147                 int status;
148                 while (waitpid(pid, &status, 0) < 0 && EINTR == errno)
149                         ;
150                 alarm(0);
151                 if (WIFEXITED(status))
152                         return WEXITSTATUS(status);
153                 if (WIFSIGNALED(status)) {
154                         /*
155                          * Some signals are special, lets die with
156                          * the same signal as child process
157                          */
158                         if (WTERMSIG(status) == SIGHUP  ||
159                             WTERMSIG(status) == SIGINT  ||
160                             WTERMSIG(status) == SIGTERM ||
161                             WTERMSIG(status) == SIGQUIT ||
162                             WTERMSIG(status) == SIGKILL) {
163                                 signal(WTERMSIG(status), SIG_DFL);
164                                 raise(WTERMSIG(status));
165                         }
166                         fprintf(stderr, "%s: %s: %s\n",
167                                 argv0, *prgargv, strsignal(WTERMSIG(status)));
168                 }
169                 else
170                         fprintf(stderr, "%s died\n", *prgargv);
171         }
172         return 2;
173 }