Annotation of 43BSDReno/usr.bin/make/job.c, revision 1.1

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: }

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.