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
32static int res = 0;
33static char *progname;
34static pid_t test_pgrp;
35static FILE *out;
36
37static 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
46static 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/*
55static void term_func(int signum) {
56	exit(res);
57}
58*/
59
60int 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