1 /*
2 * Copyright (c) 2004, Bull S.A.. All rights reserved.
3 * Created by: Sebastien Decugis
4
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of version 2 of the GNU General Public License as
7 * published by the Free Software Foundation.
8 *
9 * This program is distributed in the hope that it would be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12 *
13 * You should have received a copy of the GNU General Public License along
14 * with this program; if not, write the Free Software Foundation, Inc.,
15 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
16
17 * This utility software allows to run any executable file with a timeout limit.
18 * The syntax is:
19 * $ ./t0 n exe arglist
20 * where n is the timeout duration in seconds,
21 * exe is the executable filename to run,
22 * arglist is the arguments to be passed to executable.
23 *
24 * The use of this utility is intended to be "transparent", which means
25 * everything is as if
26 * $ exe arglist
27 * had been called, and a call to "alarm(n)" had been added inside exe's main.
28 *
29 * SPECIAL CASE:
30 * $ ./t0 0
31 * Here another arg is not required. This special case will return immediatly
32 * as if it has been timedout. This is useful to check a timeout return code value.
33 *
34 */
35
36 #include <sys/types.h>
37 #include <sys/wait.h>
38 #include <errno.h>
39 #include <pthread.h>
40 #include <signal.h>
41 #include <stdio.h>
42 #include <stdlib.h>
43 #include <string.h>
44 #include <unistd.h>
45
46 pid_t pid_to_monitor;
47
sighandler(int sig)48 void sighandler(int sig)
49 {
50 if (0 < pid_to_monitor) {
51 if (kill(pid_to_monitor, SIGKILL) == -1) {
52 perror("kill(.., SIGKILL) failed");
53 abort(); /* Something's really screwed up if we get here. */
54 }
55 waitpid(pid_to_monitor, NULL, WNOHANG);
56 }
57 exit(SIGALRM + 128);
58 }
59
main(int argc,char * argv[])60 int main(int argc, char *argv[])
61 {
62 int status, timeout;
63
64 /* Special case: t0 0 */
65 if (argc == 2 && (strncmp(argv[1], "0", 1) == 0)) {
66 kill(getpid(), SIGALRM);
67 exit(1);
68 }
69
70 /* General case */
71 if (argc < 3) {
72 printf("\nUsage: \n");
73 printf(" $ %s n exe arglist\n", argv[0]);
74 printf(" $ %s 0\n", argv[0]);
75 printf("\nWhere:\n");
76 printf(" n is the timeout duration in seconds,\n");
77 printf(" exe is the executable filename to run,\n");
78 printf
79 (" arglist is the arguments to be passed to executable.\n\n");
80 printf
81 (" The second use case will emulate an immediate timeout.\n\n");
82 exit(1);
83 }
84
85 timeout = atoi(argv[1]);
86 if (timeout < 1) {
87 fprintf(stderr,
88 "Invalid timeout value \"%s\". Timeout must be a positive integer.\n",
89 argv[1]);
90 exit(1);
91 }
92
93 if (signal(SIGALRM, sighandler) == SIG_ERR) {
94 perror("signal failed");
95 exit(1);
96 }
97
98 alarm(timeout);
99
100 switch (pid_to_monitor = fork()) {
101 case -1:
102 perror("fork failed");
103 exit(1);
104 case 0:
105 setpgid(0, 0);
106 execvp(argv[2], &argv[2]);
107 perror("execvp failed");
108 exit(1);
109 default:
110
111 for (;;) {
112 if (waitpid(pid_to_monitor, &status, 0) ==
113 pid_to_monitor)
114 break;
115 else if (errno == EINTR) {
116 perror("waitpid failed");
117 exit(1);
118 }
119 }
120 /* Relay the child's status back to run-tests.sh */
121 if (WIFEXITED(status))
122 exit(WEXITSTATUS(status));
123 else if (WIFSIGNALED(status))
124 exit(WTERMSIG(status) + 128);
125
126 }
127
128 exit(1);
129 }
130