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, ¬ime, ¬ime);
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