1 /* Repeatedly run a program with a given uid, gid and termination signal. */
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 
37 static int the_signal = SIGTERM;
38 
int_func(int signum)39 static void int_func(int signum)
40 {
41 	pounder_fprintf(out,
42 			"%s: Killed by interrupt.  Last exit code = %d.\n",
43 			progname, res);
44 	kill(-test_pgrp, the_signal);
45 	exit(res);
46 }
47 
alarm_func(int signum)48 static void alarm_func(int signum)
49 {
50 	pounder_fprintf(out, "%s: Killed by timer.  Last exit code = %d.\n",
51 			progname, res);
52 	kill(-test_pgrp, the_signal);
53 	exit(res);
54 }
55 
main(int argc,char * argv[])56 int main(int argc, char *argv[])
57 {
58 	int secs, stat;
59 	pid_t pid;
60 	unsigned int revs = 0;
61 	struct sigaction zig;
62 	uid_t uid;
63 	gid_t gid;
64 	int use_max_failures = 0;
65 	int max_failures = 0;
66 	int fail_counter = 1;
67 
68 	if (argc < 5) {
69 		printf
70 		    ("Usage: %s [-m max_failures] time_in_sec uid gid signal command [args]\n",
71 		     argv[0]);
72 		exit(1);
73 	}
74 	//by default, set max_failures to whatever the env variable $MAX_FAILURES is
75 	char *max_failures_env = getenv("MAX_FAILURES");
76 	max_failures = atoi(max_failures_env);
77 
78 	//if the -m option is used when calling fancy_timed_loop, override max_failures
79 	//specified by $MAX_FAILURES with the given argument instead
80 	if (argc > 6 && strcmp(argv[1], "-m") == 0) {
81 		if ((max_failures = atoi(argv[2])) >= 0) {
82 			use_max_failures = 1;
83 		} else {
84 			printf
85 			    ("Usage: %s [-m max_failures] time_in_sec uid gid signal command [args]\n",
86 			     argv[0]);
87 			printf
88 			    ("max_failures should be a nonnegative integer\n");
89 			exit(1);
90 		}
91 	}
92 
93 	out = stdout;
94 
95 	if (use_max_failures) {
96 		progname = rindex(argv[7], '/');
97 		if (progname == NULL) {
98 			progname = argv[7];
99 		} else {
100 			progname++;
101 		}
102 	} else {
103 		progname = rindex(argv[5], '/');
104 		if (progname == NULL) {
105 			progname = argv[5];
106 		} else {
107 			progname++;
108 		}
109 	}
110 
111 	/* Set up signals */
112 	memset(&zig, 0x00, sizeof(zig));
113 	zig.sa_handler = alarm_func;
114 	sigaction(SIGALRM, &zig, NULL);
115 	zig.sa_handler = int_func;
116 	sigaction(SIGINT, &zig, NULL);
117 	sigaction(SIGTERM, &zig, NULL);
118 
119 	/* set up process groups so that we can kill the
120 	 * loop test and descendants easily */
121 
122 	if (use_max_failures) {
123 		secs = atoi(argv[3]);
124 		alarm(secs);
125 
126 		the_signal = atoi(argv[6]);
127 		uid = atoi(argv[4]);
128 		gid = atoi(argv[5]);
129 	} else {
130 		secs = atoi(argv[1]);
131 		alarm(secs);
132 
133 		the_signal = atoi(argv[4]);
134 		uid = atoi(argv[2]);
135 		gid = atoi(argv[3]);
136 	}
137 
138 	pounder_fprintf(out, "%s: uid = %d, gid = %d, sig = %d\n",
139 			progname, uid, gid, the_signal);
140 
141 	while (1) {
142 		pounder_fprintf(out, "%s: %s loop #%d.\n", progname,
143 				start_msg, revs++);
144 		pid = fork();
145 		if (pid == 0) {
146 			// set process group
147 			if (setpgrp() < 0) {
148 				perror("setpgid");
149 			}
150 			// set group and user id
151 			if (setregid(gid, gid) != 0) {
152 				perror("setregid");
153 				exit(-1);
154 			}
155 
156 			if (setreuid(uid, uid) != 0) {
157 				perror("setreuid");
158 				exit(-1);
159 			}
160 			// run the program
161 			if (use_max_failures) {
162 				if (argc > 5) {
163 					stat = execvp(argv[7], &argv[7]);
164 				} else {
165 					stat = execvp(argv[7], &argv[7]);
166 				}
167 
168 				perror(argv[7]);
169 			} else {
170 				if (argc > 3) {
171 					stat = execvp(argv[5], &argv[5]);
172 				} else {
173 					stat = execvp(argv[5], &argv[5]);
174 				}
175 
176 				perror(argv[5]);
177 			}
178 
179 			exit(-1);
180 		}
181 
182 		/* save the pgrp of the spawned process */
183 		test_pgrp = pid;
184 
185 		// wait for it to be done
186 		if (waitpid(pid, &stat, 0) != pid) {
187 			perror("waitpid");
188 			exit(1);
189 		}
190 		// interrogate it
191 		if (WIFSIGNALED(stat)) {
192 			pounder_fprintf(out, "%s: %s on signal %d.\n",
193 					progname, fail_msg, WTERMSIG(stat));
194 			res = 255;
195 		} else {
196 			res = WEXITSTATUS(stat);
197 			if (res == 0) {
198 				pounder_fprintf(out, "%s: %s.\n", progname,
199 						pass_msg);
200 			} else if (res < 0 || res == 255) {
201 				pounder_fprintf(out,
202 						"%s: %s with code %d.\n",
203 						progname, abort_msg, res);
204 				exit(-1);
205 				// FIXME: add test to blacklist
206 			} else {
207 				pounder_fprintf(out,
208 						"%s: %s with code %d.\n",
209 						progname, fail_msg, res);
210 				if (max_failures > 0) {
211 					if (++fail_counter > max_failures) {
212 						exit(-1);
213 					}
214 				}
215 			}
216 		}
217 	}
218 }
219