📄 job.c
字号:
/* * Copyright (c) 1988, 1989, 1990, 1993 * The Regents of the University of California. All rights reserved. * Copyright (c) 1989 by Berkeley Softworks * All rights reserved. * * This code is derived from software contributed to Berkeley by * Adam de Boor. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */#ifndef lintstatic char sccsid[] = "@(#)job.c 8.2 (Berkeley) 3/19/94";#endif /* not lint *//*- * job.c -- * handle the creation etc. of our child processes. * * Interface: * Job_Make Start the creation of the given target. * * Job_CatchChildren Check for and handle the termination of any * children. This must be called reasonably * frequently to keep the whole make going at * a decent clip, since job table entries aren't * removed until their process is caught this way. * Its single argument is TRUE if the function * should block waiting for a child to terminate. * * Job_CatchOutput Print any output our children have produced. * Should also be called fairly frequently to * keep the user informed of what's going on. * If no output is waiting, it will block for * a time given by the SEL_* constants, below, * or until output is ready. * * Job_Init Called to intialize this module. in addition, * any commands attached to the .BEGIN target * are executed before this function returns. * Hence, the makefile must have been parsed * before this function is called. * * Job_Full Return TRUE if the job table is filled. * * Job_Empty Return TRUE if the job table is completely * empty. * * Job_ParseShell Given the line following a .SHELL target, parse * the line as a shell specification. Returns * FAILURE if the spec was incorrect. * * Job_End Perform any final processing which needs doing. * This includes the execution of any commands * which have been/were attached to the .END * target. It should only be called when the * job table is empty. * * Job_AbortAll Abort all currently running jobs. It doesn't * handle output or do anything for the jobs, * just kills them. It should only be called in * an emergency, as it were. * * Job_CheckCommands Verify that the commands for a target are * ok. Provide them if necessary and possible. * * Job_Touch Update a target without really updating it. * * Job_Wait Wait for all currently-running jobs to finish. */#include <sys/types.h>#include <sys/stat.h>#include <sys/file.h>#include <sys/time.h>#include <sys/wait.h>#include <errno.h>#include <fcntl.h>#include <signal.h>#include <stdio.h>#include <string.h>#include "make.h"#include "hash.h"#include "dir.h"#include "job.h"#include "pathnames.h"extern int errno;/* * error handling variables */static int errors = 0; /* number of errors reported */static int aborting = 0; /* why is the make aborting? */#define ABORT_ERROR 1 /* Because of an error */#define ABORT_INTERRUPT 2 /* Because it was interrupted */#define ABORT_WAIT 3 /* Waiting for jobs to finish *//* * post-make command processing. The node postCommands is really just the * .END target but we keep it around to avoid having to search for it * all the time. */static GNode *postCommands; /* node containing commands to execute when * everything else is done */static int numCommands; /* The number of commands actually printed * for a target. Should this number be * 0, no shell will be executed. *//* * Return values from JobStart. */#define JOB_RUNNING 0 /* Job is running */#define JOB_ERROR 1 /* Error in starting the job */#define JOB_FINISHED 2 /* The job is already finished */#define JOB_STOPPED 3 /* The job is stopped *//* * tfile is the name of a file into which all shell commands are put. It is * used over by removing it before the child shell is executed. The XXXXX in * the string are replaced by the pid of the make process in a 5-character * field with leading zeroes. */static char tfile[] = TMPPAT;/* * Descriptions for various shells. */static Shell shells[] = { /* * CSH description. The csh can do echo control by playing * with the setting of the 'echo' shell variable. Sadly, * however, it is unable to do error control nicely. */{ "csh", TRUE, "unset verbose", "set verbose", "unset verbose", 10, FALSE, "echo \"%s\"\n", "csh -c \"%s || exit 0\"", "v", "e",}, /* * SH description. Echo control is also possible and, under * sun UNIX anyway, one can even control error checking. */{ "sh", TRUE, "set -", "set -v", "set -", 5, FALSE, "echo \"%s\"\n", "sh -c '%s || exit 0'\n", "v", "e",}, /* * UNKNOWN. */{ (char *)0, FALSE, (char *)0, (char *)0, (char *)0, 0, FALSE, (char *)0, (char *)0, (char *)0, (char *)0,}};static Shell *commandShell = &shells[DEFSHELL];/* this is the shell to * which we pass all * commands in the Makefile. * It is set by the * Job_ParseShell function */static char *shellPath = (char *) NULL, /* full pathname of * executable image */ *shellName; /* last component of shell */static int maxJobs; /* The most children we can run at once */static int maxLocal; /* The most local ones we can have */static int nJobs; /* The number of children currently running */static int nLocal; /* The number of local children */static Lst jobs; /* The structures that describe them */static Boolean jobFull; /* Flag to tell when the job table is full. It * is set TRUE when (1) the total number of * running jobs equals the maximum allowed or * (2) a job can only be run locally, but * nLocal equals maxLocal */#ifndef RMT_WILL_WATCHstatic fd_set outputs; /* Set of descriptors of pipes connected to * the output channels of children */#endifstatic GNode *lastNode; /* The node for which output was most recently * produced. */static char *targFmt; /* Format string to use to head output from a * job when it's not the most-recent job heard * from */#define TARG_FMT "--- %s ---\n" /* Default format *//* * When JobStart attempts to run a job remotely but can't, and isn't allowed * to run the job locally, or when Job_CatchChildren detects a job that has * been migrated home, the job is placed on the stoppedJobs queue to be run * when the next job finishes. */static Lst stoppedJobs; /* Lst of Job structures describing * jobs that were stopped due to concurrency * limits or migration home */#if defined(USE_PGRP) && defined(SYSV)#define KILL(pid,sig) killpg (-(pid),(sig))#else# if defined(USE_PGRP)#define KILL(pid,sig) killpg ((pid),(sig))# else#define KILL(pid,sig) kill ((pid),(sig))# endif#endifstatic int JobCondPassSig __P((Job *, int));static void JobPassSig __P((int));static int JobCmpPid __P((Job *, int));static int JobPrintCommand __P((char *, Job *));static int JobSaveCommand __P((char *, GNode *));static void JobFinish __P((Job *, union wait));static void JobExec __P((Job *, char **));static void JobMakeArgv __P((Job *, char **));static void JobRestart __P((Job *));static int JobStart __P((GNode *, int, Job *));static void JobDoOutput __P((Job *, Boolean));static Shell *JobMatchShell __P((char *));static void JobInterrupt __P((int));/*- *----------------------------------------------------------------------- * JobCondPassSig -- * Pass a signal to a job if the job is remote or if USE_PGRP * is defined. * * Results: * === 0 * * Side Effects: * None, except the job may bite it. * *----------------------------------------------------------------------- */static intJobCondPassSig(job, signo) Job *job; /* Job to biff */ int signo; /* Signal to send it */{#ifdef RMT_WANTS_SIGNALS if (job->flags & JOB_REMOTE) { (void)Rmt_Signal(job, signo); } else { KILL(job->pid, signo); }#else /* * Assume that sending the signal to job->pid will signal any remote * job as well. */ KILL(job->pid, signo);#endif return(0);}/*- *----------------------------------------------------------------------- * JobPassSig -- * Pass a signal on to all remote jobs and to all local jobs if * USE_PGRP is defined, then die ourselves. * * Results: * None. * * Side Effects: * We die by the same signal. * *----------------------------------------------------------------------- */static voidJobPassSig(signo) int signo; /* The signal number we've received */{ int mask; Lst_ForEach(jobs, JobCondPassSig, (ClientData)signo); /* * Deal with proper cleanup based on the signal received. We only run * the .INTERRUPT target if the signal was in fact an interrupt. The other * three termination signals are more of a "get out *now*" command. */ if (signo == SIGINT) { JobInterrupt(TRUE); } else if ((signo == SIGHUP) || (signo == SIGTERM) || (signo == SIGQUIT)) { JobInterrupt(FALSE); } /* * Leave gracefully if SIGQUIT, rather than core dumping. */ if (signo == SIGQUIT) { Finish(0); } /* * Send ourselves the signal now we've given the message to everyone else. * Note we block everything else possible while we're getting the signal. * This ensures that all our jobs get continued when we wake up before * we take any other signal. */ mask = sigblock(0); (void) sigsetmask(~0 & ~(1 << (signo-1))); signal(signo, SIG_DFL); kill(getpid(), signo); Lst_ForEach(jobs, JobCondPassSig, (ClientData)SIGCONT); sigsetmask(mask); signal(signo, JobPassSig);}/*- *----------------------------------------------------------------------- * JobCmpPid -- * Compare the pid of the job with the given pid and return 0 if they * are equal. This function is called from Job_CatchChildren via * Lst_Find to find the job descriptor of the finished job. * * Results: * 0 if the pid's match * * Side Effects: * None *----------------------------------------------------------------------- */static intJobCmpPid (job, pid) int pid; /* process id desired */ Job *job; /* job to examine */{ return (pid - job->pid);}/*- *----------------------------------------------------------------------- * JobPrintCommand -- * Put out another command for the given job. If the command starts * with an @ or a - we process it specially. In the former case, * so long as the -s and -n flags weren't given to make, we stick * a shell-specific echoOff command in the script. In the latter, * we ignore errors for the entire job, unless the shell has error * control. * If the command is just "..." we take all future commands for this * job to be commands to be executed once the entire graph has been * made and return non-zero to signal that the end of the commands * was reached. These commands are later attached to the postCommands * node and executed by Job_End when all things are done. * This function is called from JobStart via Lst_ForEach. * * Results: * Always 0, unless the command was "..." * * Side Effects: * If the command begins with a '-' and the shell has no error control, * the JOB_IGNERR flag is set in the job descriptor. * If the command is "..." and we're not ignoring such things, * tailCmds is set to the successor node of the cmd. * numCommands is incremented if the command is actually printed. *----------------------------------------------------------------------- */static intJobPrintCommand (cmd, job) char *cmd; /* command string to print */ Job *job; /* job for which to print it */{ Boolean noSpecials; /* true if we shouldn't worry about * inserting special commands into * the input stream. */ Boolean shutUp = FALSE; /* true if we put a no echo command * into the command file */ Boolean errOff = FALSE; /* true if we turned error checking * off before printing the command * and need to turn it back on */ char *cmdTemplate; /* Template to use when printing the * command */ char *cmdStart; /* Start of expanded command */ LstNode cmdNode; /* Node for replacing the command */ noSpecials = (noExecute && ! (job->node->type & OP_MAKE)); if (strcmp (cmd, "...") == 0) { job->node->type |= OP_SAVE_CMDS; if ((job->flags & JOB_IGNDOTS) == 0) { job->tailCmds = Lst_Succ (Lst_Member (job->node->commands, (ClientData)cmd)); return (1); } return (0); }#define DBPRINTF(fmt, arg) if (DEBUG(JOB)) printf (fmt, arg); fprintf (job->cmdFILE, fmt, arg) numCommands += 1; /* * For debugging, we replace each command with the result of expanding * the variables in the command. */ cmdNode = Lst_Member (job->node->commands, (ClientData)cmd); cmdStart = cmd = Var_Subst (NULL, cmd, job->node, FALSE); Lst_Replace (cmdNode, (ClientData)cmdStart); cmdTemplate = "%s\n"; /* * Check for leading @' and -'s to control echoing and error checking. */ while (*cmd == '@' || *cmd == '-') { if (*cmd == '@') { shutUp = TRUE;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -