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