Home | History | Annotate | Download | only in pan
      1 /*
      2  * Copyright (c) 2000 Silicon Graphics, Inc.  All Rights Reserved.
      3  *
      4  * This program is free software; you can redistribute it and/or modify it
      5  * under the terms of version 2 of the GNU General Public License as
      6  * published by the Free Software Foundation.
      7  *
      8  * This program is distributed in the hope that it would be useful, but
      9  * WITHOUT ANY WARRANTY; without even the implied warranty of
     10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
     11  *
     12  * Further, this software is distributed without any warranty that it is
     13  * free of the rightful claim of any third person regarding infringement
     14  * or the like.  Any license provided herein, whether implied or
     15  * otherwise, applies only to this software file.  Patent licenses, if
     16  * any, provided herein do not apply to combinations of this program with
     17  * other software, or any other product whatsoever.
     18  *
     19  * You should have received a copy of the GNU General Public License along
     20  * with this program; if not, write the Free Software Foundation, Inc.,
     21  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
     22  *
     23  * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
     24  * Mountain View, CA  94043, or:
     25  *
     26  * http://www.sgi.com
     27  *
     28  * For further information regarding this notice, see:
     29  *
     30  * http://oss.sgi.com/projects/GenInfo/NoticeExplan/
     31  *
     32  * Changelog:
     33  *
     34  *	Added timer options: William Jay Huie, IBM
     35  *	01/27/03 - Added: Manoj Iyer, manjo (at) mail.utexas.edu
     36  *			   - option '-p' (pretty printing)i to enabled formatted printing
     37  *			     of results.
     38  *
     39  *	01/27/03 - Added: Manoj Iyer, manjo (at) mail.utexas.edu
     40  *			   - added code to print system information
     41  *
     42  *	01/28/03 - Added: Manoj Iyer, manjo (at) mail.utexas.edu
     43  *			   - added code to print test exit value.
     44  *
     45  *	01/29/03 - Added: Manoj Iyer, manjo (at) mail.utexas.edu
     46  *			   - added code supresses test start and test end tags.
     47  *
     48  * 	07/22/07 - Added: Ricardo Salveti de Araujo, rsalveti (at) linux.vnet.ibm.com
     49  *			   - added option to create a command file with all failed tests.
     50  *
     51  */
     52 /* $Id: ltp-pan.c,v 1.4 2009/10/15 18:45:55 yaberauneya Exp $ */
     53 
     54 #include <sys/param.h>
     55 #include <sys/stat.h>
     56 #include <sys/times.h>
     57 #include <sys/types.h>
     58 #include <sys/wait.h>
     59 #include <sys/utsname.h>
     60 #include <errno.h>
     61 #include <err.h>
     62 #include <limits.h>
     63 #include <signal.h>
     64 #include <stdlib.h>
     65 #include <string.h>
     66 #include <time.h>
     67 
     68 #include "splitstr.h"
     69 #include "zoolib.h"
     70 #include "tst_res_flags.h"
     71 
     72 /* One entry in the command line collection.  */
     73 struct coll_entry {
     74 	char *name;		/* tag name */
     75 	char *cmdline;		/* command line */
     76 	char *pcnt_f;		/* location of %f in the command line args, flag */
     77 	struct coll_entry *next;
     78 };
     79 
     80 struct collection {
     81 	int cnt;
     82 	struct coll_entry **ary;
     83 };
     84 
     85 struct tag_pgrp {
     86 	int pgrp;
     87 	int stopping;
     88 	time_t mystime;
     89 	struct coll_entry *cmd;
     90 	char output[PATH_MAX];
     91 };
     92 
     93 struct orphan_pgrp {
     94 	int pgrp;
     95 	struct orphan_pgrp *next;
     96 };
     97 
     98 static pid_t run_child(struct coll_entry *colle, struct tag_pgrp *active,
     99 		       int quiet_mode, int *failcnt, int fmt_print,
    100 		       FILE * logfile);
    101 static char *slurp(char *file);
    102 static struct collection *get_collection(char *file, int optind, int argc,
    103 					 char **argv);
    104 static void pids_running(struct tag_pgrp *running, int keep_active);
    105 static int check_pids(struct tag_pgrp *running, int *num_active,
    106 		      int keep_active, FILE * logfile, FILE * failcmdfile,
    107 		      FILE *tconfcmdfile, struct orphan_pgrp *orphans,
    108 		      int fmt_print, int *failcnt, int *tconfcnt,
    109 		      int quiet_mode);
    110 static void propagate_signal(struct tag_pgrp *running, int keep_active,
    111 			     struct orphan_pgrp *orphans);
    112 static void dump_coll(struct collection *coll);
    113 static char *subst_pcnt_f(struct coll_entry *colle);
    114 static void mark_orphan(struct orphan_pgrp *orphans, pid_t cpid);
    115 static void orphans_running(struct orphan_pgrp *orphans);
    116 static void check_orphans(struct orphan_pgrp *orphans, int sig);
    117 
    118 static void copy_buffered_output(struct tag_pgrp *running);
    119 static void write_test_start(struct tag_pgrp *running);
    120 static void write_test_end(struct tag_pgrp *running, const char *init_status,
    121 			   time_t exit_time, char *term_type, int stat_loc,
    122 			   int term_id, struct tms *tms1, struct tms *tms2);
    123 
    124 //wjh
    125 static char PAN_STOP_FILE[] = "PAN_STOP_FILE";
    126 
    127 static char *panname = NULL;
    128 static char *test_out_dir = NULL;	/* dir to buffer output to */
    129 zoo_t zoofile;
    130 static char *reporttype = NULL;
    131 
    132 /* zoolib */
    133 int rec_signal;			/* received signal */
    134 int send_signal;		/* signal to send */
    135 
    136 /* Debug Bits */
    137 int Debug = 0;
    138 #define Dbuffile	0x000400	/* buffer file use */
    139 #define	Dsetup		0x000200	/* one-time set-up */
    140 #define	Dshutdown	0x000100	/* killed by signal */
    141 #define	Dexit		0x000020	/* exit status */
    142 #define	Drunning	0x000010	/* current pids running */
    143 #define	Dstartup	0x000004	/* started command */
    144 #define	Dstart		0x000002	/* started command */
    145 #define Dwait		0x000001	/* wait interrupted */
    146 
    147 int main(int argc, char **argv)
    148 {
    149 	extern char *optarg;
    150 	extern int optind;
    151 	char *zooname = NULL;	/* name of the zoo file to use */
    152 	char *filename = "/dev/null";	/* filename to read test tags from */
    153 	char *logfilename = NULL;
    154 	char *failcmdfilename = NULL;
    155 	char *tconfcmdfilename = NULL;
    156 	char *outputfilename = NULL;
    157 	struct collection *coll = NULL;
    158 	struct tag_pgrp *running;
    159 	struct orphan_pgrp *orphans, *orph;
    160 	struct utsname unamebuf;
    161 	FILE *logfile = NULL;
    162 	FILE *failcmdfile = NULL;
    163 	FILE *tconfcmdfile = NULL;
    164 	int keep_active = 1;
    165 	int num_active = 0;
    166 	int failcnt = 0;  /* count of total testcases that failed. */
    167 	int tconfcnt = 0; /* count of total testcases that return TCONF */
    168 	int err, i;
    169 	int starts = -1;
    170 	int timed = 0;
    171 	int run_time = -1;
    172 	char modifier = 'm';
    173 	int ret = 0;
    174 	int stop;
    175 	int go_idle;
    176 	int has_brakes = 0;	/* stop everything if a test case fails */
    177 	int sequential = 0;	/* run tests sequentially */
    178 	int fork_in_road = 0;
    179 	int exit_stat;
    180 	int track_exit_stats = 0;	/* exit non-zero if any test exits non-zero */
    181 	int fmt_print = 0;	/* enables formatted printing of logfiles. */
    182 	int quiet_mode = 0;	/* supresses test start and test end tags. */
    183 	int c;
    184 	pid_t cpid;
    185 	struct sigaction sa;
    186 
    187 	while ((c =
    188 		getopt(argc, argv, "AO:Sa:C:T:d:ef:hl:n:o:pqr:s:t:x:y"))
    189 		       != -1) {
    190 		switch (c) {
    191 		case 'A':	/* all-stop flag */
    192 			has_brakes = 1;
    193 			track_exit_stats = 1;
    194 			break;
    195 		case 'O':	/* output buffering directory */
    196 			test_out_dir = strdup(optarg);
    197 			break;
    198 		case 'S':	/* run tests sequentially */
    199 			sequential = 1;
    200 			break;
    201 		case 'a':	/* name of the zoo file to use */
    202 			zooname = strdup(optarg);
    203 			break;
    204 		case 'C':	/* name of the file where all failed commands will be */
    205 			failcmdfilename = strdup(optarg);
    206 			break;
    207 		case 'T':
    208 			/*
    209 			 * test cases that are not fully tested will be recorded
    210 			 * in this file
    211 			 */
    212 			tconfcmdfilename = strdup(optarg);
    213 			break;
    214 		case 'd':	/* debug options */
    215 			sscanf(optarg, "%i", &Debug);
    216 			break;
    217 		case 'e':	/* exit non-zero if any test exists non-zero */
    218 			track_exit_stats = 1;
    219 			break;
    220 		case 'f':	/* filename to read test tags from */
    221 			filename = strdup(optarg);
    222 			break;
    223 		case 'h':	/* help */
    224 			fprintf(stdout,
    225 				"Usage: pan -n name [ -SyAehpq ] [ -s starts ]"
    226 				" [-t time[s|m|h|d] [ -x nactive ] [ -l logfile ]\n\t"
    227 				"[ -a active-file ] [ -f command-file ] "
    228 				"[ -C fail-command-file ] "
    229 				"[ -d debug-level ]\n\t[-o output-file] "
    230 				"[-O output-buffer-directory] [cmd]\n");
    231 			exit(0);
    232 		case 'l':	/* log file */
    233 			logfilename = strdup(optarg);
    234 			break;
    235 		case 'n':	/* tag given to pan */
    236 			panname = strdup(optarg);
    237 			break;
    238 		case 'o':	/* send test output here */
    239 			outputfilename = strdup(optarg);
    240 			break;
    241 		case 'p':	/* formatted printing. */
    242 			fmt_print = 1;
    243 			break;
    244 		case 'q':	/* supress test start and test end messages */
    245 			quiet_mode = 1;
    246 			break;
    247 		case 'r':	/* reporting type: none, rts */
    248 			reporttype = strdup(optarg);
    249 			break;
    250 		case 's':	/* number of tags to run */
    251 			starts = atoi(optarg);
    252 			break;
    253 		case 't':	/* run_time to run */
    254 			ret = sscanf(optarg, "%d%c", &run_time, &modifier);
    255 			if (ret == 0) {
    256 				fprintf(stderr,
    257 					"Need proper time input: ####x where"
    258 					"x is one of s,m,h,d\n");
    259 				break;
    260 			} else if (ret == 1) {
    261 				fprintf(stderr, "Only got a time value of %d "
    262 					"modifiers need to come immediately after #"
    263 					" assuming %c\n", run_time, modifier);
    264 			} else {
    265 				switch (modifier) {
    266 				case 's':
    267 					run_time = run_time;
    268 					break;
    269 				case 'm':
    270 					run_time = run_time * 60;
    271 					break;
    272 				case 'h':
    273 					run_time = run_time * 60 * 60;
    274 					break;
    275 				case 'd':
    276 					run_time = run_time * 60 * 60 * 24;
    277 					break;
    278 				default:
    279 					fprintf(stderr,
    280 						"Invalid time modifier, try: s|h|m|d\n");
    281 					exit(-1);
    282 				}
    283 				if (!quiet_mode)
    284 					printf("PAN will run for %d seconds\n",
    285 					       run_time);
    286 			}
    287 			timed = 1;	//-t implies run as many starts as possible, by default
    288 			break;
    289 		case 'x':	/* number of tags to keep running */
    290 			keep_active = atoi(optarg);
    291 			break;
    292 		case 'y':	/* restart on failure or signal */
    293 			fork_in_road = 1;
    294 			break;
    295 		}
    296 	}
    297 
    298 	if (panname == NULL) {
    299 		fprintf(stderr, "pan: Must supply -n\n");
    300 		exit(1);
    301 	}
    302 	if (zooname == NULL) {
    303 		zooname = zoo_getname();
    304 		if (zooname == NULL) {
    305 			fprintf(stderr,
    306 				"pan(%s): Must supply -a or set ZOO env variable\n",
    307 				panname);
    308 			exit(1);
    309 		}
    310 	}
    311 	if (reporttype) {
    312 		/* make sure we understand the report type */
    313 		if (strcasecmp(reporttype, "rts")
    314 		    && strcasecmp(reporttype, "none")
    315 		    /* && strcasecmp(reporttype, "xml") */
    316 		    )
    317 			reporttype = "rts";
    318 	} else {
    319 		/* set the default */
    320 		reporttype = "rts";
    321 	}
    322 
    323 	if (logfilename != NULL) {
    324 		time_t startup;
    325 		char *s;
    326 
    327 		if (!strcmp(logfilename, "-")) {
    328 			logfile = stdout;
    329 		} else {
    330 			if ((logfile = fopen(logfilename, "a+")) == NULL) {
    331 				fprintf(stderr,
    332 					"pan(%s): Error %s (%d) opening log file '%s'\n",
    333 					panname, strerror(errno), errno,
    334 					logfilename);
    335 				exit(1);
    336 			}
    337 		}
    338 
    339 		time(&startup);
    340 		s = ctime(&startup);
    341 		*(s + strlen(s) - 1) = '\0';
    342 		if (!fmt_print)
    343 			fprintf(logfile, "startup='%s'\n", s);
    344 		else {
    345 			fprintf(logfile, "Test Start Time: %s\n", s);
    346 			fprintf(logfile,
    347 				"-----------------------------------------\n");
    348 			fprintf(logfile, "%-30.20s %-10.10s %-10.10s\n",
    349 				"Testcase", "Result", "Exit Value");
    350 			fprintf(logfile, "%-30.20s %-10.10s %-10.10s\n",
    351 				"--------", "------", "------------");
    352 		}
    353 		fflush(logfile);
    354 	}
    355 
    356 	coll = get_collection(filename, optind, argc, argv);
    357 	if (!coll)
    358 		exit(1);
    359 	if (coll->cnt == 0) {
    360 		fprintf(stderr,
    361 			"pan(%s): Must supply a file collection or a command\n",
    362 			panname);
    363 		exit(1);
    364 	}
    365 
    366 	if (Debug & Dsetup)
    367 		dump_coll(coll);
    368 
    369 	/* a place to store the pgrps we're watching */
    370 	running =
    371 		malloc((keep_active + 1) *
    372 			sizeof(struct tag_pgrp));
    373 	if (running == NULL) {
    374 		fprintf(stderr, "pan(%s): Failed to allocate memory: %s\n",
    375 			panname, strerror(errno));
    376 		exit(2);
    377 	}
    378 	memset(running, 0, keep_active * sizeof(struct tag_pgrp));
    379 	running[keep_active].pgrp = -1;	/* end sentinel */
    380 
    381 	/* a head to the orphaned pgrp list */
    382 	orphans = malloc(sizeof(struct orphan_pgrp));
    383 	memset(orphans, 0, sizeof(struct orphan_pgrp));
    384 
    385 	srand48(time(NULL) ^ (getpid() + (getpid() << 15)));
    386 
    387 	/* Supply a default for starts.  If we are in sequential mode, use
    388 	 * the number of commands available; otherwise 1.
    389 	 */
    390 	if (timed == 1 && starts == -1) {	/* timed, infinite by default */
    391 		starts = -1;
    392 	} else if (starts == -1) {
    393 		if (sequential) {
    394 			starts = coll->cnt;
    395 		} else {
    396 			starts = 1;
    397 		}
    398 	} else if (starts == 0) {	/* if the user specified infinite, set it */
    399 		starts = -1;
    400 	} else {		/* else, make sure we are starting at least keep_active processes */
    401 		if (starts < keep_active)
    402 			starts = keep_active;
    403 	}
    404 
    405 	/* if we're buffering output, but we're only running on process at a time,
    406 	 * then essentially "turn off buffering"
    407 	 */
    408 	if (test_out_dir && (keep_active == 1)) {
    409 		free(test_out_dir);
    410 		test_out_dir = NULL;
    411 	}
    412 
    413 	if (test_out_dir) {
    414 		struct stat sbuf;
    415 
    416 		if (stat(test_out_dir, &sbuf) < 0) {
    417 			fprintf(stderr,
    418 				"pan(%s): stat of -O arg '%s' failed.  errno: %d  %s\n",
    419 				panname, test_out_dir, errno, strerror(errno));
    420 			exit(1);
    421 		}
    422 		if (!S_ISDIR(sbuf.st_mode)) {
    423 			fprintf(stderr,
    424 				"pan(%s): -O arg '%s' must be a directory.\n",
    425 				panname, test_out_dir);
    426 			exit(1);
    427 		}
    428 		if (access(test_out_dir, W_OK | R_OK | X_OK) < 0) {
    429 			fprintf(stderr,
    430 				"pan(%s): permission denied on -O arg '%s'.  errno: %d  %s\n",
    431 				panname, test_out_dir, errno, strerror(errno));
    432 			exit(1);
    433 		}
    434 	}
    435 
    436 	if (outputfilename) {
    437 		if (!freopen(outputfilename, "a+", stdout)) {
    438 			fprintf(stderr,
    439 				"pan(%s): Error %s (%d) opening output file '%s'\n",
    440 				panname, strerror(errno), errno,
    441 				outputfilename);
    442 			exit(1);
    443 		}
    444 	}
    445 
    446 	if (failcmdfilename) {
    447 		if (!(failcmdfile = fopen(failcmdfilename, "a+"))) {
    448 			fprintf(stderr,
    449 				"pan(%s): Error %s (%d) opening fail cmd file '%s'\n",
    450 				panname, strerror(errno), errno,
    451 				failcmdfilename);
    452 			exit(1);
    453 		}
    454 	}
    455 
    456 	if (tconfcmdfilename) {
    457 		tconfcmdfile = fopen(tconfcmdfilename, "a+");
    458 		if (!tconfcmdfile) {
    459 			fprintf(stderr, "pan(%s): Error %s (%d) opening "
    460 				"tconf cmd file '%s'\n", panname,
    461 				strerror(errno), errno, tconfcmdfilename);
    462 			exit(1);
    463 		}
    464 	}
    465 
    466 	if ((zoofile = zoo_open(zooname)) == NULL) {
    467 		fprintf(stderr, "pan(%s): %s\n", panname, zoo_error);
    468 		exit(1);
    469 	}
    470 	if (zoo_mark_args(zoofile, getpid(), panname, argc, argv)) {
    471 		fprintf(stderr, "pan(%s): %s\n", panname, zoo_error);
    472 		exit(1);
    473 	}
    474 
    475 	/* Allocate N spaces for max-arg commands.
    476 	 * this is an "active file cleanliness" thing
    477 	 */
    478 	{
    479 		for (c = 0; c < keep_active; c++) {
    480 			if (zoo_mark_cmdline(zoofile, c, panname, "")) {
    481 				fprintf(stderr, "pan(%s): %s\n", panname,
    482 					zoo_error);
    483 				exit(1);
    484 			}
    485 		}
    486 		for (c = 0; c < keep_active; c++) {
    487 			if (zoo_clear(zoofile, c)) {
    488 				fprintf(stderr, "pan(%s): %s\n", panname,
    489 					zoo_error);
    490 				exit(1);
    491 			}
    492 		}
    493 	}
    494 
    495 	rec_signal = send_signal = 0;
    496 	if (run_time != -1) {
    497 		alarm(run_time);
    498 	}
    499 
    500 	sigemptyset(&sa.sa_mask);
    501 	sa.sa_flags = 0;
    502 	sa.sa_handler = wait_handler;
    503 
    504 	sigaction(SIGALRM, &sa, NULL);
    505 	sigaction(SIGINT, &sa, NULL);
    506 	sigaction(SIGTERM, &sa, NULL);
    507 	sigaction(SIGHUP, &sa, NULL);
    508 	sigaction(SIGUSR1, &sa, NULL);	/* ignore fork_in_road */
    509 	sigaction(SIGUSR2, &sa, NULL);	/* stop the scheduler */
    510 
    511 	c = 0;			/* in this loop, c is the command index */
    512 	stop = 0;
    513 	exit_stat = 0;
    514 	go_idle = 0;
    515 	while (1) {
    516 
    517 		while ((num_active < keep_active) && (starts != 0)) {
    518 			if (stop || rec_signal || go_idle)
    519 				break;
    520 
    521 			if (!sequential)
    522 				c = lrand48() % coll->cnt;
    523 
    524 			/* find a slot for the child */
    525 			for (i = 0; i < keep_active; ++i) {
    526 				if (running[i].pgrp == 0)
    527 					break;
    528 			}
    529 			if (i == keep_active) {
    530 				fprintf(stderr,
    531 					"pan(%s): Aborting: i == keep_active = %d\n",
    532 					panname, i);
    533 				wait_handler(SIGINT);
    534 				exit_stat++;
    535 				break;
    536 			}
    537 
    538 			cpid =
    539 			    run_child(coll->ary[c], running + i, quiet_mode,
    540 				      &failcnt, fmt_print, logfile);
    541 			if (cpid != -1)
    542 				++num_active;
    543 			if ((cpid != -1 || sequential) && starts > 0)
    544 				--starts;
    545 
    546 			if (sequential)
    547 				if (++c >= coll->cnt)
    548 					c = 0;
    549 
    550 		}		/* while ((num_active < keep_active) && (starts != 0)) */
    551 
    552 		if (starts == 0) {
    553 			if (!quiet_mode)
    554 				printf("incrementing stop\n");
    555 			++stop;
    556 		} else if (starts == -1)	//wjh
    557 		{
    558 			FILE *f = (FILE *) - 1;
    559 			if ((f = fopen(PAN_STOP_FILE, "r")) != 0) {
    560 				printf("Got %s Stopping!\n", PAN_STOP_FILE);
    561 				fclose(f);
    562 				unlink(PAN_STOP_FILE);
    563 				stop++;
    564 			}
    565 		}
    566 
    567 		if (rec_signal) {
    568 			/* propagate everything except sigusr2 */
    569 
    570 			if (rec_signal == SIGUSR2) {
    571 				if (fork_in_road)
    572 					++go_idle;
    573 				else
    574 					++stop;
    575 				rec_signal = send_signal = 0;
    576 			} else {
    577 				if (rec_signal == SIGUSR1)
    578 					fork_in_road = 0;
    579 				propagate_signal(running, keep_active, orphans);
    580 				if (fork_in_road)
    581 					++go_idle;
    582 				else
    583 					++stop;
    584 			}
    585 		}
    586 
    587 		err = check_pids(running, &num_active, keep_active, logfile,
    588 				 failcmdfile, tconfcmdfile, orphans, fmt_print,
    589 				 &failcnt, &tconfcnt, quiet_mode);
    590 		if (Debug & Drunning) {
    591 			pids_running(running, keep_active);
    592 			orphans_running(orphans);
    593 		}
    594 		if (err) {
    595 			if (fork_in_road)
    596 				++go_idle;
    597 			if (track_exit_stats)
    598 				exit_stat++;
    599 			if (has_brakes) {
    600 				fprintf(stderr, "pan(%s): All stop!%s\n",
    601 					panname, go_idle ? " (idling)" : "");
    602 				wait_handler(SIGINT);
    603 			}
    604 		}
    605 
    606 		if (stop && (num_active == 0))
    607 			break;
    608 
    609 		if (go_idle && (num_active == 0)) {
    610 			go_idle = 0;	/* It is idle, now resume scheduling. */
    611 			wait_handler(0);	/* Reset the signal ratchet. */
    612 		}
    613 	}
    614 
    615 	/* Wait for orphaned pgrps */
    616 	while (1) {
    617 		for (orph = orphans; orph != NULL; orph = orph->next) {
    618 			if (orph->pgrp == 0)
    619 				continue;
    620 			/* Yes, we have orphaned pgrps */
    621 			sleep(5);
    622 			if (!rec_signal) {
    623 				/* force an artificial signal, move us
    624 				 * through the signal ratchet.
    625 				 */
    626 				wait_handler(SIGINT);
    627 			}
    628 			propagate_signal(running, keep_active, orphans);
    629 			if (Debug & Drunning)
    630 				orphans_running(orphans);
    631 			break;
    632 		}
    633 		if (orph == NULL)
    634 			break;
    635 	}
    636 
    637 	if (zoo_clear(zoofile, getpid())) {
    638 		fprintf(stderr, "pan(%s): %s\n", panname, zoo_error);
    639 		++exit_stat;
    640 	}
    641 	fclose(zoofile);
    642 	if (logfile && fmt_print) {
    643 		if (uname(&unamebuf) == -1)
    644 			fprintf(stderr, "ERROR: uname(): %s\n",
    645 				strerror(errno));
    646 		fprintf(logfile,
    647 			"\n-----------------------------------------------\n");
    648 		fprintf(logfile, "Total Tests: %d\n", coll->cnt);
    649 		fprintf(logfile, "Total Skipped Tests: %d\n", tconfcnt);
    650 		fprintf(logfile, "Total Failures: %d\n", failcnt);
    651 		fprintf(logfile, "Kernel Version: %s\n", unamebuf.release);
    652 		fprintf(logfile, "Machine Architecture: %s\n",
    653 			unamebuf.machine);
    654 		fprintf(logfile, "Hostname: %s\n\n", unamebuf.nodename);
    655 	}
    656 	if (logfile && (logfile != stdout))
    657 		fclose(logfile);
    658 
    659 	if (failcmdfile)
    660 		fclose(failcmdfile);
    661 
    662 	if (tconfcmdfile)
    663 		fclose(tconfcmdfile);
    664 	exit(exit_stat);
    665 }
    666 
    667 static void
    668 propagate_signal(struct tag_pgrp *running, int keep_active,
    669 		 struct orphan_pgrp *orphans)
    670 {
    671 	int i;
    672 
    673 	if (Debug & Dshutdown)
    674 		fprintf(stderr, "pan was signaled with sig %d...\n",
    675 			rec_signal);
    676 
    677 	if (rec_signal == SIGALRM) {
    678 		printf("PAN stop Alarm was received\n");
    679 		rec_signal = SIGTERM;
    680 	}
    681 
    682 	for (i = 0; i < keep_active; ++i) {
    683 		if (running[i].pgrp == 0)
    684 			continue;
    685 
    686 		if (Debug & Dshutdown)
    687 			fprintf(stderr, "  propagating sig %d to %d\n",
    688 				send_signal, -running[i].pgrp);
    689 		if (kill(-running[i].pgrp, send_signal) != 0) {
    690 			fprintf(stderr,
    691 				"pan(%s): kill(%d,%d) failed on tag (%s).  errno:%d  %s\n",
    692 				panname, -running[i].pgrp, send_signal,
    693 				running[i].cmd->name, errno, strerror(errno));
    694 		}
    695 		running[i].stopping = 1;
    696 	}
    697 
    698 	check_orphans(orphans, send_signal);
    699 
    700 	rec_signal = send_signal = 0;
    701 }
    702 
    703 static int
    704 check_pids(struct tag_pgrp *running, int *num_active, int keep_active,
    705 	   FILE *logfile, FILE *failcmdfile, FILE *tconfcmdfile,
    706 	   struct orphan_pgrp *orphans, int fmt_print, int *failcnt,
    707 	   int *tconfcnt, int quiet_mode)
    708 {
    709 	int w;
    710 	pid_t cpid;
    711 	int stat_loc;
    712 	int ret = 0;
    713 	int i;
    714 	time_t t;
    715 	char *status;
    716 	char *result_str;
    717 	int signaled = 0;
    718 	struct tms tms1, tms2;
    719 	clock_t tck;
    720 
    721 	check_orphans(orphans, 0);
    722 
    723 	tck = times(&tms1);
    724 	if (tck == -1) {
    725 		fprintf(stderr, "pan(%s): times(&tms1) failed.  errno:%d  %s\n",
    726 			panname, errno, strerror(errno));
    727 	}
    728 	cpid = wait(&stat_loc);
    729 	tck = times(&tms2);
    730 	if (tck == -1) {
    731 		fprintf(stderr, "pan(%s): times(&tms2) failed.  errno:%d  %s\n",
    732 			panname, errno, strerror(errno));
    733 	}
    734 
    735 	if (cpid < 0) {
    736 		if (errno == EINTR) {
    737 			if (Debug)
    738 				fprintf(stderr, "pan(%s): wait() interrupted\n",
    739 					panname);
    740 		} else if (errno != ECHILD) {
    741 			fprintf(stderr,
    742 				"pan(%s): wait() failed.  errno:%d  %s\n",
    743 				panname, errno, strerror(errno));
    744 		}
    745 	} else if (cpid > 0) {
    746 
    747 		if (WIFSIGNALED(stat_loc)) {
    748 			w = WTERMSIG(stat_loc);
    749 			status = "signaled";
    750 			if (Debug & Dexit)
    751 				fprintf(stderr,
    752 					"child %d terminated with signal %d\n",
    753 					cpid, w);
    754 			--*num_active;
    755 			signaled = 1;
    756 		} else if (WIFEXITED(stat_loc)) {
    757 			w = WEXITSTATUS(stat_loc);
    758 			status = "exited";
    759 			if (Debug & Dexit)
    760 				fprintf(stderr,
    761 					"child %d exited with status %d\n",
    762 					cpid, w);
    763 			--*num_active;
    764 			if (w != 0 && w != TCONF)
    765 				ret++;
    766 		} else if (WIFSTOPPED(stat_loc)) {	/* should never happen */
    767 			w = WSTOPSIG(stat_loc);
    768 			status = "stopped";
    769 			ret++;
    770 		} else {	/* should never happen */
    771 			w = 0;
    772 			status = "unknown";
    773 			ret++;
    774 		}
    775 
    776 		for (i = 0; i < keep_active; ++i) {
    777 			if (running[i].pgrp == cpid) {
    778 				if ((w == 130) && running[i].stopping &&
    779 				    (strcmp(status, "exited") == 0)) {
    780 					/* The child received sigint, but
    781 					 * did not trap for it?  Compensate
    782 					 * for it here.
    783 					 */
    784 					w = 0;
    785 					ret--;	/* undo */
    786 					if (Debug & Drunning)
    787 						fprintf(stderr,
    788 							"pan(%s): tag=%s exited 130, known to be signaled; will give it an exit 0.\n",
    789 							panname,
    790 							running[i].cmd->name);
    791 				}
    792 				time(&t);
    793 				if (logfile != NULL) {
    794 					if (!fmt_print)
    795 						fprintf(logfile,
    796 							"tag=%s stime=%d dur=%d exit=%s stat=%d core=%s cu=%d cs=%d\n",
    797 							running[i].cmd->name,
    798 							(int)(running[i].
    799 							      mystime),
    800 							(int)(t -
    801 							      running[i].
    802 							      mystime), status,
    803 							w,
    804 							(stat_loc & 0200) ?
    805 							"yes" : "no",
    806 							(int)(tms2.tms_cutime -
    807 							      tms1.tms_cutime),
    808 							(int)(tms2.tms_cstime -
    809 							      tms1.tms_cstime));
    810 					else {
    811 						if (strcmp(status, "exited") ==
    812 						    0 && w == TCONF) {
    813 							++*tconfcnt;
    814 							result_str = "CONF";
    815 						} else if (w != 0) {
    816 							++*failcnt;
    817 							result_str = "FAIL";
    818 						} else {
    819 							result_str = "PASS";
    820 						}
    821 
    822 						fprintf(logfile,
    823 							"%-30.30s %-10.10s %-5d\n",
    824 							running[i].cmd->name,
    825 							result_str,
    826 							w);
    827 					}
    828 
    829 					fflush(logfile);
    830 				}
    831 
    832 				if (w != 0) {
    833 					if (tconfcmdfile != NULL &&
    834 					    w == TCONF) {
    835 						fprintf(tconfcmdfile, "%s %s\n",
    836 						running[i].cmd->name,
    837 						running[i].cmd->cmdline);
    838 					} else if (failcmdfile != NULL) {
    839 						fprintf(failcmdfile, "%s %s\n",
    840 						running[i].cmd->name,
    841 						running[i].cmd->cmdline);
    842 					}
    843 				}
    844 
    845 				if (running[i].stopping)
    846 					status = "driver_interrupt";
    847 
    848 				if (test_out_dir) {
    849 					if (!quiet_mode)
    850 						write_test_start(running + i);
    851 					copy_buffered_output(running + i);
    852 					unlink(running[i].output);
    853 				}
    854 				if (!quiet_mode)
    855 					write_test_end(running + i, "ok", t,
    856 						       status, stat_loc, w,
    857 						       &tms1, &tms2);
    858 
    859 				/* If signaled and we weren't expecting
    860 				 * this to be stopped then the proc
    861 				 * had a problem.
    862 				 */
    863 				if (signaled && !running[i].stopping)
    864 					ret++;
    865 
    866 				running[i].pgrp = 0;
    867 				if (zoo_clear(zoofile, cpid)) {
    868 					fprintf(stderr, "pan(%s): %s\n",
    869 						panname, zoo_error);
    870 					exit(1);
    871 				}
    872 
    873 				/* Check for orphaned pgrps */
    874 				if ((kill(-cpid, 0) == 0) || (errno == EPERM)) {
    875 					if (zoo_mark_cmdline
    876 					    (zoofile, cpid, "panorphan",
    877 					     running[i].cmd->cmdline)) {
    878 						fprintf(stderr, "pan(%s): %s\n",
    879 							panname, zoo_error);
    880 						exit(1);
    881 					}
    882 					mark_orphan(orphans, cpid);
    883 					/* status of kill doesn't matter */
    884 					kill(-cpid, SIGTERM);
    885 				}
    886 
    887 				break;
    888 			}
    889 		}
    890 	}
    891 	return ret;
    892 }
    893 
    894 static pid_t
    895 run_child(struct coll_entry *colle, struct tag_pgrp *active, int quiet_mode,
    896 	  int *failcnt, int fmt_print, FILE * logfile)
    897 {
    898 	ssize_t errlen;
    899 	int cpid;
    900 	int c_stdout = -1;	/* child's stdout, stderr */
    901 	int capturing = 0;	/* output is going to a file instead of stdout */
    902 	char *c_cmdline;
    903 	static long cmdno = 0;
    904 	int errpipe[2];		/* way to communicate to parent that the tag  */
    905 	char errbuf[1024];	/* didn't actually start */
    906 
    907 	/* Try to open the file that will be stdout for the test */
    908 	if (test_out_dir) {
    909 		capturing = 1;
    910 		do {
    911 			sprintf(active->output, "%s/%s.%ld",
    912 				test_out_dir, colle->name, cmdno++);
    913 			c_stdout =
    914 			    open(active->output,
    915 				 O_CREAT | O_RDWR | O_EXCL | O_SYNC, 0666);
    916 		} while (c_stdout < 0 && errno == EEXIST);
    917 		if (c_stdout < 0) {
    918 			fprintf(stderr,
    919 				"pan(%s): open of stdout file failed (tag %s).  errno: %d  %s\n  file: %s\n",
    920 				panname, colle->name, errno, strerror(errno),
    921 				active->output);
    922 			return -1;
    923 		}
    924 	}
    925 
    926 	/* get the tag's command line arguments ready.  subst_pcnt_f() uses a
    927 	 * static counter, that's why we do it here instead of after we fork.
    928 	 */
    929 	if (colle->pcnt_f) {
    930 		c_cmdline = subst_pcnt_f(colle);
    931 	} else {
    932 		c_cmdline = colle->cmdline;
    933 	}
    934 
    935 	if (pipe(errpipe) < 0) {
    936 		fprintf(stderr, "pan(%s): pipe() failed. errno:%d %s\n",
    937 			panname, errno, strerror(errno));
    938 		if (capturing) {
    939 			close(c_stdout);
    940 			unlink(active->output);
    941 		}
    942 		return -1;
    943 	}
    944 
    945 	time(&active->mystime);
    946 	active->cmd = colle;
    947 
    948 	if (!test_out_dir)
    949 		if (!quiet_mode)
    950 			write_test_start(active);
    951 
    952 	fflush(NULL);
    953 
    954 	if ((cpid = fork()) == -1) {
    955 		fprintf(stderr,
    956 			"pan(%s): fork failed (tag %s).  errno:%d  %s\n",
    957 			panname, colle->name, errno, strerror(errno));
    958 		if (capturing) {
    959 			unlink(active->output);
    960 			close(c_stdout);
    961 		}
    962 		close(errpipe[0]);
    963 		close(errpipe[1]);
    964 		return -1;
    965 	} else if (cpid == 0) {
    966 		/* child */
    967 
    968 		fclose(zoofile);
    969 		close(errpipe[0]);
    970 		fcntl(errpipe[1], F_SETFD, 1);	/* close the pipe if we succeed */
    971 		setpgrp();
    972 
    973 		umask(0);
    974 
    975 #define WRITE_OR_DIE(fd, buf, buflen) do {				\
    976 	if (write((fd), (buf), (buflen)) != (buflen)) {			\
    977 		err(1, "failed to write out %zd bytes at line %d",	\
    978 		    buflen, __LINE__);					\
    979 	}								\
    980 } while(0)
    981 
    982 		/* if we're putting output into a buffer file, we need to do the
    983 		 * redirection now.  If we fail
    984 		 */
    985 		if (capturing) {
    986 			if (dup2(c_stdout, fileno(stdout)) == -1) {
    987 				errlen =
    988 				    sprintf(errbuf,
    989 					    "pan(%s): couldn't redirect stdout for tag %s.  errno:%d  %s",
    990 					    panname, colle->name, errno,
    991 					    strerror(errno));
    992 				WRITE_OR_DIE(errpipe[1], &errlen,
    993 					     sizeof(errlen));
    994 				WRITE_OR_DIE(errpipe[1], errbuf, errlen);
    995 				exit(2);
    996 			}
    997 			if (dup2(c_stdout, fileno(stderr)) == -1) {
    998 				errlen =
    999 				    sprintf(errbuf,
   1000 					    "pan(%s): couldn't redirect stderr for tag %s.  errno:%d  %s",
   1001 					    panname, colle->name, errno,
   1002 					    strerror(errno));
   1003 				WRITE_OR_DIE(errpipe[1], &errlen,
   1004 					     sizeof(errlen));
   1005 				WRITE_OR_DIE(errpipe[1], errbuf, errlen);
   1006 				exit(2);
   1007 			}
   1008 		} else {	/* stderr still needs to be redirected */
   1009 			if (dup2(fileno(stdout), fileno(stderr)) == -1) {
   1010 				errlen =
   1011 				    sprintf(errbuf,
   1012 					    "pan(%s): couldn't redirect stderr for tag %s.  errno:%d  %s",
   1013 					    panname, colle->name, errno,
   1014 					    strerror(errno));
   1015 				WRITE_OR_DIE(errpipe[1], &errlen,
   1016 					     sizeof(errlen));
   1017 				WRITE_OR_DIE(errpipe[1], errbuf, errlen);
   1018 				exit(2);
   1019 			}
   1020 		}
   1021 		/* If there are any shell-type characters in the cmdline
   1022 		 * such as '>', '<', '$', '|', etc, then we exec a shell and
   1023 		 * run the cmd under a shell.
   1024 		 *
   1025 		 * Otherwise, break the cmdline at white space and exec the
   1026 		 * cmd directly.
   1027 		 */
   1028 		if (strpbrk(c_cmdline, "\"';|<>$\\")) {
   1029 			execlp("sh", "sh", "-c", c_cmdline, NULL);
   1030 			errlen = sprintf(errbuf,
   1031 					 "pan(%s): execlp of '%s' (tag %s) failed.  errno:%d %s",
   1032 					 panname, c_cmdline, colle->name, errno,
   1033 					 strerror(errno));
   1034 		} else {
   1035 			char **arg_v;
   1036 
   1037 			arg_v = (char **)splitstr(c_cmdline, NULL, NULL);
   1038 
   1039 			execvp(arg_v[0], arg_v);
   1040 			errlen = sprintf(errbuf,
   1041 					 "pan(%s): execvp of '%s' (tag %s) failed.  errno:%d  %s",
   1042 					 panname, arg_v[0], colle->name, errno,
   1043 					 strerror(errno));
   1044 		}
   1045 		WRITE_OR_DIE(errpipe[1], &errlen, sizeof(errlen));
   1046 		WRITE_OR_DIE(errpipe[1], errbuf, errlen);
   1047 		exit(errno);
   1048 	}
   1049 
   1050 	/* parent */
   1051 
   1052 	/* subst_pcnt_f() allocates the command line dynamically
   1053 	 * free the malloc to prevent a memory leak
   1054 	 */
   1055 	if (colle->pcnt_f)
   1056 		free(c_cmdline);
   1057 
   1058 	close(errpipe[1]);
   1059 
   1060 	/* if the child couldn't go through with the exec,
   1061 	 * clean up the mess, note it, and move on
   1062 	 */
   1063 	if (read(errpipe[0], &errlen, sizeof(errlen))) {
   1064 		int status;
   1065 		time_t end_time;
   1066 		int termid;
   1067 		char *termtype;
   1068 		struct tms notime = { 0, 0, 0, 0 };
   1069 
   1070 		if (read(errpipe[0], errbuf, errlen) < 0)
   1071 			fprintf(stderr, "Failed to read from errpipe[0]\n");
   1072 		close(errpipe[0]);
   1073 		errbuf[errlen] = '\0';
   1074 		/* fprintf(stderr, "%s", errbuf); */
   1075 		waitpid(cpid, &status, 0);
   1076 		if (WIFSIGNALED(status)) {
   1077 			termid = WTERMSIG(status);
   1078 			termtype = "signaled";
   1079 		} else if (WIFEXITED(status)) {
   1080 			termid = WEXITSTATUS(status);
   1081 			termtype = "exited";
   1082 		} else if (WIFSTOPPED(status)) {
   1083 			termid = WSTOPSIG(status);
   1084 			termtype = "stopped";
   1085 		} else {
   1086 			termid = 0;
   1087 			termtype = "unknown";
   1088 		}
   1089 		time(&end_time);
   1090 		if (logfile != NULL) {
   1091 			if (!fmt_print) {
   1092 				fprintf(logfile,
   1093 					"tag=%s stime=%d dur=%d exit=%s "
   1094 					"stat=%d core=%s cu=%d cs=%d\n",
   1095 					colle->name, (int)(active->mystime),
   1096 					(int)(end_time - active->mystime),
   1097 					termtype, termid,
   1098 					(status & 0200) ? "yes" : "no", 0, 0);
   1099 			} else {
   1100 				if (termid != 0)
   1101 					++ * failcnt;
   1102 
   1103 				fprintf(logfile, "%-30.30s %-10.10s %-5d\n",
   1104 					colle->name,
   1105 					((termid != 0) ? "FAIL" : "PASS"),
   1106 					termid);
   1107 			}
   1108 			fflush(logfile);
   1109 		}
   1110 
   1111 		if (!quiet_mode) {
   1112 			//write_test_start(active, errbuf);
   1113 			write_test_end(active, errbuf, end_time, termtype,
   1114 				       status, termid, &notime, &notime);
   1115 		}
   1116 		if (capturing) {
   1117 			close(c_stdout);
   1118 			unlink(active->output);
   1119 		}
   1120 		return -1;
   1121 	}
   1122 
   1123 	close(errpipe[0]);
   1124 	if (capturing)
   1125 		close(c_stdout);
   1126 
   1127 	active->pgrp = cpid;
   1128 	active->stopping = 0;
   1129 
   1130 	if (zoo_mark_cmdline(zoofile, cpid, colle->name, colle->cmdline)) {
   1131 		fprintf(stderr, "pan(%s): %s\n", panname, zoo_error);
   1132 		exit(1);
   1133 	}
   1134 
   1135 	if (Debug & Dstartup)
   1136 		fprintf(stderr, "started %s cpid=%d at %s",
   1137 			colle->name, cpid, ctime(&active->mystime));
   1138 
   1139 	if (Debug & Dstart) {
   1140 		fprintf(stderr, "Executing test = %s as %s", colle->name,
   1141 			colle->cmdline);
   1142 		if (capturing)
   1143 			fprintf(stderr, "with output file = %s\n",
   1144 				active->output);
   1145 		else
   1146 			fprintf(stderr, "\n");
   1147 	}
   1148 
   1149 	return cpid;
   1150 }
   1151 
   1152 static char *subst_pcnt_f(struct coll_entry *colle)
   1153 {
   1154 	static int counter = 1;
   1155 	char pid_and_counter[20];
   1156 	char new_cmdline[1024];
   1157 
   1158 	/* if we get called falsely, do the right thing anyway */
   1159 	if (!colle->pcnt_f)
   1160 		return colle->cmdline;
   1161 
   1162 	snprintf(pid_and_counter, 20, "%d_%d", getpid(), counter++);
   1163 	snprintf(new_cmdline, 1024, colle->cmdline, pid_and_counter);
   1164 	return strdup(new_cmdline);
   1165 }
   1166 
   1167 static struct collection *get_collection(char *file, int optind, int argc,
   1168 					 char **argv)
   1169 {
   1170 	char *buf, *a, *b;
   1171 	struct coll_entry *head, *p, *n;
   1172 	struct collection *coll;
   1173 	int i;
   1174 
   1175 	buf = slurp(file);
   1176 	if (!buf)
   1177 		return NULL;
   1178 
   1179 	coll = malloc(sizeof(struct collection));
   1180 	coll->cnt = 0;
   1181 
   1182 	head = p = n = NULL;
   1183 	a = b = buf;
   1184 	while (a) {
   1185 		/* set b to the start of the next line and add a NULL character
   1186 		 * to separate the two lines */
   1187 		if ((b = strchr(a, '\n')) != NULL)
   1188 			*b++ = '\0';
   1189 
   1190 		/* If this is line isn't a comment */
   1191 		if ((*a != '#') && (*a != '\0') && (*a != ' ')) {
   1192 			n = malloc(sizeof(struct coll_entry));
   1193 			if ((n->pcnt_f = strstr(a, "%f"))) {
   1194 				n->pcnt_f[1] = 's';
   1195 			}
   1196 			n->name = strdup(strsep(&a, " \t"));
   1197 			n->cmdline = strdup(a);
   1198 			n->next = NULL;
   1199 
   1200 			if (p) {
   1201 				p->next = n;
   1202 			}
   1203 			if (head == NULL) {
   1204 				head = n;
   1205 			}
   1206 			p = n;
   1207 			coll->cnt++;
   1208 		}
   1209 		a = b;
   1210 	}
   1211 	free(buf);
   1212 
   1213 	/* is there something on the commandline to be counted? */
   1214 	if (optind < argc) {
   1215 		char workstr[1024] = "";
   1216 		int workstr_left = 1023;
   1217 
   1218 		/* fill arg list */
   1219 		for (i = 0; optind < argc; ++optind, ++i) {
   1220 			strncat(workstr, argv[optind], workstr_left);
   1221 			workstr_left = workstr_left - strlen(argv[optind]);
   1222 			strncat(workstr, " ", workstr_left);
   1223 			workstr_left--;
   1224 		}
   1225 
   1226 		n = malloc(sizeof(struct coll_entry));
   1227 		if ((n->pcnt_f = strstr(workstr, "%f"))) {
   1228 			n->pcnt_f[1] = 's';
   1229 		}
   1230 		n->cmdline = strdup(workstr);
   1231 		n->name = "cmdln";
   1232 		n->next = NULL;
   1233 		if (p) {
   1234 			p->next = n;
   1235 		}
   1236 		if (head == NULL) {
   1237 			head = n;
   1238 		}
   1239 		coll->cnt++;
   1240 	}
   1241 
   1242 	/* get an array */
   1243 	coll->ary = malloc(coll->cnt * sizeof(struct coll_entry *));
   1244 
   1245 	/* fill the array */
   1246 	i = 0;
   1247 	n = head;
   1248 	while (n != NULL) {
   1249 		coll->ary[i] = n;
   1250 		n = n->next;
   1251 		++i;
   1252 	}
   1253 	if (i != coll->cnt)
   1254 		fprintf(stderr, "pan(%s): i doesn't match cnt\n", panname);
   1255 
   1256 	return coll;
   1257 }
   1258 
   1259 static char *slurp(char *file)
   1260 {
   1261 	char *buf;
   1262 	int fd;
   1263 	struct stat sbuf;
   1264 
   1265 	if ((fd = open(file, O_RDONLY)) < 0) {
   1266 		fprintf(stderr,
   1267 			"pan(%s): open(%s,O_RDONLY) failed.  errno:%d  %s\n",
   1268 			panname, file, errno, strerror(errno));
   1269 		return NULL;
   1270 	}
   1271 
   1272 	if (fstat(fd, &sbuf) < 0) {
   1273 		fprintf(stderr, "pan(%s): fstat(%s) failed.  errno:%d  %s\n",
   1274 			panname, file, errno, strerror(errno));
   1275 		return NULL;
   1276 	}
   1277 
   1278 	buf = malloc(sbuf.st_size + 1);
   1279 	if (read(fd, buf, sbuf.st_size) != sbuf.st_size) {
   1280 		fprintf(stderr, "pan(%s): slurp failed.  errno:%d  %s\n",
   1281 			panname, errno, strerror(errno));
   1282 		free(buf);
   1283 		return NULL;
   1284 	}
   1285 	buf[sbuf.st_size] = '\0';
   1286 
   1287 	close(fd);
   1288 	return buf;
   1289 }
   1290 
   1291 static void check_orphans(struct orphan_pgrp *orphans, int sig)
   1292 {
   1293 	struct orphan_pgrp *orph;
   1294 
   1295 	for (orph = orphans; orph != NULL; orph = orph->next) {
   1296 		if (orph->pgrp == 0)
   1297 			continue;
   1298 
   1299 		if (Debug & Dshutdown)
   1300 			fprintf(stderr,
   1301 				"  propagating sig %d to orphaned pgrp %d\n",
   1302 				sig, -(orph->pgrp));
   1303 		if (kill(-(orph->pgrp), sig) != 0) {
   1304 			if (errno == ESRCH) {
   1305 				/* This pgrp is now empty */
   1306 				if (zoo_clear(zoofile, orph->pgrp)) {
   1307 					fprintf(stderr, "pan(%s): %s\n",
   1308 						panname, zoo_error);
   1309 				}
   1310 				orph->pgrp = 0;
   1311 			} else {
   1312 				fprintf(stderr,
   1313 					"pan(%s): kill(%d,%d) on orphaned pgrp failed.  errno:%d  %s\n",
   1314 					panname, -(orph->pgrp), sig, errno,
   1315 					strerror(errno));
   1316 			}
   1317 		}
   1318 	}
   1319 }
   1320 
   1321 static void mark_orphan(struct orphan_pgrp *orphans, pid_t cpid)
   1322 {
   1323 	struct orphan_pgrp *orph;
   1324 
   1325 	for (orph = orphans; orph != NULL; orph = orph->next) {
   1326 		if (orph->pgrp == 0)
   1327 			break;
   1328 	}
   1329 	if (orph == NULL) {
   1330 		/* make a new struct */
   1331 		orph = malloc(sizeof(struct orphan_pgrp));
   1332 
   1333 		/* plug in the new struct just after the head */
   1334 		orph->next = orphans->next;
   1335 		orphans->next = orph;
   1336 	}
   1337 	orph->pgrp = cpid;
   1338 }
   1339 
   1340 static void copy_buffered_output(struct tag_pgrp *running)
   1341 {
   1342 	char *tag_output;
   1343 
   1344 	tag_output = slurp(running->output);
   1345 	if (tag_output) {
   1346 		printf("%s", tag_output);
   1347 		/* make sure the output ends with a newline */
   1348 		if (tag_output[strlen(tag_output) - 1] != '\n')
   1349 			printf("\n");
   1350 		fflush(stdout);
   1351 		free(tag_output);
   1352 	}
   1353 }
   1354 
   1355 static void write_test_start(struct tag_pgrp *running)
   1356 {
   1357 	if (!strcmp(reporttype, "rts")) {
   1358 
   1359 		printf
   1360 		    ("%s\ntag=%s stime=%ld\ncmdline=\"%s\"\ncontacts=\"%s\"\nanalysis=%s\n%s\n",
   1361 		     "<<<test_start>>>", running->cmd->name, running->mystime,
   1362 		     running->cmd->cmdline, "", "exit", "<<<test_output>>>");
   1363 	}
   1364 	fflush(stdout);
   1365 }
   1366 
   1367 static void
   1368 write_test_end(struct tag_pgrp *running, const char *init_status,
   1369 	       time_t exit_time, char *term_type, int stat_loc,
   1370 	       int term_id, struct tms *tms1, struct tms *tms2)
   1371 {
   1372 	if (!strcmp(reporttype, "rts")) {
   1373 		printf
   1374 		    ("%s\ninitiation_status=\"%s\"\nduration=%ld termination_type=%s "
   1375 		     "termination_id=%d corefile=%s\ncutime=%d cstime=%d\n%s\n",
   1376 		     "<<<execution_status>>>", init_status,
   1377 		     (long)(exit_time - running->mystime), term_type, term_id,
   1378 		     (stat_loc & 0200) ? "yes" : "no",
   1379 		     (int)(tms2->tms_cutime - tms1->tms_cutime),
   1380 		     (int)(tms2->tms_cstime - tms1->tms_cstime),
   1381 		     "<<<test_end>>>");
   1382 	}
   1383 	fflush(stdout);
   1384 }
   1385 
   1386 /* The functions below are all debugging related */
   1387 
   1388 static void pids_running(struct tag_pgrp *running, int keep_active)
   1389 {
   1390 	int i;
   1391 
   1392 	fprintf(stderr, "pids still running: ");
   1393 	for (i = 0; i < keep_active; ++i) {
   1394 		if (running[i].pgrp != 0)
   1395 			fprintf(stderr, "%d ", running[i].pgrp);
   1396 	}
   1397 	fprintf(stderr, "\n");
   1398 }
   1399 
   1400 static void orphans_running(struct orphan_pgrp *orphans)
   1401 {
   1402 	struct orphan_pgrp *orph;
   1403 
   1404 	fprintf(stderr, "orphans still running: ");
   1405 	for (orph = orphans; orph != NULL; orph = orph->next) {
   1406 		if (orph->pgrp != 0)
   1407 			fprintf(stderr, "%d ", -(orph->pgrp));
   1408 	}
   1409 	fprintf(stderr, "\n");
   1410 }
   1411 
   1412 static void dump_coll(struct collection *coll)
   1413 {
   1414 	int i;
   1415 
   1416 	for (i = 0; i < coll->cnt; ++i) {
   1417 		fprintf(stderr, "coll %d\n", i);
   1418 		fprintf(stderr, "  name=%s cmdline=%s\n", coll->ary[i]->name,
   1419 			coll->ary[i]->cmdline);
   1420 	}
   1421 }
   1422 
   1423 void wait_handler(int sig)
   1424 {
   1425 	static int lastsent = 0;
   1426 
   1427 	if (sig == 0) {
   1428 		lastsent = 0;
   1429 	} else {
   1430 		rec_signal = sig;
   1431 		if (sig == SIGUSR2)
   1432 			return;
   1433 		if (lastsent == 0)
   1434 			send_signal = sig;
   1435 		else if (lastsent == SIGUSR1)
   1436 			send_signal = SIGINT;
   1437 		else if (lastsent == sig)
   1438 			send_signal = SIGTERM;
   1439 		else if (lastsent == SIGTERM)
   1440 			send_signal = SIGHUP;
   1441 		else if (lastsent == SIGHUP)
   1442 			send_signal = SIGKILL;
   1443 		lastsent = send_signal;
   1444 	}
   1445 }
   1446