|
|
1.1 ! root 1: /* ! 2: * Copyright (c) 1988, 1989, 1990 The Regents of the University of California. ! 3: * Copyright (c) 1988, 1989 by Adam de Boor ! 4: * Copyright (c) 1989 by Berkeley Softworks ! 5: * All rights reserved. ! 6: * ! 7: * This code is derived from software contributed to Berkeley by ! 8: * Adam de Boor. ! 9: * ! 10: * Redistribution and use in source and binary forms are permitted ! 11: * provided that: (1) source distributions retain this entire copyright ! 12: * notice and comment, and (2) distributions including binaries display ! 13: * the following acknowledgement: ``This product includes software ! 14: * developed by the University of California, Berkeley and its contributors'' ! 15: * in the documentation or other materials provided with the distribution ! 16: * and in all advertising materials mentioning features or use of this ! 17: * software. Neither the name of the University nor the names of its ! 18: * contributors may be used to endorse or promote products derived ! 19: * from this software without specific prior written permission. ! 20: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ! 21: * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ! 22: * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ! 23: */ ! 24: ! 25: #ifndef lint ! 26: static char sccsid[] = "@(#)job.c 5.13 (Berkeley) 6/1/90"; ! 27: #endif /* not lint */ ! 28: ! 29: /*- ! 30: * job.c -- ! 31: * handle the creation etc. of our child processes. ! 32: * ! 33: * Interface: ! 34: * Job_Make Start the creation of the given target. ! 35: * ! 36: * Job_CatchChildren Check for and handle the termination of any ! 37: * children. This must be called reasonably ! 38: * frequently to keep the whole make going at ! 39: * a decent clip, since job table entries aren't ! 40: * removed until their process is caught this way. ! 41: * Its single argument is TRUE if the function ! 42: * should block waiting for a child to terminate. ! 43: * ! 44: * Job_CatchOutput Print any output our children have produced. ! 45: * Should also be called fairly frequently to ! 46: * keep the user informed of what's going on. ! 47: * If no output is waiting, it will block for ! 48: * a time given by the SEL_* constants, below, ! 49: * or until output is ready. ! 50: * ! 51: * Job_Init Called to intialize this module. in addition, ! 52: * any commands attached to the .BEGIN target ! 53: * are executed before this function returns. ! 54: * Hence, the makefile must have been parsed ! 55: * before this function is called. ! 56: * ! 57: * Job_Full Return TRUE if the job table is filled. ! 58: * ! 59: * Job_Empty Return TRUE if the job table is completely ! 60: * empty. ! 61: * ! 62: * Job_ParseShell Given the line following a .SHELL target, parse ! 63: * the line as a shell specification. Returns ! 64: * FAILURE if the spec was incorrect. ! 65: * ! 66: * Job_End Perform any final processing which needs doing. ! 67: * This includes the execution of any commands ! 68: * which have been/were attached to the .END ! 69: * target. It should only be called when the ! 70: * job table is empty. ! 71: * ! 72: * Job_AbortAll Abort all currently running jobs. It doesn't ! 73: * handle output or do anything for the jobs, ! 74: * just kills them. It should only be called in ! 75: * an emergency, as it were. ! 76: * ! 77: * Job_CheckCommands Verify that the commands for a target are ! 78: * ok. Provide them if necessary and possible. ! 79: * ! 80: * Job_Touch Update a target without really updating it. ! 81: * ! 82: * Job_Wait Wait for all currently-running jobs to finish. ! 83: */ ! 84: ! 85: #include "make.h" ! 86: #include <sys/signal.h> ! 87: #include <sys/stat.h> ! 88: #include <sys/file.h> ! 89: #include <sys/time.h> ! 90: #include <sys/wait.h> ! 91: #include <fcntl.h> ! 92: #include <errno.h> ! 93: #include <stdio.h> ! 94: #include <string.h> ! 95: #include "job.h" ! 96: #include "pathnames.h" ! 97: ! 98: extern int errno; ! 99: ! 100: /* ! 101: * error handling variables ! 102: */ ! 103: int errors = 0; /* number of errors reported */ ! 104: int aborting = 0; /* why is the make aborting? */ ! 105: #define ABORT_ERROR 1 /* Because of an error */ ! 106: #define ABORT_INTERRUPT 2 /* Because it was interrupted */ ! 107: #define ABORT_WAIT 3 /* Waiting for jobs to finish */ ! 108: ! 109: ! 110: /* ! 111: * post-make command processing. The node postCommands is really just the ! 112: * .END target but we keep it around to avoid having to search for it ! 113: * all the time. ! 114: */ ! 115: static GNode *postCommands; /* node containing commands to execute when ! 116: * everything else is done */ ! 117: static int numCommands; /* The number of commands actually printed ! 118: * for a target. Should this number be ! 119: * 0, no shell will be executed. */ ! 120: ! 121: ! 122: /* ! 123: * Return values from JobStart. ! 124: */ ! 125: #define JOB_RUNNING 0 /* Job is running */ ! 126: #define JOB_ERROR 1 /* Error in starting the job */ ! 127: #define JOB_FINISHED 2 /* The job is already finished */ ! 128: #define JOB_STOPPED 3 /* The job is stopped */ ! 129: ! 130: /* ! 131: * tfile is the name of a file into which all shell commands are put. It is ! 132: * used over by removing it before the child shell is executed. The XXXXX in ! 133: * the string are replaced by the pid of the make process in a 5-character ! 134: * field with leading zeroes. ! 135: */ ! 136: static char tfile[] = TMPPAT; ! 137: ! 138: ! 139: /* ! 140: * Descriptions for various shells. ! 141: */ ! 142: static Shell shells[] = { ! 143: /* ! 144: * CSH description. The csh can do echo control by playing ! 145: * with the setting of the 'echo' shell variable. Sadly, ! 146: * however, it is unable to do error control nicely. ! 147: */ ! 148: { ! 149: "csh", ! 150: TRUE, "unset verbose", "set verbose", "unset verbose", 10, ! 151: FALSE, "echo \"%s\"\n", "csh -c \"%s || exit 0\"", ! 152: "v", "e", ! 153: }, ! 154: /* ! 155: * SH description. Echo control is also possible and, under ! 156: * sun UNIX anyway, one can even control error checking. ! 157: */ ! 158: { ! 159: "sh", ! 160: TRUE, "set -", "set -v", "set -", 5, ! 161: FALSE, "echo \"%s\"\n", "sh -c '%s || exit 0'\n", ! 162: "v", "e", ! 163: }, ! 164: /* ! 165: * UNKNOWN. ! 166: */ ! 167: { ! 168: (char *)0, ! 169: FALSE, (char *)0, (char *)0, (char *)0, 0, ! 170: FALSE, (char *)0, (char *)0, ! 171: (char *)0, (char *)0, ! 172: } ! 173: }; ! 174: Shell *commandShell = &shells[DEFSHELL]; /* this is the shell to ! 175: * which we pass all ! 176: * commands in the Makefile. ! 177: * It is set by the ! 178: * Job_ParseShell function */ ! 179: char *shellPath = (char *) NULL, /* full pathname of ! 180: * executable image */ ! 181: *shellName; /* last component of shell */ ! 182: ! 183: ! 184: static int maxJobs; /* The most children we can run at once */ ! 185: static int maxLocal; /* The most local ones we can have */ ! 186: int nJobs; /* The number of children currently running */ ! 187: int nLocal; /* The number of local children */ ! 188: Lst jobs; /* The structures that describe them */ ! 189: Boolean jobFull; /* Flag to tell when the job table is full. It ! 190: * is set TRUE when (1) the total number of ! 191: * running jobs equals the maximum allowed or ! 192: * (2) a job can only be run locally, but ! 193: * nLocal equals maxLocal */ ! 194: #ifndef RMT_WILL_WATCH ! 195: static fd_set outputs; /* Set of descriptors of pipes connected to ! 196: * the output channels of children */ ! 197: #endif ! 198: ! 199: GNode *lastNode; /* The node for which output was most recently ! 200: * produced. */ ! 201: char *targFmt; /* Format string to use to head output from a ! 202: * job when it's not the most-recent job heard ! 203: * from */ ! 204: #define TARG_FMT "--- %s ---\n" /* Default format */ ! 205: ! 206: /* ! 207: * When JobStart attempts to run a job remotely but can't, and isn't allowed ! 208: * to run the job locally, or when Job_CatchChildren detects a job that has ! 209: * been migrated home, the job is placed on the stoppedJobs queue to be run ! 210: * when the next job finishes. ! 211: */ ! 212: Lst stoppedJobs; /* Lst of Job structures describing ! 213: * jobs that were stopped due to concurrency ! 214: * limits or migration home */ ! 215: ! 216: ! 217: # if defined(USE_PGRP) ! 218: #define KILL(pid,sig) killpg((pid),(sig)) ! 219: # else ! 220: #define KILL(pid,sig) kill((pid),(sig)) ! 221: # endif ! 222: ! 223: static void JobRestart(); ! 224: static int JobStart(); ! 225: static void JobInterrupt(); ! 226: ! 227: /*- ! 228: *----------------------------------------------------------------------- ! 229: * JobCondPassSig -- ! 230: * Pass a signal to a job if the job is remote or if USE_PGRP ! 231: * is defined. ! 232: * ! 233: * Results: ! 234: * === 0 ! 235: * ! 236: * Side Effects: ! 237: * None, except the job may bite it. ! 238: * ! 239: *----------------------------------------------------------------------- ! 240: */ ! 241: static int ! 242: JobCondPassSig(job, signo) ! 243: Job *job; /* Job to biff */ ! 244: int signo; /* Signal to send it */ ! 245: { ! 246: #ifdef RMT_WANTS_SIGNALS ! 247: if (job->flags & JOB_REMOTE) { ! 248: (void)Rmt_Signal(job, signo); ! 249: } else { ! 250: KILL(job->pid, signo); ! 251: } ! 252: #else ! 253: /* ! 254: * Assume that sending the signal to job->pid will signal any remote ! 255: * job as well. ! 256: */ ! 257: KILL(job->pid, signo); ! 258: #endif ! 259: return(0); ! 260: } ! 261: ! 262: /*- ! 263: *----------------------------------------------------------------------- ! 264: * JobPassSig -- ! 265: * Pass a signal on to all remote jobs and to all local jobs if ! 266: * USE_PGRP is defined, then die ourselves. ! 267: * ! 268: * Results: ! 269: * None. ! 270: * ! 271: * Side Effects: ! 272: * We die by the same signal. ! 273: * ! 274: *----------------------------------------------------------------------- ! 275: */ ! 276: static void ! 277: JobPassSig(signo) ! 278: int signo; /* The signal number we've received */ ! 279: { ! 280: int mask; ! 281: ! 282: Lst_ForEach(jobs, JobCondPassSig, (ClientData)signo); ! 283: ! 284: /* ! 285: * Deal with proper cleanup based on the signal received. We only run ! 286: * the .INTERRUPT target if the signal was in fact an interrupt. The other ! 287: * three termination signals are more of a "get out *now*" command. ! 288: */ ! 289: if (signo == SIGINT) { ! 290: JobInterrupt(TRUE); ! 291: } else if ((signo == SIGHUP) || (signo == SIGTERM) || (signo == SIGQUIT)) { ! 292: JobInterrupt(FALSE); ! 293: } ! 294: ! 295: /* ! 296: * Leave gracefully if SIGQUIT, rather than core dumping. ! 297: */ ! 298: if (signo == SIGQUIT) { ! 299: Finish(); ! 300: } ! 301: ! 302: /* ! 303: * Send ourselves the signal now we've given the message to everyone else. ! 304: * Note we block everything else possible while we're getting the signal. ! 305: * This ensures that all our jobs get continued when we wake up before ! 306: * we take any other signal. ! 307: */ ! 308: mask = sigblock(0); ! 309: (void) sigsetmask(~0 & ~(1 << (signo-1))); ! 310: signal(signo, SIG_DFL); ! 311: ! 312: kill(getpid(), signo); ! 313: ! 314: Lst_ForEach(jobs, JobCondPassSig, (ClientData)SIGCONT); ! 315: ! 316: sigsetmask(mask); ! 317: signal(signo, JobPassSig); ! 318: ! 319: } ! 320: ! 321: /*- ! 322: *----------------------------------------------------------------------- ! 323: * JobCmpPid -- ! 324: * Compare the pid of the job with the given pid and return 0 if they ! 325: * are equal. This function is called from Job_CatchChildren via ! 326: * Lst_Find to find the job descriptor of the finished job. ! 327: * ! 328: * Results: ! 329: * 0 if the pid's match ! 330: * ! 331: * Side Effects: ! 332: * None ! 333: *----------------------------------------------------------------------- ! 334: */ ! 335: static int ! 336: JobCmpPid (job, pid) ! 337: int pid; /* process id desired */ ! 338: Job *job; /* job to examine */ ! 339: { ! 340: return (pid - job->pid); ! 341: } ! 342: ! 343: /*- ! 344: *----------------------------------------------------------------------- ! 345: * JobPrintCommand -- ! 346: * Put out another command for the given job. If the command starts ! 347: * with an @ or a - we process it specially. In the former case, ! 348: * so long as the -s and -n flags weren't given to make, we stick ! 349: * a shell-specific echoOff command in the script. In the latter, ! 350: * we ignore errors for the entire job, unless the shell has error ! 351: * control. ! 352: * If the command is just "..." we take all future commands for this ! 353: * job to be commands to be executed once the entire graph has been ! 354: * made and return non-zero to signal that the end of the commands ! 355: * was reached. These commands are later attached to the postCommands ! 356: * node and executed by Job_End when all things are done. ! 357: * This function is called from JobStart via Lst_ForEach. ! 358: * ! 359: * Results: ! 360: * Always 0, unless the command was "..." ! 361: * ! 362: * Side Effects: ! 363: * If the command begins with a '-' and the shell has no error control, ! 364: * the JOB_IGNERR flag is set in the job descriptor. ! 365: * If the command is "..." and we're not ignoring such things, ! 366: * tailCmds is set to the successor node of the cmd. ! 367: * numCommands is incremented if the command is actually printed. ! 368: *----------------------------------------------------------------------- ! 369: */ ! 370: static int ! 371: JobPrintCommand (cmd, job) ! 372: char *cmd; /* command string to print */ ! 373: Job *job; /* job for which to print it */ ! 374: { ! 375: Boolean noSpecials; /* true if we shouldn't worry about ! 376: * inserting special commands into ! 377: * the input stream. */ ! 378: Boolean shutUp = FALSE; /* true if we put a no echo command ! 379: * into the command file */ ! 380: Boolean errOff = FALSE; /* true if we turned error checking ! 381: * off before printing the command ! 382: * and need to turn it back on */ ! 383: char *cmdTemplate; /* Template to use when printing the ! 384: * command */ ! 385: char *cmdStart; /* Start of expanded command */ ! 386: LstNode cmdNode; /* Node for replacing the command */ ! 387: ! 388: noSpecials = (noExecute && ! (job->node->type & OP_MAKE)); ! 389: ! 390: if (strcmp (cmd, "...") == 0) { ! 391: if ((job->flags & JOB_IGNDOTS) == 0) { ! 392: job->tailCmds = Lst_Succ (Lst_Member (job->node->commands, ! 393: (ClientData)cmd)); ! 394: return (1); ! 395: } ! 396: return (0); ! 397: } ! 398: ! 399: #define DBPRINTF(fmt, arg) if (DEBUG(JOB)) printf (fmt, arg); fprintf (job->cmdFILE, fmt, arg) ! 400: ! 401: numCommands += 1; ! 402: ! 403: /* ! 404: * For debugging, we replace each command with the result of expanding ! 405: * the variables in the command. ! 406: */ ! 407: cmdNode = Lst_Member (job->node->commands, (ClientData)cmd); ! 408: cmdStart = cmd = Var_Subst (cmd, job->node, FALSE); ! 409: Lst_Replace (cmdNode, (ClientData)cmdStart); ! 410: ! 411: cmdTemplate = "%s\n"; ! 412: ! 413: /* ! 414: * Check for leading @' and -'s to control echoing and error checking. ! 415: */ ! 416: while (*cmd == '@' || *cmd == '-') { ! 417: if (*cmd == '@') { ! 418: shutUp = TRUE; ! 419: } else { ! 420: errOff = TRUE; ! 421: } ! 422: cmd++; ! 423: } ! 424: ! 425: if (shutUp) { ! 426: if (! (job->flags & JOB_SILENT) && !noSpecials && ! 427: commandShell->hasEchoCtl) { ! 428: DBPRINTF ("%s\n", commandShell->echoOff); ! 429: } else { ! 430: shutUp = FALSE; ! 431: } ! 432: } ! 433: ! 434: if (errOff) { ! 435: if ( ! (job->flags & JOB_IGNERR) && !noSpecials) { ! 436: if (commandShell->hasErrCtl) { ! 437: /* ! 438: * we don't want the error-control commands showing ! 439: * up either, so we turn off echoing while executing ! 440: * them. We could put another field in the shell ! 441: * structure to tell JobDoOutput to look for this ! 442: * string too, but why make it any more complex than ! 443: * it already is? ! 444: */ ! 445: if (! (job->flags & JOB_SILENT) && !shutUp && ! 446: commandShell->hasEchoCtl) { ! 447: DBPRINTF ("%s\n", commandShell->echoOff); ! 448: DBPRINTF ("%s\n", commandShell->ignErr); ! 449: DBPRINTF ("%s\n", commandShell->echoOn); ! 450: } else { ! 451: DBPRINTF ("%s\n", commandShell->ignErr); ! 452: } ! 453: } else if (commandShell->ignErr && ! 454: (*commandShell->ignErr != '\0')) ! 455: { ! 456: /* ! 457: * The shell has no error control, so we need to be ! 458: * weird to get it to ignore any errors from the command. ! 459: * If echoing is turned on, we turn it off and use the ! 460: * errCheck template to echo the command. Leave echoing ! 461: * off so the user doesn't see the weirdness we go through ! 462: * to ignore errors. Set cmdTemplate to use the weirdness ! 463: * instead of the simple "%s\n" template. ! 464: */ ! 465: if (! (job->flags & JOB_SILENT) && !shutUp && ! 466: commandShell->hasEchoCtl) { ! 467: DBPRINTF ("%s\n", commandShell->echoOff); ! 468: DBPRINTF (commandShell->errCheck, cmd); ! 469: shutUp = TRUE; ! 470: } ! 471: cmdTemplate = commandShell->ignErr; ! 472: /* ! 473: * The error ignoration (hee hee) is already taken care ! 474: * of by the ignErr template, so pretend error checking ! 475: * is still on. ! 476: */ ! 477: errOff = FALSE; ! 478: } else { ! 479: errOff = FALSE; ! 480: } ! 481: } else { ! 482: errOff = FALSE; ! 483: } ! 484: } ! 485: ! 486: DBPRINTF (cmdTemplate, cmd); ! 487: ! 488: if (errOff) { ! 489: /* ! 490: * If echoing is already off, there's no point in issuing the ! 491: * echoOff command. Otherwise we issue it and pretend it was on ! 492: * for the whole command... ! 493: */ ! 494: if (!shutUp && !(job->flags & JOB_SILENT) && commandShell->hasEchoCtl){ ! 495: DBPRINTF ("%s\n", commandShell->echoOff); ! 496: shutUp = TRUE; ! 497: } ! 498: DBPRINTF ("%s\n", commandShell->errCheck); ! 499: } ! 500: if (shutUp) { ! 501: DBPRINTF ("%s\n", commandShell->echoOn); ! 502: } ! 503: return (0); ! 504: } ! 505: ! 506: /*- ! 507: *----------------------------------------------------------------------- ! 508: * JobSaveCommand -- ! 509: * Save a command to be executed when everything else is done. ! 510: * Callback function for JobFinish... ! 511: * ! 512: * Results: ! 513: * Always returns 0 ! 514: * ! 515: * Side Effects: ! 516: * The command is tacked onto the end of postCommands's commands list. ! 517: * ! 518: *----------------------------------------------------------------------- ! 519: */ ! 520: static int ! 521: JobSaveCommand (cmd, gn) ! 522: char *cmd; ! 523: GNode *gn; ! 524: { ! 525: cmd = Var_Subst (cmd, gn, FALSE); ! 526: (void)Lst_AtEnd (postCommands->commands, (ClientData)cmd); ! 527: return (0); ! 528: } ! 529: ! 530: /*- ! 531: *----------------------------------------------------------------------- ! 532: * JobFinish -- ! 533: * Do final processing for the given job including updating ! 534: * parents and starting new jobs as available/necessary. Note ! 535: * that we pay no attention to the JOB_IGNERR flag here. ! 536: * This is because when we're called because of a noexecute flag ! 537: * or something, jstat.w_status is 0 and when called from ! 538: * Job_CatchChildren, the status is zeroed if it s/b ignored. ! 539: * ! 540: * Results: ! 541: * None ! 542: * ! 543: * Side Effects: ! 544: * Some nodes may be put on the toBeMade queue. ! 545: * Final commands for the job are placed on postCommands. ! 546: * ! 547: * If we got an error and are aborting (aborting == ABORT_ERROR) and ! 548: * the job list is now empty, we are done for the day. ! 549: * If we recognized an error (errors !=0), we set the aborting flag ! 550: * to ABORT_ERROR so no more jobs will be started. ! 551: *----------------------------------------------------------------------- ! 552: */ ! 553: /*ARGSUSED*/ ! 554: void ! 555: JobFinish (job, status) ! 556: Job *job; /* job to finish */ ! 557: union wait status; /* sub-why job went away */ ! 558: { ! 559: Boolean done; ! 560: ! 561: if ((WIFEXITED(status) && ! 562: (((status.w_retcode != 0) && !(job->flags & JOB_IGNERR)))) || ! 563: (WIFSIGNALED(status) && (status.w_termsig != SIGCONT))) ! 564: { ! 565: /* ! 566: * If it exited non-zero and either we're doing things our ! 567: * way or we're not ignoring errors, the job is finished. ! 568: * Similarly, if the shell died because of a signal ! 569: * the job is also finished. In these ! 570: * cases, finish out the job's output before printing the exit ! 571: * status... ! 572: */ ! 573: if (usePipes) { ! 574: #ifdef RMT_WILL_WATCH ! 575: Rmt_Ignore(job->inPipe); ! 576: #else ! 577: FD_CLR(job->inPipe, &outputs); ! 578: #endif /* RMT_WILL_WATCH */ ! 579: if (job->outPipe != job->inPipe) { ! 580: (void)close (job->outPipe); ! 581: } ! 582: JobDoOutput (job, TRUE); ! 583: (void)close (job->inPipe); ! 584: } else { ! 585: (void)close (job->outFd); ! 586: JobDoOutput (job, TRUE); ! 587: } ! 588: ! 589: if (job->cmdFILE != NULL && job->cmdFILE != stdout) { ! 590: fclose(job->cmdFILE); ! 591: } ! 592: done = TRUE; ! 593: } else if (WIFEXITED(status) && status.w_retcode != 0) { ! 594: /* ! 595: * Deal with ignored errors in -B mode. We need to print a message ! 596: * telling of the ignored error as well as setting status.w_status ! 597: * to 0 so the next command gets run. To do this, we set done to be ! 598: * TRUE if in -B mode and the job exited non-zero. Note we don't ! 599: * want to close down any of the streams until we know we're at the ! 600: * end. ! 601: */ ! 602: done = TRUE; ! 603: } else { ! 604: /* ! 605: * No need to close things down or anything. ! 606: */ ! 607: done = FALSE; ! 608: } ! 609: ! 610: if (done || ! 611: WIFSTOPPED(status) || ! 612: (WIFSIGNALED(status) && (status.w_termsig == SIGCONT)) || ! 613: DEBUG(JOB)) ! 614: { ! 615: FILE *out; ! 616: ! 617: if (!usePipes && (job->flags & JOB_IGNERR)) { ! 618: /* ! 619: * If output is going to a file and this job is ignoring ! 620: * errors, arrange to have the exit status sent to the ! 621: * output file as well. ! 622: */ ! 623: out = fdopen (job->outFd, "w"); ! 624: } else { ! 625: out = stdout; ! 626: } ! 627: ! 628: if (WIFEXITED(status)) { ! 629: if (status.w_retcode != 0) { ! 630: if (usePipes && job->node != lastNode) { ! 631: fprintf (out, targFmt, job->node->name); ! 632: lastNode = job->node; ! 633: } ! 634: fprintf (out, "*** Error code %d%s\n", status.w_retcode, ! 635: (job->flags & JOB_IGNERR) ? " (ignored)" : ""); ! 636: ! 637: if (job->flags & JOB_IGNERR) { ! 638: status.w_status = 0; ! 639: } ! 640: } else if (DEBUG(JOB)) { ! 641: if (usePipes && job->node != lastNode) { ! 642: fprintf (out, targFmt, job->node->name); ! 643: lastNode = job->node; ! 644: } ! 645: fprintf (out, "*** Completed successfully\n"); ! 646: } ! 647: } else if (WIFSTOPPED(status)) { ! 648: if (usePipes && job->node != lastNode) { ! 649: fprintf (out, targFmt, job->node->name); ! 650: lastNode = job->node; ! 651: } ! 652: if (! (job->flags & JOB_REMIGRATE)) { ! 653: fprintf (out, "*** Stopped -- signal %d\n", status.w_stopsig); ! 654: } ! 655: job->flags |= JOB_RESUME; ! 656: (void)Lst_AtEnd(stoppedJobs, (ClientData)job); ! 657: fflush(out); ! 658: return; ! 659: } else if (status.w_termsig == SIGCONT) { ! 660: /* ! 661: * If the beastie has continued, shift the Job from the stopped ! 662: * list to the running one (or re-stop it if concurrency is ! 663: * exceeded) and go and get another child. ! 664: */ ! 665: if (job->flags & (JOB_RESUME|JOB_REMIGRATE|JOB_RESTART)) { ! 666: if (usePipes && job->node != lastNode) { ! 667: fprintf (out, targFmt, job->node->name); ! 668: lastNode = job->node; ! 669: } ! 670: fprintf (out, "*** Continued\n"); ! 671: } ! 672: if (! (job->flags & JOB_CONTINUING)) { ! 673: JobRestart(job); ! 674: } else { ! 675: Lst_AtEnd(jobs, (ClientData)job); ! 676: nJobs += 1; ! 677: if (! (job->flags & JOB_REMOTE)) { ! 678: nLocal += 1; ! 679: } ! 680: if (nJobs == maxJobs) { ! 681: jobFull = TRUE; ! 682: if (DEBUG(JOB)) { ! 683: printf("Job queue is full.\n"); ! 684: } ! 685: } ! 686: } ! 687: fflush(out); ! 688: return; ! 689: } else { ! 690: if (usePipes && job->node != lastNode) { ! 691: fprintf (out, targFmt, job->node->name); ! 692: lastNode = job->node; ! 693: } ! 694: fprintf (out, "*** Signal %d\n", status.w_termsig); ! 695: } ! 696: ! 697: fflush (out); ! 698: } ! 699: ! 700: /* ! 701: * Now handle the -B-mode stuff. If the beast still isn't finished, ! 702: * try and restart the job on the next command. If JobStart says it's ! 703: * ok, it's ok. If there's an error, this puppy is done. ! 704: */ ! 705: if ((status.w_status == 0) && ! 706: !Lst_IsAtEnd (job->node->commands)) ! 707: { ! 708: switch (JobStart (job->node, ! 709: job->flags & JOB_IGNDOTS, ! 710: job)) ! 711: { ! 712: case JOB_RUNNING: ! 713: done = FALSE; ! 714: break; ! 715: case JOB_ERROR: ! 716: done = TRUE; ! 717: status.w_retcode = 1; ! 718: break; ! 719: case JOB_FINISHED: ! 720: /* ! 721: * If we got back a JOB_FINISHED code, JobStart has already ! 722: * called Make_Update and freed the job descriptor. We set ! 723: * done to false here to avoid fake cycles and double frees. ! 724: * JobStart needs to do the update so we can proceed up the ! 725: * graph when given the -n flag.. ! 726: */ ! 727: done = FALSE; ! 728: break; ! 729: } ! 730: } else { ! 731: done = TRUE; ! 732: } ! 733: ! 734: ! 735: if (done && ! 736: (aborting != ABORT_ERROR) && ! 737: (aborting != ABORT_INTERRUPT) && ! 738: (status.w_status == 0)) ! 739: { ! 740: /* ! 741: * As long as we aren't aborting and the job didn't return a non-zero ! 742: * status that we shouldn't ignore, we call Make_Update to update ! 743: * the parents. In addition, any saved commands for the node are placed ! 744: * on the .END target. ! 745: */ ! 746: if (job->tailCmds != NILLNODE) { ! 747: Lst_ForEachFrom (job->node->commands, job->tailCmds, ! 748: JobSaveCommand, ! 749: (ClientData)job->node); ! 750: } ! 751: job->node->made = MADE; ! 752: Make_Update (job->node); ! 753: free((Address)job); ! 754: } else if (status.w_status) { ! 755: errors += 1; ! 756: free((Address)job); ! 757: } ! 758: ! 759: while (!errors && !jobFull && !Lst_IsEmpty(stoppedJobs)) { ! 760: JobRestart((Job *)Lst_DeQueue(stoppedJobs)); ! 761: } ! 762: ! 763: /* ! 764: * Set aborting if any error. ! 765: */ ! 766: if (errors && !keepgoing && (aborting != ABORT_INTERRUPT)) { ! 767: /* ! 768: * If we found any errors in this batch of children and the -k flag ! 769: * wasn't given, we set the aborting flag so no more jobs get ! 770: * started. ! 771: */ ! 772: aborting = ABORT_ERROR; ! 773: } ! 774: ! 775: if ((aborting == ABORT_ERROR) && Job_Empty()) { ! 776: /* ! 777: * If we are aborting and the job table is now empty, we finish. ! 778: */ ! 779: (void) unlink (tfile); ! 780: Finish (errors); ! 781: } ! 782: } ! 783: ! 784: /*- ! 785: *----------------------------------------------------------------------- ! 786: * Job_Touch -- ! 787: * Touch the given target. Called by JobStart when the -t flag was ! 788: * given ! 789: * ! 790: * Results: ! 791: * None ! 792: * ! 793: * Side Effects: ! 794: * The data modification of the file is changed. In addition, if the ! 795: * file did not exist, it is created. ! 796: *----------------------------------------------------------------------- ! 797: */ ! 798: void ! 799: Job_Touch (gn, silent) ! 800: GNode *gn; /* the node of the file to touch */ ! 801: Boolean silent; /* TRUE if should not print messages */ ! 802: { ! 803: int streamID; /* ID of stream opened to do the touch */ ! 804: struct timeval times[2]; /* Times for utimes() call */ ! 805: struct stat attr; /* Attributes of the file */ ! 806: ! 807: if (gn->type & (OP_JOIN|OP_USE|OP_EXEC|OP_OPTIONAL)) { ! 808: /* ! 809: * .JOIN, .USE, .ZEROTIME and .OPTIONAL targets are "virtual" targets ! 810: * and, as such, shouldn't really be created. ! 811: */ ! 812: return; ! 813: } ! 814: ! 815: if (!silent) { ! 816: printf ("touch %s\n", gn->name); ! 817: } ! 818: ! 819: if (noExecute) { ! 820: return; ! 821: } ! 822: ! 823: if (gn->type & OP_ARCHV) { ! 824: Arch_Touch (gn); ! 825: } else if (gn->type & OP_LIB) { ! 826: Arch_TouchLib (gn); ! 827: } else { ! 828: char *file = gn->path ? gn->path : gn->name; ! 829: ! 830: times[0].tv_sec = times[1].tv_sec = now; ! 831: times[0].tv_usec = times[1].tv_usec = 0; ! 832: if (utimes(file, times) < 0){ ! 833: streamID = open (file, O_RDWR | O_CREAT, 0666); ! 834: ! 835: if (streamID >= 0) { ! 836: char c; ! 837: ! 838: /* ! 839: * Read and write a byte to the file to change the ! 840: * modification time, then close the file. ! 841: */ ! 842: if (read(streamID, &c, 1) == 1) { ! 843: lseek(streamID, 0L, L_SET); ! 844: write(streamID, &c, 1); ! 845: } ! 846: ! 847: (void)close (streamID); ! 848: } else ! 849: printf("*** couldn't touch %s: %s", file, strerror(errno)); ! 850: } ! 851: } ! 852: } ! 853: ! 854: /*- ! 855: *----------------------------------------------------------------------- ! 856: * Job_CheckCommands -- ! 857: * Make sure the given node has all the commands it needs. ! 858: * ! 859: * Results: ! 860: * TRUE if the commands list is/was ok. ! 861: * ! 862: * Side Effects: ! 863: * The node will have commands from the .DEFAULT rule added to it ! 864: * if it needs them. ! 865: *----------------------------------------------------------------------- ! 866: */ ! 867: Boolean ! 868: Job_CheckCommands (gn, abortProc) ! 869: GNode *gn; /* The target whose commands need ! 870: * verifying */ ! 871: void (*abortProc)(); /* Function to abort with message */ ! 872: { ! 873: if (OP_NOP(gn->type) && Lst_IsEmpty (gn->commands) && ! 874: (gn->type & OP_LIB) == 0) { ! 875: /* ! 876: * No commands. Look for .DEFAULT rule from which we might infer ! 877: * commands ! 878: */ ! 879: if ((DEFAULT != NILGNODE) && !Lst_IsEmpty(DEFAULT->commands)) { ! 880: /* ! 881: * Make only looks for a .DEFAULT if the node was never the ! 882: * target of an operator, so that's what we do too. If ! 883: * a .DEFAULT was given, we substitute its commands for gn's ! 884: * commands and set the IMPSRC variable to be the target's name ! 885: * The DEFAULT node acts like a transformation rule, in that ! 886: * gn also inherits any attributes or sources attached to ! 887: * .DEFAULT itself. ! 888: */ ! 889: Make_HandleUse(DEFAULT, gn); ! 890: Var_Set (IMPSRC, Var_Value (TARGET, gn), gn); ! 891: } else if (Dir_MTime (gn) == 0) { ! 892: /* ! 893: * The node wasn't the target of an operator we have no .DEFAULT ! 894: * rule to go on and the target doesn't already exist. There's ! 895: * nothing more we can do for this branch. If the -k flag wasn't ! 896: * given, we stop in our tracks, otherwise we just don't update ! 897: * this node's parents so they never get examined. ! 898: */ ! 899: if (gn->type & OP_OPTIONAL) { ! 900: printf ("Can't figure out how to make %s (ignored)\n", ! 901: gn->name); ! 902: } else if (keepgoing) { ! 903: printf ("Can't figure out how to make %s (continuing)\n", ! 904: gn->name); ! 905: return (FALSE); ! 906: } else { ! 907: (*abortProc) ("Can't figure out how to make %s. Stop", ! 908: gn->name); ! 909: return(FALSE); ! 910: } ! 911: } ! 912: } ! 913: return (TRUE); ! 914: } ! 915: #ifdef RMT_WILL_WATCH ! 916: /*- ! 917: *----------------------------------------------------------------------- ! 918: * JobLocalInput -- ! 919: * Handle a pipe becoming readable. Callback function for Rmt_Watch ! 920: * ! 921: * Results: ! 922: * None ! 923: * ! 924: * Side Effects: ! 925: * JobDoOutput is called. ! 926: * ! 927: *----------------------------------------------------------------------- ! 928: */ ! 929: /*ARGSUSED*/ ! 930: static void ! 931: JobLocalInput(stream, job) ! 932: int stream; /* Stream that's ready (ignored) */ ! 933: Job *job; /* Job to which the stream belongs */ ! 934: { ! 935: JobDoOutput(job, FALSE); ! 936: } ! 937: #endif /* RMT_WILL_WATCH */ ! 938: ! 939: /*- ! 940: *----------------------------------------------------------------------- ! 941: * JobExec -- ! 942: * Execute the shell for the given job. Called from JobStart and ! 943: * JobRestart. ! 944: * ! 945: * Results: ! 946: * None. ! 947: * ! 948: * Side Effects: ! 949: * A shell is executed, outputs is altered and the Job structure added ! 950: * to the job table. ! 951: * ! 952: *----------------------------------------------------------------------- ! 953: */ ! 954: static void ! 955: JobExec(job, argv) ! 956: Job *job; /* Job to execute */ ! 957: char **argv; ! 958: { ! 959: int cpid; /* ID of new child */ ! 960: ! 961: if (DEBUG(JOB)) { ! 962: int i; ! 963: ! 964: printf("Running %s %sly\n", job->node->name, ! 965: job->flags&JOB_REMOTE?"remote":"local"); ! 966: printf("\tCommand: "); ! 967: for (i = 0; argv[i] != (char *)NULL; i++) { ! 968: printf("%s ", argv[i]); ! 969: } ! 970: printf("\n"); ! 971: } ! 972: ! 973: /* ! 974: * Some jobs produce no output and it's disconcerting to have ! 975: * no feedback of their running (since they produce no output, the ! 976: * banner with their name in it never appears). This is an attempt to ! 977: * provide that feedback, even if nothing follows it. ! 978: */ ! 979: if ((lastNode != job->node) && (job->flags & JOB_FIRST) && ! 980: !(job->flags & JOB_SILENT)) ! 981: { ! 982: printf(targFmt, job->node->name); ! 983: lastNode = job->node; ! 984: } ! 985: ! 986: #ifdef RMT_NO_EXEC ! 987: if (job->flags & JOB_REMOTE) { ! 988: goto jobExecFinish; ! 989: } ! 990: #endif /* RMT_NO_EXEC */ ! 991: ! 992: if ((cpid = vfork()) == -1) { ! 993: Punt ("Cannot fork"); ! 994: } else if (cpid == 0) { ! 995: ! 996: /* ! 997: * Must duplicate the input stream down to the child's input and ! 998: * reset it to the beginning (again). Since the stream was marked ! 999: * close-on-exec, we must clear that bit in the new input. ! 1000: */ ! 1001: (void) dup2(fileno(job->cmdFILE), 0); ! 1002: fcntl(0, F_SETFD, 0); ! 1003: lseek(0, 0, L_SET); ! 1004: ! 1005: if (usePipes) { ! 1006: /* ! 1007: * Set up the child's output to be routed through the pipe ! 1008: * we've created for it. ! 1009: */ ! 1010: (void) dup2 (job->outPipe, 1); ! 1011: } else { ! 1012: /* ! 1013: * We're capturing output in a file, so we duplicate the ! 1014: * descriptor to the temporary file into the standard ! 1015: * output. ! 1016: */ ! 1017: (void) dup2 (job->outFd, 1); ! 1018: } ! 1019: /* ! 1020: * The output channels are marked close on exec. This bit was ! 1021: * duplicated by the dup2 (on some systems), so we have to clear ! 1022: * it before routing the shell's error output to the same place as ! 1023: * its standard output. ! 1024: */ ! 1025: fcntl(1, F_SETFD, 0); ! 1026: (void) dup2 (1, 2); ! 1027: ! 1028: #ifdef USE_PGRP ! 1029: /* ! 1030: * We want to switch the child into a different process family so ! 1031: * we can kill it and all its descendants in one fell swoop, ! 1032: * by killing its process family, but not commit suicide. ! 1033: */ ! 1034: ! 1035: (void) setpgrp(0, getpid()); ! 1036: #endif USE_PGRP ! 1037: ! 1038: (void) execv (shellPath, argv); ! 1039: (void) write (2, "Could not execute shell\n", ! 1040: sizeof ("Could not execute shell")); ! 1041: _exit (1); ! 1042: } else { ! 1043: job->pid = cpid; ! 1044: ! 1045: if (usePipes && (job->flags & JOB_FIRST) ) { ! 1046: /* ! 1047: * The first time a job is run for a node, we set the current ! 1048: * position in the buffer to the beginning and mark another ! 1049: * stream to watch in the outputs mask ! 1050: */ ! 1051: job->curPos = 0; ! 1052: ! 1053: #ifdef RMT_WILL_WATCH ! 1054: Rmt_Watch(job->inPipe, JobLocalInput, job); ! 1055: #else ! 1056: FD_SET(job->inPipe, &outputs); ! 1057: #endif /* RMT_WILL_WATCH */ ! 1058: } ! 1059: ! 1060: if (job->flags & JOB_REMOTE) { ! 1061: job->rmtID = (char *)0; ! 1062: } else { ! 1063: nLocal += 1; ! 1064: /* ! 1065: * XXX: Used to not happen if CUSTOMS. Why? ! 1066: */ ! 1067: if (job->cmdFILE != stdout) { ! 1068: fclose(job->cmdFILE); ! 1069: job->cmdFILE = NULL; ! 1070: } ! 1071: } ! 1072: } ! 1073: ! 1074: jobExecFinish: ! 1075: /* ! 1076: * Now the job is actually running, add it to the table. ! 1077: */ ! 1078: nJobs += 1; ! 1079: (void)Lst_AtEnd (jobs, (ClientData)job); ! 1080: if (nJobs == maxJobs) { ! 1081: jobFull = TRUE; ! 1082: } ! 1083: } ! 1084: ! 1085: /*- ! 1086: *----------------------------------------------------------------------- ! 1087: * JobMakeArgv -- ! 1088: * Create the argv needed to execute the shell for a given job. ! 1089: * ! 1090: * ! 1091: * Results: ! 1092: * ! 1093: * Side Effects: ! 1094: * ! 1095: *----------------------------------------------------------------------- ! 1096: */ ! 1097: static void ! 1098: JobMakeArgv(job, argv) ! 1099: Job *job; ! 1100: char **argv; ! 1101: { ! 1102: int argc; ! 1103: static char args[10]; /* For merged arguments */ ! 1104: ! 1105: argv[0] = shellName; ! 1106: argc = 1; ! 1107: ! 1108: if ((commandShell->exit && (*commandShell->exit != '-')) || ! 1109: (commandShell->echo && (*commandShell->echo != '-'))) ! 1110: { ! 1111: /* ! 1112: * At least one of the flags doesn't have a minus before it, so ! 1113: * merge them together. Have to do this because the *(&(@*#*&#$# ! 1114: * Bourne shell thinks its second argument is a file to source. ! 1115: * Grrrr. Note the ten-character limitation on the combined arguments. ! 1116: */ ! 1117: (void)sprintf(args, "-%s%s", ! 1118: ((job->flags & JOB_IGNERR) ? "" : ! 1119: (commandShell->exit ? commandShell->exit : "")), ! 1120: ((job->flags & JOB_SILENT) ? "" : ! 1121: (commandShell->echo ? commandShell->echo : ""))); ! 1122: ! 1123: if (args[1]) { ! 1124: argv[argc] = args; ! 1125: argc++; ! 1126: } ! 1127: } else { ! 1128: if (!(job->flags & JOB_IGNERR) && commandShell->exit) { ! 1129: argv[argc] = commandShell->exit; ! 1130: argc++; ! 1131: } ! 1132: if (!(job->flags & JOB_SILENT) && commandShell->echo) { ! 1133: argv[argc] = commandShell->echo; ! 1134: argc++; ! 1135: } ! 1136: } ! 1137: argv[argc] = (char *)NULL; ! 1138: } ! 1139: ! 1140: /*- ! 1141: *----------------------------------------------------------------------- ! 1142: * JobRestart -- ! 1143: * Restart a job that stopped for some reason. ! 1144: * ! 1145: * Results: ! 1146: * None. ! 1147: * ! 1148: * Side Effects: ! 1149: * jobFull will be set if the job couldn't be run. ! 1150: * ! 1151: *----------------------------------------------------------------------- ! 1152: */ ! 1153: static void ! 1154: JobRestart(job) ! 1155: Job *job; /* Job to restart */ ! 1156: { ! 1157: if (job->flags & JOB_REMIGRATE) { ! 1158: if (DEBUG(JOB)) { ! 1159: printf("Remigrating %x\n", job->pid); ! 1160: } ! 1161: if (nLocal != maxLocal) { ! 1162: /* ! 1163: * Job cannot be remigrated, but there's room on the local ! 1164: * machine, so resume the job and note that another ! 1165: * local job has started. ! 1166: */ ! 1167: if (DEBUG(JOB)) { ! 1168: printf("resuming on local machine\n"); ! 1169: } ! 1170: KILL(job->pid, SIGCONT); ! 1171: nLocal +=1; ! 1172: job->flags &= ~(JOB_REMIGRATE|JOB_RESUME); ! 1173: } else { ! 1174: /* ! 1175: * Job cannot be restarted. Mark the table as full and ! 1176: * place the job back on the list of stopped jobs. ! 1177: */ ! 1178: if (DEBUG(JOB)) { ! 1179: printf("holding\n"); ! 1180: } ! 1181: (void)Lst_AtFront(stoppedJobs, (ClientData)job); ! 1182: jobFull = TRUE; ! 1183: if (DEBUG(JOB)) { ! 1184: printf("Job queue is full.\n"); ! 1185: } ! 1186: return; ! 1187: } ! 1188: ! 1189: (void)Lst_AtEnd(jobs, (ClientData)job); ! 1190: nJobs += 1; ! 1191: if (nJobs == maxJobs) { ! 1192: jobFull = TRUE; ! 1193: if (DEBUG(JOB)) { ! 1194: printf("Job queue is full.\n"); ! 1195: } ! 1196: } ! 1197: } else if (job->flags & JOB_RESTART) { ! 1198: /* ! 1199: * Set up the control arguments to the shell. This is based on the ! 1200: * flags set earlier for this job. If the JOB_IGNERR flag is clear, ! 1201: * the 'exit' flag of the commandShell is used to cause it to exit ! 1202: * upon receiving an error. If the JOB_SILENT flag is clear, the ! 1203: * 'echo' flag of the commandShell is used to get it to start echoing ! 1204: * as soon as it starts processing commands. ! 1205: */ ! 1206: char *argv[4]; ! 1207: ! 1208: JobMakeArgv(job, argv); ! 1209: ! 1210: if (DEBUG(JOB)) { ! 1211: printf("Restarting %s...", job->node->name); ! 1212: } ! 1213: if (((nLocal >= maxLocal) && ! (job->flags & JOB_SPECIAL))) { ! 1214: /* ! 1215: * Can't be exported and not allowed to run locally -- put it ! 1216: * back on the hold queue and mark the table full ! 1217: */ ! 1218: if (DEBUG(JOB)) { ! 1219: printf("holding\n"); ! 1220: } ! 1221: (void)Lst_AtFront(stoppedJobs, (ClientData)job); ! 1222: jobFull = TRUE; ! 1223: if (DEBUG(JOB)) { ! 1224: printf("Job queue is full.\n"); ! 1225: } ! 1226: return; ! 1227: } else { ! 1228: /* ! 1229: * Job may be run locally. ! 1230: */ ! 1231: if (DEBUG(JOB)) { ! 1232: printf("running locally\n"); ! 1233: } ! 1234: job->flags &= ~JOB_REMOTE; ! 1235: } ! 1236: JobExec(job, argv); ! 1237: } else { ! 1238: /* ! 1239: * The job has stopped and needs to be restarted. Why it stopped, ! 1240: * we don't know... ! 1241: */ ! 1242: if (DEBUG(JOB)) { ! 1243: printf("Resuming %s...", job->node->name); ! 1244: } ! 1245: if (((job->flags & JOB_REMOTE) || ! 1246: (nLocal < maxLocal) || ! 1247: (((job->flags & JOB_SPECIAL)) && ! 1248: (maxLocal == 0))) && ! 1249: (nJobs != maxJobs)) ! 1250: { ! 1251: /* ! 1252: * If the job is remote, it's ok to resume it as long as the ! 1253: * maximum concurrency won't be exceeded. If it's local and ! 1254: * we haven't reached the local concurrency limit already (or the ! 1255: * job must be run locally and maxLocal is 0), it's also ok to ! 1256: * resume it. ! 1257: */ ! 1258: Boolean error; ! 1259: extern int errno; ! 1260: union wait status; ! 1261: ! 1262: #ifdef RMT_WANTS_SIGNALS ! 1263: if (job->flags & JOB_REMOTE) { ! 1264: error = !Rmt_Signal(job, SIGCONT); ! 1265: } else ! 1266: #endif /* RMT_WANTS_SIGNALS */ ! 1267: error = (KILL(job->pid, SIGCONT) != 0); ! 1268: ! 1269: if (!error) { ! 1270: /* ! 1271: * Make sure the user knows we've continued the beast and ! 1272: * actually put the thing in the job table. ! 1273: */ ! 1274: job->flags |= JOB_CONTINUING; ! 1275: status.w_termsig = SIGCONT; ! 1276: JobFinish(job, status); ! 1277: ! 1278: job->flags &= ~(JOB_RESUME|JOB_CONTINUING); ! 1279: if (DEBUG(JOB)) { ! 1280: printf("done\n"); ! 1281: } ! 1282: } else { ! 1283: Error("couldn't resume %s: %s", ! 1284: job->node->name, strerror(errno)); ! 1285: status.w_status = 0; ! 1286: status.w_retcode = 1; ! 1287: JobFinish(job, status); ! 1288: } ! 1289: } else { ! 1290: /* ! 1291: * Job cannot be restarted. Mark the table as full and ! 1292: * place the job back on the list of stopped jobs. ! 1293: */ ! 1294: if (DEBUG(JOB)) { ! 1295: printf("table full\n"); ! 1296: } ! 1297: (void)Lst_AtFront(stoppedJobs, (ClientData)job); ! 1298: jobFull = TRUE; ! 1299: if (DEBUG(JOB)) { ! 1300: printf("Job queue is full.\n"); ! 1301: } ! 1302: } ! 1303: } ! 1304: } ! 1305: ! 1306: /*- ! 1307: *----------------------------------------------------------------------- ! 1308: * JobStart -- ! 1309: * Start a target-creation process going for the target described ! 1310: * by the graph node gn. ! 1311: * ! 1312: * Results: ! 1313: * JOB_ERROR if there was an error in the commands, JOB_FINISHED ! 1314: * if there isn't actually anything left to do for the job and ! 1315: * JOB_RUNNING if the job has been started. ! 1316: * ! 1317: * Side Effects: ! 1318: * A new Job node is created and added to the list of running ! 1319: * jobs. PMake is forked and a child shell created. ! 1320: *----------------------------------------------------------------------- ! 1321: */ ! 1322: static int ! 1323: JobStart (gn, flags, previous) ! 1324: GNode *gn; /* target to create */ ! 1325: short flags; /* flags for the job to override normal ones. ! 1326: * e.g. JOB_SPECIAL or JOB_IGNDOTS */ ! 1327: Job *previous; /* The previous Job structure for this node, ! 1328: * if any. */ ! 1329: { ! 1330: register Job *job; /* new job descriptor */ ! 1331: char *argv[4]; /* Argument vector to shell */ ! 1332: char args[5]; /* arguments to shell */ ! 1333: static int jobno = 0; /* job number of catching output in a file */ ! 1334: Boolean cmdsOK; /* true if the nodes commands were all right */ ! 1335: Boolean local; /* Set true if the job was run locally */ ! 1336: Boolean noExec; /* Set true if we decide not to run the job */ ! 1337: ! 1338: if (previous != (Job *)NULL) { ! 1339: previous->flags &= ~ (JOB_FIRST|JOB_IGNERR|JOB_SILENT|JOB_REMOTE); ! 1340: job = previous; ! 1341: } else { ! 1342: job = (Job *) emalloc (sizeof (Job)); ! 1343: if (job == (Job *)NULL) { ! 1344: Punt("JobStart out of memory"); ! 1345: } ! 1346: flags |= JOB_FIRST; ! 1347: } ! 1348: ! 1349: job->node = gn; ! 1350: job->tailCmds = NILLNODE; ! 1351: ! 1352: /* ! 1353: * Set the initial value of the flags for this job based on the global ! 1354: * ones and the node's attributes... Any flags supplied by the caller ! 1355: * are also added to the field. ! 1356: */ ! 1357: job->flags = 0; ! 1358: if (Targ_Ignore (gn)) { ! 1359: job->flags |= JOB_IGNERR; ! 1360: } ! 1361: if (Targ_Silent (gn)) { ! 1362: job->flags |= JOB_SILENT; ! 1363: } ! 1364: job->flags |= flags; ! 1365: ! 1366: /* ! 1367: * Check the commands now so any attributes from .DEFAULT have a chance ! 1368: * to migrate to the node ! 1369: */ ! 1370: if (job->flags & JOB_FIRST) { ! 1371: cmdsOK = Job_CheckCommands(gn, Error); ! 1372: } else { ! 1373: cmdsOK = TRUE; ! 1374: } ! 1375: ! 1376: /* ! 1377: * If the -n flag wasn't given, we open up OUR (not the child's) ! 1378: * temporary file to stuff commands in it. The thing is rd/wr so we don't ! 1379: * need to reopen it to feed it to the shell. If the -n flag *was* given, ! 1380: * we just set the file to be stdout. Cute, huh? ! 1381: */ ! 1382: if ((gn->type & OP_MAKE) || (!noExecute && !touchFlag)) { ! 1383: /* ! 1384: * We're serious here, but if the commands were bogus, we're ! 1385: * also dead... ! 1386: */ ! 1387: if (!cmdsOK) { ! 1388: DieHorribly(); ! 1389: } ! 1390: ! 1391: job->cmdFILE = fopen (tfile, "w+"); ! 1392: if (job->cmdFILE == (FILE *) NULL) { ! 1393: Punt ("Could not open %s", tfile); ! 1394: } ! 1395: fcntl(fileno(job->cmdFILE), F_SETFD, 1); ! 1396: /* ! 1397: * Send the commands to the command file, flush all its buffers then ! 1398: * rewind and remove the thing. ! 1399: */ ! 1400: noExec = FALSE; ! 1401: ! 1402: /* ! 1403: * used to be backwards; replace when start doing multiple commands ! 1404: * per shell. ! 1405: */ ! 1406: if (1) { ! 1407: /* ! 1408: * Be compatible: If this is the first time for this node, ! 1409: * verify its commands are ok and open the commands list for ! 1410: * sequential access by later invocations of JobStart. ! 1411: * Once that is done, we take the next command off the list ! 1412: * and print it to the command file. If the command was an ! 1413: * ellipsis, note that there's nothing more to execute. ! 1414: */ ! 1415: if ((job->flags&JOB_FIRST) && (Lst_Open(gn->commands) != SUCCESS)){ ! 1416: cmdsOK = FALSE; ! 1417: } else { ! 1418: LstNode ln = Lst_Next (gn->commands); ! 1419: ! 1420: if ((ln == NILLNODE) || ! 1421: JobPrintCommand ((char *)Lst_Datum (ln), job)) ! 1422: { ! 1423: noExec = TRUE; ! 1424: Lst_Close (gn->commands); ! 1425: } ! 1426: if (noExec && !(job->flags & JOB_FIRST)) { ! 1427: /* ! 1428: * If we're not going to execute anything, the job ! 1429: * is done and we need to close down the various ! 1430: * file descriptors we've opened for output, then ! 1431: * call JobDoOutput to catch the final characters or ! 1432: * send the file to the screen... Note that the i/o streams ! 1433: * are only open if this isn't the first job. ! 1434: * Note also that this could not be done in ! 1435: * Job_CatchChildren b/c it wasn't clear if there were ! 1436: * more commands to execute or not... ! 1437: */ ! 1438: if (usePipes) { ! 1439: #ifdef RMT_WILL_WATCH ! 1440: Rmt_Ignore(job->inPipe); ! 1441: #else ! 1442: FD_CLR(job->inPipe, &outputs); ! 1443: #endif ! 1444: if (job->outPipe != job->inPipe) { ! 1445: (void)close (job->outPipe); ! 1446: } ! 1447: JobDoOutput (job, TRUE); ! 1448: (void)close (job->inPipe); ! 1449: } else { ! 1450: (void)close (job->outFd); ! 1451: JobDoOutput (job, TRUE); ! 1452: } ! 1453: } ! 1454: } ! 1455: } else { ! 1456: /* ! 1457: * We can do all the commands at once. hooray for sanity ! 1458: */ ! 1459: numCommands = 0; ! 1460: Lst_ForEach (gn->commands, JobPrintCommand, (ClientData)job); ! 1461: ! 1462: /* ! 1463: * If we didn't print out any commands to the shell script, ! 1464: * there's not much point in executing the shell, is there? ! 1465: */ ! 1466: if (numCommands == 0) { ! 1467: noExec = TRUE; ! 1468: } ! 1469: } ! 1470: } else if (noExecute) { ! 1471: /* ! 1472: * Not executing anything -- just print all the commands to stdout ! 1473: * in one fell swoop. This will still set up job->tailCmds correctly. ! 1474: */ ! 1475: if (lastNode != gn) { ! 1476: printf (targFmt, gn->name); ! 1477: lastNode = gn; ! 1478: } ! 1479: job->cmdFILE = stdout; ! 1480: /* ! 1481: * Only print the commands if they're ok, but don't die if they're ! 1482: * not -- just let the user know they're bad and keep going. It ! 1483: * doesn't do any harm in this case and may do some good. ! 1484: */ ! 1485: if (cmdsOK) { ! 1486: Lst_ForEach(gn->commands, JobPrintCommand, (ClientData)job); ! 1487: } ! 1488: /* ! 1489: * Don't execute the shell, thank you. ! 1490: */ ! 1491: noExec = TRUE; ! 1492: } else { ! 1493: /* ! 1494: * Just touch the target and note that no shell should be executed. ! 1495: * Set cmdFILE to stdout to make life easier. Check the commands, too, ! 1496: * but don't die if they're no good -- it does no harm to keep working ! 1497: * up the graph. ! 1498: */ ! 1499: job->cmdFILE = stdout; ! 1500: Job_Touch (gn, job->flags&JOB_SILENT); ! 1501: noExec = TRUE; ! 1502: } ! 1503: ! 1504: /* ! 1505: * If we're not supposed to execute a shell, don't. ! 1506: */ ! 1507: if (noExec) { ! 1508: /* ! 1509: * Unlink and close the command file if we opened one ! 1510: */ ! 1511: if (job->cmdFILE != stdout) { ! 1512: (void) unlink (tfile); ! 1513: fclose(job->cmdFILE); ! 1514: } else { ! 1515: fflush (stdout); ! 1516: } ! 1517: ! 1518: /* ! 1519: * We only want to work our way up the graph if we aren't here because ! 1520: * the commands for the job were no good. ! 1521: */ ! 1522: if (cmdsOK) { ! 1523: if (aborting == 0) { ! 1524: if (job->tailCmds != NILLNODE) { ! 1525: Lst_ForEachFrom(job->node->commands, job->tailCmds, ! 1526: JobSaveCommand, ! 1527: (ClientData)job->node); ! 1528: } ! 1529: Make_Update(job->node); ! 1530: } ! 1531: free((Address)job); ! 1532: return(JOB_FINISHED); ! 1533: } else { ! 1534: free((Address)job); ! 1535: return(JOB_ERROR); ! 1536: } ! 1537: } else { ! 1538: fflush (job->cmdFILE); ! 1539: (void) unlink (tfile); ! 1540: } ! 1541: ! 1542: /* ! 1543: * Set up the control arguments to the shell. This is based on the flags ! 1544: * set earlier for this job. ! 1545: */ ! 1546: JobMakeArgv(job, argv); ! 1547: ! 1548: /* ! 1549: * If we're using pipes to catch output, create the pipe by which we'll ! 1550: * get the shell's output. If we're using files, print out that we're ! 1551: * starting a job and then set up its temporary-file name. This is just ! 1552: * tfile with two extra digits tacked on -- jobno. ! 1553: */ ! 1554: if (job->flags & JOB_FIRST) { ! 1555: if (usePipes) { ! 1556: int fd[2]; ! 1557: (void) pipe(fd); ! 1558: job->inPipe = fd[0]; ! 1559: job->outPipe = fd[1]; ! 1560: (void)fcntl (job->inPipe, F_SETFD, 1); ! 1561: (void)fcntl (job->outPipe, F_SETFD, 1); ! 1562: } else { ! 1563: printf ("Remaking `%s'\n", gn->name); ! 1564: fflush (stdout); ! 1565: sprintf (job->outFile, "%s%02d", tfile, jobno); ! 1566: jobno = (jobno + 1) % 100; ! 1567: job->outFd = open(job->outFile,O_WRONLY|O_CREAT|O_APPEND,0600); ! 1568: (void)fcntl (job->outFd, F_SETFD, 1); ! 1569: } ! 1570: } ! 1571: ! 1572: local = TRUE; ! 1573: ! 1574: if (local && (((nLocal >= maxLocal) && ! 1575: !(job->flags & JOB_SPECIAL) && ! 1576: (maxLocal != 0)))) ! 1577: { ! 1578: /* ! 1579: * The job can only be run locally, but we've hit the limit of ! 1580: * local concurrency, so put the job on hold until some other job ! 1581: * finishes. Note that the special jobs (.BEGIN, .INTERRUPT and .END) ! 1582: * may be run locally even when the local limit has been reached ! 1583: * (e.g. when maxLocal == 0), though they will be exported if at ! 1584: * all possible. ! 1585: */ ! 1586: jobFull = TRUE; ! 1587: ! 1588: if (DEBUG(JOB)) { ! 1589: printf("Can only run job locally.\n"); ! 1590: } ! 1591: job->flags |= JOB_RESTART; ! 1592: (void)Lst_AtEnd(stoppedJobs, (ClientData)job); ! 1593: } else { ! 1594: if ((nLocal >= maxLocal) && local) { ! 1595: /* ! 1596: * If we're running this job locally as a special case (see above), ! 1597: * at least say the table is full. ! 1598: */ ! 1599: jobFull = TRUE; ! 1600: if (DEBUG(JOB)) { ! 1601: printf("Local job queue is full.\n"); ! 1602: } ! 1603: } ! 1604: JobExec(job, argv); ! 1605: } ! 1606: return(JOB_RUNNING); ! 1607: } ! 1608: ! 1609: /*- ! 1610: *----------------------------------------------------------------------- ! 1611: * JobDoOutput -- ! 1612: * This function is called at different times depending on ! 1613: * whether the user has specified that output is to be collected ! 1614: * via pipes or temporary files. In the former case, we are called ! 1615: * whenever there is something to read on the pipe. We collect more ! 1616: * output from the given job and store it in the job's outBuf. If ! 1617: * this makes up a line, we print it tagged by the job's identifier, ! 1618: * as necessary. ! 1619: * If output has been collected in a temporary file, we open the ! 1620: * file and read it line by line, transfering it to our own ! 1621: * output channel until the file is empty. At which point we ! 1622: * remove the temporary file. ! 1623: * In both cases, however, we keep our figurative eye out for the ! 1624: * 'noPrint' line for the shell from which the output came. If ! 1625: * we recognize a line, we don't print it. If the command is not ! 1626: * alone on the line (the character after it is not \0 or \n), we ! 1627: * do print whatever follows it. ! 1628: * ! 1629: * Results: ! 1630: * None ! 1631: * ! 1632: * Side Effects: ! 1633: * curPos may be shifted as may the contents of outBuf. ! 1634: *----------------------------------------------------------------------- ! 1635: */ ! 1636: void ! 1637: JobDoOutput (job, finish) ! 1638: register Job *job; /* the job whose output needs printing */ ! 1639: Boolean finish; /* TRUE if this is the last time we'll be ! 1640: * called for this job */ ! 1641: { ! 1642: Boolean gotNL = FALSE; /* true if got a newline */ ! 1643: register int nr; /* number of bytes read */ ! 1644: register int i; /* auxiliary index into outBuf */ ! 1645: register int max; /* limit for i (end of current data) */ ! 1646: int nRead; /* (Temporary) number of bytes read */ ! 1647: ! 1648: char c; /* character after noPrint string */ ! 1649: FILE *oFILE; /* Stream pointer to shell's output file */ ! 1650: char inLine[132]; ! 1651: ! 1652: ! 1653: if (usePipes) { ! 1654: /* ! 1655: * Read as many bytes as will fit in the buffer. ! 1656: */ ! 1657: end_loop: ! 1658: ! 1659: nRead = read (job->inPipe, &job->outBuf[job->curPos], ! 1660: JOB_BUFSIZE - job->curPos); ! 1661: if (nRead < 0) { ! 1662: if (DEBUG(JOB)) { ! 1663: perror("JobDoOutput(piperead)"); ! 1664: } ! 1665: nr = 0; ! 1666: } else { ! 1667: nr = nRead; ! 1668: } ! 1669: ! 1670: /* ! 1671: * If we hit the end-of-file (the job is dead), we must flush its ! 1672: * remaining output, so pretend we read a newline if there's any ! 1673: * output remaining in the buffer. ! 1674: * Also clear the 'finish' flag so we stop looping. ! 1675: */ ! 1676: if ((nr == 0) && (job->curPos != 0)) { ! 1677: job->outBuf[job->curPos] = '\n'; ! 1678: nr = 1; ! 1679: finish = FALSE; ! 1680: } else if (nr == 0) { ! 1681: finish = FALSE; ! 1682: } ! 1683: ! 1684: /* ! 1685: * Look for the last newline in the bytes we just got. If there is ! 1686: * one, break out of the loop with 'i' as its index and gotNL set ! 1687: * TRUE. ! 1688: */ ! 1689: max = job->curPos + nr; ! 1690: for (i = job->curPos + nr - 1; i >= job->curPos; i--) { ! 1691: if (job->outBuf[i] == '\n') { ! 1692: gotNL = TRUE; ! 1693: break; ! 1694: } else if (job->outBuf[i] == '\0') { ! 1695: /* ! 1696: * Why? ! 1697: */ ! 1698: job->outBuf[i] = ' '; ! 1699: } ! 1700: } ! 1701: ! 1702: if (!gotNL) { ! 1703: job->curPos += nr; ! 1704: if (job->curPos == JOB_BUFSIZE) { ! 1705: /* ! 1706: * If we've run out of buffer space, we have no choice ! 1707: * but to print the stuff. sigh. ! 1708: */ ! 1709: gotNL = TRUE; ! 1710: i = job->curPos; ! 1711: } ! 1712: } ! 1713: if (gotNL) { ! 1714: /* ! 1715: * Need to send the output to the screen. Null terminate it ! 1716: * first, overwriting the newline character if there was one. ! 1717: * So long as the line isn't one we should filter (according ! 1718: * to the shell description), we print the line, preceeded ! 1719: * by a target banner if this target isn't the same as the ! 1720: * one for which we last printed something. ! 1721: * The rest of the data in the buffer are then shifted down ! 1722: * to the start of the buffer and curPos is set accordingly. ! 1723: */ ! 1724: job->outBuf[i] = '\0'; ! 1725: if (i >= job->curPos) { ! 1726: register char *cp, *ecp; ! 1727: ! 1728: cp = job->outBuf; ! 1729: if (commandShell->noPrint) { ! 1730: ecp = Str_FindSubstring(job->outBuf, ! 1731: commandShell->noPrint); ! 1732: while (ecp != (char *)NULL) { ! 1733: if (cp != ecp) { ! 1734: *ecp = '\0'; ! 1735: if (job->node != lastNode) { ! 1736: printf (targFmt, job->node->name); ! 1737: lastNode = job->node; ! 1738: } ! 1739: /* ! 1740: * The only way there wouldn't be a newline after ! 1741: * this line is if it were the last in the buffer. ! 1742: * however, since the non-printable comes after it, ! 1743: * there must be a newline, so we don't print one. ! 1744: */ ! 1745: printf ("%s", cp); ! 1746: } ! 1747: cp = ecp + commandShell->noPLen; ! 1748: if (cp != &job->outBuf[i]) { ! 1749: /* ! 1750: * Still more to print, look again after skipping ! 1751: * the whitespace following the non-printable ! 1752: * command.... ! 1753: */ ! 1754: cp++; ! 1755: while (*cp == ' ' || *cp == '\t' || *cp == '\n') { ! 1756: cp++; ! 1757: } ! 1758: ecp = Str_FindSubstring (cp, ! 1759: commandShell->noPrint); ! 1760: } else { ! 1761: break; ! 1762: } ! 1763: } ! 1764: } ! 1765: ! 1766: /* ! 1767: * There's still more in that thar buffer. This time, though, ! 1768: * we know there's no newline at the end, so we add one of ! 1769: * our own free will. ! 1770: */ ! 1771: if (*cp != '\0') { ! 1772: if (job->node != lastNode) { ! 1773: printf (targFmt, job->node->name); ! 1774: lastNode = job->node; ! 1775: } ! 1776: printf ("%s\n", cp); ! 1777: } ! 1778: ! 1779: fflush (stdout); ! 1780: } ! 1781: if (i < max - 1) { ! 1782: bcopy (&job->outBuf[i + 1], /* shift the remaining */ ! 1783: job->outBuf, /* characters down */ ! 1784: max - (i + 1)); ! 1785: job->curPos = max - (i + 1); ! 1786: ! 1787: } else { ! 1788: /* ! 1789: * We have written everything out, so we just start over ! 1790: * from the start of the buffer. No copying. No nothing. ! 1791: */ ! 1792: job->curPos = 0; ! 1793: } ! 1794: } ! 1795: if (finish) { ! 1796: /* ! 1797: * If the finish flag is true, we must loop until we hit ! 1798: * end-of-file on the pipe. This is guaranteed to happen eventually ! 1799: * since the other end of the pipe is now closed (we closed it ! 1800: * explicitly and the child has exited). When we do get an EOF, ! 1801: * finish will be set FALSE and we'll fall through and out. ! 1802: */ ! 1803: goto end_loop; ! 1804: } ! 1805: } else { ! 1806: /* ! 1807: * We've been called to retrieve the output of the job from the ! 1808: * temporary file where it's been squirreled away. This consists of ! 1809: * opening the file, reading the output line by line, being sure not ! 1810: * to print the noPrint line for the shell we used, then close and ! 1811: * remove the temporary file. Very simple. ! 1812: * ! 1813: * Change to read in blocks and do FindSubString type things as for ! 1814: * pipes? That would allow for "@echo -n..." ! 1815: */ ! 1816: oFILE = fopen (job->outFile, "r"); ! 1817: if (oFILE != (FILE *) NULL) { ! 1818: printf ("Results of making %s:\n", job->node->name); ! 1819: while (fgets (inLine, sizeof(inLine), oFILE) != NULL) { ! 1820: register char *cp, *ecp, *endp; ! 1821: ! 1822: cp = inLine; ! 1823: endp = inLine + strlen(inLine); ! 1824: if (endp[-1] == '\n') { ! 1825: *--endp = '\0'; ! 1826: } ! 1827: if (commandShell->noPrint) { ! 1828: ecp = Str_FindSubstring(cp, commandShell->noPrint); ! 1829: while (ecp != (char *)NULL) { ! 1830: if (cp != ecp) { ! 1831: *ecp = '\0'; ! 1832: /* ! 1833: * The only way there wouldn't be a newline after ! 1834: * this line is if it were the last in the buffer. ! 1835: * however, since the non-printable comes after it, ! 1836: * there must be a newline, so we don't print one. ! 1837: */ ! 1838: printf ("%s", cp); ! 1839: } ! 1840: cp = ecp + commandShell->noPLen; ! 1841: if (cp != endp) { ! 1842: /* ! 1843: * Still more to print, look again after skipping ! 1844: * the whitespace following the non-printable ! 1845: * command.... ! 1846: */ ! 1847: cp++; ! 1848: while (*cp == ' ' || *cp == '\t' || *cp == '\n') { ! 1849: cp++; ! 1850: } ! 1851: ecp = Str_FindSubstring(cp, commandShell->noPrint); ! 1852: } else { ! 1853: break; ! 1854: } ! 1855: } ! 1856: } ! 1857: ! 1858: /* ! 1859: * There's still more in that thar buffer. This time, though, ! 1860: * we know there's no newline at the end, so we add one of ! 1861: * our own free will. ! 1862: */ ! 1863: if (*cp != '\0') { ! 1864: printf ("%s\n", cp); ! 1865: } ! 1866: } ! 1867: fclose (oFILE); ! 1868: (void) unlink (job->outFile); ! 1869: } ! 1870: } ! 1871: fflush(stdout); ! 1872: } ! 1873: ! 1874: /*- ! 1875: *----------------------------------------------------------------------- ! 1876: * Job_CatchChildren -- ! 1877: * Handle the exit of a child. Called from Make_Make. ! 1878: * ! 1879: * Results: ! 1880: * none. ! 1881: * ! 1882: * Side Effects: ! 1883: * The job descriptor is removed from the list of children. ! 1884: * ! 1885: * Notes: ! 1886: * We do waits, blocking or not, according to the wisdom of our ! 1887: * caller, until there are no more children to report. For each ! 1888: * job, call JobFinish to finish things off. This will take care of ! 1889: * putting jobs on the stoppedJobs queue. ! 1890: * ! 1891: *----------------------------------------------------------------------- ! 1892: */ ! 1893: void ! 1894: Job_CatchChildren (block) ! 1895: Boolean block; /* TRUE if should block on the wait. */ ! 1896: { ! 1897: int pid; /* pid of dead child */ ! 1898: register Job *job; /* job descriptor for dead child */ ! 1899: LstNode jnode; /* list element for finding job */ ! 1900: union wait status; /* Exit/termination status */ ! 1901: ! 1902: /* ! 1903: * Don't even bother if we know there's no one around. ! 1904: */ ! 1905: if (nLocal == 0) { ! 1906: return; ! 1907: } ! 1908: ! 1909: while ((pid = wait3(&status, (block?0:WNOHANG)|WUNTRACED, ! 1910: (struct rusage *)0)) > 0) ! 1911: { ! 1912: if (DEBUG(JOB)) ! 1913: printf("Process %d exited or stopped.\n", pid); ! 1914: ! 1915: ! 1916: jnode = Lst_Find (jobs, (ClientData)pid, JobCmpPid); ! 1917: ! 1918: if (jnode == NILLNODE) { ! 1919: if (WIFSIGNALED(status) && (status.w_termsig == SIGCONT)) { ! 1920: jnode = Lst_Find(stoppedJobs, (ClientData)pid, JobCmpPid); ! 1921: if (jnode == NILLNODE) { ! 1922: Error("Resumed child (%d) not in table", pid); ! 1923: continue; ! 1924: } ! 1925: job = (Job *)Lst_Datum(jnode); ! 1926: (void)Lst_Remove(stoppedJobs, jnode); ! 1927: } else { ! 1928: Error ("Child (%d) not in table?", pid); ! 1929: continue; ! 1930: } ! 1931: } else { ! 1932: job = (Job *) Lst_Datum (jnode); ! 1933: (void)Lst_Remove (jobs, jnode); ! 1934: nJobs -= 1; ! 1935: if (jobFull && DEBUG(JOB)) { ! 1936: printf("Job queue is no longer full.\n"); ! 1937: } ! 1938: jobFull = FALSE; ! 1939: nLocal -= 1; ! 1940: } ! 1941: ! 1942: JobFinish (job, status); ! 1943: } ! 1944: } ! 1945: ! 1946: /*- ! 1947: *----------------------------------------------------------------------- ! 1948: * Job_CatchOutput -- ! 1949: * Catch the output from our children, if we're using ! 1950: * pipes do so. Otherwise just block time until we get a ! 1951: * signal (most likely a SIGCHLD) since there's no point in ! 1952: * just spinning when there's nothing to do and the reaping ! 1953: * of a child can wait for a while. ! 1954: * ! 1955: * Results: ! 1956: * None ! 1957: * ! 1958: * Side Effects: ! 1959: * Output is read from pipes if we're piping. ! 1960: * ----------------------------------------------------------------------- ! 1961: */ ! 1962: void ! 1963: Job_CatchOutput () ! 1964: { ! 1965: int nfds; ! 1966: struct timeval timeout; ! 1967: fd_set readfds; ! 1968: register LstNode ln; ! 1969: register Job *job; ! 1970: int pnJobs; /* Previous nJobs */ ! 1971: ! 1972: fflush(stdout); ! 1973: #ifdef RMT_WILL_WATCH ! 1974: pnJobs = nJobs; ! 1975: ! 1976: /* ! 1977: * It is possible for us to be called with nJobs equal to 0. This happens ! 1978: * if all the jobs finish and a job that is stopped cannot be run ! 1979: * locally (eg if maxLocal is 0) and cannot be exported. The job will ! 1980: * be placed back on the stoppedJobs queue, Job_Empty() will return false, ! 1981: * Make_Run will call us again when there's nothing for which to wait. ! 1982: * nJobs never changes, so we loop forever. Hence the check. It could ! 1983: * be argued that we should sleep for a bit so as not to swamp the ! 1984: * exportation system with requests. Perhaps we should. ! 1985: * ! 1986: * NOTE: IT IS THE RESPONSIBILITY OF Rmt_Wait TO CALL Job_CatchChildren ! 1987: * IN A TIMELY FASHION TO CATCH ANY LOCALLY RUNNING JOBS THAT EXIT. ! 1988: * It may use the variable nLocal to determine if it needs to call ! 1989: * Job_CatchChildren (if nLocal is 0, there's nothing for which to ! 1990: * wait...) ! 1991: */ ! 1992: while (nJobs != 0 && pnJobs == nJobs) { ! 1993: Rmt_Wait(); ! 1994: } ! 1995: #else ! 1996: if (usePipes) { ! 1997: readfds = outputs; ! 1998: timeout.tv_sec = SEL_SEC; ! 1999: timeout.tv_usec = SEL_USEC; ! 2000: ! 2001: if ((nfds = select (FD_SETSIZE, &readfds, (int *) 0, (int *) 0, &timeout)) < 0) ! 2002: { ! 2003: return; ! 2004: } else { ! 2005: if (Lst_Open (jobs) == FAILURE) { ! 2006: Punt ("Cannot open job table"); ! 2007: } ! 2008: while (nfds && (ln = Lst_Next (jobs)) != NILLNODE) { ! 2009: job = (Job *) Lst_Datum (ln); ! 2010: if (FD_ISSET(job->inPipe, &readfds)) { ! 2011: JobDoOutput (job, FALSE); ! 2012: nfds -= 1; ! 2013: } ! 2014: } ! 2015: Lst_Close (jobs); ! 2016: } ! 2017: } ! 2018: #endif /* RMT_WILL_WATCH */ ! 2019: } ! 2020: ! 2021: /*- ! 2022: *----------------------------------------------------------------------- ! 2023: * Job_Make -- ! 2024: * Start the creation of a target. Basically a front-end for ! 2025: * JobStart used by the Make module. ! 2026: * ! 2027: * Results: ! 2028: * None. ! 2029: * ! 2030: * Side Effects: ! 2031: * Another job is started. ! 2032: * ! 2033: *----------------------------------------------------------------------- ! 2034: */ ! 2035: void ! 2036: Job_Make (gn) ! 2037: GNode *gn; ! 2038: { ! 2039: (void)JobStart (gn, 0, (Job *)NULL); ! 2040: } ! 2041: ! 2042: /*- ! 2043: *----------------------------------------------------------------------- ! 2044: * Job_Init -- ! 2045: * Initialize the process module ! 2046: * ! 2047: * Results: ! 2048: * none ! 2049: * ! 2050: * Side Effects: ! 2051: * lists and counters are initialized ! 2052: *----------------------------------------------------------------------- ! 2053: */ ! 2054: void ! 2055: Job_Init (maxproc, maxlocal) ! 2056: int maxproc; /* the greatest number of jobs which may be ! 2057: * running at one time */ ! 2058: int maxlocal; /* the greatest number of local jobs which may ! 2059: * be running at once. */ ! 2060: { ! 2061: GNode *begin; /* node for commands to do at the very start */ ! 2062: ! 2063: sprintf (tfile, "/tmp/make%05d", getpid()); ! 2064: ! 2065: jobs = Lst_Init (FALSE); ! 2066: stoppedJobs = Lst_Init(FALSE); ! 2067: maxJobs = maxproc; ! 2068: maxLocal = maxlocal; ! 2069: nJobs = 0; ! 2070: nLocal = 0; ! 2071: jobFull = FALSE; ! 2072: ! 2073: aborting = 0; ! 2074: errors = 0; ! 2075: ! 2076: lastNode = NILGNODE; ! 2077: ! 2078: if (maxJobs == 1) { ! 2079: /* ! 2080: * If only one job can run at a time, there's no need for a banner, ! 2081: * no is there? ! 2082: */ ! 2083: targFmt = ""; ! 2084: } else { ! 2085: targFmt = TARG_FMT; ! 2086: } ! 2087: ! 2088: if (shellPath == (char *) NULL) { ! 2089: /* ! 2090: * The user didn't specify a shell to use, so we are using the ! 2091: * default one... Both the absolute path and the last component ! 2092: * must be set. The last component is taken from the 'name' field ! 2093: * of the default shell description pointed-to by commandShell. ! 2094: * All default shells are located in _PATH_DEFSHELLDIR. ! 2095: */ ! 2096: shellName = commandShell->name; ! 2097: shellPath = str_concat (_PATH_DEFSHELLDIR, shellName, STR_ADDSLASH); ! 2098: } ! 2099: ! 2100: if (commandShell->exit == (char *)NULL) { ! 2101: commandShell->exit = ""; ! 2102: } ! 2103: if (commandShell->echo == (char *)NULL) { ! 2104: commandShell->echo = ""; ! 2105: } ! 2106: ! 2107: /* ! 2108: * Catch the four signals that POSIX specifies if they aren't ignored. ! 2109: * JobPassSig will take care of calling JobInterrupt if appropriate. ! 2110: */ ! 2111: if (signal (SIGINT, SIG_IGN) != SIG_IGN) { ! 2112: signal (SIGINT, JobPassSig); ! 2113: } ! 2114: if (signal (SIGHUP, SIG_IGN) != SIG_IGN) { ! 2115: signal (SIGHUP, JobPassSig); ! 2116: } ! 2117: if (signal (SIGQUIT, SIG_IGN) != SIG_IGN) { ! 2118: signal (SIGQUIT, JobPassSig); ! 2119: } ! 2120: if (signal (SIGTERM, SIG_IGN) != SIG_IGN) { ! 2121: signal (SIGTERM, JobPassSig); ! 2122: } ! 2123: /* ! 2124: * There are additional signals that need to be caught and passed if ! 2125: * either the export system wants to be told directly of signals or if ! 2126: * we're giving each job its own process group (since then it won't get ! 2127: * signals from the terminal driver as we own the terminal) ! 2128: */ ! 2129: #if defined(RMT_WANTS_SIGNALS) || defined(USE_PGRP) ! 2130: if (signal (SIGTSTP, SIG_IGN) != SIG_IGN) { ! 2131: signal (SIGTSTP, JobPassSig); ! 2132: } ! 2133: if (signal (SIGTTOU, SIG_IGN) != SIG_IGN) { ! 2134: signal (SIGTTOU, JobPassSig); ! 2135: } ! 2136: if (signal (SIGTTIN, SIG_IGN) != SIG_IGN) { ! 2137: signal (SIGTTIN, JobPassSig); ! 2138: } ! 2139: if (signal (SIGWINCH, SIG_IGN) != SIG_IGN) { ! 2140: signal (SIGWINCH, JobPassSig); ! 2141: } ! 2142: #endif ! 2143: ! 2144: begin = Targ_FindNode (".BEGIN", TARG_NOCREATE); ! 2145: ! 2146: if (begin != NILGNODE) { ! 2147: JobStart (begin, JOB_SPECIAL, (Job *)0); ! 2148: while (nJobs) { ! 2149: Job_CatchOutput(); ! 2150: #ifndef RMT_WILL_WATCH ! 2151: Job_CatchChildren (!usePipes); ! 2152: #endif /* RMT_WILL_WATCH */ ! 2153: } ! 2154: } ! 2155: postCommands = Targ_FindNode (".END", TARG_CREATE); ! 2156: } ! 2157: ! 2158: /*- ! 2159: *----------------------------------------------------------------------- ! 2160: * Job_Full -- ! 2161: * See if the job table is full. It is considered full if it is OR ! 2162: * if we are in the process of aborting OR if we have ! 2163: * reached/exceeded our local quota. This prevents any more jobs ! 2164: * from starting up. ! 2165: * ! 2166: * Results: ! 2167: * TRUE if the job table is full, FALSE otherwise ! 2168: * Side Effects: ! 2169: * None. ! 2170: *----------------------------------------------------------------------- ! 2171: */ ! 2172: Boolean ! 2173: Job_Full () ! 2174: { ! 2175: return (aborting || jobFull); ! 2176: } ! 2177: ! 2178: /*- ! 2179: *----------------------------------------------------------------------- ! 2180: * Job_Empty -- ! 2181: * See if the job table is empty. Because the local concurrency may ! 2182: * be set to 0, it is possible for the job table to become empty, ! 2183: * while the list of stoppedJobs remains non-empty. In such a case, ! 2184: * we want to restart as many jobs as we can. ! 2185: * ! 2186: * Results: ! 2187: * TRUE if it is. FALSE if it ain't. ! 2188: * ! 2189: * Side Effects: ! 2190: * None. ! 2191: * ! 2192: * ----------------------------------------------------------------------- ! 2193: */ ! 2194: Boolean ! 2195: Job_Empty () ! 2196: { ! 2197: if (nJobs == 0) { ! 2198: if (!Lst_IsEmpty(stoppedJobs) && !aborting) { ! 2199: /* ! 2200: * The job table is obviously not full if it has no jobs in ! 2201: * it...Try and restart the stopped jobs. ! 2202: */ ! 2203: jobFull = FALSE; ! 2204: while (!jobFull && !Lst_IsEmpty(stoppedJobs)) { ! 2205: JobRestart((Job *)Lst_DeQueue(stoppedJobs)); ! 2206: } ! 2207: return(FALSE); ! 2208: } else { ! 2209: return(TRUE); ! 2210: } ! 2211: } else { ! 2212: return(FALSE); ! 2213: } ! 2214: } ! 2215: ! 2216: /*- ! 2217: *----------------------------------------------------------------------- ! 2218: * JobMatchShell -- ! 2219: * Find a matching shell in 'shells' given its final component. ! 2220: * ! 2221: * Results: ! 2222: * A pointer to the Shell structure. ! 2223: * ! 2224: * Side Effects: ! 2225: * None. ! 2226: * ! 2227: *----------------------------------------------------------------------- ! 2228: */ ! 2229: static Shell * ! 2230: JobMatchShell (name) ! 2231: char *name; /* Final component of shell path */ ! 2232: { ! 2233: register Shell *sh; /* Pointer into shells table */ ! 2234: Shell *match; /* Longest-matching shell */ ! 2235: register char *cp1, ! 2236: *cp2; ! 2237: char *eoname; ! 2238: ! 2239: eoname = name + strlen (name); ! 2240: ! 2241: match = (Shell *) NULL; ! 2242: ! 2243: for (sh = shells; sh->name != NULL; sh++) { ! 2244: for (cp1 = eoname - strlen (sh->name), cp2 = sh->name; ! 2245: *cp1 != '\0' && *cp1 == *cp2; ! 2246: cp1++, cp2++) { ! 2247: continue; ! 2248: } ! 2249: if (*cp1 != *cp2) { ! 2250: continue; ! 2251: } else if (match == (Shell *) NULL || ! 2252: strlen (match->name) < strlen (sh->name)) { ! 2253: match = sh; ! 2254: } ! 2255: } ! 2256: return (match == (Shell *) NULL ? sh : match); ! 2257: } ! 2258: ! 2259: /*- ! 2260: *----------------------------------------------------------------------- ! 2261: * Job_ParseShell -- ! 2262: * Parse a shell specification and set up commandShell, shellPath ! 2263: * and shellName appropriately. ! 2264: * ! 2265: * Results: ! 2266: * FAILURE if the specification was incorrect. ! 2267: * ! 2268: * Side Effects: ! 2269: * commandShell points to a Shell structure (either predefined or ! 2270: * created from the shell spec), shellPath is the full path of the ! 2271: * shell described by commandShell, while shellName is just the ! 2272: * final component of shellPath. ! 2273: * ! 2274: * Notes: ! 2275: * A shell specification consists of a .SHELL target, with dependency ! 2276: * operator, followed by a series of blank-separated words. Double ! 2277: * quotes can be used to use blanks in words. A backslash escapes ! 2278: * anything (most notably a double-quote and a space) and ! 2279: * provides the functionality it does in C. Each word consists of ! 2280: * keyword and value separated by an equal sign. There should be no ! 2281: * unnecessary spaces in the word. The keywords are as follows: ! 2282: * name Name of shell. ! 2283: * path Location of shell. Overrides "name" if given ! 2284: * quiet Command to turn off echoing. ! 2285: * echo Command to turn echoing on ! 2286: * filter Result of turning off echoing that shouldn't be ! 2287: * printed. ! 2288: * echoFlag Flag to turn echoing on at the start ! 2289: * errFlag Flag to turn error checking on at the start ! 2290: * hasErrCtl True if shell has error checking control ! 2291: * check Command to turn on error checking if hasErrCtl ! 2292: * is TRUE or template of command to echo a command ! 2293: * for which error checking is off if hasErrCtl is ! 2294: * FALSE. ! 2295: * ignore Command to turn off error checking if hasErrCtl ! 2296: * is TRUE or template of command to execute a ! 2297: * command so as to ignore any errors it returns if ! 2298: * hasErrCtl is FALSE. ! 2299: * ! 2300: *----------------------------------------------------------------------- ! 2301: */ ! 2302: ReturnStatus ! 2303: Job_ParseShell (line) ! 2304: char *line; /* The shell spec */ ! 2305: { ! 2306: char **words; ! 2307: int wordCount; ! 2308: register char **argv; ! 2309: register int argc; ! 2310: char *path; ! 2311: Shell newShell; ! 2312: Boolean fullSpec = FALSE; ! 2313: ! 2314: while (isspace (*line)) { ! 2315: line++; ! 2316: } ! 2317: words = brk_string (line, &wordCount); ! 2318: ! 2319: bzero ((Address)&newShell, sizeof(newShell)); ! 2320: ! 2321: /* ! 2322: * Parse the specification by keyword ! 2323: */ ! 2324: for (path = (char *)NULL, argc = wordCount - 1, argv = words + 1; ! 2325: argc != 0; ! 2326: argc--, argv++) { ! 2327: if (strncmp (*argv, "path=", 5) == 0) { ! 2328: path = &argv[0][5]; ! 2329: } else if (strncmp (*argv, "name=", 5) == 0) { ! 2330: newShell.name = &argv[0][5]; ! 2331: } else { ! 2332: if (strncmp (*argv, "quiet=", 6) == 0) { ! 2333: newShell.echoOff = &argv[0][6]; ! 2334: } else if (strncmp (*argv, "echo=", 5) == 0) { ! 2335: newShell.echoOn = &argv[0][5]; ! 2336: } else if (strncmp (*argv, "filter=", 7) == 0) { ! 2337: newShell.noPrint = &argv[0][7]; ! 2338: newShell.noPLen = strlen(newShell.noPrint); ! 2339: } else if (strncmp (*argv, "echoFlag=", 9) == 0) { ! 2340: newShell.echo = &argv[0][9]; ! 2341: } else if (strncmp (*argv, "errFlag=", 8) == 0) { ! 2342: newShell.exit = &argv[0][8]; ! 2343: } else if (strncmp (*argv, "hasErrCtl=", 10) == 0) { ! 2344: char c = argv[0][10]; ! 2345: newShell.hasErrCtl = !((c != 'Y') && (c != 'y') && ! 2346: (c != 'T') && (c != 't')); ! 2347: } else if (strncmp (*argv, "check=", 6) == 0) { ! 2348: newShell.errCheck = &argv[0][6]; ! 2349: } else if (strncmp (*argv, "ignore=", 7) == 0) { ! 2350: newShell.ignErr = &argv[0][7]; ! 2351: } else { ! 2352: Parse_Error (PARSE_FATAL, "Unknown keyword \"%s\"", ! 2353: *argv); ! 2354: return (FAILURE); ! 2355: } ! 2356: fullSpec = TRUE; ! 2357: } ! 2358: } ! 2359: ! 2360: if (path == (char *)NULL) { ! 2361: /* ! 2362: * If no path was given, the user wants one of the pre-defined shells, ! 2363: * yes? So we find the one s/he wants with the help of JobMatchShell ! 2364: * and set things up the right way. shellPath will be set up by ! 2365: * Job_Init. ! 2366: */ ! 2367: if (newShell.name == (char *)NULL) { ! 2368: Parse_Error (PARSE_FATAL, "Neither path nor name specified"); ! 2369: return (FAILURE); ! 2370: } else { ! 2371: commandShell = JobMatchShell (newShell.name); ! 2372: shellName = newShell.name; ! 2373: } ! 2374: } else { ! 2375: /* ! 2376: * The user provided a path. If s/he gave nothing else (fullSpec is ! 2377: * FALSE), try and find a matching shell in the ones we know of. ! 2378: * Else we just take the specification at its word and copy it ! 2379: * to a new location. In either case, we need to record the ! 2380: * path the user gave for the shell. ! 2381: */ ! 2382: shellPath = path; ! 2383: path = rindex (path, '/'); ! 2384: if (path == (char *)NULL) { ! 2385: path = shellPath; ! 2386: } else { ! 2387: path += 1; ! 2388: } ! 2389: if (newShell.name != (char *)NULL) { ! 2390: shellName = newShell.name; ! 2391: } else { ! 2392: shellName = path; ! 2393: } ! 2394: if (!fullSpec) { ! 2395: commandShell = JobMatchShell (shellName); ! 2396: } else { ! 2397: commandShell = (Shell *) emalloc(sizeof(Shell)); ! 2398: *commandShell = newShell; ! 2399: } ! 2400: } ! 2401: ! 2402: if (commandShell->echoOn && commandShell->echoOff) { ! 2403: commandShell->hasEchoCtl = TRUE; ! 2404: } ! 2405: ! 2406: if (!commandShell->hasErrCtl) { ! 2407: if (commandShell->errCheck == (char *)NULL) { ! 2408: commandShell->errCheck = ""; ! 2409: } ! 2410: if (commandShell->ignErr == (char *)NULL) { ! 2411: commandShell->ignErr = "%s\n"; ! 2412: } ! 2413: } ! 2414: ! 2415: /* ! 2416: * Do not free up the words themselves, since they might be in use by the ! 2417: * shell specification... ! 2418: */ ! 2419: free (words); ! 2420: return SUCCESS; ! 2421: } ! 2422: ! 2423: /*- ! 2424: *----------------------------------------------------------------------- ! 2425: * JobInterrupt -- ! 2426: * Handle the receipt of an interrupt. ! 2427: * ! 2428: * Results: ! 2429: * None ! 2430: * ! 2431: * Side Effects: ! 2432: * All children are killed. Another job will be started if the ! 2433: * .INTERRUPT target was given. ! 2434: *----------------------------------------------------------------------- ! 2435: */ ! 2436: static void ! 2437: JobInterrupt (runINTERRUPT) ! 2438: int runINTERRUPT; /* Non-zero if commands for the .INTERRUPT ! 2439: * target should be executed */ ! 2440: { ! 2441: LstNode ln; /* element in job table */ ! 2442: Job *job; /* job descriptor in that element */ ! 2443: GNode *interrupt; /* the node describing the .INTERRUPT target */ ! 2444: ! 2445: aborting = ABORT_INTERRUPT; ! 2446: ! 2447: (void)Lst_Open (jobs); ! 2448: while ((ln = Lst_Next (jobs)) != NILLNODE) { ! 2449: job = (Job *) Lst_Datum (ln); ! 2450: ! 2451: if (!Targ_Precious (job->node)) { ! 2452: char *file = (job->node->path == (char *)NULL ? ! 2453: job->node->name : ! 2454: job->node->path); ! 2455: if (unlink (file) == 0) { ! 2456: Error ("*** %s removed", file); ! 2457: } ! 2458: } ! 2459: #ifdef RMT_WANTS_SIGNALS ! 2460: if (job->flags & JOB_REMOTE) { ! 2461: /* ! 2462: * If job is remote, let the Rmt module do the killing. ! 2463: */ ! 2464: if (!Rmt_Signal(job, SIGINT)) { ! 2465: /* ! 2466: * If couldn't kill the thing, finish it out now with an ! 2467: * error code, since no exit report will come in likely. ! 2468: */ ! 2469: union wait status; ! 2470: ! 2471: status.w_status = 0; ! 2472: status.w_retcode = 1; ! 2473: JobFinish(job, status); ! 2474: } ! 2475: } else if (job->pid) { ! 2476: KILL(job->pid, SIGINT); ! 2477: } ! 2478: #else ! 2479: if (job->pid) { ! 2480: KILL(job->pid, SIGINT); ! 2481: } ! 2482: #endif /* RMT_WANTS_SIGNALS */ ! 2483: } ! 2484: Lst_Close (jobs); ! 2485: ! 2486: if (runINTERRUPT && !touchFlag) { ! 2487: interrupt = Targ_FindNode (".INTERRUPT", TARG_NOCREATE); ! 2488: if (interrupt != NILGNODE) { ! 2489: ignoreErrors = FALSE; ! 2490: ! 2491: JobStart (interrupt, JOB_IGNDOTS, (Job *)0); ! 2492: while (nJobs) { ! 2493: Job_CatchOutput(); ! 2494: #ifndef RMT_WILL_WATCH ! 2495: Job_CatchChildren (!usePipes); ! 2496: #endif /* RMT_WILL_WATCH */ ! 2497: } ! 2498: } ! 2499: } ! 2500: (void) unlink (tfile); ! 2501: exit (0); ! 2502: } ! 2503: ! 2504: /* ! 2505: *----------------------------------------------------------------------- ! 2506: * Job_End -- ! 2507: * Do final processing such as the running of the commands ! 2508: * attached to the .END target. ! 2509: * ! 2510: * Results: ! 2511: * Number of errors reported. ! 2512: * ! 2513: * Side Effects: ! 2514: * The process' temporary file (tfile) is removed if it still ! 2515: * existed. ! 2516: *----------------------------------------------------------------------- ! 2517: */ ! 2518: int ! 2519: Job_End () ! 2520: { ! 2521: if (postCommands != NILGNODE && !Lst_IsEmpty (postCommands->commands)) { ! 2522: if (errors) { ! 2523: Error ("Errors reported so .END ignored"); ! 2524: } else { ! 2525: JobStart (postCommands, JOB_SPECIAL | JOB_IGNDOTS, ! 2526: (Job *)0); ! 2527: ! 2528: while (nJobs) { ! 2529: Job_CatchOutput(); ! 2530: #ifndef RMT_WILL_WATCH ! 2531: Job_CatchChildren (!usePipes); ! 2532: #endif /* RMT_WILL_WATCH */ ! 2533: } ! 2534: } ! 2535: } ! 2536: (void) unlink (tfile); ! 2537: return(errors); ! 2538: } ! 2539: ! 2540: /*- ! 2541: *----------------------------------------------------------------------- ! 2542: * Job_Wait -- ! 2543: * Waits for all running jobs to finish and returns. Sets 'aborting' ! 2544: * to ABORT_WAIT to prevent other jobs from starting. ! 2545: * ! 2546: * Results: ! 2547: * None. ! 2548: * ! 2549: * Side Effects: ! 2550: * Currently running jobs finish. ! 2551: * ! 2552: *----------------------------------------------------------------------- ! 2553: */ ! 2554: void ! 2555: Job_Wait() ! 2556: { ! 2557: aborting = ABORT_WAIT; ! 2558: while (nJobs != 0) { ! 2559: Job_CatchOutput(); ! 2560: #ifndef RMT_WILL_WATCH ! 2561: Job_CatchChildren(!usePipes); ! 2562: #endif /* RMT_WILL_WATCH */ ! 2563: } ! 2564: aborting = 0; ! 2565: } ! 2566: ! 2567: /*- ! 2568: *----------------------------------------------------------------------- ! 2569: * Job_AbortAll -- ! 2570: * Abort all currently running jobs without handling output or anything. ! 2571: * This function is to be called only in the event of a major ! 2572: * error. Most definitely NOT to be called from JobInterrupt. ! 2573: * ! 2574: * Results: ! 2575: * None ! 2576: * ! 2577: * Side Effects: ! 2578: * All children are killed, not just the firstborn ! 2579: *----------------------------------------------------------------------- ! 2580: */ ! 2581: void ! 2582: Job_AbortAll () ! 2583: { ! 2584: LstNode ln; /* element in job table */ ! 2585: Job *job; /* the job descriptor in that element */ ! 2586: int foo; ! 2587: ! 2588: aborting = ABORT_ERROR; ! 2589: ! 2590: if (nJobs) { ! 2591: ! 2592: (void)Lst_Open (jobs); ! 2593: while ((ln = Lst_Next (jobs)) != NILLNODE) { ! 2594: job = (Job *) Lst_Datum (ln); ! 2595: ! 2596: /* ! 2597: * kill the child process with increasingly drastic signals to make ! 2598: * darn sure it's dead. ! 2599: */ ! 2600: #ifdef RMT_WANTS_SIGNALS ! 2601: if (job->flags & JOB_REMOTE) { ! 2602: Rmt_Signal(job, SIGINT); ! 2603: Rmt_Signal(job, SIGKILL); ! 2604: } else { ! 2605: KILL(job->pid, SIGINT); ! 2606: KILL(job->pid, SIGKILL); ! 2607: } ! 2608: #else ! 2609: KILL(job->pid, SIGINT); ! 2610: KILL(job->pid, SIGKILL); ! 2611: #endif /* RMT_WANTS_SIGNALS */ ! 2612: } ! 2613: } ! 2614: ! 2615: /* ! 2616: * Catch as many children as want to report in at first, then give up ! 2617: */ ! 2618: while (wait3(&foo, WNOHANG, (struct rusage *)0) > 0) { ! 2619: ; ! 2620: } ! 2621: (void) unlink (tfile); ! 2622: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.