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 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 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 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