static void check_orphans(struct orphan_pgrp *orphans, int sig) { struct orphan_pgrp *orph; for (orph = orphans; orph != NULL; orph = orph->next) { if (orph->pgrp == 0) continue; if (Debug & Dshutdown) fprintf(stderr, " propagating sig %d to orphaned pgrp %d\n", sig, -(orph->pgrp)); if (kill(-(orph->pgrp), sig) != 0) { if (errno == ESRCH) { /* This pgrp is now empty */ if (zoo_clear(zoofile, orph->pgrp)) { fprintf(stderr, "pan(%s): %s\n", panname, zoo_error); } orph->pgrp = 0; } else { fprintf(stderr, "pan(%s): kill(%d,%d) on orphaned pgrp failed. errno:%d %s\n", panname, -(orph->pgrp), sig, errno, strerror(errno)); } } } }
int main(int argc, char *argv[]) { char *zooname; zoo_t test_zoo; char *test_tag = "unittest"; int i,j; zooname = zoo_getname(); if (!zooname) { zooname = strdup("test_zoo"); } printf("Test zoo filename is %s\n", zooname); if ((test_zoo = zoo_open(zooname)) == NULL) { printf("Error opennning zoo\n"); exit(-1); } zoo_mark_args(test_zoo, getpid(), test_tag, argc, argv); for (j = 0; j < 5; j++) { for (i = 0; i < 20; i++) { zt_add(test_zoo, i); } for (; i >=0; i--) { zoo_clear(test_zoo, i); } } zoo_clear(test_zoo, getpid()); return 0; }
static int check_pids(struct tag_pgrp *running, int *num_active, int keep_active, FILE *logfile, FILE *failcmdfile, FILE *tconfcmdfile, struct orphan_pgrp *orphans, int fmt_print, int *failcnt, int *tconfcnt, int quiet_mode) { int w; pid_t cpid; int stat_loc; int ret = 0; int i; time_t t; char *status; char *result_str; int signaled = 0; struct tms tms1, tms2; clock_t tck; check_orphans(orphans, 0); tck = times(&tms1); if (tck == -1) { fprintf(stderr, "pan(%s): times(&tms1) failed. errno:%d %s\n", panname, errno, strerror(errno)); } cpid = wait(&stat_loc); tck = times(&tms2); if (tck == -1) { fprintf(stderr, "pan(%s): times(&tms2) failed. errno:%d %s\n", panname, errno, strerror(errno)); } if (cpid < 0) { if (errno == EINTR) { if (Debug) fprintf(stderr, "pan(%s): wait() interrupted\n", panname); } else if (errno != ECHILD) { fprintf(stderr, "pan(%s): wait() failed. errno:%d %s\n", panname, errno, strerror(errno)); } } else if (cpid > 0) { if (WIFSIGNALED(stat_loc)) { w = WTERMSIG(stat_loc); status = "signaled"; if (Debug & Dexit) fprintf(stderr, "child %d terminated with signal %d\n", cpid, w); --*num_active; signaled = 1; } else if (WIFEXITED(stat_loc)) { w = WEXITSTATUS(stat_loc); status = "exited"; if (Debug & Dexit) fprintf(stderr, "child %d exited with status %d\n", cpid, w); --*num_active; if (w != 0 && w != TCONF) ret++; } else if (WIFSTOPPED(stat_loc)) { /* should never happen */ w = WSTOPSIG(stat_loc); status = "stopped"; ret++; } else { /* should never happen */ w = 0; status = "unknown"; ret++; } for (i = 0; i < keep_active; ++i) { if (running[i].pgrp == cpid) { if ((w == 130) && running[i].stopping && (strcmp(status, "exited") == 0)) { /* The child received sigint, but * did not trap for it? Compensate * for it here. */ w = 0; ret--; /* undo */ if (Debug & Drunning) fprintf(stderr, "pan(%s): tag=%s exited 130, known to be signaled; will give it an exit 0.\n", panname, running[i].cmd->name); } time(&t); if (logfile != NULL) { if (!fmt_print) fprintf(logfile, "tag=%s stime=%d dur=%d exit=%s stat=%d core=%s cu=%d cs=%d\n", running[i].cmd->name, (int)(running[i]. mystime), (int)(t - running[i]. mystime), status, w, (stat_loc & 0200) ? "yes" : "no", (int)(tms2.tms_cutime - tms1.tms_cutime), (int)(tms2.tms_cstime - tms1.tms_cstime)); else { if (strcmp(status, "exited") == 0 && w == TCONF) { ++*tconfcnt; result_str = "CONF"; } else if (w != 0) { ++*failcnt; result_str = "FAIL"; } else { result_str = "PASS"; } fprintf(logfile, "%-30.30s %-10.10s %-5d\n", running[i].cmd->name, result_str, w); } fflush(logfile); } if (w != 0) { if (tconfcmdfile != NULL && w == TCONF) { fprintf(tconfcmdfile, "%s %s\n", running[i].cmd->name, running[i].cmd->cmdline); } else if (failcmdfile != NULL) { fprintf(failcmdfile, "%s %s\n", running[i].cmd->name, running[i].cmd->cmdline); } } if (running[i].stopping) status = "driver_interrupt"; if (test_out_dir) { if (!quiet_mode) write_test_start(running + i); copy_buffered_output(running + i); unlink(running[i].output); } if (!quiet_mode) write_test_end(running + i, "ok", t, status, stat_loc, w, &tms1, &tms2); /* If signaled and we weren't expecting * this to be stopped then the proc * had a problem. */ if (signaled && !running[i].stopping) ret++; running[i].pgrp = 0; if (zoo_clear(zoofile, cpid)) { fprintf(stderr, "pan(%s): %s\n", panname, zoo_error); exit(1); } /* Check for orphaned pgrps */ if ((kill(-cpid, 0) == 0) || (errno == EPERM)) { if (zoo_mark_cmdline (zoofile, cpid, "panorphan", running[i].cmd->cmdline)) { fprintf(stderr, "pan(%s): %s\n", panname, zoo_error); exit(1); } mark_orphan(orphans, cpid); /* status of kill doesn't matter */ kill(-cpid, SIGTERM); } break; } } } return ret; }
int main(int argc, char **argv) { extern char *optarg; extern int optind; char *zooname = NULL; /* name of the zoo file to use */ char *filename = "/dev/null"; /* filename to read test tags from */ char *logfilename = NULL; char *failcmdfilename = NULL; char *tconfcmdfilename = NULL; char *outputfilename = NULL; struct collection *coll = NULL; struct tag_pgrp *running; struct orphan_pgrp *orphans, *orph; struct utsname unamebuf; FILE *logfile = NULL; FILE *failcmdfile = NULL; FILE *tconfcmdfile = NULL; int keep_active = 1; int num_active = 0; int failcnt = 0; /* count of total testcases that failed. */ int tconfcnt = 0; /* count of total testcases that return TCONF */ int err, i; int starts = -1; int timed = 0; int run_time = -1; char modifier = 'm'; int ret = 0; int stop; int go_idle; int has_brakes = 0; /* stop everything if a test case fails */ int sequential = 0; /* run tests sequentially */ int fork_in_road = 0; int exit_stat; int track_exit_stats = 0; /* exit non-zero if any test exits non-zero */ int fmt_print = 0; /* enables formatted printing of logfiles. */ int quiet_mode = 0; /* supresses test start and test end tags. */ int c; pid_t cpid; struct sigaction sa; while ((c = getopt(argc, argv, "AO:Sa:C:T:d:ef:hl:n:o:pqr:s:t:x:y")) != -1) { switch (c) { case 'A': /* all-stop flag */ has_brakes = 1; track_exit_stats = 1; break; case 'O': /* output buffering directory */ test_out_dir = strdup(optarg); break; case 'S': /* run tests sequentially */ sequential = 1; break; case 'a': /* name of the zoo file to use */ zooname = strdup(optarg); break; case 'C': /* name of the file where all failed commands will be */ failcmdfilename = strdup(optarg); break; case 'T': /* * test cases that are not fully tested will be recorded * in this file */ tconfcmdfilename = strdup(optarg); break; case 'd': /* debug options */ sscanf(optarg, "%i", &Debug); break; case 'e': /* exit non-zero if any test exists non-zero */ track_exit_stats = 1; break; case 'f': /* filename to read test tags from */ filename = strdup(optarg); break; case 'h': /* help */ fprintf(stdout, "Usage: pan -n name [ -SyAehpq ] [ -s starts ]" " [-t time[s|m|h|d] [ -x nactive ] [ -l logfile ]\n\t" "[ -a active-file ] [ -f command-file ] " "[ -C fail-command-file ] " "[ -d debug-level ]\n\t[-o output-file] " "[-O output-buffer-directory] [cmd]\n"); exit(0); case 'l': /* log file */ logfilename = strdup(optarg); break; case 'n': /* tag given to pan */ panname = strdup(optarg); break; case 'o': /* send test output here */ outputfilename = strdup(optarg); break; case 'p': /* formatted printing. */ fmt_print = 1; break; case 'q': /* supress test start and test end messages */ quiet_mode = 1; break; case 'r': /* reporting type: none, rts */ reporttype = strdup(optarg); break; case 's': /* number of tags to run */ starts = atoi(optarg); break; case 't': /* run_time to run */ ret = sscanf(optarg, "%d%c", &run_time, &modifier); if (ret == 0) { fprintf(stderr, "Need proper time input: ####x where" "x is one of s,m,h,d\n"); break; } else if (ret == 1) { fprintf(stderr, "Only got a time value of %d " "modifiers need to come immediately after #" " assuming %c\n", run_time, modifier); } else { switch (modifier) { case 's': run_time = run_time; break; case 'm': run_time = run_time * 60; break; case 'h': run_time = run_time * 60 * 60; break; case 'd': run_time = run_time * 60 * 60 * 24; break; default: fprintf(stderr, "Invalid time modifier, try: s|h|m|d\n"); exit(-1); } if (!quiet_mode) printf("PAN will run for %d seconds\n", run_time); } timed = 1; //-t implies run as many starts as possible, by default break; case 'x': /* number of tags to keep running */ keep_active = atoi(optarg); break; case 'y': /* restart on failure or signal */ fork_in_road = 1; break; } } if (panname == NULL) { fprintf(stderr, "pan: Must supply -n\n"); exit(1); } if (zooname == NULL) { zooname = zoo_getname(); if (zooname == NULL) { fprintf(stderr, "pan(%s): Must supply -a or set ZOO env variable\n", panname); exit(1); } } if (reporttype) { /* make sure we understand the report type */ if (strcasecmp(reporttype, "rts") && strcasecmp(reporttype, "none") /* && strcasecmp(reporttype, "xml") */ ) reporttype = "rts"; } else { /* set the default */ reporttype = "rts"; } if (logfilename != NULL) { time_t startup; char *s; if (!strcmp(logfilename, "-")) { logfile = stdout; } else { if ((logfile = fopen(logfilename, "a+")) == NULL) { fprintf(stderr, "pan(%s): Error %s (%d) opening log file '%s'\n", panname, strerror(errno), errno, logfilename); exit(1); } } time(&startup); s = ctime(&startup); *(s + strlen(s) - 1) = '\0'; if (!fmt_print) fprintf(logfile, "startup='%s'\n", s); else { fprintf(logfile, "Test Start Time: %s\n", s); fprintf(logfile, "-----------------------------------------\n"); fprintf(logfile, "%-30.20s %-10.10s %-10.10s\n", "Testcase", "Result", "Exit Value"); fprintf(logfile, "%-30.20s %-10.10s %-10.10s\n", "--------", "------", "------------"); } fflush(logfile); } coll = get_collection(filename, optind, argc, argv); if (!coll) exit(1); if (coll->cnt == 0) { fprintf(stderr, "pan(%s): Must supply a file collection or a command\n", panname); exit(1); } if (Debug & Dsetup) dump_coll(coll); /* a place to store the pgrps we're watching */ running = malloc((keep_active + 1) * sizeof(struct tag_pgrp)); if (running == NULL) { fprintf(stderr, "pan(%s): Failed to allocate memory: %s\n", panname, strerror(errno)); exit(2); } memset(running, 0, keep_active * sizeof(struct tag_pgrp)); running[keep_active].pgrp = -1; /* end sentinel */ /* a head to the orphaned pgrp list */ orphans = malloc(sizeof(struct orphan_pgrp)); memset(orphans, 0, sizeof(struct orphan_pgrp)); srand48(time(NULL) ^ (getpid() + (getpid() << 15))); /* Supply a default for starts. If we are in sequential mode, use * the number of commands available; otherwise 1. */ if (timed == 1 && starts == -1) { /* timed, infinite by default */ starts = -1; } else if (starts == -1) { if (sequential) { starts = coll->cnt; } else { starts = 1; } } else if (starts == 0) { /* if the user specified infinite, set it */ starts = -1; } else { /* else, make sure we are starting at least keep_active processes */ if (starts < keep_active) starts = keep_active; } /* if we're buffering output, but we're only running on process at a time, * then essentially "turn off buffering" */ if (test_out_dir && (keep_active == 1)) { free(test_out_dir); test_out_dir = NULL; } if (test_out_dir) { struct stat sbuf; if (stat(test_out_dir, &sbuf) < 0) { fprintf(stderr, "pan(%s): stat of -O arg '%s' failed. errno: %d %s\n", panname, test_out_dir, errno, strerror(errno)); exit(1); } if (!S_ISDIR(sbuf.st_mode)) { fprintf(stderr, "pan(%s): -O arg '%s' must be a directory.\n", panname, test_out_dir); exit(1); } if (access(test_out_dir, W_OK | R_OK | X_OK) < 0) { fprintf(stderr, "pan(%s): permission denied on -O arg '%s'. errno: %d %s\n", panname, test_out_dir, errno, strerror(errno)); exit(1); } } if (outputfilename) { if (!freopen(outputfilename, "a+", stdout)) { fprintf(stderr, "pan(%s): Error %s (%d) opening output file '%s'\n", panname, strerror(errno), errno, outputfilename); exit(1); } } if (failcmdfilename) { if (!(failcmdfile = fopen(failcmdfilename, "a+"))) { fprintf(stderr, "pan(%s): Error %s (%d) opening fail cmd file '%s'\n", panname, strerror(errno), errno, failcmdfilename); exit(1); } } if (tconfcmdfilename) { tconfcmdfile = fopen(tconfcmdfilename, "a+"); if (!tconfcmdfile) { fprintf(stderr, "pan(%s): Error %s (%d) opening " "tconf cmd file '%s'\n", panname, strerror(errno), errno, tconfcmdfilename); exit(1); } } if ((zoofile = zoo_open(zooname)) == NULL) { fprintf(stderr, "pan(%s): %s\n", panname, zoo_error); exit(1); } if (zoo_mark_args(zoofile, getpid(), panname, argc, argv)) { fprintf(stderr, "pan(%s): %s\n", panname, zoo_error); exit(1); } /* Allocate N spaces for max-arg commands. * this is an "active file cleanliness" thing */ { for (c = 0; c < keep_active; c++) { if (zoo_mark_cmdline(zoofile, c, panname, "")) { fprintf(stderr, "pan(%s): %s\n", panname, zoo_error); exit(1); } } for (c = 0; c < keep_active; c++) { if (zoo_clear(zoofile, c)) { fprintf(stderr, "pan(%s): %s\n", panname, zoo_error); exit(1); } } } rec_signal = send_signal = 0; if (run_time != -1) { alarm(run_time); } sigemptyset(&sa.sa_mask); sa.sa_flags = 0; sa.sa_handler = wait_handler; sigaction(SIGALRM, &sa, NULL); sigaction(SIGINT, &sa, NULL); sigaction(SIGTERM, &sa, NULL); sigaction(SIGHUP, &sa, NULL); sigaction(SIGUSR1, &sa, NULL); /* ignore fork_in_road */ sigaction(SIGUSR2, &sa, NULL); /* stop the scheduler */ c = 0; /* in this loop, c is the command index */ stop = 0; exit_stat = 0; go_idle = 0; while (1) { while ((num_active < keep_active) && (starts != 0)) { if (stop || rec_signal || go_idle) break; if (!sequential) c = lrand48() % coll->cnt; /* find a slot for the child */ for (i = 0; i < keep_active; ++i) { if (running[i].pgrp == 0) break; } if (i == keep_active) { fprintf(stderr, "pan(%s): Aborting: i == keep_active = %d\n", panname, i); wait_handler(SIGINT); exit_stat++; break; } cpid = run_child(coll->ary[c], running + i, quiet_mode, &failcnt, fmt_print, logfile); if (cpid != -1) ++num_active; if ((cpid != -1 || sequential) && starts > 0) --starts; if (sequential) if (++c >= coll->cnt) c = 0; } /* while ((num_active < keep_active) && (starts != 0)) */ if (starts == 0) { if (!quiet_mode) printf("incrementing stop\n"); ++stop; } else if (starts == -1) //wjh { FILE *f = (FILE *) - 1; if ((f = fopen(PAN_STOP_FILE, "r")) != 0) { printf("Got %s Stopping!\n", PAN_STOP_FILE); fclose(f); unlink(PAN_STOP_FILE); stop++; } } if (rec_signal) { /* propagate everything except sigusr2 */ if (rec_signal == SIGUSR2) { if (fork_in_road) ++go_idle; else ++stop; rec_signal = send_signal = 0; } else { if (rec_signal == SIGUSR1) fork_in_road = 0; propagate_signal(running, keep_active, orphans); if (fork_in_road) ++go_idle; else ++stop; } } err = check_pids(running, &num_active, keep_active, logfile, failcmdfile, tconfcmdfile, orphans, fmt_print, &failcnt, &tconfcnt, quiet_mode); if (Debug & Drunning) { pids_running(running, keep_active); orphans_running(orphans); } if (err) { if (fork_in_road) ++go_idle; if (track_exit_stats) exit_stat++; if (has_brakes) { fprintf(stderr, "pan(%s): All stop!%s\n", panname, go_idle ? " (idling)" : ""); wait_handler(SIGINT); } } if (stop && (num_active == 0)) break; if (go_idle && (num_active == 0)) { go_idle = 0; /* It is idle, now resume scheduling. */ wait_handler(0); /* Reset the signal ratchet. */ } } /* Wait for orphaned pgrps */ while (1) { for (orph = orphans; orph != NULL; orph = orph->next) { if (orph->pgrp == 0) continue; /* Yes, we have orphaned pgrps */ sleep(5); if (!rec_signal) { /* force an artificial signal, move us * through the signal ratchet. */ wait_handler(SIGINT); } propagate_signal(running, keep_active, orphans); if (Debug & Drunning) orphans_running(orphans); break; } if (orph == NULL) break; } if (zoo_clear(zoofile, getpid())) { fprintf(stderr, "pan(%s): %s\n", panname, zoo_error); ++exit_stat; } fclose(zoofile); if (logfile && fmt_print) { if (uname(&unamebuf) == -1) fprintf(stderr, "ERROR: uname(): %s\n", strerror(errno)); fprintf(logfile, "\n-----------------------------------------------\n"); fprintf(logfile, "Total Tests: %d\n", coll->cnt); fprintf(logfile, "Total Skipped Tests: %d\n", tconfcnt); fprintf(logfile, "Total Failures: %d\n", failcnt); fprintf(logfile, "Kernel Version: %s\n", unamebuf.release); fprintf(logfile, "Machine Architecture: %s\n", unamebuf.machine); fprintf(logfile, "Hostname: %s\n\n", unamebuf.nodename); } if (logfile && (logfile != stdout)) fclose(logfile); if (failcmdfile) fclose(failcmdfile); if (tconfcmdfile) fclose(tconfcmdfile); exit(exit_stat); }