job.c revision 8874
11590Srgrimes/* 25814Sjkh * Copyright (c) 1988, 1989, 1990 The Regents of the University of California. 35814Sjkh * Copyright (c) 1988, 1989 by Adam de Boor 41590Srgrimes * Copyright (c) 1989 by Berkeley Softworks 51590Srgrimes * All rights reserved. 61590Srgrimes * 71590Srgrimes * This code is derived from software contributed to Berkeley by 81590Srgrimes * Adam de Boor. 91590Srgrimes * 101590Srgrimes * Redistribution and use in source and binary forms, with or without 111590Srgrimes * modification, are permitted provided that the following conditions 121590Srgrimes * are met: 131590Srgrimes * 1. Redistributions of source code must retain the above copyright 141590Srgrimes * notice, this list of conditions and the following disclaimer. 151590Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 161590Srgrimes * notice, this list of conditions and the following disclaimer in the 171590Srgrimes * documentation and/or other materials provided with the distribution. 181590Srgrimes * 3. All advertising materials mentioning features or use of this software 191590Srgrimes * must display the following acknowledgement: 201590Srgrimes * This product includes software developed by the University of 211590Srgrimes * California, Berkeley and its contributors. 221590Srgrimes * 4. Neither the name of the University nor the names of its contributors 231590Srgrimes * may be used to endorse or promote products derived from this software 241590Srgrimes * without specific prior written permission. 251590Srgrimes * 261590Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 271590Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 281590Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 291590Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 301590Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 311590Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 321590Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 331590Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 341590Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 351590Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 361590Srgrimes * SUCH DAMAGE. 371590Srgrimes */ 381590Srgrimes 391590Srgrimes#ifndef lint 401590Srgrimesstatic char sccsid[] = "@(#)job.c 8.2 (Berkeley) 3/19/94"; 411590Srgrimes#endif /* not lint */ 421590Srgrimes 431590Srgrimes/*- 441590Srgrimes * job.c -- 451590Srgrimes * handle the creation etc. of our child processes. 461590Srgrimes * 471590Srgrimes * Interface: 481590Srgrimes * Job_Make Start the creation of the given target. 491590Srgrimes * 501590Srgrimes * Job_CatchChildren Check for and handle the termination of any 511590Srgrimes * children. This must be called reasonably 521590Srgrimes * frequently to keep the whole make going at 531590Srgrimes * a decent clip, since job table entries aren't 541590Srgrimes * removed until their process is caught this way. 551590Srgrimes * Its single argument is TRUE if the function 561590Srgrimes * should block waiting for a child to terminate. 571590Srgrimes * 581590Srgrimes * Job_CatchOutput Print any output our children have produced. 591590Srgrimes * Should also be called fairly frequently to 601590Srgrimes * keep the user informed of what's going on. 611590Srgrimes * If no output is waiting, it will block for 621590Srgrimes * a time given by the SEL_* constants, below, 631590Srgrimes * or until output is ready. 641590Srgrimes * 651590Srgrimes * Job_Init Called to intialize this module. in addition, 661590Srgrimes * any commands attached to the .BEGIN target 671590Srgrimes * are executed before this function returns. 681590Srgrimes * Hence, the makefile must have been parsed 691590Srgrimes * before this function is called. 701590Srgrimes * 711590Srgrimes * Job_Full Return TRUE if the job table is filled. 721590Srgrimes * 731590Srgrimes * Job_Empty Return TRUE if the job table is completely 741590Srgrimes * empty. 751590Srgrimes * 761590Srgrimes * Job_ParseShell Given the line following a .SHELL target, parse 771590Srgrimes * the line as a shell specification. Returns 781590Srgrimes * FAILURE if the spec was incorrect. 791590Srgrimes * 801590Srgrimes * Job_End Perform any final processing which needs doing. 811590Srgrimes * This includes the execution of any commands 821590Srgrimes * which have been/were attached to the .END 831590Srgrimes * target. It should only be called when the 841590Srgrimes * job table is empty. 851590Srgrimes * 861590Srgrimes * Job_AbortAll Abort all currently running jobs. It doesn't 871590Srgrimes * handle output or do anything for the jobs, 881590Srgrimes * just kills them. It should only be called in 891590Srgrimes * an emergency, as it were. 901590Srgrimes * 911590Srgrimes * Job_CheckCommands Verify that the commands for a target are 921590Srgrimes * ok. Provide them if necessary and possible. 931590Srgrimes * 941590Srgrimes * Job_Touch Update a target without really updating it. 951590Srgrimes * 961590Srgrimes * Job_Wait Wait for all currently-running jobs to finish. 971590Srgrimes */ 981590Srgrimes 991590Srgrimes#include <sys/types.h> 1005814Sjkh#include <sys/signal.h> 1011590Srgrimes#include <sys/stat.h> 1021590Srgrimes#include <sys/file.h> 1031590Srgrimes#include <sys/time.h> 1041590Srgrimes#include <sys/wait.h> 1055814Sjkh#include <fcntl.h> 1061590Srgrimes#include <errno.h> 1071590Srgrimes#include <stdio.h> 1081590Srgrimes#include <string.h> 1095814Sjkh#include <signal.h> 1101590Srgrimes#include "make.h" 1111590Srgrimes#include "hash.h" 1121590Srgrimes#include "dir.h" 1131590Srgrimes#include "job.h" 1141590Srgrimes#include "pathnames.h" 1151590Srgrimes 1161590Srgrimesextern int errno; 1171590Srgrimes 1181590Srgrimes/* 1198874Srgrimes * error handling variables 1201590Srgrimes */ 1211590Srgrimesstatic int errors = 0; /* number of errors reported */ 1221590Srgrimesstatic int aborting = 0; /* why is the make aborting? */ 1231590Srgrimes#define ABORT_ERROR 1 /* Because of an error */ 1241590Srgrimes#define ABORT_INTERRUPT 2 /* Because it was interrupted */ 1251590Srgrimes#define ABORT_WAIT 3 /* Waiting for jobs to finish */ 1261590Srgrimes 1271590Srgrimes 1281590Srgrimes/* 1291590Srgrimes * post-make command processing. The node postCommands is really just the 1301590Srgrimes * .END target but we keep it around to avoid having to search for it 1311590Srgrimes * all the time. 1321590Srgrimes */ 1331590Srgrimesstatic GNode *postCommands; /* node containing commands to execute when 1341590Srgrimes * everything else is done */ 1351590Srgrimesstatic int numCommands; /* The number of commands actually printed 1361590Srgrimes * for a target. Should this number be 1371590Srgrimes * 0, no shell will be executed. */ 1381590Srgrimes 1391590Srgrimes 1401590Srgrimes/* 1411590Srgrimes * Return values from JobStart. 1421590Srgrimes */ 1431590Srgrimes#define JOB_RUNNING 0 /* Job is running */ 1441590Srgrimes#define JOB_ERROR 1 /* Error in starting the job */ 1451590Srgrimes#define JOB_FINISHED 2 /* The job is already finished */ 1461590Srgrimes#define JOB_STOPPED 3 /* The job is stopped */ 1471590Srgrimes 1481590Srgrimes/* 1491590Srgrimes * tfile is the name of a file into which all shell commands are put. It is 1501590Srgrimes * used over by removing it before the child shell is executed. The XXXXX in 1511590Srgrimes * the string are replaced by the pid of the make process in a 5-character 1528874Srgrimes * field with leading zeroes. 1531590Srgrimes */ 1541590Srgrimesstatic char tfile[] = TMPPAT; 1551590Srgrimes 1561590Srgrimes 1571590Srgrimes/* 1581590Srgrimes * Descriptions for various shells. 1591590Srgrimes */ 1601590Srgrimesstatic Shell shells[] = { 1611590Srgrimes /* 1621590Srgrimes * CSH description. The csh can do echo control by playing 1631590Srgrimes * with the setting of the 'echo' shell variable. Sadly, 1641590Srgrimes * however, it is unable to do error control nicely. 1651590Srgrimes */ 1661590Srgrimes{ 1671590Srgrimes "csh", 1681590Srgrimes TRUE, "unset verbose", "set verbose", "unset verbose", 10, 1691590Srgrimes FALSE, "echo \"%s\"\n", "csh -c \"%s || exit 0\"", 1701590Srgrimes "v", "e", 1711590Srgrimes}, 1721590Srgrimes /* 1731590Srgrimes * SH description. Echo control is also possible and, under 1741590Srgrimes * sun UNIX anyway, one can even control error checking. 1751590Srgrimes */ 1761590Srgrimes{ 1771590Srgrimes "sh", 1781590Srgrimes TRUE, "set -", "set -v", "set -", 5, 1791590Srgrimes FALSE, "echo \"%s\"\n", "sh -c '%s || exit 0'\n", 1801590Srgrimes "v", "e", 1811590Srgrimes}, 1821590Srgrimes /* 1831590Srgrimes * UNKNOWN. 1841590Srgrimes */ 1851590Srgrimes{ 1861590Srgrimes (char *)0, 1871590Srgrimes FALSE, (char *)0, (char *)0, (char *)0, 0, 1881590Srgrimes FALSE, (char *)0, (char *)0, 1891590Srgrimes (char *)0, (char *)0, 1901590Srgrimes} 1911590Srgrimes}; 1921590Srgrimesstatic Shell *commandShell = &shells[DEFSHELL];/* this is the shell to 1931590Srgrimes * which we pass all 1941590Srgrimes * commands in the Makefile. 1951590Srgrimes * It is set by the 1961590Srgrimes * Job_ParseShell function */ 1971590Srgrimesstatic char *shellPath = (char *) NULL, /* full pathname of 1981590Srgrimes * executable image */ 1991590Srgrimes *shellName; /* last component of shell */ 2001590Srgrimes 2011590Srgrimes 2021590Srgrimesstatic int maxJobs; /* The most children we can run at once */ 2031590Srgrimesstatic int maxLocal; /* The most local ones we can have */ 2045814Sjkhint nJobs; /* The number of children currently running */ 2055814Sjkhint nLocal; /* The number of local children */ 2065814SjkhLst jobs; /* The structures that describe them */ 2075814SjkhBoolean jobFull; /* Flag to tell when the job table is full. It 2081590Srgrimes * is set TRUE when (1) the total number of 2091590Srgrimes * running jobs equals the maximum allowed or 2101590Srgrimes * (2) a job can only be run locally, but 2111590Srgrimes * nLocal equals maxLocal */ 2121590Srgrimes#ifndef RMT_WILL_WATCH 2131590Srgrimesstatic fd_set outputs; /* Set of descriptors of pipes connected to 2141590Srgrimes * the output channels of children */ 2151590Srgrimes#endif 2161590Srgrimes 2175814SjkhGNode *lastNode; /* The node for which output was most recently 2181590Srgrimes * produced. */ 2195814Sjkhchar *targFmt; /* Format string to use to head output from a 2201590Srgrimes * job when it's not the most-recent job heard 2211590Srgrimes * from */ 2221590Srgrimes#define TARG_FMT "--- %s ---\n" /* Default format */ 2231590Srgrimes 2241590Srgrimes/* 2251590Srgrimes * When JobStart attempts to run a job remotely but can't, and isn't allowed 2261590Srgrimes * to run the job locally, or when Job_CatchChildren detects a job that has 2271590Srgrimes * been migrated home, the job is placed on the stoppedJobs queue to be run 2288874Srgrimes * when the next job finishes. 2291590Srgrimes */ 2305814SjkhLst stoppedJobs; /* Lst of Job structures describing 2311590Srgrimes * jobs that were stopped due to concurrency 2321590Srgrimes * limits or migration home */ 2331590Srgrimes 2341590Srgrimes 2351590Srgrimes#if defined(USE_PGRP) && defined(SYSV) 2361590Srgrimes#define KILL(pid,sig) killpg (-(pid),(sig)) 2371590Srgrimes#else 2381590Srgrimes# if defined(USE_PGRP) 2391590Srgrimes#define KILL(pid,sig) killpg ((pid),(sig)) 2401590Srgrimes# else 2411590Srgrimes#define KILL(pid,sig) kill ((pid),(sig)) 2421590Srgrimes# endif 2431590Srgrimes#endif 2441590Srgrimes 2455814Sjkhstatic int JobCondPassSig __P((ClientData, ClientData)); 2461590Srgrimesstatic void JobPassSig __P((int)); 2475814Sjkhstatic int JobCmpPid __P((ClientData, ClientData)); 2485814Sjkhstatic int JobPrintCommand __P((ClientData, ClientData)); 2495814Sjkhstatic int JobSaveCommand __P((ClientData, ClientData)); 2501590Srgrimesstatic void JobFinish __P((Job *, union wait)); 2511590Srgrimesstatic void JobExec __P((Job *, char **)); 2521590Srgrimesstatic void JobMakeArgv __P((Job *, char **)); 2531590Srgrimesstatic void JobRestart __P((Job *)); 2541590Srgrimesstatic int JobStart __P((GNode *, int, Job *)); 2551590Srgrimesstatic void JobDoOutput __P((Job *, Boolean)); 2561590Srgrimesstatic Shell *JobMatchShell __P((char *)); 2571590Srgrimesstatic void JobInterrupt __P((int)); 2581590Srgrimes 2591590Srgrimes/*- 2601590Srgrimes *----------------------------------------------------------------------- 2611590Srgrimes * JobCondPassSig -- 2621590Srgrimes * Pass a signal to a job if the job is remote or if USE_PGRP 2631590Srgrimes * is defined. 2641590Srgrimes * 2651590Srgrimes * Results: 2661590Srgrimes * === 0 2671590Srgrimes * 2681590Srgrimes * Side Effects: 2691590Srgrimes * None, except the job may bite it. 2701590Srgrimes * 2711590Srgrimes *----------------------------------------------------------------------- 2721590Srgrimes */ 2731590Srgrimesstatic int 2745814SjkhJobCondPassSig(jobp, signop) 2755814Sjkh ClientData jobp; /* Job to biff */ 2765814Sjkh ClientData signop; /* Signal to send it */ 2771590Srgrimes{ 2785814Sjkh Job *job = (Job *) jobp; 2795814Sjkh int signo = *(int *) signop; 2801590Srgrimes#ifdef RMT_WANTS_SIGNALS 2811590Srgrimes if (job->flags & JOB_REMOTE) { 2821590Srgrimes (void)Rmt_Signal(job, signo); 2831590Srgrimes } else { 2841590Srgrimes KILL(job->pid, signo); 2851590Srgrimes } 2861590Srgrimes#else 2871590Srgrimes /* 2881590Srgrimes * Assume that sending the signal to job->pid will signal any remote 2891590Srgrimes * job as well. 2901590Srgrimes */ 2911590Srgrimes KILL(job->pid, signo); 2921590Srgrimes#endif 2931590Srgrimes return(0); 2941590Srgrimes} 2951590Srgrimes 2961590Srgrimes/*- 2971590Srgrimes *----------------------------------------------------------------------- 2981590Srgrimes * JobPassSig -- 2991590Srgrimes * Pass a signal on to all remote jobs and to all local jobs if 3001590Srgrimes * USE_PGRP is defined, then die ourselves. 3011590Srgrimes * 3021590Srgrimes * Results: 3031590Srgrimes * None. 3041590Srgrimes * 3051590Srgrimes * Side Effects: 3061590Srgrimes * We die by the same signal. 3078874Srgrimes * 3081590Srgrimes *----------------------------------------------------------------------- 3091590Srgrimes */ 3101590Srgrimesstatic void 3111590SrgrimesJobPassSig(signo) 3121590Srgrimes int signo; /* The signal number we've received */ 3131590Srgrimes{ 3141590Srgrimes int mask; 3158874Srgrimes 3165814Sjkh Lst_ForEach(jobs, JobCondPassSig, (ClientData)(long)signo); 3171590Srgrimes 3181590Srgrimes /* 3191590Srgrimes * Deal with proper cleanup based on the signal received. We only run 3201590Srgrimes * the .INTERRUPT target if the signal was in fact an interrupt. The other 3211590Srgrimes * three termination signals are more of a "get out *now*" command. 3221590Srgrimes */ 3231590Srgrimes if (signo == SIGINT) { 3241590Srgrimes JobInterrupt(TRUE); 3251590Srgrimes } else if ((signo == SIGHUP) || (signo == SIGTERM) || (signo == SIGQUIT)) { 3261590Srgrimes JobInterrupt(FALSE); 3271590Srgrimes } 3288874Srgrimes 3291590Srgrimes /* 3301590Srgrimes * Leave gracefully if SIGQUIT, rather than core dumping. 3311590Srgrimes */ 3321590Srgrimes if (signo == SIGQUIT) { 3331590Srgrimes Finish(0); 3341590Srgrimes } 3358874Srgrimes 3361590Srgrimes /* 3371590Srgrimes * Send ourselves the signal now we've given the message to everyone else. 3381590Srgrimes * Note we block everything else possible while we're getting the signal. 3391590Srgrimes * This ensures that all our jobs get continued when we wake up before 3401590Srgrimes * we take any other signal. 3411590Srgrimes */ 3421590Srgrimes mask = sigblock(0); 3431590Srgrimes (void) sigsetmask(~0 & ~(1 << (signo-1))); 3441590Srgrimes signal(signo, SIG_DFL); 3451590Srgrimes 3461590Srgrimes kill(getpid(), signo); 3471590Srgrimes 3485814Sjkh signo = SIGCONT; 3495814Sjkh Lst_ForEach(jobs, JobCondPassSig, (ClientData) &signo); 3501590Srgrimes 3511590Srgrimes sigsetmask(mask); 3521590Srgrimes signal(signo, JobPassSig); 3531590Srgrimes 3541590Srgrimes} 3551590Srgrimes 3561590Srgrimes/*- 3571590Srgrimes *----------------------------------------------------------------------- 3581590Srgrimes * JobCmpPid -- 3591590Srgrimes * Compare the pid of the job with the given pid and return 0 if they 3601590Srgrimes * are equal. This function is called from Job_CatchChildren via 3611590Srgrimes * Lst_Find to find the job descriptor of the finished job. 3621590Srgrimes * 3631590Srgrimes * Results: 3641590Srgrimes * 0 if the pid's match 3651590Srgrimes * 3661590Srgrimes * Side Effects: 3671590Srgrimes * None 3681590Srgrimes *----------------------------------------------------------------------- 3691590Srgrimes */ 3701590Srgrimesstatic int 3711590SrgrimesJobCmpPid (job, pid) 3725814Sjkh ClientData job; /* job to examine */ 3735814Sjkh ClientData pid; /* process id desired */ 3741590Srgrimes{ 3755814Sjkh return ( *(int *) pid - ((Job *) job)->pid); 3761590Srgrimes} 3771590Srgrimes 3781590Srgrimes/*- 3791590Srgrimes *----------------------------------------------------------------------- 3801590Srgrimes * JobPrintCommand -- 3811590Srgrimes * Put out another command for the given job. If the command starts 3821590Srgrimes * with an @ or a - we process it specially. In the former case, 3831590Srgrimes * so long as the -s and -n flags weren't given to make, we stick 3841590Srgrimes * a shell-specific echoOff command in the script. In the latter, 3851590Srgrimes * we ignore errors for the entire job, unless the shell has error 3861590Srgrimes * control. 3871590Srgrimes * If the command is just "..." we take all future commands for this 3881590Srgrimes * job to be commands to be executed once the entire graph has been 3891590Srgrimes * made and return non-zero to signal that the end of the commands 3901590Srgrimes * was reached. These commands are later attached to the postCommands 3911590Srgrimes * node and executed by Job_End when all things are done. 3921590Srgrimes * This function is called from JobStart via Lst_ForEach. 3931590Srgrimes * 3941590Srgrimes * Results: 3951590Srgrimes * Always 0, unless the command was "..." 3961590Srgrimes * 3971590Srgrimes * Side Effects: 3981590Srgrimes * If the command begins with a '-' and the shell has no error control, 3991590Srgrimes * the JOB_IGNERR flag is set in the job descriptor. 4001590Srgrimes * If the command is "..." and we're not ignoring such things, 4011590Srgrimes * tailCmds is set to the successor node of the cmd. 4021590Srgrimes * numCommands is incremented if the command is actually printed. 4031590Srgrimes *----------------------------------------------------------------------- 4041590Srgrimes */ 4051590Srgrimesstatic int 4065814SjkhJobPrintCommand (cmdp, jobp) 4075814Sjkh ClientData cmdp; /* command string to print */ 4085814Sjkh ClientData jobp; /* job for which to print it */ 4091590Srgrimes{ 4101590Srgrimes Boolean noSpecials; /* true if we shouldn't worry about 4111590Srgrimes * inserting special commands into 4121590Srgrimes * the input stream. */ 4131590Srgrimes Boolean shutUp = FALSE; /* true if we put a no echo command 4141590Srgrimes * into the command file */ 4151590Srgrimes Boolean errOff = FALSE; /* true if we turned error checking 4161590Srgrimes * off before printing the command 4171590Srgrimes * and need to turn it back on */ 4181590Srgrimes char *cmdTemplate; /* Template to use when printing the 4191590Srgrimes * command */ 4201590Srgrimes char *cmdStart; /* Start of expanded command */ 4211590Srgrimes LstNode cmdNode; /* Node for replacing the command */ 4225814Sjkh char *cmd = (char *) cmdp; 4238874Srgrimes Job *job = (Job *) jobp; 4241590Srgrimes 4251590Srgrimes noSpecials = (noExecute && ! (job->node->type & OP_MAKE)); 4261590Srgrimes 4271590Srgrimes if (strcmp (cmd, "...") == 0) { 4288874Srgrimes job->node->type |= OP_SAVE_CMDS; 4291590Srgrimes if ((job->flags & JOB_IGNDOTS) == 0) { 4301590Srgrimes job->tailCmds = Lst_Succ (Lst_Member (job->node->commands, 4311590Srgrimes (ClientData)cmd)); 4321590Srgrimes return (1); 4331590Srgrimes } 4341590Srgrimes return (0); 4351590Srgrimes } 4361590Srgrimes 4371590Srgrimes#define DBPRINTF(fmt, arg) if (DEBUG(JOB)) printf (fmt, arg); fprintf (job->cmdFILE, fmt, arg) 4381590Srgrimes 4391590Srgrimes numCommands += 1; 4401590Srgrimes 4411590Srgrimes /* 4421590Srgrimes * For debugging, we replace each command with the result of expanding 4431590Srgrimes * the variables in the command. 4441590Srgrimes */ 4451590Srgrimes cmdNode = Lst_Member (job->node->commands, (ClientData)cmd); 4461590Srgrimes cmdStart = cmd = Var_Subst (NULL, cmd, job->node, FALSE); 4471590Srgrimes Lst_Replace (cmdNode, (ClientData)cmdStart); 4481590Srgrimes 4491590Srgrimes cmdTemplate = "%s\n"; 4501590Srgrimes 4511590Srgrimes /* 4521590Srgrimes * Check for leading @' and -'s to control echoing and error checking. 4531590Srgrimes */ 4541590Srgrimes while (*cmd == '@' || *cmd == '-') { 4551590Srgrimes if (*cmd == '@') { 4561590Srgrimes shutUp = TRUE; 4571590Srgrimes } else { 4581590Srgrimes errOff = TRUE; 4591590Srgrimes } 4601590Srgrimes cmd++; 4611590Srgrimes } 4621590Srgrimes 4631590Srgrimes while (isspace((unsigned char) *cmd)) 4641590Srgrimes cmd++; 4651590Srgrimes 4661590Srgrimes if (shutUp) { 4671590Srgrimes if (! (job->flags & JOB_SILENT) && !noSpecials && 4681590Srgrimes commandShell->hasEchoCtl) { 4691590Srgrimes DBPRINTF ("%s\n", commandShell->echoOff); 4701590Srgrimes } else { 4711590Srgrimes shutUp = FALSE; 4721590Srgrimes } 4731590Srgrimes } 4741590Srgrimes 4751590Srgrimes if (errOff) { 4761590Srgrimes if ( ! (job->flags & JOB_IGNERR) && !noSpecials) { 4771590Srgrimes if (commandShell->hasErrCtl) { 4781590Srgrimes /* 4791590Srgrimes * we don't want the error-control commands showing 4801590Srgrimes * up either, so we turn off echoing while executing 4811590Srgrimes * them. We could put another field in the shell 4821590Srgrimes * structure to tell JobDoOutput to look for this 4831590Srgrimes * string too, but why make it any more complex than 4841590Srgrimes * it already is? 4851590Srgrimes */ 4861590Srgrimes if (! (job->flags & JOB_SILENT) && !shutUp && 4871590Srgrimes commandShell->hasEchoCtl) { 4881590Srgrimes DBPRINTF ("%s\n", commandShell->echoOff); 4891590Srgrimes DBPRINTF ("%s\n", commandShell->ignErr); 4901590Srgrimes DBPRINTF ("%s\n", commandShell->echoOn); 4911590Srgrimes } else { 4921590Srgrimes DBPRINTF ("%s\n", commandShell->ignErr); 4931590Srgrimes } 4941590Srgrimes } else if (commandShell->ignErr && 4951590Srgrimes (*commandShell->ignErr != '\0')) 4961590Srgrimes { 4971590Srgrimes /* 4981590Srgrimes * The shell has no error control, so we need to be 4991590Srgrimes * weird to get it to ignore any errors from the command. 5001590Srgrimes * If echoing is turned on, we turn it off and use the 5011590Srgrimes * errCheck template to echo the command. Leave echoing 5021590Srgrimes * off so the user doesn't see the weirdness we go through 5031590Srgrimes * to ignore errors. Set cmdTemplate to use the weirdness 5041590Srgrimes * instead of the simple "%s\n" template. 5051590Srgrimes */ 5061590Srgrimes if (! (job->flags & JOB_SILENT) && !shutUp && 5071590Srgrimes commandShell->hasEchoCtl) { 5081590Srgrimes DBPRINTF ("%s\n", commandShell->echoOff); 5091590Srgrimes DBPRINTF (commandShell->errCheck, cmd); 5101590Srgrimes shutUp = TRUE; 5111590Srgrimes } 5121590Srgrimes cmdTemplate = commandShell->ignErr; 5131590Srgrimes /* 5141590Srgrimes * The error ignoration (hee hee) is already taken care 5151590Srgrimes * of by the ignErr template, so pretend error checking 5161590Srgrimes * is still on. 5171590Srgrimes */ 5181590Srgrimes errOff = FALSE; 5191590Srgrimes } else { 5201590Srgrimes errOff = FALSE; 5211590Srgrimes } 5221590Srgrimes } else { 5231590Srgrimes errOff = FALSE; 5241590Srgrimes } 5251590Srgrimes } 5268874Srgrimes 5271590Srgrimes DBPRINTF (cmdTemplate, cmd); 5288874Srgrimes 5291590Srgrimes if (errOff) { 5301590Srgrimes /* 5311590Srgrimes * If echoing is already off, there's no point in issuing the 5321590Srgrimes * echoOff command. Otherwise we issue it and pretend it was on 5331590Srgrimes * for the whole command... 5341590Srgrimes */ 5351590Srgrimes if (!shutUp && !(job->flags & JOB_SILENT) && commandShell->hasEchoCtl){ 5361590Srgrimes DBPRINTF ("%s\n", commandShell->echoOff); 5371590Srgrimes shutUp = TRUE; 5381590Srgrimes } 5391590Srgrimes DBPRINTF ("%s\n", commandShell->errCheck); 5401590Srgrimes } 5411590Srgrimes if (shutUp) { 5421590Srgrimes DBPRINTF ("%s\n", commandShell->echoOn); 5431590Srgrimes } 5441590Srgrimes return (0); 5451590Srgrimes} 5461590Srgrimes 5471590Srgrimes/*- 5481590Srgrimes *----------------------------------------------------------------------- 5491590Srgrimes * JobSaveCommand -- 5501590Srgrimes * Save a command to be executed when everything else is done. 5511590Srgrimes * Callback function for JobFinish... 5521590Srgrimes * 5531590Srgrimes * Results: 5541590Srgrimes * Always returns 0 5551590Srgrimes * 5561590Srgrimes * Side Effects: 5571590Srgrimes * The command is tacked onto the end of postCommands's commands list. 5581590Srgrimes * 5591590Srgrimes *----------------------------------------------------------------------- 5601590Srgrimes */ 5611590Srgrimesstatic int 5621590SrgrimesJobSaveCommand (cmd, gn) 5635814Sjkh ClientData cmd; 5645814Sjkh ClientData gn; 5651590Srgrimes{ 5665814Sjkh cmd = (ClientData) Var_Subst (NULL, (char *) cmd, (GNode *) gn, FALSE); 5675814Sjkh (void)Lst_AtEnd (postCommands->commands, cmd); 5681590Srgrimes return (0); 5691590Srgrimes} 5701590Srgrimes 5711590Srgrimes/*- 5721590Srgrimes *----------------------------------------------------------------------- 5731590Srgrimes * JobFinish -- 5741590Srgrimes * Do final processing for the given job including updating 5751590Srgrimes * parents and starting new jobs as available/necessary. Note 5761590Srgrimes * that we pay no attention to the JOB_IGNERR flag here. 5771590Srgrimes * This is because when we're called because of a noexecute flag 5781590Srgrimes * or something, jstat.w_status is 0 and when called from 5791590Srgrimes * Job_CatchChildren, the status is zeroed if it s/b ignored. 5801590Srgrimes * 5811590Srgrimes * Results: 5821590Srgrimes * None 5831590Srgrimes * 5841590Srgrimes * Side Effects: 5851590Srgrimes * Some nodes may be put on the toBeMade queue. 5861590Srgrimes * Final commands for the job are placed on postCommands. 5871590Srgrimes * 5881590Srgrimes * If we got an error and are aborting (aborting == ABORT_ERROR) and 5891590Srgrimes * the job list is now empty, we are done for the day. 5901590Srgrimes * If we recognized an error (errors !=0), we set the aborting flag 5911590Srgrimes * to ABORT_ERROR so no more jobs will be started. 5921590Srgrimes *----------------------------------------------------------------------- 5931590Srgrimes */ 5941590Srgrimes/*ARGSUSED*/ 5951590Srgrimesstatic void 5961590SrgrimesJobFinish (job, status) 5971590Srgrimes Job *job; /* job to finish */ 5981590Srgrimes union wait status; /* sub-why job went away */ 5991590Srgrimes{ 6001590Srgrimes Boolean done; 6011590Srgrimes 6021590Srgrimes if ((WIFEXITED(status) && 6031590Srgrimes (((status.w_retcode != 0) && !(job->flags & JOB_IGNERR)))) || 6041590Srgrimes (WIFSIGNALED(status) && (status.w_termsig != SIGCONT))) 6051590Srgrimes { 6061590Srgrimes /* 6071590Srgrimes * If it exited non-zero and either we're doing things our 6081590Srgrimes * way or we're not ignoring errors, the job is finished. 6091590Srgrimes * Similarly, if the shell died because of a signal 6101590Srgrimes * the job is also finished. In these 6111590Srgrimes * cases, finish out the job's output before printing the exit 6121590Srgrimes * status... 6131590Srgrimes */ 6141590Srgrimes if (usePipes) { 6151590Srgrimes#ifdef RMT_WILL_WATCH 6161590Srgrimes Rmt_Ignore(job->inPipe); 6171590Srgrimes#else 6181590Srgrimes FD_CLR(job->inPipe, &outputs); 6191590Srgrimes#endif /* RMT_WILL_WATCH */ 6201590Srgrimes if (job->outPipe != job->inPipe) { 6211590Srgrimes (void)close (job->outPipe); 6221590Srgrimes } 6231590Srgrimes JobDoOutput (job, TRUE); 6241590Srgrimes (void)close (job->inPipe); 6251590Srgrimes } else { 6261590Srgrimes (void)close (job->outFd); 6271590Srgrimes JobDoOutput (job, TRUE); 6281590Srgrimes } 6291590Srgrimes 6301590Srgrimes if (job->cmdFILE != NULL && job->cmdFILE != stdout) { 6311590Srgrimes fclose(job->cmdFILE); 6321590Srgrimes } 6331590Srgrimes done = TRUE; 6341590Srgrimes } else if (WIFEXITED(status) && status.w_retcode != 0) { 6351590Srgrimes /* 6361590Srgrimes * Deal with ignored errors in -B mode. We need to print a message 6371590Srgrimes * telling of the ignored error as well as setting status.w_status 6381590Srgrimes * to 0 so the next command gets run. To do this, we set done to be 6391590Srgrimes * TRUE if in -B mode and the job exited non-zero. Note we don't 6401590Srgrimes * want to close down any of the streams until we know we're at the 6411590Srgrimes * end. 6421590Srgrimes */ 6431590Srgrimes done = TRUE; 6441590Srgrimes } else { 6451590Srgrimes /* 6461590Srgrimes * No need to close things down or anything. 6471590Srgrimes */ 6481590Srgrimes done = FALSE; 6491590Srgrimes } 6508874Srgrimes 6511590Srgrimes if (done || 6521590Srgrimes WIFSTOPPED(status) || 6531590Srgrimes (WIFSIGNALED(status) && (status.w_termsig == SIGCONT)) || 6541590Srgrimes DEBUG(JOB)) 6551590Srgrimes { 6561590Srgrimes FILE *out; 6578874Srgrimes 6581590Srgrimes if (!usePipes && (job->flags & JOB_IGNERR)) { 6591590Srgrimes /* 6601590Srgrimes * If output is going to a file and this job is ignoring 6611590Srgrimes * errors, arrange to have the exit status sent to the 6621590Srgrimes * output file as well. 6631590Srgrimes */ 6641590Srgrimes out = fdopen (job->outFd, "w"); 6651590Srgrimes } else { 6661590Srgrimes out = stdout; 6671590Srgrimes } 6681590Srgrimes 6691590Srgrimes if (WIFEXITED(status)) { 6701590Srgrimes if (status.w_retcode != 0) { 6711590Srgrimes if (usePipes && job->node != lastNode) { 6721590Srgrimes fprintf (out, targFmt, job->node->name); 6731590Srgrimes lastNode = job->node; 6741590Srgrimes } 6751590Srgrimes fprintf (out, "*** Error code %d%s\n", status.w_retcode, 6761590Srgrimes (job->flags & JOB_IGNERR) ? " (ignored)" : ""); 6771590Srgrimes 6781590Srgrimes if (job->flags & JOB_IGNERR) { 6791590Srgrimes status.w_status = 0; 6801590Srgrimes } 6811590Srgrimes } else if (DEBUG(JOB)) { 6821590Srgrimes if (usePipes && job->node != lastNode) { 6831590Srgrimes fprintf (out, targFmt, job->node->name); 6841590Srgrimes lastNode = job->node; 6851590Srgrimes } 6861590Srgrimes fprintf (out, "*** Completed successfully\n"); 6871590Srgrimes } 6881590Srgrimes } else if (WIFSTOPPED(status)) { 6891590Srgrimes if (usePipes && job->node != lastNode) { 6901590Srgrimes fprintf (out, targFmt, job->node->name); 6911590Srgrimes lastNode = job->node; 6921590Srgrimes } 6931590Srgrimes if (! (job->flags & JOB_REMIGRATE)) { 6941590Srgrimes fprintf (out, "*** Stopped -- signal %d\n", status.w_stopsig); 6951590Srgrimes } 6961590Srgrimes job->flags |= JOB_RESUME; 6971590Srgrimes (void)Lst_AtEnd(stoppedJobs, (ClientData)job); 6981590Srgrimes fflush(out); 6991590Srgrimes return; 7001590Srgrimes } else if (status.w_termsig == SIGCONT) { 7011590Srgrimes /* 7021590Srgrimes * If the beastie has continued, shift the Job from the stopped 7031590Srgrimes * list to the running one (or re-stop it if concurrency is 7041590Srgrimes * exceeded) and go and get another child. 7051590Srgrimes */ 7061590Srgrimes if (job->flags & (JOB_RESUME|JOB_REMIGRATE|JOB_RESTART)) { 7071590Srgrimes if (usePipes && job->node != lastNode) { 7081590Srgrimes fprintf (out, targFmt, job->node->name); 7091590Srgrimes lastNode = job->node; 7101590Srgrimes } 7111590Srgrimes fprintf (out, "*** Continued\n"); 7121590Srgrimes } 7131590Srgrimes if (! (job->flags & JOB_CONTINUING)) { 7141590Srgrimes JobRestart(job); 7151590Srgrimes } else { 7161590Srgrimes Lst_AtEnd(jobs, (ClientData)job); 7171590Srgrimes nJobs += 1; 7181590Srgrimes if (! (job->flags & JOB_REMOTE)) { 7191590Srgrimes nLocal += 1; 7201590Srgrimes } 7211590Srgrimes if (nJobs == maxJobs) { 7221590Srgrimes jobFull = TRUE; 7231590Srgrimes if (DEBUG(JOB)) { 7241590Srgrimes printf("Job queue is full.\n"); 7251590Srgrimes } 7261590Srgrimes } 7271590Srgrimes } 7281590Srgrimes fflush(out); 7291590Srgrimes return; 7301590Srgrimes } else { 7311590Srgrimes if (usePipes && job->node != lastNode) { 7321590Srgrimes fprintf (out, targFmt, job->node->name); 7331590Srgrimes lastNode = job->node; 7341590Srgrimes } 7351590Srgrimes fprintf (out, "*** Signal %d\n", status.w_termsig); 7361590Srgrimes } 7371590Srgrimes 7381590Srgrimes fflush (out); 7391590Srgrimes } 7401590Srgrimes 7411590Srgrimes /* 7421590Srgrimes * Now handle the -B-mode stuff. If the beast still isn't finished, 7431590Srgrimes * try and restart the job on the next command. If JobStart says it's 7441590Srgrimes * ok, it's ok. If there's an error, this puppy is done. 7451590Srgrimes */ 7461590Srgrimes if ((status.w_status == 0) && 7471590Srgrimes !Lst_IsAtEnd (job->node->commands)) 7481590Srgrimes { 7491590Srgrimes switch (JobStart (job->node, 7501590Srgrimes job->flags & JOB_IGNDOTS, 7511590Srgrimes job)) 7521590Srgrimes { 7531590Srgrimes case JOB_RUNNING: 7541590Srgrimes done = FALSE; 7551590Srgrimes break; 7561590Srgrimes case JOB_ERROR: 7571590Srgrimes done = TRUE; 7581590Srgrimes status.w_retcode = 1; 7591590Srgrimes break; 7601590Srgrimes case JOB_FINISHED: 7611590Srgrimes /* 7621590Srgrimes * If we got back a JOB_FINISHED code, JobStart has already 7631590Srgrimes * called Make_Update and freed the job descriptor. We set 7641590Srgrimes * done to false here to avoid fake cycles and double frees. 7651590Srgrimes * JobStart needs to do the update so we can proceed up the 7661590Srgrimes * graph when given the -n flag.. 7671590Srgrimes */ 7681590Srgrimes done = FALSE; 7691590Srgrimes break; 7701590Srgrimes } 7711590Srgrimes } else { 7721590Srgrimes done = TRUE; 7731590Srgrimes } 7741590Srgrimes 7758874Srgrimes 7761590Srgrimes if (done && 7771590Srgrimes (aborting != ABORT_ERROR) && 7781590Srgrimes (aborting != ABORT_INTERRUPT) && 7791590Srgrimes (status.w_status == 0)) 7801590Srgrimes { 7811590Srgrimes /* 7821590Srgrimes * As long as we aren't aborting and the job didn't return a non-zero 7831590Srgrimes * status that we shouldn't ignore, we call Make_Update to update 7841590Srgrimes * the parents. In addition, any saved commands for the node are placed 7851590Srgrimes * on the .END target. 7861590Srgrimes */ 7871590Srgrimes if (job->tailCmds != NILLNODE) { 7881590Srgrimes Lst_ForEachFrom (job->node->commands, job->tailCmds, 7891590Srgrimes JobSaveCommand, 7901590Srgrimes (ClientData)job->node); 7911590Srgrimes } 7921590Srgrimes job->node->made = MADE; 7931590Srgrimes Make_Update (job->node); 7941590Srgrimes free((Address)job); 7951590Srgrimes } else if (status.w_status) { 7961590Srgrimes errors += 1; 7971590Srgrimes free((Address)job); 7981590Srgrimes } 7991590Srgrimes 8001590Srgrimes while (!errors && !jobFull && !Lst_IsEmpty(stoppedJobs)) { 8011590Srgrimes JobRestart((Job *)Lst_DeQueue(stoppedJobs)); 8021590Srgrimes } 8031590Srgrimes 8041590Srgrimes /* 8051590Srgrimes * Set aborting if any error. 8061590Srgrimes */ 8071590Srgrimes if (errors && !keepgoing && (aborting != ABORT_INTERRUPT)) { 8081590Srgrimes /* 8091590Srgrimes * If we found any errors in this batch of children and the -k flag 8101590Srgrimes * wasn't given, we set the aborting flag so no more jobs get 8111590Srgrimes * started. 8121590Srgrimes */ 8131590Srgrimes aborting = ABORT_ERROR; 8141590Srgrimes } 8158874Srgrimes 8161590Srgrimes if ((aborting == ABORT_ERROR) && Job_Empty()) { 8171590Srgrimes /* 8181590Srgrimes * If we are aborting and the job table is now empty, we finish. 8191590Srgrimes */ 8201590Srgrimes (void) unlink (tfile); 8211590Srgrimes Finish (errors); 8221590Srgrimes } 8231590Srgrimes} 8241590Srgrimes 8251590Srgrimes/*- 8261590Srgrimes *----------------------------------------------------------------------- 8271590Srgrimes * Job_Touch -- 8281590Srgrimes * Touch the given target. Called by JobStart when the -t flag was 8291590Srgrimes * given 8301590Srgrimes * 8311590Srgrimes * Results: 8321590Srgrimes * None 8331590Srgrimes * 8341590Srgrimes * Side Effects: 8351590Srgrimes * The data modification of the file is changed. In addition, if the 8361590Srgrimes * file did not exist, it is created. 8371590Srgrimes *----------------------------------------------------------------------- 8381590Srgrimes */ 8391590Srgrimesvoid 8401590SrgrimesJob_Touch (gn, silent) 8411590Srgrimes GNode *gn; /* the node of the file to touch */ 8421590Srgrimes Boolean silent; /* TRUE if should not print messages */ 8431590Srgrimes{ 8441590Srgrimes int streamID; /* ID of stream opened to do the touch */ 8451590Srgrimes struct timeval times[2]; /* Times for utimes() call */ 8461590Srgrimes 8471590Srgrimes if (gn->type & (OP_JOIN|OP_USE|OP_EXEC|OP_OPTIONAL)) { 8481590Srgrimes /* 8491590Srgrimes * .JOIN, .USE, .ZEROTIME and .OPTIONAL targets are "virtual" targets 8501590Srgrimes * and, as such, shouldn't really be created. 8511590Srgrimes */ 8521590Srgrimes return; 8531590Srgrimes } 8548874Srgrimes 8551590Srgrimes if (!silent) { 8561590Srgrimes printf ("touch %s\n", gn->name); 8571590Srgrimes } 8581590Srgrimes 8591590Srgrimes if (noExecute) { 8601590Srgrimes return; 8611590Srgrimes } 8621590Srgrimes 8631590Srgrimes if (gn->type & OP_ARCHV) { 8641590Srgrimes Arch_Touch (gn); 8651590Srgrimes } else if (gn->type & OP_LIB) { 8661590Srgrimes Arch_TouchLib (gn); 8671590Srgrimes } else { 8681590Srgrimes char *file = gn->path ? gn->path : gn->name; 8691590Srgrimes 8701590Srgrimes times[0].tv_sec = times[1].tv_sec = now; 8711590Srgrimes times[0].tv_usec = times[1].tv_usec = 0; 8721590Srgrimes if (utimes(file, times) < 0){ 8731590Srgrimes streamID = open (file, O_RDWR | O_CREAT, 0666); 8741590Srgrimes 8751590Srgrimes if (streamID >= 0) { 8761590Srgrimes char c; 8771590Srgrimes 8781590Srgrimes /* 8791590Srgrimes * Read and write a byte to the file to change the 8801590Srgrimes * modification time, then close the file. 8811590Srgrimes */ 8821590Srgrimes if (read(streamID, &c, 1) == 1) { 8831590Srgrimes lseek(streamID, 0L, L_SET); 8841590Srgrimes write(streamID, &c, 1); 8851590Srgrimes } 8868874Srgrimes 8871590Srgrimes (void)close (streamID); 8881590Srgrimes } else 8891590Srgrimes printf("*** couldn't touch %s: %s", file, strerror(errno)); 8901590Srgrimes } 8911590Srgrimes } 8921590Srgrimes} 8931590Srgrimes 8941590Srgrimes/*- 8951590Srgrimes *----------------------------------------------------------------------- 8961590Srgrimes * Job_CheckCommands -- 8978874Srgrimes * Make sure the given node has all the commands it needs. 8981590Srgrimes * 8991590Srgrimes * Results: 9001590Srgrimes * TRUE if the commands list is/was ok. 9011590Srgrimes * 9021590Srgrimes * Side Effects: 9031590Srgrimes * The node will have commands from the .DEFAULT rule added to it 9041590Srgrimes * if it needs them. 9051590Srgrimes *----------------------------------------------------------------------- 9061590Srgrimes */ 9071590SrgrimesBoolean 9081590SrgrimesJob_CheckCommands (gn, abortProc) 9091590Srgrimes GNode *gn; /* The target whose commands need 9101590Srgrimes * verifying */ 9118874Srgrimes void (*abortProc) __P((char *, ...)); 9121590Srgrimes /* Function to abort with message */ 9131590Srgrimes{ 9141590Srgrimes if (OP_NOP(gn->type) && Lst_IsEmpty (gn->commands) && 9151590Srgrimes (gn->type & OP_LIB) == 0) { 9161590Srgrimes /* 9171590Srgrimes * No commands. Look for .DEFAULT rule from which we might infer 9188874Srgrimes * commands 9191590Srgrimes */ 9201590Srgrimes if ((DEFAULT != NILGNODE) && !Lst_IsEmpty(DEFAULT->commands)) { 9215814Sjkh char *p1; 9221590Srgrimes /* 9231590Srgrimes * Make only looks for a .DEFAULT if the node was never the 9241590Srgrimes * target of an operator, so that's what we do too. If 9251590Srgrimes * a .DEFAULT was given, we substitute its commands for gn's 9261590Srgrimes * commands and set the IMPSRC variable to be the target's name 9271590Srgrimes * The DEFAULT node acts like a transformation rule, in that 9281590Srgrimes * gn also inherits any attributes or sources attached to 9291590Srgrimes * .DEFAULT itself. 9301590Srgrimes */ 9311590Srgrimes Make_HandleUse(DEFAULT, gn); 9325814Sjkh Var_Set (IMPSRC, Var_Value (TARGET, gn, &p1), gn); 9335814Sjkh if (p1) 9345814Sjkh free(p1); 9351590Srgrimes } else if (Dir_MTime (gn) == 0) { 9361590Srgrimes /* 9371590Srgrimes * The node wasn't the target of an operator we have no .DEFAULT 9381590Srgrimes * rule to go on and the target doesn't already exist. There's 9391590Srgrimes * nothing more we can do for this branch. If the -k flag wasn't 9401590Srgrimes * given, we stop in our tracks, otherwise we just don't update 9418874Srgrimes * this node's parents so they never get examined. 9421590Srgrimes */ 9431590Srgrimes if (gn->type & OP_OPTIONAL) { 9441590Srgrimes printf ("make: don't know how to make %s (ignored)\n", 9451590Srgrimes gn->name); 9461590Srgrimes } else if (keepgoing) { 9471590Srgrimes printf ("make: don't know how to make %s (continuing)\n", 9481590Srgrimes gn->name); 9491590Srgrimes return (FALSE); 9501590Srgrimes } else { 9511590Srgrimes (*abortProc) ("make: don't know how to make %s. Stop", 9521590Srgrimes gn->name); 9531590Srgrimes return(FALSE); 9541590Srgrimes } 9551590Srgrimes } 9561590Srgrimes } 9571590Srgrimes return (TRUE); 9581590Srgrimes} 9591590Srgrimes#ifdef RMT_WILL_WATCH 9601590Srgrimes/*- 9611590Srgrimes *----------------------------------------------------------------------- 9621590Srgrimes * JobLocalInput -- 9631590Srgrimes * Handle a pipe becoming readable. Callback function for Rmt_Watch 9641590Srgrimes * 9651590Srgrimes * Results: 9661590Srgrimes * None 9671590Srgrimes * 9681590Srgrimes * Side Effects: 9691590Srgrimes * JobDoOutput is called. 9708874Srgrimes * 9711590Srgrimes *----------------------------------------------------------------------- 9721590Srgrimes */ 9731590Srgrimes/*ARGSUSED*/ 9741590Srgrimesstatic void 9751590SrgrimesJobLocalInput(stream, job) 9761590Srgrimes int stream; /* Stream that's ready (ignored) */ 9771590Srgrimes Job *job; /* Job to which the stream belongs */ 9781590Srgrimes{ 9791590Srgrimes JobDoOutput(job, FALSE); 9801590Srgrimes} 9811590Srgrimes#endif /* RMT_WILL_WATCH */ 9821590Srgrimes 9831590Srgrimes/*- 9841590Srgrimes *----------------------------------------------------------------------- 9851590Srgrimes * JobExec -- 9861590Srgrimes * Execute the shell for the given job. Called from JobStart and 9871590Srgrimes * JobRestart. 9881590Srgrimes * 9891590Srgrimes * Results: 9901590Srgrimes * None. 9911590Srgrimes * 9921590Srgrimes * Side Effects: 9931590Srgrimes * A shell is executed, outputs is altered and the Job structure added 9941590Srgrimes * to the job table. 9951590Srgrimes * 9961590Srgrimes *----------------------------------------------------------------------- 9971590Srgrimes */ 9981590Srgrimesstatic void 9991590SrgrimesJobExec(job, argv) 10001590Srgrimes Job *job; /* Job to execute */ 10011590Srgrimes char **argv; 10021590Srgrimes{ 10031590Srgrimes int cpid; /* ID of new child */ 10048874Srgrimes 10051590Srgrimes if (DEBUG(JOB)) { 10061590Srgrimes int i; 10078874Srgrimes 10081590Srgrimes printf("Running %s %sly\n", job->node->name, 10091590Srgrimes job->flags&JOB_REMOTE?"remote":"local"); 10101590Srgrimes printf("\tCommand: "); 10111590Srgrimes for (i = 0; argv[i] != (char *)NULL; i++) { 10121590Srgrimes printf("%s ", argv[i]); 10131590Srgrimes } 10141590Srgrimes printf("\n"); 10151590Srgrimes } 10168874Srgrimes 10171590Srgrimes /* 10181590Srgrimes * Some jobs produce no output and it's disconcerting to have 10191590Srgrimes * no feedback of their running (since they produce no output, the 10201590Srgrimes * banner with their name in it never appears). This is an attempt to 10211590Srgrimes * provide that feedback, even if nothing follows it. 10221590Srgrimes */ 10231590Srgrimes if ((lastNode != job->node) && (job->flags & JOB_FIRST) && 10241590Srgrimes !(job->flags & JOB_SILENT)) 10251590Srgrimes { 10261590Srgrimes printf(targFmt, job->node->name); 10271590Srgrimes lastNode = job->node; 10281590Srgrimes } 10298874Srgrimes 10301590Srgrimes#ifdef RMT_NO_EXEC 10311590Srgrimes if (job->flags & JOB_REMOTE) { 10321590Srgrimes goto jobExecFinish; 10331590Srgrimes } 10341590Srgrimes#endif /* RMT_NO_EXEC */ 10351590Srgrimes 10361590Srgrimes if ((cpid = vfork()) == -1) { 10371590Srgrimes Punt ("Cannot fork"); 10381590Srgrimes } else if (cpid == 0) { 10391590Srgrimes 10401590Srgrimes /* 10411590Srgrimes * Must duplicate the input stream down to the child's input and 10421590Srgrimes * reset it to the beginning (again). Since the stream was marked 10431590Srgrimes * close-on-exec, we must clear that bit in the new input. 10441590Srgrimes */ 10451590Srgrimes (void) dup2(fileno(job->cmdFILE), 0); 10461590Srgrimes fcntl(0, F_SETFD, 0); 10471590Srgrimes lseek(0, 0, L_SET); 10488874Srgrimes 10491590Srgrimes if (usePipes) { 10501590Srgrimes /* 10511590Srgrimes * Set up the child's output to be routed through the pipe 10521590Srgrimes * we've created for it. 10531590Srgrimes */ 10541590Srgrimes (void) dup2 (job->outPipe, 1); 10551590Srgrimes } else { 10561590Srgrimes /* 10571590Srgrimes * We're capturing output in a file, so we duplicate the 10581590Srgrimes * descriptor to the temporary file into the standard 10591590Srgrimes * output. 10601590Srgrimes */ 10611590Srgrimes (void) dup2 (job->outFd, 1); 10621590Srgrimes } 10631590Srgrimes /* 10641590Srgrimes * The output channels are marked close on exec. This bit was 10651590Srgrimes * duplicated by the dup2 (on some systems), so we have to clear 10661590Srgrimes * it before routing the shell's error output to the same place as 10671590Srgrimes * its standard output. 10681590Srgrimes */ 10691590Srgrimes fcntl(1, F_SETFD, 0); 10701590Srgrimes (void) dup2 (1, 2); 10711590Srgrimes 10721590Srgrimes#ifdef USE_PGRP 10731590Srgrimes /* 10741590Srgrimes * We want to switch the child into a different process family so 10751590Srgrimes * we can kill it and all its descendants in one fell swoop, 10761590Srgrimes * by killing its process family, but not commit suicide. 10771590Srgrimes */ 10788874Srgrimes 10791590Srgrimes (void) setpgrp(0, getpid()); 10801590Srgrimes#endif USE_PGRP 10811590Srgrimes 10821590Srgrimes (void) execv (shellPath, argv); 10831590Srgrimes (void) write (2, "Could not execute shell\n", 10841590Srgrimes sizeof ("Could not execute shell")); 10851590Srgrimes _exit (1); 10861590Srgrimes } else { 10871590Srgrimes job->pid = cpid; 10881590Srgrimes 10891590Srgrimes if (usePipes && (job->flags & JOB_FIRST) ) { 10901590Srgrimes /* 10911590Srgrimes * The first time a job is run for a node, we set the current 10921590Srgrimes * position in the buffer to the beginning and mark another 10931590Srgrimes * stream to watch in the outputs mask 10941590Srgrimes */ 10951590Srgrimes job->curPos = 0; 10968874Srgrimes 10971590Srgrimes#ifdef RMT_WILL_WATCH 10981590Srgrimes Rmt_Watch(job->inPipe, JobLocalInput, job); 10991590Srgrimes#else 11001590Srgrimes FD_SET(job->inPipe, &outputs); 11011590Srgrimes#endif /* RMT_WILL_WATCH */ 11021590Srgrimes } 11031590Srgrimes 11041590Srgrimes if (job->flags & JOB_REMOTE) { 11051590Srgrimes job->rmtID = 0; 11061590Srgrimes } else { 11071590Srgrimes nLocal += 1; 11081590Srgrimes /* 11091590Srgrimes * XXX: Used to not happen if CUSTOMS. Why? 11101590Srgrimes */ 11111590Srgrimes if (job->cmdFILE != stdout) { 11121590Srgrimes fclose(job->cmdFILE); 11131590Srgrimes job->cmdFILE = NULL; 11141590Srgrimes } 11151590Srgrimes } 11161590Srgrimes } 11171590Srgrimes 11181590Srgrimes#ifdef RMT_NO_EXEC 11198874SrgrimesjobExecFinish: 11201590Srgrimes#endif 11211590Srgrimes /* 11221590Srgrimes * Now the job is actually running, add it to the table. 11231590Srgrimes */ 11241590Srgrimes nJobs += 1; 11251590Srgrimes (void)Lst_AtEnd (jobs, (ClientData)job); 11261590Srgrimes if (nJobs == maxJobs) { 11271590Srgrimes jobFull = TRUE; 11281590Srgrimes } 11291590Srgrimes} 11301590Srgrimes 11311590Srgrimes/*- 11321590Srgrimes *----------------------------------------------------------------------- 11331590Srgrimes * JobMakeArgv -- 11341590Srgrimes * Create the argv needed to execute the shell for a given job. 11351590Srgrimes * 11368874Srgrimes * 11371590Srgrimes * Results: 11381590Srgrimes * 11391590Srgrimes * Side Effects: 11401590Srgrimes * 11411590Srgrimes *----------------------------------------------------------------------- 11421590Srgrimes */ 11431590Srgrimesstatic void 11441590SrgrimesJobMakeArgv(job, argv) 11451590Srgrimes Job *job; 11461590Srgrimes char **argv; 11471590Srgrimes{ 11481590Srgrimes int argc; 11491590Srgrimes static char args[10]; /* For merged arguments */ 11508874Srgrimes 11511590Srgrimes argv[0] = shellName; 11521590Srgrimes argc = 1; 11531590Srgrimes 11541590Srgrimes if ((commandShell->exit && (*commandShell->exit != '-')) || 11551590Srgrimes (commandShell->echo && (*commandShell->echo != '-'))) 11561590Srgrimes { 11571590Srgrimes /* 11581590Srgrimes * At least one of the flags doesn't have a minus before it, so 11591590Srgrimes * merge them together. Have to do this because the *(&(@*#*&#$# 11601590Srgrimes * Bourne shell thinks its second argument is a file to source. 11611590Srgrimes * Grrrr. Note the ten-character limitation on the combined arguments. 11621590Srgrimes */ 11631590Srgrimes (void)sprintf(args, "-%s%s", 11641590Srgrimes ((job->flags & JOB_IGNERR) ? "" : 11651590Srgrimes (commandShell->exit ? commandShell->exit : "")), 11661590Srgrimes ((job->flags & JOB_SILENT) ? "" : 11671590Srgrimes (commandShell->echo ? commandShell->echo : ""))); 11681590Srgrimes 11691590Srgrimes if (args[1]) { 11701590Srgrimes argv[argc] = args; 11711590Srgrimes argc++; 11721590Srgrimes } 11731590Srgrimes } else { 11741590Srgrimes if (!(job->flags & JOB_IGNERR) && commandShell->exit) { 11751590Srgrimes argv[argc] = commandShell->exit; 11761590Srgrimes argc++; 11771590Srgrimes } 11781590Srgrimes if (!(job->flags & JOB_SILENT) && commandShell->echo) { 11791590Srgrimes argv[argc] = commandShell->echo; 11801590Srgrimes argc++; 11811590Srgrimes } 11821590Srgrimes } 11831590Srgrimes argv[argc] = (char *)NULL; 11841590Srgrimes} 11851590Srgrimes 11861590Srgrimes/*- 11871590Srgrimes *----------------------------------------------------------------------- 11881590Srgrimes * JobRestart -- 11898874Srgrimes * Restart a job that stopped for some reason. 11901590Srgrimes * 11911590Srgrimes * Results: 11921590Srgrimes * None. 11931590Srgrimes * 11941590Srgrimes * Side Effects: 11951590Srgrimes * jobFull will be set if the job couldn't be run. 11961590Srgrimes * 11971590Srgrimes *----------------------------------------------------------------------- 11981590Srgrimes */ 11991590Srgrimesstatic void 12001590SrgrimesJobRestart(job) 12011590Srgrimes Job *job; /* Job to restart */ 12021590Srgrimes{ 12031590Srgrimes if (job->flags & JOB_REMIGRATE) { 12041590Srgrimes if (DEBUG(JOB)) { 12051590Srgrimes printf("Remigrating %x\n", job->pid); 12061590Srgrimes } 12071590Srgrimes if (nLocal != maxLocal) { 12081590Srgrimes /* 12091590Srgrimes * Job cannot be remigrated, but there's room on the local 12101590Srgrimes * machine, so resume the job and note that another 12111590Srgrimes * local job has started. 12121590Srgrimes */ 12131590Srgrimes if (DEBUG(JOB)) { 12141590Srgrimes printf("resuming on local machine\n"); 12151590Srgrimes } 12161590Srgrimes KILL(job->pid, SIGCONT); 12171590Srgrimes nLocal +=1; 12181590Srgrimes job->flags &= ~(JOB_REMIGRATE|JOB_RESUME); 12191590Srgrimes } else { 12201590Srgrimes /* 12211590Srgrimes * Job cannot be restarted. Mark the table as full and 12221590Srgrimes * place the job back on the list of stopped jobs. 12231590Srgrimes */ 12241590Srgrimes if (DEBUG(JOB)) { 12251590Srgrimes printf("holding\n"); 12261590Srgrimes } 12271590Srgrimes (void)Lst_AtFront(stoppedJobs, (ClientData)job); 12281590Srgrimes jobFull = TRUE; 12291590Srgrimes if (DEBUG(JOB)) { 12301590Srgrimes printf("Job queue is full.\n"); 12311590Srgrimes } 12321590Srgrimes return; 12331590Srgrimes } 12348874Srgrimes 12351590Srgrimes (void)Lst_AtEnd(jobs, (ClientData)job); 12361590Srgrimes nJobs += 1; 12371590Srgrimes if (nJobs == maxJobs) { 12381590Srgrimes jobFull = TRUE; 12391590Srgrimes if (DEBUG(JOB)) { 12401590Srgrimes printf("Job queue is full.\n"); 12411590Srgrimes } 12421590Srgrimes } 12431590Srgrimes } else if (job->flags & JOB_RESTART) { 12441590Srgrimes /* 12451590Srgrimes * Set up the control arguments to the shell. This is based on the 12461590Srgrimes * flags set earlier for this job. If the JOB_IGNERR flag is clear, 12471590Srgrimes * the 'exit' flag of the commandShell is used to cause it to exit 12481590Srgrimes * upon receiving an error. If the JOB_SILENT flag is clear, the 12491590Srgrimes * 'echo' flag of the commandShell is used to get it to start echoing 12508874Srgrimes * as soon as it starts processing commands. 12511590Srgrimes */ 12521590Srgrimes char *argv[4]; 12538874Srgrimes 12541590Srgrimes JobMakeArgv(job, argv); 12558874Srgrimes 12561590Srgrimes if (DEBUG(JOB)) { 12571590Srgrimes printf("Restarting %s...", job->node->name); 12581590Srgrimes } 12591590Srgrimes if (((nLocal >= maxLocal) && ! (job->flags & JOB_SPECIAL))) { 12601590Srgrimes /* 12611590Srgrimes * Can't be exported and not allowed to run locally -- put it 12621590Srgrimes * back on the hold queue and mark the table full 12631590Srgrimes */ 12641590Srgrimes if (DEBUG(JOB)) { 12651590Srgrimes printf("holding\n"); 12661590Srgrimes } 12671590Srgrimes (void)Lst_AtFront(stoppedJobs, (ClientData)job); 12681590Srgrimes jobFull = TRUE; 12691590Srgrimes if (DEBUG(JOB)) { 12701590Srgrimes printf("Job queue is full.\n"); 12711590Srgrimes } 12721590Srgrimes return; 12731590Srgrimes } else { 12741590Srgrimes /* 12751590Srgrimes * Job may be run locally. 12761590Srgrimes */ 12771590Srgrimes if (DEBUG(JOB)) { 12781590Srgrimes printf("running locally\n"); 12791590Srgrimes } 12801590Srgrimes job->flags &= ~JOB_REMOTE; 12811590Srgrimes } 12821590Srgrimes JobExec(job, argv); 12831590Srgrimes } else { 12841590Srgrimes /* 12851590Srgrimes * The job has stopped and needs to be restarted. Why it stopped, 12861590Srgrimes * we don't know... 12871590Srgrimes */ 12881590Srgrimes if (DEBUG(JOB)) { 12891590Srgrimes printf("Resuming %s...", job->node->name); 12901590Srgrimes } 12911590Srgrimes if (((job->flags & JOB_REMOTE) || 12921590Srgrimes (nLocal < maxLocal) || 12931590Srgrimes (((job->flags & JOB_SPECIAL)) && 12941590Srgrimes (maxLocal == 0))) && 12951590Srgrimes (nJobs != maxJobs)) 12961590Srgrimes { 12971590Srgrimes /* 12981590Srgrimes * If the job is remote, it's ok to resume it as long as the 12991590Srgrimes * maximum concurrency won't be exceeded. If it's local and 13001590Srgrimes * we haven't reached the local concurrency limit already (or the 13011590Srgrimes * job must be run locally and maxLocal is 0), it's also ok to 13021590Srgrimes * resume it. 13031590Srgrimes */ 13041590Srgrimes Boolean error; 13051590Srgrimes extern int errno; 13061590Srgrimes union wait status; 13078874Srgrimes 13081590Srgrimes#ifdef RMT_WANTS_SIGNALS 13091590Srgrimes if (job->flags & JOB_REMOTE) { 13101590Srgrimes error = !Rmt_Signal(job, SIGCONT); 13111590Srgrimes } else 13121590Srgrimes#endif /* RMT_WANTS_SIGNALS */ 13131590Srgrimes error = (KILL(job->pid, SIGCONT) != 0); 13141590Srgrimes 13151590Srgrimes if (!error) { 13161590Srgrimes /* 13171590Srgrimes * Make sure the user knows we've continued the beast and 13181590Srgrimes * actually put the thing in the job table. 13191590Srgrimes */ 13201590Srgrimes job->flags |= JOB_CONTINUING; 13211590Srgrimes status.w_termsig = SIGCONT; 13221590Srgrimes JobFinish(job, status); 13238874Srgrimes 13241590Srgrimes job->flags &= ~(JOB_RESUME|JOB_CONTINUING); 13251590Srgrimes if (DEBUG(JOB)) { 13261590Srgrimes printf("done\n"); 13271590Srgrimes } 13281590Srgrimes } else { 13291590Srgrimes Error("couldn't resume %s: %s", 13301590Srgrimes job->node->name, strerror(errno)); 13311590Srgrimes status.w_status = 0; 13321590Srgrimes status.w_retcode = 1; 13331590Srgrimes JobFinish(job, status); 13341590Srgrimes } 13351590Srgrimes } else { 13361590Srgrimes /* 13371590Srgrimes * Job cannot be restarted. Mark the table as full and 13381590Srgrimes * place the job back on the list of stopped jobs. 13391590Srgrimes */ 13401590Srgrimes if (DEBUG(JOB)) { 13411590Srgrimes printf("table full\n"); 13421590Srgrimes } 13431590Srgrimes (void)Lst_AtFront(stoppedJobs, (ClientData)job); 13441590Srgrimes jobFull = TRUE; 13451590Srgrimes if (DEBUG(JOB)) { 13461590Srgrimes printf("Job queue is full.\n"); 13471590Srgrimes } 13481590Srgrimes } 13491590Srgrimes } 13501590Srgrimes} 13511590Srgrimes 13521590Srgrimes/*- 13531590Srgrimes *----------------------------------------------------------------------- 13541590Srgrimes * JobStart -- 13551590Srgrimes * Start a target-creation process going for the target described 13568874Srgrimes * by the graph node gn. 13571590Srgrimes * 13581590Srgrimes * Results: 13591590Srgrimes * JOB_ERROR if there was an error in the commands, JOB_FINISHED 13601590Srgrimes * if there isn't actually anything left to do for the job and 13611590Srgrimes * JOB_RUNNING if the job has been started. 13621590Srgrimes * 13631590Srgrimes * Side Effects: 13641590Srgrimes * A new Job node is created and added to the list of running 13651590Srgrimes * jobs. PMake is forked and a child shell created. 13661590Srgrimes *----------------------------------------------------------------------- 13671590Srgrimes */ 13681590Srgrimesstatic int 13691590SrgrimesJobStart (gn, flags, previous) 13701590Srgrimes GNode *gn; /* target to create */ 13715814Sjkh int flags; /* flags for the job to override normal ones. 13721590Srgrimes * e.g. JOB_SPECIAL or JOB_IGNDOTS */ 13731590Srgrimes Job *previous; /* The previous Job structure for this node, 13741590Srgrimes * if any. */ 13751590Srgrimes{ 13761590Srgrimes register Job *job; /* new job descriptor */ 13771590Srgrimes char *argv[4]; /* Argument vector to shell */ 13781590Srgrimes static int jobno = 0; /* job number of catching output in a file */ 13791590Srgrimes Boolean cmdsOK; /* true if the nodes commands were all right */ 13801590Srgrimes Boolean local; /* Set true if the job was run locally */ 13811590Srgrimes Boolean noExec; /* Set true if we decide not to run the job */ 13821590Srgrimes 13831590Srgrimes if (previous != (Job *)NULL) { 13841590Srgrimes previous->flags &= ~ (JOB_FIRST|JOB_IGNERR|JOB_SILENT|JOB_REMOTE); 13851590Srgrimes job = previous; 13861590Srgrimes } else { 13871590Srgrimes job = (Job *) emalloc (sizeof (Job)); 13881590Srgrimes if (job == (Job *)NULL) { 13891590Srgrimes Punt("JobStart out of memory"); 13901590Srgrimes } 13911590Srgrimes flags |= JOB_FIRST; 13921590Srgrimes } 13931590Srgrimes 13941590Srgrimes job->node = gn; 13951590Srgrimes job->tailCmds = NILLNODE; 13961590Srgrimes 13971590Srgrimes /* 13981590Srgrimes * Set the initial value of the flags for this job based on the global 13991590Srgrimes * ones and the node's attributes... Any flags supplied by the caller 14001590Srgrimes * are also added to the field. 14011590Srgrimes */ 14021590Srgrimes job->flags = 0; 14031590Srgrimes if (Targ_Ignore (gn)) { 14041590Srgrimes job->flags |= JOB_IGNERR; 14051590Srgrimes } 14061590Srgrimes if (Targ_Silent (gn)) { 14071590Srgrimes job->flags |= JOB_SILENT; 14081590Srgrimes } 14091590Srgrimes job->flags |= flags; 14101590Srgrimes 14111590Srgrimes /* 14121590Srgrimes * Check the commands now so any attributes from .DEFAULT have a chance 14131590Srgrimes * to migrate to the node 14141590Srgrimes */ 14151590Srgrimes if (job->flags & JOB_FIRST) { 14161590Srgrimes cmdsOK = Job_CheckCommands(gn, Error); 14171590Srgrimes } else { 14181590Srgrimes cmdsOK = TRUE; 14191590Srgrimes } 14208874Srgrimes 14211590Srgrimes /* 14221590Srgrimes * If the -n flag wasn't given, we open up OUR (not the child's) 14231590Srgrimes * temporary file to stuff commands in it. The thing is rd/wr so we don't 14241590Srgrimes * need to reopen it to feed it to the shell. If the -n flag *was* given, 14251590Srgrimes * we just set the file to be stdout. Cute, huh? 14261590Srgrimes */ 14271590Srgrimes if ((gn->type & OP_MAKE) || (!noExecute && !touchFlag)) { 14281590Srgrimes /* 14291590Srgrimes * We're serious here, but if the commands were bogus, we're 14301590Srgrimes * also dead... 14311590Srgrimes */ 14321590Srgrimes if (!cmdsOK) { 14331590Srgrimes DieHorribly(); 14341590Srgrimes } 14358874Srgrimes 14361590Srgrimes job->cmdFILE = fopen (tfile, "w+"); 14371590Srgrimes if (job->cmdFILE == (FILE *) NULL) { 14381590Srgrimes Punt ("Could not open %s", tfile); 14391590Srgrimes } 14401590Srgrimes fcntl(fileno(job->cmdFILE), F_SETFD, 1); 14411590Srgrimes /* 14421590Srgrimes * Send the commands to the command file, flush all its buffers then 14431590Srgrimes * rewind and remove the thing. 14441590Srgrimes */ 14451590Srgrimes noExec = FALSE; 14461590Srgrimes 14471590Srgrimes /* 14481590Srgrimes * used to be backwards; replace when start doing multiple commands 14491590Srgrimes * per shell. 14501590Srgrimes */ 14511590Srgrimes if (compatMake) { 14521590Srgrimes /* 14531590Srgrimes * Be compatible: If this is the first time for this node, 14541590Srgrimes * verify its commands are ok and open the commands list for 14551590Srgrimes * sequential access by later invocations of JobStart. 14561590Srgrimes * Once that is done, we take the next command off the list 14571590Srgrimes * and print it to the command file. If the command was an 14581590Srgrimes * ellipsis, note that there's nothing more to execute. 14591590Srgrimes */ 14601590Srgrimes if ((job->flags&JOB_FIRST) && (Lst_Open(gn->commands) != SUCCESS)){ 14611590Srgrimes cmdsOK = FALSE; 14621590Srgrimes } else { 14631590Srgrimes LstNode ln = Lst_Next (gn->commands); 14648874Srgrimes 14651590Srgrimes if ((ln == NILLNODE) || 14661590Srgrimes JobPrintCommand ((char *)Lst_Datum (ln), job)) 14671590Srgrimes { 14681590Srgrimes noExec = TRUE; 14691590Srgrimes Lst_Close (gn->commands); 14701590Srgrimes } 14711590Srgrimes if (noExec && !(job->flags & JOB_FIRST)) { 14721590Srgrimes /* 14731590Srgrimes * If we're not going to execute anything, the job 14741590Srgrimes * is done and we need to close down the various 14751590Srgrimes * file descriptors we've opened for output, then 14761590Srgrimes * call JobDoOutput to catch the final characters or 14771590Srgrimes * send the file to the screen... Note that the i/o streams 14781590Srgrimes * are only open if this isn't the first job. 14791590Srgrimes * Note also that this could not be done in 14801590Srgrimes * Job_CatchChildren b/c it wasn't clear if there were 14811590Srgrimes * more commands to execute or not... 14821590Srgrimes */ 14831590Srgrimes if (usePipes) { 14841590Srgrimes#ifdef RMT_WILL_WATCH 14851590Srgrimes Rmt_Ignore(job->inPipe); 14861590Srgrimes#else 14871590Srgrimes FD_CLR(job->inPipe, &outputs); 14881590Srgrimes#endif 14891590Srgrimes if (job->outPipe != job->inPipe) { 14901590Srgrimes (void)close (job->outPipe); 14911590Srgrimes } 14921590Srgrimes JobDoOutput (job, TRUE); 14931590Srgrimes (void)close (job->inPipe); 14941590Srgrimes } else { 14951590Srgrimes (void)close (job->outFd); 14961590Srgrimes JobDoOutput (job, TRUE); 14971590Srgrimes } 14981590Srgrimes } 14991590Srgrimes } 15001590Srgrimes } else { 15011590Srgrimes /* 15021590Srgrimes * We can do all the commands at once. hooray for sanity 15031590Srgrimes */ 15041590Srgrimes numCommands = 0; 15051590Srgrimes Lst_ForEach (gn->commands, JobPrintCommand, (ClientData)job); 15068874Srgrimes 15071590Srgrimes /* 15081590Srgrimes * If we didn't print out any commands to the shell script, 15091590Srgrimes * there's not much point in executing the shell, is there? 15101590Srgrimes */ 15111590Srgrimes if (numCommands == 0) { 15121590Srgrimes noExec = TRUE; 15131590Srgrimes } 15141590Srgrimes } 15151590Srgrimes } else if (noExecute) { 15161590Srgrimes /* 15171590Srgrimes * Not executing anything -- just print all the commands to stdout 15181590Srgrimes * in one fell swoop. This will still set up job->tailCmds correctly. 15191590Srgrimes */ 15201590Srgrimes if (lastNode != gn) { 15211590Srgrimes printf (targFmt, gn->name); 15221590Srgrimes lastNode = gn; 15231590Srgrimes } 15241590Srgrimes job->cmdFILE = stdout; 15251590Srgrimes /* 15261590Srgrimes * Only print the commands if they're ok, but don't die if they're 15271590Srgrimes * not -- just let the user know they're bad and keep going. It 15281590Srgrimes * doesn't do any harm in this case and may do some good. 15291590Srgrimes */ 15301590Srgrimes if (cmdsOK) { 15311590Srgrimes Lst_ForEach(gn->commands, JobPrintCommand, (ClientData)job); 15321590Srgrimes } 15331590Srgrimes /* 15341590Srgrimes * Don't execute the shell, thank you. 15351590Srgrimes */ 15361590Srgrimes noExec = TRUE; 15371590Srgrimes } else { 15381590Srgrimes /* 15391590Srgrimes * Just touch the target and note that no shell should be executed. 15401590Srgrimes * Set cmdFILE to stdout to make life easier. Check the commands, too, 15411590Srgrimes * but don't die if they're no good -- it does no harm to keep working 15421590Srgrimes * up the graph. 15431590Srgrimes */ 15441590Srgrimes job->cmdFILE = stdout; 15451590Srgrimes Job_Touch (gn, job->flags&JOB_SILENT); 15461590Srgrimes noExec = TRUE; 15471590Srgrimes } 15481590Srgrimes 15491590Srgrimes /* 15508874Srgrimes * If we're not supposed to execute a shell, don't. 15511590Srgrimes */ 15521590Srgrimes if (noExec) { 15531590Srgrimes /* 15541590Srgrimes * Unlink and close the command file if we opened one 15551590Srgrimes */ 15561590Srgrimes if (job->cmdFILE != stdout) { 15571590Srgrimes (void) unlink (tfile); 15581590Srgrimes fclose(job->cmdFILE); 15591590Srgrimes } else { 15601590Srgrimes fflush (stdout); 15611590Srgrimes } 15621590Srgrimes 15631590Srgrimes /* 15641590Srgrimes * We only want to work our way up the graph if we aren't here because 15651590Srgrimes * the commands for the job were no good. 15661590Srgrimes */ 15671590Srgrimes if (cmdsOK) { 15681590Srgrimes if (aborting == 0) { 15691590Srgrimes if (job->tailCmds != NILLNODE) { 15701590Srgrimes Lst_ForEachFrom(job->node->commands, job->tailCmds, 15711590Srgrimes JobSaveCommand, 15721590Srgrimes (ClientData)job->node); 15731590Srgrimes } 15741590Srgrimes Make_Update(job->node); 15751590Srgrimes } 15761590Srgrimes free((Address)job); 15771590Srgrimes return(JOB_FINISHED); 15781590Srgrimes } else { 15791590Srgrimes free((Address)job); 15801590Srgrimes return(JOB_ERROR); 15811590Srgrimes } 15821590Srgrimes } else { 15831590Srgrimes fflush (job->cmdFILE); 15841590Srgrimes (void) unlink (tfile); 15851590Srgrimes } 15861590Srgrimes 15871590Srgrimes /* 15881590Srgrimes * Set up the control arguments to the shell. This is based on the flags 15891590Srgrimes * set earlier for this job. 15901590Srgrimes */ 15911590Srgrimes JobMakeArgv(job, argv); 15921590Srgrimes 15931590Srgrimes /* 15941590Srgrimes * If we're using pipes to catch output, create the pipe by which we'll 15951590Srgrimes * get the shell's output. If we're using files, print out that we're 15961590Srgrimes * starting a job and then set up its temporary-file name. This is just 15971590Srgrimes * tfile with two extra digits tacked on -- jobno. 15981590Srgrimes */ 15991590Srgrimes if (job->flags & JOB_FIRST) { 16001590Srgrimes if (usePipes) { 16011590Srgrimes int fd[2]; 16028874Srgrimes (void) pipe(fd); 16031590Srgrimes job->inPipe = fd[0]; 16041590Srgrimes job->outPipe = fd[1]; 16051590Srgrimes (void)fcntl (job->inPipe, F_SETFD, 1); 16061590Srgrimes (void)fcntl (job->outPipe, F_SETFD, 1); 16071590Srgrimes } else { 16081590Srgrimes printf ("Remaking `%s'\n", gn->name); 16091590Srgrimes fflush (stdout); 16101590Srgrimes sprintf (job->outFile, "%s%02d", tfile, jobno); 16111590Srgrimes jobno = (jobno + 1) % 100; 16121590Srgrimes job->outFd = open(job->outFile,O_WRONLY|O_CREAT|O_APPEND,0600); 16131590Srgrimes (void)fcntl (job->outFd, F_SETFD, 1); 16141590Srgrimes } 16151590Srgrimes } 16161590Srgrimes 16171590Srgrimes local = TRUE; 16181590Srgrimes 16191590Srgrimes if (local && (((nLocal >= maxLocal) && 16201590Srgrimes !(job->flags & JOB_SPECIAL) && 16211590Srgrimes (maxLocal != 0)))) 16221590Srgrimes { 16231590Srgrimes /* 16241590Srgrimes * The job can only be run locally, but we've hit the limit of 16251590Srgrimes * local concurrency, so put the job on hold until some other job 16261590Srgrimes * finishes. Note that the special jobs (.BEGIN, .INTERRUPT and .END) 16271590Srgrimes * may be run locally even when the local limit has been reached 16281590Srgrimes * (e.g. when maxLocal == 0), though they will be exported if at 16298874Srgrimes * all possible. 16301590Srgrimes */ 16311590Srgrimes jobFull = TRUE; 16328874Srgrimes 16331590Srgrimes if (DEBUG(JOB)) { 16341590Srgrimes printf("Can only run job locally.\n"); 16351590Srgrimes } 16361590Srgrimes job->flags |= JOB_RESTART; 16371590Srgrimes (void)Lst_AtEnd(stoppedJobs, (ClientData)job); 16381590Srgrimes } else { 16391590Srgrimes if ((nLocal >= maxLocal) && local) { 16401590Srgrimes /* 16411590Srgrimes * If we're running this job locally as a special case (see above), 16421590Srgrimes * at least say the table is full. 16431590Srgrimes */ 16441590Srgrimes jobFull = TRUE; 16451590Srgrimes if (DEBUG(JOB)) { 16461590Srgrimes printf("Local job queue is full.\n"); 16471590Srgrimes } 16481590Srgrimes } 16491590Srgrimes JobExec(job, argv); 16501590Srgrimes } 16511590Srgrimes return(JOB_RUNNING); 16521590Srgrimes} 16531590Srgrimes 16541590Srgrimes/*- 16551590Srgrimes *----------------------------------------------------------------------- 16561590Srgrimes * JobDoOutput -- 16571590Srgrimes * This function is called at different times depending on 16581590Srgrimes * whether the user has specified that output is to be collected 16591590Srgrimes * via pipes or temporary files. In the former case, we are called 16601590Srgrimes * whenever there is something to read on the pipe. We collect more 16611590Srgrimes * output from the given job and store it in the job's outBuf. If 16621590Srgrimes * this makes up a line, we print it tagged by the job's identifier, 16631590Srgrimes * as necessary. 16641590Srgrimes * If output has been collected in a temporary file, we open the 16651590Srgrimes * file and read it line by line, transfering it to our own 16661590Srgrimes * output channel until the file is empty. At which point we 16671590Srgrimes * remove the temporary file. 16681590Srgrimes * In both cases, however, we keep our figurative eye out for the 16691590Srgrimes * 'noPrint' line for the shell from which the output came. If 16701590Srgrimes * we recognize a line, we don't print it. If the command is not 16711590Srgrimes * alone on the line (the character after it is not \0 or \n), we 16721590Srgrimes * do print whatever follows it. 16731590Srgrimes * 16741590Srgrimes * Results: 16751590Srgrimes * None 16761590Srgrimes * 16771590Srgrimes * Side Effects: 16781590Srgrimes * curPos may be shifted as may the contents of outBuf. 16791590Srgrimes *----------------------------------------------------------------------- 16801590Srgrimes */ 16811590Srgrimesstatic void 16821590SrgrimesJobDoOutput (job, finish) 16831590Srgrimes register Job *job; /* the job whose output needs printing */ 16841590Srgrimes Boolean finish; /* TRUE if this is the last time we'll be 16851590Srgrimes * called for this job */ 16861590Srgrimes{ 16871590Srgrimes Boolean gotNL = FALSE; /* true if got a newline */ 16881590Srgrimes register int nr; /* number of bytes read */ 16891590Srgrimes register int i; /* auxiliary index into outBuf */ 16901590Srgrimes register int max; /* limit for i (end of current data) */ 16911590Srgrimes int nRead; /* (Temporary) number of bytes read */ 16921590Srgrimes 16931590Srgrimes FILE *oFILE; /* Stream pointer to shell's output file */ 16941590Srgrimes char inLine[132]; 16951590Srgrimes 16968874Srgrimes 16971590Srgrimes if (usePipes) { 16981590Srgrimes /* 16991590Srgrimes * Read as many bytes as will fit in the buffer. 17001590Srgrimes */ 17011590Srgrimesend_loop: 17028874Srgrimes 17031590Srgrimes nRead = read (job->inPipe, &job->outBuf[job->curPos], 17041590Srgrimes JOB_BUFSIZE - job->curPos); 17051590Srgrimes if (nRead < 0) { 17061590Srgrimes if (DEBUG(JOB)) { 17071590Srgrimes perror("JobDoOutput(piperead)"); 17081590Srgrimes } 17091590Srgrimes nr = 0; 17101590Srgrimes } else { 17111590Srgrimes nr = nRead; 17121590Srgrimes } 17131590Srgrimes 17141590Srgrimes /* 17151590Srgrimes * If we hit the end-of-file (the job is dead), we must flush its 17161590Srgrimes * remaining output, so pretend we read a newline if there's any 17171590Srgrimes * output remaining in the buffer. 17181590Srgrimes * Also clear the 'finish' flag so we stop looping. 17191590Srgrimes */ 17201590Srgrimes if ((nr == 0) && (job->curPos != 0)) { 17211590Srgrimes job->outBuf[job->curPos] = '\n'; 17221590Srgrimes nr = 1; 17231590Srgrimes finish = FALSE; 17241590Srgrimes } else if (nr == 0) { 17251590Srgrimes finish = FALSE; 17261590Srgrimes } 17278874Srgrimes 17281590Srgrimes /* 17291590Srgrimes * Look for the last newline in the bytes we just got. If there is 17301590Srgrimes * one, break out of the loop with 'i' as its index and gotNL set 17318874Srgrimes * TRUE. 17321590Srgrimes */ 17331590Srgrimes max = job->curPos + nr; 17341590Srgrimes for (i = job->curPos + nr - 1; i >= job->curPos; i--) { 17351590Srgrimes if (job->outBuf[i] == '\n') { 17361590Srgrimes gotNL = TRUE; 17371590Srgrimes break; 17381590Srgrimes } else if (job->outBuf[i] == '\0') { 17391590Srgrimes /* 17401590Srgrimes * Why? 17411590Srgrimes */ 17421590Srgrimes job->outBuf[i] = ' '; 17431590Srgrimes } 17441590Srgrimes } 17458874Srgrimes 17461590Srgrimes if (!gotNL) { 17471590Srgrimes job->curPos += nr; 17481590Srgrimes if (job->curPos == JOB_BUFSIZE) { 17491590Srgrimes /* 17501590Srgrimes * If we've run out of buffer space, we have no choice 17518874Srgrimes * but to print the stuff. sigh. 17521590Srgrimes */ 17531590Srgrimes gotNL = TRUE; 17541590Srgrimes i = job->curPos; 17551590Srgrimes } 17561590Srgrimes } 17571590Srgrimes if (gotNL) { 17581590Srgrimes /* 17591590Srgrimes * Need to send the output to the screen. Null terminate it 17601590Srgrimes * first, overwriting the newline character if there was one. 17611590Srgrimes * So long as the line isn't one we should filter (according 17621590Srgrimes * to the shell description), we print the line, preceeded 17631590Srgrimes * by a target banner if this target isn't the same as the 17641590Srgrimes * one for which we last printed something. 17651590Srgrimes * The rest of the data in the buffer are then shifted down 17668874Srgrimes * to the start of the buffer and curPos is set accordingly. 17671590Srgrimes */ 17681590Srgrimes job->outBuf[i] = '\0'; 17691590Srgrimes if (i >= job->curPos) { 17701590Srgrimes register char *cp, *ecp; 17711590Srgrimes 17721590Srgrimes cp = job->outBuf; 17731590Srgrimes if (commandShell->noPrint) { 17741590Srgrimes ecp = Str_FindSubstring(job->outBuf, 17751590Srgrimes commandShell->noPrint); 17761590Srgrimes while (ecp != (char *)NULL) { 17771590Srgrimes if (cp != ecp) { 17781590Srgrimes *ecp = '\0'; 17791590Srgrimes if (job->node != lastNode) { 17801590Srgrimes printf (targFmt, job->node->name); 17811590Srgrimes lastNode = job->node; 17821590Srgrimes } 17831590Srgrimes /* 17841590Srgrimes * The only way there wouldn't be a newline after 17851590Srgrimes * this line is if it were the last in the buffer. 17861590Srgrimes * however, since the non-printable comes after it, 17871590Srgrimes * there must be a newline, so we don't print one. 17881590Srgrimes */ 17891590Srgrimes printf ("%s", cp); 17901590Srgrimes } 17911590Srgrimes cp = ecp + commandShell->noPLen; 17921590Srgrimes if (cp != &job->outBuf[i]) { 17931590Srgrimes /* 17941590Srgrimes * Still more to print, look again after skipping 17951590Srgrimes * the whitespace following the non-printable 17961590Srgrimes * command.... 17971590Srgrimes */ 17981590Srgrimes cp++; 17991590Srgrimes while (*cp == ' ' || *cp == '\t' || *cp == '\n') { 18001590Srgrimes cp++; 18011590Srgrimes } 18021590Srgrimes ecp = Str_FindSubstring (cp, 18031590Srgrimes commandShell->noPrint); 18041590Srgrimes } else { 18051590Srgrimes break; 18061590Srgrimes } 18071590Srgrimes } 18081590Srgrimes } 18091590Srgrimes 18101590Srgrimes /* 18111590Srgrimes * There's still more in that thar buffer. This time, though, 18121590Srgrimes * we know there's no newline at the end, so we add one of 18131590Srgrimes * our own free will. 18141590Srgrimes */ 18151590Srgrimes if (*cp != '\0') { 18161590Srgrimes if (job->node != lastNode) { 18171590Srgrimes printf (targFmt, job->node->name); 18181590Srgrimes lastNode = job->node; 18191590Srgrimes } 18201590Srgrimes printf ("%s\n", cp); 18211590Srgrimes } 18221590Srgrimes 18231590Srgrimes fflush (stdout); 18241590Srgrimes } 18251590Srgrimes if (i < max - 1) { 18261590Srgrimes /* shift the remaining characters down */ 18271590Srgrimes memcpy ( job->outBuf, &job->outBuf[i + 1], max - (i + 1)); 18281590Srgrimes job->curPos = max - (i + 1); 18298874Srgrimes 18301590Srgrimes } else { 18311590Srgrimes /* 18321590Srgrimes * We have written everything out, so we just start over 18331590Srgrimes * from the start of the buffer. No copying. No nothing. 18341590Srgrimes */ 18351590Srgrimes job->curPos = 0; 18361590Srgrimes } 18371590Srgrimes } 18381590Srgrimes if (finish) { 18391590Srgrimes /* 18401590Srgrimes * If the finish flag is true, we must loop until we hit 18411590Srgrimes * end-of-file on the pipe. This is guaranteed to happen eventually 18421590Srgrimes * since the other end of the pipe is now closed (we closed it 18431590Srgrimes * explicitly and the child has exited). When we do get an EOF, 18441590Srgrimes * finish will be set FALSE and we'll fall through and out. 18451590Srgrimes */ 18461590Srgrimes goto end_loop; 18471590Srgrimes } 18481590Srgrimes } else { 18491590Srgrimes /* 18501590Srgrimes * We've been called to retrieve the output of the job from the 18511590Srgrimes * temporary file where it's been squirreled away. This consists of 18521590Srgrimes * opening the file, reading the output line by line, being sure not 18531590Srgrimes * to print the noPrint line for the shell we used, then close and 18541590Srgrimes * remove the temporary file. Very simple. 18551590Srgrimes * 18561590Srgrimes * Change to read in blocks and do FindSubString type things as for 18571590Srgrimes * pipes? That would allow for "@echo -n..." 18581590Srgrimes */ 18591590Srgrimes oFILE = fopen (job->outFile, "r"); 18601590Srgrimes if (oFILE != (FILE *) NULL) { 18611590Srgrimes printf ("Results of making %s:\n", job->node->name); 18621590Srgrimes while (fgets (inLine, sizeof(inLine), oFILE) != NULL) { 18631590Srgrimes register char *cp, *ecp, *endp; 18641590Srgrimes 18651590Srgrimes cp = inLine; 18661590Srgrimes endp = inLine + strlen(inLine); 18671590Srgrimes if (endp[-1] == '\n') { 18681590Srgrimes *--endp = '\0'; 18691590Srgrimes } 18701590Srgrimes if (commandShell->noPrint) { 18711590Srgrimes ecp = Str_FindSubstring(cp, commandShell->noPrint); 18721590Srgrimes while (ecp != (char *)NULL) { 18731590Srgrimes if (cp != ecp) { 18741590Srgrimes *ecp = '\0'; 18751590Srgrimes /* 18761590Srgrimes * The only way there wouldn't be a newline after 18771590Srgrimes * this line is if it were the last in the buffer. 18781590Srgrimes * however, since the non-printable comes after it, 18791590Srgrimes * there must be a newline, so we don't print one. 18801590Srgrimes */ 18811590Srgrimes printf ("%s", cp); 18821590Srgrimes } 18831590Srgrimes cp = ecp + commandShell->noPLen; 18841590Srgrimes if (cp != endp) { 18851590Srgrimes /* 18861590Srgrimes * Still more to print, look again after skipping 18871590Srgrimes * the whitespace following the non-printable 18881590Srgrimes * command.... 18891590Srgrimes */ 18901590Srgrimes cp++; 18911590Srgrimes while (*cp == ' ' || *cp == '\t' || *cp == '\n') { 18921590Srgrimes cp++; 18931590Srgrimes } 18941590Srgrimes ecp = Str_FindSubstring(cp, commandShell->noPrint); 18951590Srgrimes } else { 18961590Srgrimes break; 18971590Srgrimes } 18981590Srgrimes } 18991590Srgrimes } 19001590Srgrimes 19011590Srgrimes /* 19021590Srgrimes * There's still more in that thar buffer. This time, though, 19031590Srgrimes * we know there's no newline at the end, so we add one of 19041590Srgrimes * our own free will. 19051590Srgrimes */ 19061590Srgrimes if (*cp != '\0') { 19071590Srgrimes printf ("%s\n", cp); 19081590Srgrimes } 19091590Srgrimes } 19101590Srgrimes fclose (oFILE); 19111590Srgrimes (void) unlink (job->outFile); 19121590Srgrimes } 19131590Srgrimes } 19141590Srgrimes fflush(stdout); 19151590Srgrimes} 19161590Srgrimes 19171590Srgrimes/*- 19181590Srgrimes *----------------------------------------------------------------------- 19191590Srgrimes * Job_CatchChildren -- 19201590Srgrimes * Handle the exit of a child. Called from Make_Make. 19211590Srgrimes * 19221590Srgrimes * Results: 19231590Srgrimes * none. 19241590Srgrimes * 19251590Srgrimes * Side Effects: 19261590Srgrimes * The job descriptor is removed from the list of children. 19271590Srgrimes * 19281590Srgrimes * Notes: 19291590Srgrimes * We do waits, blocking or not, according to the wisdom of our 19301590Srgrimes * caller, until there are no more children to report. For each 19311590Srgrimes * job, call JobFinish to finish things off. This will take care of 19321590Srgrimes * putting jobs on the stoppedJobs queue. 19331590Srgrimes * 19341590Srgrimes *----------------------------------------------------------------------- 19351590Srgrimes */ 19361590Srgrimesvoid 19371590SrgrimesJob_CatchChildren (block) 19381590Srgrimes Boolean block; /* TRUE if should block on the wait. */ 19391590Srgrimes{ 19401590Srgrimes int pid; /* pid of dead child */ 19411590Srgrimes register Job *job; /* job descriptor for dead child */ 19421590Srgrimes LstNode jnode; /* list element for finding job */ 19431590Srgrimes union wait status; /* Exit/termination status */ 19441590Srgrimes 19451590Srgrimes /* 19461590Srgrimes * Don't even bother if we know there's no one around. 19471590Srgrimes */ 19481590Srgrimes if (nLocal == 0) { 19491590Srgrimes return; 19501590Srgrimes } 19518874Srgrimes 19521590Srgrimes while ((pid = wait3((int *)&status, (block?0:WNOHANG)|WUNTRACED, 19531590Srgrimes (struct rusage *)0)) > 0) 19541590Srgrimes { 19551590Srgrimes if (DEBUG(JOB)) 19561590Srgrimes printf("Process %d exited or stopped.\n", pid); 19571590Srgrimes 19588874Srgrimes 19595814Sjkh jnode = Lst_Find (jobs, (ClientData)&pid, JobCmpPid); 19601590Srgrimes 19611590Srgrimes if (jnode == NILLNODE) { 19621590Srgrimes if (WIFSIGNALED(status) && (status.w_termsig == SIGCONT)) { 19635814Sjkh jnode = Lst_Find(stoppedJobs, (ClientData) &pid, JobCmpPid); 19641590Srgrimes if (jnode == NILLNODE) { 19651590Srgrimes Error("Resumed child (%d) not in table", pid); 19661590Srgrimes continue; 19671590Srgrimes } 19681590Srgrimes job = (Job *)Lst_Datum(jnode); 19691590Srgrimes (void)Lst_Remove(stoppedJobs, jnode); 19701590Srgrimes } else { 19711590Srgrimes Error ("Child (%d) not in table?", pid); 19721590Srgrimes continue; 19731590Srgrimes } 19741590Srgrimes } else { 19751590Srgrimes job = (Job *) Lst_Datum (jnode); 19761590Srgrimes (void)Lst_Remove (jobs, jnode); 19771590Srgrimes nJobs -= 1; 19781590Srgrimes if (jobFull && DEBUG(JOB)) { 19791590Srgrimes printf("Job queue is no longer full.\n"); 19801590Srgrimes } 19811590Srgrimes jobFull = FALSE; 19821590Srgrimes nLocal -= 1; 19831590Srgrimes } 19841590Srgrimes 19851590Srgrimes JobFinish (job, status); 19861590Srgrimes } 19871590Srgrimes} 19881590Srgrimes 19891590Srgrimes/*- 19901590Srgrimes *----------------------------------------------------------------------- 19911590Srgrimes * Job_CatchOutput -- 19921590Srgrimes * Catch the output from our children, if we're using 19931590Srgrimes * pipes do so. Otherwise just block time until we get a 19941590Srgrimes * signal (most likely a SIGCHLD) since there's no point in 19951590Srgrimes * just spinning when there's nothing to do and the reaping 19968874Srgrimes * of a child can wait for a while. 19971590Srgrimes * 19981590Srgrimes * Results: 19998874Srgrimes * None 20001590Srgrimes * 20011590Srgrimes * Side Effects: 20021590Srgrimes * Output is read from pipes if we're piping. 20031590Srgrimes * ----------------------------------------------------------------------- 20041590Srgrimes */ 20051590Srgrimesvoid 20061590SrgrimesJob_CatchOutput () 20071590Srgrimes{ 20081590Srgrimes int nfds; 20091590Srgrimes struct timeval timeout; 20101590Srgrimes fd_set readfds; 20111590Srgrimes register LstNode ln; 20121590Srgrimes register Job *job; 20131590Srgrimes#ifdef RMT_WILL_WATCH 20141590Srgrimes int pnJobs; /* Previous nJobs */ 20151590Srgrimes#endif 20161590Srgrimes 20171590Srgrimes fflush(stdout); 20181590Srgrimes#ifdef RMT_WILL_WATCH 20191590Srgrimes pnJobs = nJobs; 20201590Srgrimes 20211590Srgrimes /* 20221590Srgrimes * It is possible for us to be called with nJobs equal to 0. This happens 20231590Srgrimes * if all the jobs finish and a job that is stopped cannot be run 20241590Srgrimes * locally (eg if maxLocal is 0) and cannot be exported. The job will 20251590Srgrimes * be placed back on the stoppedJobs queue, Job_Empty() will return false, 20261590Srgrimes * Make_Run will call us again when there's nothing for which to wait. 20271590Srgrimes * nJobs never changes, so we loop forever. Hence the check. It could 20281590Srgrimes * be argued that we should sleep for a bit so as not to swamp the 20291590Srgrimes * exportation system with requests. Perhaps we should. 20301590Srgrimes * 20311590Srgrimes * NOTE: IT IS THE RESPONSIBILITY OF Rmt_Wait TO CALL Job_CatchChildren 20321590Srgrimes * IN A TIMELY FASHION TO CATCH ANY LOCALLY RUNNING JOBS THAT EXIT. 20331590Srgrimes * It may use the variable nLocal to determine if it needs to call 20341590Srgrimes * Job_CatchChildren (if nLocal is 0, there's nothing for which to 20351590Srgrimes * wait...) 20361590Srgrimes */ 20371590Srgrimes while (nJobs != 0 && pnJobs == nJobs) { 20381590Srgrimes Rmt_Wait(); 20391590Srgrimes } 20401590Srgrimes#else 20411590Srgrimes if (usePipes) { 20421590Srgrimes readfds = outputs; 20431590Srgrimes timeout.tv_sec = SEL_SEC; 20441590Srgrimes timeout.tv_usec = SEL_USEC; 20451590Srgrimes 20461590Srgrimes if ((nfds = select (FD_SETSIZE, &readfds, (fd_set *) 0, (fd_set *) 0, &timeout)) < 0) 20471590Srgrimes { 20481590Srgrimes return; 20491590Srgrimes } else { 20501590Srgrimes if (Lst_Open (jobs) == FAILURE) { 20511590Srgrimes Punt ("Cannot open job table"); 20521590Srgrimes } 20531590Srgrimes while (nfds && (ln = Lst_Next (jobs)) != NILLNODE) { 20541590Srgrimes job = (Job *) Lst_Datum (ln); 20551590Srgrimes if (FD_ISSET(job->inPipe, &readfds)) { 20561590Srgrimes JobDoOutput (job, FALSE); 20571590Srgrimes nfds -= 1; 20581590Srgrimes } 20591590Srgrimes } 20601590Srgrimes Lst_Close (jobs); 20611590Srgrimes } 20621590Srgrimes } 20631590Srgrimes#endif /* RMT_WILL_WATCH */ 20641590Srgrimes} 20651590Srgrimes 20661590Srgrimes/*- 20671590Srgrimes *----------------------------------------------------------------------- 20681590Srgrimes * Job_Make -- 20691590Srgrimes * Start the creation of a target. Basically a front-end for 20701590Srgrimes * JobStart used by the Make module. 20711590Srgrimes * 20721590Srgrimes * Results: 20731590Srgrimes * None. 20741590Srgrimes * 20751590Srgrimes * Side Effects: 20761590Srgrimes * Another job is started. 20771590Srgrimes * 20781590Srgrimes *----------------------------------------------------------------------- 20791590Srgrimes */ 20801590Srgrimesvoid 20811590SrgrimesJob_Make (gn) 20821590Srgrimes GNode *gn; 20831590Srgrimes{ 20841590Srgrimes (void)JobStart (gn, 0, (Job *)NULL); 20851590Srgrimes} 20861590Srgrimes 20871590Srgrimes/*- 20881590Srgrimes *----------------------------------------------------------------------- 20891590Srgrimes * Job_Init -- 20901590Srgrimes * Initialize the process module 20911590Srgrimes * 20921590Srgrimes * Results: 20931590Srgrimes * none 20941590Srgrimes * 20951590Srgrimes * Side Effects: 20961590Srgrimes * lists and counters are initialized 20971590Srgrimes *----------------------------------------------------------------------- 20981590Srgrimes */ 20991590Srgrimesvoid 21001590SrgrimesJob_Init (maxproc, maxlocal) 21011590Srgrimes int maxproc; /* the greatest number of jobs which may be 21021590Srgrimes * running at one time */ 21031590Srgrimes int maxlocal; /* the greatest number of local jobs which may 21041590Srgrimes * be running at once. */ 21051590Srgrimes{ 21061590Srgrimes GNode *begin; /* node for commands to do at the very start */ 21071590Srgrimes 21081590Srgrimes sprintf (tfile, "/tmp/make%05d", getpid()); 21091590Srgrimes 21101590Srgrimes jobs = Lst_Init (FALSE); 21111590Srgrimes stoppedJobs = Lst_Init(FALSE); 21121590Srgrimes maxJobs = maxproc; 21131590Srgrimes maxLocal = maxlocal; 21141590Srgrimes nJobs = 0; 21151590Srgrimes nLocal = 0; 21161590Srgrimes jobFull = FALSE; 21171590Srgrimes 21181590Srgrimes aborting = 0; 21191590Srgrimes errors = 0; 21201590Srgrimes 21211590Srgrimes lastNode = NILGNODE; 21221590Srgrimes 21231590Srgrimes if (maxJobs == 1) { 21241590Srgrimes /* 21251590Srgrimes * If only one job can run at a time, there's no need for a banner, 21261590Srgrimes * no is there? 21271590Srgrimes */ 21281590Srgrimes targFmt = ""; 21291590Srgrimes } else { 21301590Srgrimes targFmt = TARG_FMT; 21311590Srgrimes } 21328874Srgrimes 21331590Srgrimes if (shellPath == (char *) NULL) { 21341590Srgrimes /* 21351590Srgrimes * The user didn't specify a shell to use, so we are using the 21361590Srgrimes * default one... Both the absolute path and the last component 21371590Srgrimes * must be set. The last component is taken from the 'name' field 21381590Srgrimes * of the default shell description pointed-to by commandShell. 21391590Srgrimes * All default shells are located in _PATH_DEFSHELLDIR. 21401590Srgrimes */ 21411590Srgrimes shellName = commandShell->name; 21421590Srgrimes shellPath = str_concat (_PATH_DEFSHELLDIR, shellName, STR_ADDSLASH); 21431590Srgrimes } 21441590Srgrimes 21451590Srgrimes if (commandShell->exit == (char *)NULL) { 21461590Srgrimes commandShell->exit = ""; 21471590Srgrimes } 21481590Srgrimes if (commandShell->echo == (char *)NULL) { 21491590Srgrimes commandShell->echo = ""; 21501590Srgrimes } 21511590Srgrimes 21521590Srgrimes /* 21531590Srgrimes * Catch the four signals that POSIX specifies if they aren't ignored. 21541590Srgrimes * JobPassSig will take care of calling JobInterrupt if appropriate. 21551590Srgrimes */ 21561590Srgrimes if (signal (SIGINT, SIG_IGN) != SIG_IGN) { 21571590Srgrimes signal (SIGINT, JobPassSig); 21581590Srgrimes } 21591590Srgrimes if (signal (SIGHUP, SIG_IGN) != SIG_IGN) { 21601590Srgrimes signal (SIGHUP, JobPassSig); 21611590Srgrimes } 21621590Srgrimes if (signal (SIGQUIT, SIG_IGN) != SIG_IGN) { 21631590Srgrimes signal (SIGQUIT, JobPassSig); 21641590Srgrimes } 21651590Srgrimes if (signal (SIGTERM, SIG_IGN) != SIG_IGN) { 21661590Srgrimes signal (SIGTERM, JobPassSig); 21671590Srgrimes } 21681590Srgrimes /* 21691590Srgrimes * There are additional signals that need to be caught and passed if 21701590Srgrimes * either the export system wants to be told directly of signals or if 21711590Srgrimes * we're giving each job its own process group (since then it won't get 21721590Srgrimes * signals from the terminal driver as we own the terminal) 21731590Srgrimes */ 21741590Srgrimes#if defined(RMT_WANTS_SIGNALS) || defined(USE_PGRP) 21751590Srgrimes if (signal (SIGTSTP, SIG_IGN) != SIG_IGN) { 21761590Srgrimes signal (SIGTSTP, JobPassSig); 21771590Srgrimes } 21781590Srgrimes if (signal (SIGTTOU, SIG_IGN) != SIG_IGN) { 21791590Srgrimes signal (SIGTTOU, JobPassSig); 21801590Srgrimes } 21811590Srgrimes if (signal (SIGTTIN, SIG_IGN) != SIG_IGN) { 21821590Srgrimes signal (SIGTTIN, JobPassSig); 21831590Srgrimes } 21841590Srgrimes if (signal (SIGWINCH, SIG_IGN) != SIG_IGN) { 21851590Srgrimes signal (SIGWINCH, JobPassSig); 21861590Srgrimes } 21871590Srgrimes#endif 21888874Srgrimes 21891590Srgrimes begin = Targ_FindNode (".BEGIN", TARG_NOCREATE); 21901590Srgrimes 21911590Srgrimes if (begin != NILGNODE) { 21921590Srgrimes JobStart (begin, JOB_SPECIAL, (Job *)0); 21931590Srgrimes while (nJobs) { 21941590Srgrimes Job_CatchOutput(); 21951590Srgrimes#ifndef RMT_WILL_WATCH 21961590Srgrimes Job_CatchChildren (!usePipes); 21971590Srgrimes#endif /* RMT_WILL_WATCH */ 21981590Srgrimes } 21991590Srgrimes } 22001590Srgrimes postCommands = Targ_FindNode (".END", TARG_CREATE); 22011590Srgrimes} 22021590Srgrimes 22031590Srgrimes/*- 22041590Srgrimes *----------------------------------------------------------------------- 22051590Srgrimes * Job_Full -- 22061590Srgrimes * See if the job table is full. It is considered full if it is OR 22071590Srgrimes * if we are in the process of aborting OR if we have 22081590Srgrimes * reached/exceeded our local quota. This prevents any more jobs 22091590Srgrimes * from starting up. 22101590Srgrimes * 22111590Srgrimes * Results: 22121590Srgrimes * TRUE if the job table is full, FALSE otherwise 22131590Srgrimes * Side Effects: 22141590Srgrimes * None. 22151590Srgrimes *----------------------------------------------------------------------- 22161590Srgrimes */ 22171590SrgrimesBoolean 22181590SrgrimesJob_Full () 22191590Srgrimes{ 22201590Srgrimes return (aborting || jobFull); 22211590Srgrimes} 22221590Srgrimes 22231590Srgrimes/*- 22241590Srgrimes *----------------------------------------------------------------------- 22251590Srgrimes * Job_Empty -- 22261590Srgrimes * See if the job table is empty. Because the local concurrency may 22271590Srgrimes * be set to 0, it is possible for the job table to become empty, 22281590Srgrimes * while the list of stoppedJobs remains non-empty. In such a case, 22291590Srgrimes * we want to restart as many jobs as we can. 22301590Srgrimes * 22311590Srgrimes * Results: 22321590Srgrimes * TRUE if it is. FALSE if it ain't. 22331590Srgrimes * 22341590Srgrimes * Side Effects: 22351590Srgrimes * None. 22361590Srgrimes * 22371590Srgrimes * ----------------------------------------------------------------------- 22381590Srgrimes */ 22391590SrgrimesBoolean 22401590SrgrimesJob_Empty () 22411590Srgrimes{ 22421590Srgrimes if (nJobs == 0) { 22431590Srgrimes if (!Lst_IsEmpty(stoppedJobs) && !aborting) { 22441590Srgrimes /* 22451590Srgrimes * The job table is obviously not full if it has no jobs in 22461590Srgrimes * it...Try and restart the stopped jobs. 22471590Srgrimes */ 22481590Srgrimes jobFull = FALSE; 22491590Srgrimes while (!jobFull && !Lst_IsEmpty(stoppedJobs)) { 22501590Srgrimes JobRestart((Job *)Lst_DeQueue(stoppedJobs)); 22511590Srgrimes } 22521590Srgrimes return(FALSE); 22531590Srgrimes } else { 22541590Srgrimes return(TRUE); 22551590Srgrimes } 22561590Srgrimes } else { 22571590Srgrimes return(FALSE); 22581590Srgrimes } 22591590Srgrimes} 22601590Srgrimes 22611590Srgrimes/*- 22621590Srgrimes *----------------------------------------------------------------------- 22631590Srgrimes * JobMatchShell -- 22641590Srgrimes * Find a matching shell in 'shells' given its final component. 22651590Srgrimes * 22661590Srgrimes * Results: 22671590Srgrimes * A pointer to the Shell structure. 22681590Srgrimes * 22691590Srgrimes * Side Effects: 22701590Srgrimes * None. 22711590Srgrimes * 22721590Srgrimes *----------------------------------------------------------------------- 22731590Srgrimes */ 22741590Srgrimesstatic Shell * 22751590SrgrimesJobMatchShell (name) 22761590Srgrimes char *name; /* Final component of shell path */ 22771590Srgrimes{ 22781590Srgrimes register Shell *sh; /* Pointer into shells table */ 22791590Srgrimes Shell *match; /* Longest-matching shell */ 22801590Srgrimes register char *cp1, 22811590Srgrimes *cp2; 22821590Srgrimes char *eoname; 22831590Srgrimes 22841590Srgrimes eoname = name + strlen (name); 22851590Srgrimes 22861590Srgrimes match = (Shell *) NULL; 22871590Srgrimes 22881590Srgrimes for (sh = shells; sh->name != NULL; sh++) { 22891590Srgrimes for (cp1 = eoname - strlen (sh->name), cp2 = sh->name; 22901590Srgrimes *cp1 != '\0' && *cp1 == *cp2; 22911590Srgrimes cp1++, cp2++) { 22921590Srgrimes continue; 22931590Srgrimes } 22941590Srgrimes if (*cp1 != *cp2) { 22951590Srgrimes continue; 22961590Srgrimes } else if (match == (Shell *) NULL || 22971590Srgrimes strlen (match->name) < strlen (sh->name)) { 22981590Srgrimes match = sh; 22991590Srgrimes } 23001590Srgrimes } 23011590Srgrimes return (match == (Shell *) NULL ? sh : match); 23021590Srgrimes} 23031590Srgrimes 23041590Srgrimes/*- 23051590Srgrimes *----------------------------------------------------------------------- 23061590Srgrimes * Job_ParseShell -- 23071590Srgrimes * Parse a shell specification and set up commandShell, shellPath 23081590Srgrimes * and shellName appropriately. 23091590Srgrimes * 23101590Srgrimes * Results: 23111590Srgrimes * FAILURE if the specification was incorrect. 23121590Srgrimes * 23131590Srgrimes * Side Effects: 23141590Srgrimes * commandShell points to a Shell structure (either predefined or 23151590Srgrimes * created from the shell spec), shellPath is the full path of the 23161590Srgrimes * shell described by commandShell, while shellName is just the 23171590Srgrimes * final component of shellPath. 23181590Srgrimes * 23191590Srgrimes * Notes: 23201590Srgrimes * A shell specification consists of a .SHELL target, with dependency 23211590Srgrimes * operator, followed by a series of blank-separated words. Double 23221590Srgrimes * quotes can be used to use blanks in words. A backslash escapes 23231590Srgrimes * anything (most notably a double-quote and a space) and 23241590Srgrimes * provides the functionality it does in C. Each word consists of 23251590Srgrimes * keyword and value separated by an equal sign. There should be no 23261590Srgrimes * unnecessary spaces in the word. The keywords are as follows: 23271590Srgrimes * name Name of shell. 23281590Srgrimes * path Location of shell. Overrides "name" if given 23291590Srgrimes * quiet Command to turn off echoing. 23301590Srgrimes * echo Command to turn echoing on 23311590Srgrimes * filter Result of turning off echoing that shouldn't be 23321590Srgrimes * printed. 23331590Srgrimes * echoFlag Flag to turn echoing on at the start 23341590Srgrimes * errFlag Flag to turn error checking on at the start 23351590Srgrimes * hasErrCtl True if shell has error checking control 23361590Srgrimes * check Command to turn on error checking if hasErrCtl 23371590Srgrimes * is TRUE or template of command to echo a command 23381590Srgrimes * for which error checking is off if hasErrCtl is 23391590Srgrimes * FALSE. 23401590Srgrimes * ignore Command to turn off error checking if hasErrCtl 23411590Srgrimes * is TRUE or template of command to execute a 23421590Srgrimes * command so as to ignore any errors it returns if 23431590Srgrimes * hasErrCtl is FALSE. 23441590Srgrimes * 23451590Srgrimes *----------------------------------------------------------------------- 23461590Srgrimes */ 23471590SrgrimesReturnStatus 23481590SrgrimesJob_ParseShell (line) 23491590Srgrimes char *line; /* The shell spec */ 23501590Srgrimes{ 23511590Srgrimes char **words; 23521590Srgrimes int wordCount; 23531590Srgrimes register char **argv; 23541590Srgrimes register int argc; 23551590Srgrimes char *path; 23561590Srgrimes Shell newShell; 23571590Srgrimes Boolean fullSpec = FALSE; 23581590Srgrimes 23591590Srgrimes while (isspace (*line)) { 23601590Srgrimes line++; 23611590Srgrimes } 23625814Sjkh words = brk_string (line, &wordCount, TRUE); 23631590Srgrimes 23641590Srgrimes memset ((Address)&newShell, 0, sizeof(newShell)); 23658874Srgrimes 23661590Srgrimes /* 23671590Srgrimes * Parse the specification by keyword 23681590Srgrimes */ 23691590Srgrimes for (path = (char *)NULL, argc = wordCount - 1, argv = words + 1; 23701590Srgrimes argc != 0; 23711590Srgrimes argc--, argv++) { 23721590Srgrimes if (strncmp (*argv, "path=", 5) == 0) { 23731590Srgrimes path = &argv[0][5]; 23741590Srgrimes } else if (strncmp (*argv, "name=", 5) == 0) { 23751590Srgrimes newShell.name = &argv[0][5]; 23761590Srgrimes } else { 23771590Srgrimes if (strncmp (*argv, "quiet=", 6) == 0) { 23781590Srgrimes newShell.echoOff = &argv[0][6]; 23791590Srgrimes } else if (strncmp (*argv, "echo=", 5) == 0) { 23801590Srgrimes newShell.echoOn = &argv[0][5]; 23811590Srgrimes } else if (strncmp (*argv, "filter=", 7) == 0) { 23821590Srgrimes newShell.noPrint = &argv[0][7]; 23831590Srgrimes newShell.noPLen = strlen(newShell.noPrint); 23841590Srgrimes } else if (strncmp (*argv, "echoFlag=", 9) == 0) { 23851590Srgrimes newShell.echo = &argv[0][9]; 23861590Srgrimes } else if (strncmp (*argv, "errFlag=", 8) == 0) { 23871590Srgrimes newShell.exit = &argv[0][8]; 23881590Srgrimes } else if (strncmp (*argv, "hasErrCtl=", 10) == 0) { 23891590Srgrimes char c = argv[0][10]; 23901590Srgrimes newShell.hasErrCtl = !((c != 'Y') && (c != 'y') && 23911590Srgrimes (c != 'T') && (c != 't')); 23921590Srgrimes } else if (strncmp (*argv, "check=", 6) == 0) { 23931590Srgrimes newShell.errCheck = &argv[0][6]; 23941590Srgrimes } else if (strncmp (*argv, "ignore=", 7) == 0) { 23951590Srgrimes newShell.ignErr = &argv[0][7]; 23961590Srgrimes } else { 23971590Srgrimes Parse_Error (PARSE_FATAL, "Unknown keyword \"%s\"", 23981590Srgrimes *argv); 23991590Srgrimes return (FAILURE); 24001590Srgrimes } 24011590Srgrimes fullSpec = TRUE; 24021590Srgrimes } 24031590Srgrimes } 24041590Srgrimes 24051590Srgrimes if (path == (char *)NULL) { 24061590Srgrimes /* 24071590Srgrimes * If no path was given, the user wants one of the pre-defined shells, 24081590Srgrimes * yes? So we find the one s/he wants with the help of JobMatchShell 24091590Srgrimes * and set things up the right way. shellPath will be set up by 24101590Srgrimes * Job_Init. 24111590Srgrimes */ 24121590Srgrimes if (newShell.name == (char *)NULL) { 24131590Srgrimes Parse_Error (PARSE_FATAL, "Neither path nor name specified"); 24141590Srgrimes return (FAILURE); 24151590Srgrimes } else { 24161590Srgrimes commandShell = JobMatchShell (newShell.name); 24171590Srgrimes shellName = newShell.name; 24181590Srgrimes } 24191590Srgrimes } else { 24201590Srgrimes /* 24211590Srgrimes * The user provided a path. If s/he gave nothing else (fullSpec is 24221590Srgrimes * FALSE), try and find a matching shell in the ones we know of. 24231590Srgrimes * Else we just take the specification at its word and copy it 24241590Srgrimes * to a new location. In either case, we need to record the 24251590Srgrimes * path the user gave for the shell. 24261590Srgrimes */ 24271590Srgrimes shellPath = path; 24281590Srgrimes path = strrchr (path, '/'); 24291590Srgrimes if (path == (char *)NULL) { 24301590Srgrimes path = shellPath; 24311590Srgrimes } else { 24321590Srgrimes path += 1; 24331590Srgrimes } 24341590Srgrimes if (newShell.name != (char *)NULL) { 24351590Srgrimes shellName = newShell.name; 24361590Srgrimes } else { 24371590Srgrimes shellName = path; 24381590Srgrimes } 24391590Srgrimes if (!fullSpec) { 24401590Srgrimes commandShell = JobMatchShell (shellName); 24411590Srgrimes } else { 24421590Srgrimes commandShell = (Shell *) emalloc(sizeof(Shell)); 24431590Srgrimes *commandShell = newShell; 24441590Srgrimes } 24451590Srgrimes } 24461590Srgrimes 24471590Srgrimes if (commandShell->echoOn && commandShell->echoOff) { 24481590Srgrimes commandShell->hasEchoCtl = TRUE; 24491590Srgrimes } 24508874Srgrimes 24511590Srgrimes if (!commandShell->hasErrCtl) { 24521590Srgrimes if (commandShell->errCheck == (char *)NULL) { 24531590Srgrimes commandShell->errCheck = ""; 24541590Srgrimes } 24551590Srgrimes if (commandShell->ignErr == (char *)NULL) { 24561590Srgrimes commandShell->ignErr = "%s\n"; 24571590Srgrimes } 24581590Srgrimes } 24598874Srgrimes 24601590Srgrimes /* 24611590Srgrimes * Do not free up the words themselves, since they might be in use by the 24621590Srgrimes * shell specification... 24631590Srgrimes */ 24641590Srgrimes free (words); 24651590Srgrimes return SUCCESS; 24661590Srgrimes} 24671590Srgrimes 24681590Srgrimes/*- 24691590Srgrimes *----------------------------------------------------------------------- 24701590Srgrimes * JobInterrupt -- 24711590Srgrimes * Handle the receipt of an interrupt. 24721590Srgrimes * 24731590Srgrimes * Results: 24741590Srgrimes * None 24751590Srgrimes * 24761590Srgrimes * Side Effects: 24771590Srgrimes * All children are killed. Another job will be started if the 24781590Srgrimes * .INTERRUPT target was given. 24791590Srgrimes *----------------------------------------------------------------------- 24801590Srgrimes */ 24811590Srgrimesstatic void 24821590SrgrimesJobInterrupt (runINTERRUPT) 24831590Srgrimes int runINTERRUPT; /* Non-zero if commands for the .INTERRUPT 24841590Srgrimes * target should be executed */ 24851590Srgrimes{ 24861590Srgrimes LstNode ln; /* element in job table */ 24871590Srgrimes Job *job; /* job descriptor in that element */ 24881590Srgrimes GNode *interrupt; /* the node describing the .INTERRUPT target */ 24898874Srgrimes 24901590Srgrimes aborting = ABORT_INTERRUPT; 24911590Srgrimes 24921590Srgrimes (void)Lst_Open (jobs); 24931590Srgrimes while ((ln = Lst_Next (jobs)) != NILLNODE) { 24941590Srgrimes job = (Job *) Lst_Datum (ln); 24951590Srgrimes 24961590Srgrimes if (!Targ_Precious (job->node)) { 24971590Srgrimes char *file = (job->node->path == (char *)NULL ? 24981590Srgrimes job->node->name : 24991590Srgrimes job->node->path); 25005814Sjkh struct stat st; 25018874Srgrimes if (!noExecute && lstat(file, &st) != -1 && !S_ISDIR(st.st_mode) && 25025814Sjkh unlink(file) != -1) { 25031590Srgrimes Error ("*** %s removed", file); 25041590Srgrimes } 25051590Srgrimes } 25061590Srgrimes#ifdef RMT_WANTS_SIGNALS 25071590Srgrimes if (job->flags & JOB_REMOTE) { 25081590Srgrimes /* 25091590Srgrimes * If job is remote, let the Rmt module do the killing. 25101590Srgrimes */ 25111590Srgrimes if (!Rmt_Signal(job, SIGINT)) { 25121590Srgrimes /* 25131590Srgrimes * If couldn't kill the thing, finish it out now with an 25141590Srgrimes * error code, since no exit report will come in likely. 25151590Srgrimes */ 25161590Srgrimes union wait status; 25171590Srgrimes 25181590Srgrimes status.w_status = 0; 25191590Srgrimes status.w_retcode = 1; 25201590Srgrimes JobFinish(job, status); 25211590Srgrimes } 25221590Srgrimes } else if (job->pid) { 25231590Srgrimes KILL(job->pid, SIGINT); 25241590Srgrimes } 25251590Srgrimes#else 25261590Srgrimes if (job->pid) { 25271590Srgrimes KILL(job->pid, SIGINT); 25281590Srgrimes } 25291590Srgrimes#endif /* RMT_WANTS_SIGNALS */ 25301590Srgrimes } 25311590Srgrimes Lst_Close (jobs); 25321590Srgrimes 25331590Srgrimes if (runINTERRUPT && !touchFlag) { 25341590Srgrimes interrupt = Targ_FindNode (".INTERRUPT", TARG_NOCREATE); 25351590Srgrimes if (interrupt != NILGNODE) { 25361590Srgrimes ignoreErrors = FALSE; 25371590Srgrimes 25381590Srgrimes JobStart (interrupt, JOB_IGNDOTS, (Job *)0); 25391590Srgrimes while (nJobs) { 25401590Srgrimes Job_CatchOutput(); 25411590Srgrimes#ifndef RMT_WILL_WATCH 25421590Srgrimes Job_CatchChildren (!usePipes); 25431590Srgrimes#endif /* RMT_WILL_WATCH */ 25441590Srgrimes } 25451590Srgrimes } 25461590Srgrimes } 25471590Srgrimes (void) unlink (tfile); 25481590Srgrimes exit (0); 25491590Srgrimes} 25501590Srgrimes 25511590Srgrimes/* 25521590Srgrimes *----------------------------------------------------------------------- 25531590Srgrimes * Job_End -- 25541590Srgrimes * Do final processing such as the running of the commands 25558874Srgrimes * attached to the .END target. 25561590Srgrimes * 25571590Srgrimes * Results: 25581590Srgrimes * Number of errors reported. 25591590Srgrimes * 25601590Srgrimes * Side Effects: 25611590Srgrimes * The process' temporary file (tfile) is removed if it still 25621590Srgrimes * existed. 25631590Srgrimes *----------------------------------------------------------------------- 25641590Srgrimes */ 25651590Srgrimesint 25661590SrgrimesJob_End () 25671590Srgrimes{ 25681590Srgrimes if (postCommands != NILGNODE && !Lst_IsEmpty (postCommands->commands)) { 25691590Srgrimes if (errors) { 25701590Srgrimes Error ("Errors reported so .END ignored"); 25711590Srgrimes } else { 25721590Srgrimes JobStart (postCommands, JOB_SPECIAL | JOB_IGNDOTS, 25731590Srgrimes (Job *)0); 25741590Srgrimes 25751590Srgrimes while (nJobs) { 25761590Srgrimes Job_CatchOutput(); 25771590Srgrimes#ifndef RMT_WILL_WATCH 25781590Srgrimes Job_CatchChildren (!usePipes); 25791590Srgrimes#endif /* RMT_WILL_WATCH */ 25801590Srgrimes } 25811590Srgrimes } 25821590Srgrimes } 25831590Srgrimes (void) unlink (tfile); 25841590Srgrimes return(errors); 25851590Srgrimes} 25861590Srgrimes 25871590Srgrimes/*- 25881590Srgrimes *----------------------------------------------------------------------- 25891590Srgrimes * Job_Wait -- 25901590Srgrimes * Waits for all running jobs to finish and returns. Sets 'aborting' 25911590Srgrimes * to ABORT_WAIT to prevent other jobs from starting. 25921590Srgrimes * 25931590Srgrimes * Results: 25941590Srgrimes * None. 25951590Srgrimes * 25961590Srgrimes * Side Effects: 25971590Srgrimes * Currently running jobs finish. 25981590Srgrimes * 25991590Srgrimes *----------------------------------------------------------------------- 26001590Srgrimes */ 26011590Srgrimesvoid 26021590SrgrimesJob_Wait() 26031590Srgrimes{ 26041590Srgrimes aborting = ABORT_WAIT; 26051590Srgrimes while (nJobs != 0) { 26061590Srgrimes Job_CatchOutput(); 26071590Srgrimes#ifndef RMT_WILL_WATCH 26081590Srgrimes Job_CatchChildren(!usePipes); 26091590Srgrimes#endif /* RMT_WILL_WATCH */ 26101590Srgrimes } 26111590Srgrimes aborting = 0; 26121590Srgrimes} 26131590Srgrimes 26141590Srgrimes/*- 26151590Srgrimes *----------------------------------------------------------------------- 26161590Srgrimes * Job_AbortAll -- 26171590Srgrimes * Abort all currently running jobs without handling output or anything. 26181590Srgrimes * This function is to be called only in the event of a major 26191590Srgrimes * error. Most definitely NOT to be called from JobInterrupt. 26201590Srgrimes * 26211590Srgrimes * Results: 26221590Srgrimes * None 26231590Srgrimes * 26241590Srgrimes * Side Effects: 26251590Srgrimes * All children are killed, not just the firstborn 26261590Srgrimes *----------------------------------------------------------------------- 26271590Srgrimes */ 26281590Srgrimesvoid 26291590SrgrimesJob_AbortAll () 26301590Srgrimes{ 26311590Srgrimes LstNode ln; /* element in job table */ 26321590Srgrimes Job *job; /* the job descriptor in that element */ 26331590Srgrimes int foo; 26348874Srgrimes 26351590Srgrimes aborting = ABORT_ERROR; 26368874Srgrimes 26371590Srgrimes if (nJobs) { 26381590Srgrimes 26391590Srgrimes (void)Lst_Open (jobs); 26401590Srgrimes while ((ln = Lst_Next (jobs)) != NILLNODE) { 26411590Srgrimes job = (Job *) Lst_Datum (ln); 26421590Srgrimes 26431590Srgrimes /* 26441590Srgrimes * kill the child process with increasingly drastic signals to make 26458874Srgrimes * darn sure it's dead. 26461590Srgrimes */ 26471590Srgrimes#ifdef RMT_WANTS_SIGNALS 26481590Srgrimes if (job->flags & JOB_REMOTE) { 26491590Srgrimes Rmt_Signal(job, SIGINT); 26501590Srgrimes Rmt_Signal(job, SIGKILL); 26511590Srgrimes } else { 26521590Srgrimes KILL(job->pid, SIGINT); 26531590Srgrimes KILL(job->pid, SIGKILL); 26541590Srgrimes } 26551590Srgrimes#else 26561590Srgrimes KILL(job->pid, SIGINT); 26571590Srgrimes KILL(job->pid, SIGKILL); 26581590Srgrimes#endif /* RMT_WANTS_SIGNALS */ 26591590Srgrimes } 26601590Srgrimes } 26618874Srgrimes 26621590Srgrimes /* 26631590Srgrimes * Catch as many children as want to report in at first, then give up 26641590Srgrimes */ 26651590Srgrimes while (wait3(&foo, WNOHANG, (struct rusage *)0) > 0) 26661590Srgrimes continue; 26671590Srgrimes (void) unlink (tfile); 26681590Srgrimes} 2669