/** * Takes the wait() status integer and prints a log message. * Returns 1 if there was a failure. */ static int child_finished(const char *name, int stat) { int x; // did we sig-exit? if (WIFSIGNALED(stat)) { pounder_fprintf(stdout, "%s: %s on signal %d.\n", name, fail_msg, WTERMSIG(stat)); return 1; } else { x = WEXITSTATUS(stat); if (x == 0) { pounder_fprintf(stdout, "%s: %s.\n", name, pass_msg); return 0; } else if (x < 0 || x == 255) { pounder_fprintf(stdout, "%s: %s with code %d.\n", name, abort_msg, x); return 1; // FIXME: add test to blacklist } else { pounder_fprintf(stdout, "%s: %s with code %d.\n", name, fail_msg, x); return 1; } } }
/** * Adds a child process to either the running-test or running-daemon * list. */ static void note_child(pid_t pid, char *fname, char type) { if (type == 'T') { note_process(pid, fname); } else if (type == 'D') { note_daemon(pid, fname); } else { pounder_fprintf(stdout, "Don't know what to do with child `%s' of type %c.\n", fname, type); } }
/** * Kill everything upon ^C. */ static void jump_out(int signum) { pounder_fprintf(stdout, "Control-C received; aborting!\n"); //unlink("pounder_pgrp"); kill_tests(); kill_daemons(); if (is_leader) { unlink(pidfile); } exit(0); }
/** * Process a directory--for each entry in a directory, execute files or spawn * a new copy of ourself on the new directory. Process execution is subject to * these rules: * * - Test files that start with the same number '00foo' and '00bar' are allowed * to run simultaneously. * - Test files are run in order of number and then name. * * If a the fork fails, bit 1 of the return code is set. If a * program runs but fails, bit 2 is set. */ static int process_dir(const char *fname) { struct dirent **namelist; int i, result = 0; char buf[TEST_PATH_LEN]; int curr_level_num = -1; int test_level_num; pid_t pid; int children_ok = 1; pounder_fprintf(stdout, "%s: Entering directory.\n", fname); i = scandir(fname, &namelist, test_filter, (int (*)(const void *, const void *))test_sort); if (i < 0) { perror(fname); return -1; } while (i--) { /* determine level number */ test_level_num = ((namelist[i]->d_name[1] - '0') * 10) + (namelist[i]->d_name[2] - '0'); if (curr_level_num == -1) { curr_level_num = test_level_num; } if (curr_level_num != test_level_num) { children_ok &= wait_for_pids(); curr_level_num = test_level_num; } snprintf(buf, TEST_PATH_LEN, "%s/%s", fname, namelist[i]->d_name); if (is_directory(buf)) { pid = fork(); if (pid == 0) { if (setpgrp() < 0) { perror("setpgid"); } // spawn a new copy of ourself. execl(progname, progname, buf, NULL); perror(progname); exit(-1); } } else { pid = spawn_test(buf); } if (pid < 0) { perror("fork"); result |= 1; free(namelist[i]); continue; } note_child(pid, buf, namelist[i]->d_name[0]); free(namelist[i]); } free(namelist); /* wait for remaining runners */ children_ok &= wait_for_pids(); if (children_ok == 0) { result |= 2; } pounder_fprintf(stdout, "%s: Leaving directory.\n", fname); return result; }
/** * Starts a test, with the stdin/out/err fd's redirected to logs. * The 'fname' parameter should be a relative path from $POUNDER_HOME. */ static pid_t spawn_test(char *fname) { pid_t pid; int fd, tmp; char buf[TEST_PATH_LEN], buf2[TEST_PATH_LEN]; char *last_slash; pid = fork(); if (pid == 0) { if (setpgrp() < 0) { perror("setpgid"); } pounder_fprintf(stdout, "%s: %s test.\n", fname, start_msg); // reroute stdin fd = open("/dev/null", O_RDWR); if (fd < 0) { perror("/dev/null"); exit(-1); } close(0); tmp = dup2(fd, 0); if (tmp < 0) { perror("dup(/dev/null)"); exit(-1); } close(fd); // generate log name-- '/' -> '-'. snprintf(buf2, TEST_PATH_LEN, "%s|%s", getenv("POUNDER_LOGDIR"), fname); fd = strlen(buf2); for (tmp = (index(buf2, '|') - buf2); tmp < fd; tmp++) { if (buf2[tmp] == '/') { buf2[tmp] = '-'; } else if (buf2[tmp] == '|') { buf2[tmp] = '/'; } } // make it so that we have a way to get back to the // original console. tmp = dup2(1, 3); if (tmp < 0) { perror("dup(stdout, 3)"); exit(-1); } // reroute stdout/stderr fd = open(buf2, O_RDWR | O_CREAT | O_TRUNC | O_SYNC, S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH); if (fd < 0) { perror(buf2); exit(-1); } close(1); tmp = dup2(fd, 1); if (tmp < 0) { perror("dup(log, 1)"); exit(-1); } close(2); tmp = dup2(fd, 2); if (tmp < 0) { perror("dup(log, 2)"); exit(-1); } close(fd); // let us construct the absolute pathname of the test. // first find the current directory if (getcwd(buf, TEST_PATH_LEN) == NULL) { perror("getcwd"); exit(-1); } // then splice cwd + fname snprintf(buf2, TEST_PATH_LEN, "%s/%s", buf, fname); // find the location of the last slash last_slash = rindex(buf2, '/'); if (last_slash != NULL) { // copy the filename part into a new buffer snprintf(buf, TEST_PATH_LEN, "./%s", last_slash + 1); // truncate at the last slash *last_slash = 0; // and chdir if (chdir(buf2) != 0) { perror(buf2); exit(-1); } // reassign variables fname = buf; } // spawn the process execlp(fname, fname, NULL); // If we get here, we can't run the test. perror(fname); exit(-1); } tmp = errno; /* yield for a short while, so that the test has * a little bit of time to run. */ usleep(TEST_FORK_WAIT); errno = tmp; return pid; }
/** * Main program. Returns 1 if all programs run successfully, 0 if * something failed and -1 if there was an error running programs. */ int main(int argc, char *argv[]) { int retcode; struct sigaction zig; pid_t pid; char *c; /* Check parameters */ if (argc < 2) { fprintf(stderr, "Usage: %s test_prog\n", argv[0]); return 1; } if (argc > 2 && strcmp(argv[2], "--leader") == 0) { pounder_fprintf(stdout, "Logging this test output to %s/POUNDERLOG.\n", getenv("POUNDER_LOGDIR")); is_leader = 1; record_pid(); } progname = argv[0]; /* Set up signals */ memset(&zig, 0x00, sizeof(zig)); zig.sa_handler = jump_out; sigaction(SIGHUP, &zig, NULL); sigaction(SIGINT, &zig, NULL); sigaction(SIGTERM, &zig, NULL); if (is_directory(argv[1])) { retcode = process_dir(argv[1]); } else { if (is_executable(argv[1])) { c = rindex(argv[1], '/'); c++; // Start the test pid = spawn_test(argv[1]); if (pid < 0) { perror("fork"); retcode = -1; goto out; } // Track the test note_process(pid, argv[1]); if (wait_for_pids() == 0) { retcode = 1; } else { retcode = 0; } } else { pounder_fprintf(stderr, "%s: Not a directory or a test.\n", argv[1]); retcode = -1; } } out: kill_daemons(); wait_for_daemons(); if (is_leader) { if (retcode == 0) { pounder_fprintf(stdout, "%s: %s.\n", argv[1], pass_msg); } else if (retcode < 0 || retcode == 255) { pounder_fprintf(stdout, "%s: %s with code %d.\n", argv[1], abort_msg, retcode); } else { pounder_fprintf(stdout, "%s: %s with code %d.\n", argv[1], fail_msg, retcode); } unlink(pidfile); } exit(retcode); }
int main(int argc, char *argv[]) { int secs, stat; pid_t pid; unsigned int revs = 0; struct sigaction zig; int use_max_failures = 0; int max_failures = 0; int fail_counter = 1; if (argc < 3) { printf("Usage: %s [-m max_failures] time_in_sec command [args]\n", argv[0]); exit(1); } //by default, set max_failures to whatever the env variable $MAX_FAILURES is char *max_failures_env = getenv("MAX_FAILURES"); max_failures = atoi(max_failures_env); //if the -m option is used when calling timed_loop, override max_failures //specified by $MAX_FAILURES with the given argument instead if (argc > 4 && strcmp(argv[1], "-m") == 0) { if ((max_failures = atoi(argv[2])) >= 0) { use_max_failures = 1; } else { printf("Usage: %s [-m max_failures] time_in_sec command [args]\n", argv[0]); printf("max_failures should be a nonnegative integer\n"); exit(1); } } tty_fp = fdopen(3, "w+"); if (tty_fp == NULL) { tty_fp = fopen("/dev/tty", "w+"); if (tty_fp == NULL) { perror("stdout"); exit(2); } } if (use_max_failures) { progname = rindex(argv[4], '/'); if (progname == NULL) { progname = argv[4]; } else { progname++; } } else { progname = rindex(argv[2], '/'); if (progname == NULL) { progname = argv[2]; } else { progname++; } } /* Set up signals */ memset(&zig, 0x00, sizeof(zig)); zig.sa_handler = alarm_func; sigaction(SIGALRM, &zig, NULL); zig.sa_handler = int_func; sigaction(SIGINT, &zig, NULL); sigaction(SIGTERM, &zig, NULL); /* set up process groups so that we can kill the * loop test and descendants easily */ if (use_max_failures) { secs = atoi(argv[3]); } else { secs = atoi(argv[1]); } alarm(secs); while (1) { pounder_fprintf(tty_fp, "%s: %s loop #%d.\n", progname, start_msg, revs++); pid = fork(); if (pid == 0) { if (setpgrp() < 0) { perror("setpgid"); } // run the program if (use_max_failures) { if (argc > 5) { stat = execvp(argv[4], &argv[4]); } else { stat = execvp(argv[4], &argv[4]); } perror(argv[4]); } else { if (argc > 3) { stat = execvp(argv[2], &argv[2]); } else { stat = execvp(argv[2], &argv[2]); } perror(argv[2]); } exit(-1); } /* save the pgrp of the spawned process */ test_pgrp = pid; // wait for it to be done if (waitpid(pid, &stat, 0) != pid) { perror("waitpid"); exit(1); } // interrogate it if (WIFSIGNALED(stat)) { pounder_fprintf(tty_fp, "%s: %s on signal %d.\n", progname, fail_msg, WTERMSIG(stat)); res = 255; } else { res = WEXITSTATUS(stat); if (res == 0) { pounder_fprintf(tty_fp, "%s: %s.\n", progname, pass_msg); } else if (res < 0 || res == 255) { pounder_fprintf(tty_fp, "CHECK %s: %s with code %d.\n", progname, abort_msg, res); exit(-1); // FIXME: add test to blacklist } else { pounder_fprintf(tty_fp, "%s: %s with code %d.\n", progname, fail_msg, res); if (max_failures > 0) { if (++fail_counter > max_failures) { exit(-1); } } } } } }
static void alarm_func(int signum) { pounder_fprintf(tty_fp, "%s: Killed by timer. Last exit code = %d.\n", progname, res); kill(-test_pgrp, SIGTERM); exit(res); }
int main(int argc, char *argv[]) { int stat; pid_t pid; struct sigaction zig; unsigned int revs = 0; if (argc < 2) { printf("Usage: %s command [args]\n", argv[0]); exit(1); } tty_fp = fdopen(3, "w+"); if (tty_fp == NULL) { tty_fp = fopen("/dev/tty", "w+"); if (tty_fp == NULL) { perror("stdout"); exit(2); } } progname = rindex(argv[1], '/'); if (progname == NULL) { progname = argv[1]; } else { progname++; } /* Set up signals */ memset(&zig, 0x00, sizeof(zig)); zig.sa_handler = int_func; sigaction(SIGINT, &zig, NULL); sigaction(SIGTERM, &zig, NULL); /* set up process groups so that we can kill the * loop test and descendants easily */ while (1) { pounder_fprintf(tty_fp, "%s: %s loop #%d.\n", progname, start_msg, revs++); pid = fork(); if (pid == 0) { if (setpgrp() < 0) { perror("setpgid"); } // run the program if (argc > 3) { stat = execvp(argv[1], &argv[1]); } else { stat = execvp(argv[1], &argv[1]); } perror(argv[1]); exit(-1); } /* save the pgrp of the spawned process */ test_pgrp = pid; // wait for it to be done if (waitpid(pid, &stat, 0) != pid) { perror("waitpid"); exit(1); } // interrogate it if (WIFSIGNALED(stat)) { pounder_fprintf(tty_fp, "%s: %s on signal %d.\n", progname, fail_msg, WTERMSIG(stat)); res = 255; } else { res = WEXITSTATUS(stat); if (res == 0) { pounder_fprintf(tty_fp, "%s: %s.\n", progname, pass_msg); } else if (res < 0 || res == 255) { pounder_fprintf(tty_fp, "%s: %s with code %d.\n", progname, abort_msg, res); exit(-1); // FIXME: add test to blacklist } else { pounder_fprintf(tty_fp, "%s: %s with code %d.\n", progname, fail_msg, res); } } } }