1 /* Repeatedly run a program for a given length of time. */
2 
3 /*
4  * Copyright (C) 2003-2006 IBM
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License as
8  * published by the Free Software Foundation; either version 2 of the
9  * License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
19  * 02111-1307, USA.
20  */
21 
22 #include <stdio.h>
23 #include <string.h>
24 #include <strings.h>
25 #include <stdlib.h>
26 #include <unistd.h>
27 #include <sys/types.h>
28 #include <sys/wait.h>
29 
30 #include "debug.h"
31 
32 static int res = 0;
33 static char *progname;
34 static pid_t test_pgrp;
35 static FILE *out;
36 
int_func(int signum)37 static void int_func(int signum)
38 {
39 	pounder_fprintf(out,
40 			"%s: Killed by interrupt.  Last exit code = %d.\n",
41 			progname, res);
42 	kill(-test_pgrp, SIGTERM);
43 	exit(res);
44 }
45 
alarm_func(int signum)46 static void alarm_func(int signum)
47 {
48 	pounder_fprintf(out, "%s: Killed by timer.  Last exit code = %d.\n",
49 			progname, res);
50 	kill(-test_pgrp, SIGTERM);
51 	exit(res);
52 }
53 
54 /*
55 static void term_func(int signum) {
56 	exit(res);
57 }
58 */
59 
main(int argc,char * argv[])60 int main(int argc, char *argv[])
61 {
62 	int secs, stat;
63 	pid_t pid;
64 	unsigned int revs = 0;
65 	struct sigaction zig;
66 	int use_max_failures = 0;
67 	int max_failures = 0;
68 	int fail_counter = 1;
69 
70 	if (argc < 3) {
71 		printf
72 		    ("Usage: %s [-m max_failures] time_in_sec command [args]\n",
73 		     argv[0]);
74 		exit(1);
75 	}
76 	//by default, set max_failures to whatever the env variable $MAX_FAILURES is
77 	char *max_failures_env = getenv("MAX_FAILURES");
78 	max_failures = atoi(max_failures_env);
79 
80 	//if the -m option is used when calling timed_loop, override max_failures
81 	//specified by $MAX_FAILURES with the given argument instead
82 	if (argc > 4 && strcmp(argv[1], "-m") == 0) {
83 		if ((max_failures = atoi(argv[2])) >= 0) {
84 			use_max_failures = 1;
85 		} else {
86 			printf
87 			    ("Usage: %s [-m max_failures] time_in_sec command [args]\n",
88 			     argv[0]);
89 			printf
90 			    ("max_failures should be a nonnegative integer\n");
91 			exit(1);
92 		}
93 	}
94 
95 	out = stdout;
96 
97 	if (use_max_failures) {
98 		progname = rindex(argv[4], '/');
99 		if (progname == NULL) {
100 			progname = argv[4];
101 		} else {
102 			progname++;
103 		}
104 	} else {
105 		progname = rindex(argv[2], '/');
106 		if (progname == NULL) {
107 			progname = argv[2];
108 		} else {
109 			progname++;
110 		}
111 	}
112 
113 	/* Set up signals */
114 	memset(&zig, 0x00, sizeof(zig));
115 	zig.sa_handler = alarm_func;
116 	sigaction(SIGALRM, &zig, NULL);
117 	zig.sa_handler = int_func;
118 	sigaction(SIGINT, &zig, NULL);
119 	sigaction(SIGTERM, &zig, NULL);
120 
121 	/* set up process groups so that we can kill the
122 	 * loop test and descendants easily */
123 
124 	if (use_max_failures) {
125 		secs = atoi(argv[3]);
126 	} else {
127 		secs = atoi(argv[1]);
128 	}
129 	alarm(secs);
130 
131 	while (1) {
132 		pounder_fprintf(out, "%s: %s loop #%d.\n", progname,
133 				start_msg, revs++);
134 		pid = fork();
135 		if (pid == 0) {
136 			if (setpgrp() < 0) {
137 				perror("setpgid");
138 			}
139 			// run the program
140 			if (use_max_failures) {
141 				if (argc > 5) {
142 					stat = execvp(argv[4], &argv[4]);
143 				} else {
144 					stat = execvp(argv[4], &argv[4]);
145 				}
146 
147 				perror(argv[4]);
148 			} else {
149 				if (argc > 3) {
150 					stat = execvp(argv[2], &argv[2]);
151 				} else {
152 					stat = execvp(argv[2], &argv[2]);
153 				}
154 
155 				perror(argv[2]);
156 			}
157 
158 			exit(-1);
159 		}
160 
161 		/* save the pgrp of the spawned process */
162 		test_pgrp = pid;
163 
164 		// wait for it to be done
165 		if (waitpid(pid, &stat, 0) != pid) {
166 			perror("waitpid");
167 			exit(1);
168 		}
169 		// interrogate it
170 		if (WIFSIGNALED(stat)) {
171 			pounder_fprintf(out, "%s: %s on signal %d.\n",
172 					progname, fail_msg, WTERMSIG(stat));
173 			res = 255;
174 		} else {
175 			res = WEXITSTATUS(stat);
176 			if (res == 0) {
177 				pounder_fprintf(out, "%s: %s.\n", progname,
178 						pass_msg);
179 			} else if (res < 0 || res == 255) {
180 				pounder_fprintf(out,
181 						"CHECK %s: %s with code %d.\n",
182 						progname, abort_msg, res);
183 				exit(-1);
184 				// FIXME: add test to blacklist
185 			} else {
186 				pounder_fprintf(out,
187 						"%s: %s with code %d.\n",
188 						progname, fail_msg, res);
189 				if (max_failures > 0) {
190 					if (++fail_counter > max_failures) {
191 						exit(-1);
192 					}
193 				}
194 			}
195 		}
196 	}
197 }
198