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