• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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@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@mail.utexas.edu
40  *			   - added code to print system information
41  *
42  *	01/28/03 - Added: Manoj Iyer, manjo@mail.utexas.edu
43  *			   - added code to print test exit value.
44  *
45  *	01/29/03 - Added: Manoj Iyer, manjo@mail.utexas.edu
46  *			   - added code supresses test start and test end tags.
47  *
48  * 	07/22/07 - Added: Ricardo Salveti de Araujo, rsalveti@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 
main(int argc,char ** argv)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
propagate_signal(struct tag_pgrp * running,int keep_active,struct orphan_pgrp * orphans)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
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)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
run_child(struct coll_entry * colle,struct tag_pgrp * active,int quiet_mode,int * failcnt,int fmt_print,FILE * logfile)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 
subst_pcnt_f(struct coll_entry * colle)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 
get_collection(char * file,int optind,int argc,char ** argv)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 
slurp(char * file)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 
check_orphans(struct orphan_pgrp * orphans,int sig)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 
mark_orphan(struct orphan_pgrp * orphans,pid_t cpid)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 
copy_buffered_output(struct tag_pgrp * running)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 
write_test_start(struct tag_pgrp * running)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
write_test_end(struct tag_pgrp * running,const char * init_status,time_t exit_time,char * term_type,int stat_loc,int term_id,struct tms * tms1,struct tms * tms2)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 
pids_running(struct tag_pgrp * running,int keep_active)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 
orphans_running(struct orphan_pgrp * orphans)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 
dump_coll(struct collection * coll)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 
wait_handler(int sig)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