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