version 1.77, 2007/09/17 12:03:40 |
version 1.78, 2007/09/17 12:42:09 |
|
|
* frequently to keep the whole make going at |
* frequently to keep the whole make going at |
* a decent clip, since job table entries aren't |
* a decent clip, since job table entries aren't |
* removed until their process is caught this way. |
* 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. |
* Job_CatchOutput Print any output our children have produced. |
* Should also be called fairly frequently to |
* Should also be called fairly frequently to |
|
|
* commands. |
* commands. |
* 4) An FILE* for writing out the commands. This is only |
* 4) An FILE* for writing out the commands. This is only |
* used before the job is actually started. |
* used before the job is actually started. |
* 5) Things used for handling the shell's output. |
* 5) Things used for handling the shell's output. |
* the output is being caught via a pipe and |
* the output is being caught via a pipe and |
* the descriptors of our pipe, an array in which output is line |
* the descriptors of our pipe, an array in which output is line |
* buffered and the current position in that buffer are all |
* buffered and the current position in that buffer are all |
* maintained for each job. |
* maintained for each job. |
* 6) An identifier provided by and for the exclusive use of the |
* 6) An identifier provided by and for the exclusive use of the |
* Rmt module. |
* Rmt module. |
* 7) A word of flags which determine how the module handles errors, |
* 7) A word of flags which determine how the module handles errors, |
|
|
short flags; /* Flags to control treatment of job */ |
short flags; /* Flags to control treatment of job */ |
#define JOB_IGNERR 0x001 /* Ignore non-zero exits */ |
#define JOB_IGNERR 0x001 /* Ignore non-zero exits */ |
#define JOB_SILENT 0x002 /* no output */ |
#define JOB_SILENT 0x002 /* no output */ |
#define JOB_SPECIAL 0x004 /* Target is a special one. i.e. run it locally |
#define JOB_SPECIAL 0x004 /* Target is a special one. i.e., always run */ |
* if we can't export it and maxLocal is 0 */ |
/* it even when the table is full */ |
#define JOB_IGNDOTS 0x008 /* Ignore "..." lines when processing |
#define JOB_IGNDOTS 0x008 /* Ignore "..." lines when processing */ |
* commands */ |
/* commands */ |
#define JOB_FIRST 0x020 /* Job is first job for the node */ |
#define JOB_FIRST 0x020 /* Job is first job for the node */ |
#define JOB_RESTART 0x080 /* Job needs to be completely restarted */ |
#define JOB_RESTART 0x080 /* Job needs to be completely restarted */ |
#define JOB_RESUME 0x100 /* Job needs to be resumed b/c it stopped, |
#define JOB_RESUME 0x100 /* Job needs to be resumed b/c it stopped, */ |
* for some reason */ |
/* for some reason */ |
#define JOB_CONTINUING 0x200 /* We are in the process of resuming this job. |
#define JOB_CONTINUING 0x200 /* We are in the process of resuming this job */ |
* Used to avoid infinite recursion between |
/* Used to avoid infinite recursion between */ |
* JobFinish and JobRestart */ |
/* JobFinish and JobRestart */ |
int inPipe; /* Input side of pipe associated |
int inPipe; /* Input side of pipe associated |
* with job's output channel */ |
* with job's output channel */ |
int outPipe; /* Output side of pipe associated with |
int outPipe; /* Output side of pipe associated with |
|
|
int curPos; /* Current position in op_outBuf */ |
int curPos; /* Current position in op_outBuf */ |
} Job; |
} Job; |
|
|
|
|
/* |
/* |
* error handling variables |
* error handling variables |
*/ |
*/ |
|
|
*/ |
*/ |
#define FILENO(a) ((unsigned) fileno(a)) |
#define FILENO(a) ((unsigned) fileno(a)) |
|
|
/* |
|
* 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 |
static int numCommands; /* The number of commands actually printed |
* for a target. Should this number be |
* for a target. Should this number be |
* 0, no shell will be executed. */ |
* 0, no shell will be executed. */ |
|
|
static const char *shellPath = _PATH_BSHELL; |
static const char *shellPath = _PATH_BSHELL; |
static const char *shellName = "sh"; |
static const char *shellName = "sh"; |
|
|
|
|
static int maxJobs; /* The most children we can run at once */ |
static int maxJobs; /* The most children we can run at once */ |
static int maxLocal; /* The most local ones we can have */ |
|
static int nJobs = 0; /* The number of children currently running */ |
static int nJobs = 0; /* The number of children currently running */ |
static int nLocal; /* The number of local children */ |
|
static LIST jobs; /* The structures that describe them */ |
static LIST jobs; /* The structures that describe them */ |
static bool jobFull; /* Flag to tell when the job table is full. It |
static bool jobFull; /* Flag to tell when the job table is full. */ |
* 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 */ |
|
static fd_set *outputsp; /* Set of descriptors of pipes connected to |
static fd_set *outputsp; /* Set of descriptors of pipes connected to |
* the output channels of children */ |
* the output channels of children */ |
static int outputsn; |
static int outputsn; |
|
|
* If the command is just "..." we take all future commands for this |
* 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 |
* 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 |
* made and return non-zero to signal that the end of the commands |
* was reached. These commands are later attached to the postCommands |
* was reached. These commands are later attached to the end_node |
* node and executed by Job_End when all things are done. |
* node and executed by Job_End when all things are done. |
* This function is called from JobStart via Lst_Find |
* This function is called from JobStart via Lst_Find |
* |
* |
|
|
*----------------------------------------------------------------------- |
*----------------------------------------------------------------------- |
*/ |
*/ |
static int |
static int |
JobPrintCommand(LstNode cmdNode, /* command string to print */ |
JobPrintCommand(LstNode cmdNode, /* command string to print */ |
void *jobp) /* job for which to print it */ |
void *jobp) /* job for which to print it */ |
{ |
{ |
bool noSpecials; /* true if we shouldn't worry about |
bool noSpecials; /* true if we shouldn't worry about |
* inserting special commands into |
* inserting special commands into |
* the input stream. */ |
* the input stream. */ |
bool shutUp = false; /* true if we put a no echo command |
bool shutUp = false; /* true if we put a no echo command |
* into the command file */ |
* into the command file */ |
bool errOff = false; /* true if we turned error checking |
bool errOff = false; /* true if we turned error checking |
* off before printing the command |
* off before printing the command |
* and need to turn it back on */ |
* and need to turn it back on */ |
char *cmdTemplate; /* Template to use when printing the |
char *cmdTemplate; /* Template to use when printing the |
* command */ |
* command */ |
char *cmdStart; /* Start of expanded command */ |
char *cmdStart; /* Start of expanded command */ |
char *cmd = (char *)Lst_Datum(cmdNode); |
char *cmd = (char *)Lst_Datum(cmdNode); |
Job *job = (Job *)jobp; |
Job *job = (Job *)jobp; |
|
|
|
|
return 1; |
return 1; |
} |
} |
|
|
|
|
numCommands++; |
numCommands++; |
|
|
/* For debugging, we replace each command with the result of expanding |
/* For debugging, we replace each command with the result of expanding |
|
|
if (errOff) { |
if (errOff) { |
if ( !(job->flags & JOB_IGNERR) && !noSpecials) { |
if ( !(job->flags & JOB_IGNERR) && !noSpecials) { |
/* |
/* |
* we don't want the error-control commands |
* we don't want the error-control commands showing |
* showing up either, so we turn off echoing |
* up either, so we turn off echoing while executing |
* while executing them. We could put another |
* them. We could put another field in the shell |
* field in the shell structure to tell |
* structure to tell JobDoOutput to look for this |
* JobDoOutput to look for this string too, but |
* string too, but why make it any more complex than |
* why make it any more complex than it already |
* it already is? |
* is? |
|
*/ |
*/ |
if (!(job->flags & JOB_SILENT) && !shutUp) { |
if (!(job->flags & JOB_SILENT) && !shutUp) { |
DBPRINTF(job, "%s; %s; %s\n", SHELL_ECHO_OFF, |
DBPRINTF(job, "%s; %s; %s\n", SHELL_ECHO_OFF, |
|
|
* Callback function for JobFinish... |
* Callback function for JobFinish... |
* |
* |
* Side Effects: |
* Side Effects: |
* The command is tacked onto the end of postCommands's commands list. |
* The command is tacked onto the end of end_node's commands list. |
*----------------------------------------------------------------------- |
*----------------------------------------------------------------------- |
*/ |
*/ |
static void |
static void |
|
|
char *result; |
char *result; |
|
|
result = Var_Subst((char *)cmd, &g->context, false); |
result = Var_Subst((char *)cmd, &g->context, false); |
Lst_AtEnd(&postCommands->commands, result); |
Lst_AtEnd(&end_node->commands, result); |
} |
} |
|
|
|
|
|
|
* |
* |
* Side Effects: |
* Side Effects: |
* Some nodes may be put on the toBeMade queue. |
* Some nodes may be put on the toBeMade queue. |
* Final commands for the job are placed on postCommands. |
* Final commands for the job are placed on end_node. |
* |
* |
* If we got an error and are aborting (aborting == ABORT_ERROR) and |
* If we got an error and are aborting (aborting == ABORT_ERROR) and |
* the job list is now empty, we are done for the day. |
* the job list is now empty, we are done for the day. |
|
|
JobFinish(Job *job, /* job to finish */ |
JobFinish(Job *job, /* job to finish */ |
int *status) /* sub-why job went away */ |
int *status) /* sub-why job went away */ |
{ |
{ |
bool done; |
bool done; |
|
|
if ((WIFEXITED(*status) && |
if ((WIFEXITED(*status) && |
WEXITSTATUS(*status) != 0 && !(job->flags & JOB_IGNERR)) || |
WEXITSTATUS(*status) != 0 && !(job->flags & JOB_IGNERR)) || |
|
|
|
|
if (WIFEXITED(*status)) { |
if (WIFEXITED(*status)) { |
if (DEBUG(JOB)) { |
if (DEBUG(JOB)) { |
(void)fprintf(stdout, "Process %ld exited.\n", |
(void)fprintf(stdout, |
(long)job->pid); |
"Process %ld exited.\n", (long)job->pid); |
(void)fflush(stdout); |
(void)fflush(stdout); |
} |
} |
if (WEXITSTATUS(*status) != 0) { |
if (WEXITSTATUS(*status) != 0) { |
|
|
} |
} |
} else if (WIFSTOPPED(*status)) { |
} else if (WIFSTOPPED(*status)) { |
if (DEBUG(JOB)) { |
if (DEBUG(JOB)) { |
(void)fprintf(stdout, "Process %ld stopped.\n", |
(void)fprintf(stdout, |
(long)job->pid); |
"Process %ld stopped.\n", (long)job->pid); |
(void)fflush(stdout); |
(void)fflush(stdout); |
} |
} |
if (job->node != lastNode) { |
if (job->node != lastNode) { |
|
|
if (!(job->flags & JOB_CONTINUING)) { |
if (!(job->flags & JOB_CONTINUING)) { |
if (DEBUG(JOB)) { |
if (DEBUG(JOB)) { |
(void)fprintf(stdout, |
(void)fprintf(stdout, |
"Warning: process %ld was not continuing.\n", |
"Warning: process %ld was not continuing.\n", |
(long)job->pid); |
(long)job->pid); |
(void)fflush(stdout); |
(void)fflush(stdout); |
} |
} |
|
|
(long)job->pid); |
(long)job->pid); |
(void)fflush(stdout); |
(void)fflush(stdout); |
} |
} |
nLocal++; |
|
if (nJobs == maxJobs) { |
if (nJobs == maxJobs) { |
jobFull = true; |
jobFull = true; |
if (DEBUG(JOB)) { |
if (DEBUG(JOB)) { |
|
|
pid_t cpid; /* ID of new child */ |
pid_t cpid; /* ID of new child */ |
|
|
if (DEBUG(JOB)) { |
if (DEBUG(JOB)) { |
int i; |
int i; |
|
|
(void)fprintf(stdout, "Running %s\n", job->node->name); |
(void)fprintf(stdout, "Running %s\n", job->node->name); |
(void)fprintf(stdout, "\tCommand: "); |
(void)fprintf(stdout, "\tCommand: "); |
|
|
(void)lseek(0, 0, SEEK_SET); |
(void)lseek(0, 0, SEEK_SET); |
|
|
/* |
/* |
* Set up the child's output to be routed through the |
* Set up the child's output to be routed through the pipe |
* pipe we've created for it. |
* we've created for it. |
*/ |
*/ |
if (dup2(job->outPipe, 1) == -1) |
if (dup2(job->outPipe, 1) == -1) |
Punt("Cannot dup2: %s", strerror(errno)); |
Punt("Cannot dup2: %s", strerror(errno)); |
|
|
FD_SET(job->inPipe, outputsp); |
FD_SET(job->inPipe, outputsp); |
} |
} |
|
|
nLocal++; |
|
/* |
/* |
* XXX: Used to not happen if REMOTE. Why? |
* XXX: Used to not happen if REMOTE. Why? |
*/ |
*/ |
|
|
if (args[1]) { |
if (args[1]) { |
argv[argc] = args; |
argv[argc] = args; |
argc++; |
argc++; |
} |
} |
argv[argc] = NULL; |
argv[argc] = NULL; |
} |
} |
|
|
|
|
job->node->name); |
job->node->name); |
(void)fflush(stdout); |
(void)fflush(stdout); |
} |
} |
if (nLocal >= maxLocal && !(job->flags & JOB_SPECIAL)) { |
if (nJobs >= maxJobs && !(job->flags & JOB_SPECIAL)) { |
/* |
/* |
* Can't be exported and not allowed to run locally -- |
* Can't be exported and not allowed to run locally -- |
* put it back on the hold queue and mark the table |
* put it back on the hold queue and mark the table |
|
|
(void)fprintf(stdout, "Resuming %s...", job->node->name); |
(void)fprintf(stdout, "Resuming %s...", job->node->name); |
(void)fflush(stdout); |
(void)fflush(stdout); |
} |
} |
if ((nLocal < maxLocal || |
if (nJobs != maxJobs || (job->flags & JOB_SPECIAL)) { |
((job->flags & JOB_SPECIAL) && |
|
maxLocal == 0) |
|
) && nJobs != maxJobs) { |
|
/* |
/* |
* If we haven't reached the concurrency limit already |
* If we haven't reached the concurrency limit already, |
* (or maxLocal is 0), it's ok to resume the job. |
* it's ok to resume the job. |
*/ |
*/ |
bool error; |
bool error; |
int status; |
int status; |
|
|
*----------------------------------------------------------------------- |
*----------------------------------------------------------------------- |
*/ |
*/ |
static int |
static int |
JobStart(GNode *gn, /* target to create */ |
JobStart(GNode *gn, /* target to create */ |
int flags, /* flags for the job to override normal ones. |
int flags, /* flags for the job to override normal ones. |
* e.g. JOB_SPECIAL or JOB_IGNDOTS */ |
* e.g. JOB_SPECIAL or JOB_IGNDOTS */ |
Job *previous) /* The previous Job structure for this node, |
Job *previous) /* The previous Job structure for this node, |
* if any. */ |
* if any. */ |
{ |
{ |
Job *job; /* new job descriptor */ |
Job *job; /* new job descriptor */ |
char *argv[4]; /* Argument vector to shell */ |
char *argv[4]; /* Argument vector to shell */ |
bool cmdsOK; /* true if the nodes commands were all right */ |
bool cmdsOK; /* true if the nodes commands were all right */ |
bool local; /* Set true if the job was run locally */ |
bool local; /* Set true if the job was run locally */ |
bool noExec; /* Set true if we decide not to run the job */ |
bool noExec; /* Set true if we decide not to run the job */ |
|
|
if (previous != NULL) { |
if (previous != NULL) { |
previous->flags &= ~(JOB_FIRST|JOB_IGNERR|JOB_SILENT); |
previous->flags &= ~(JOB_FIRST|JOB_IGNERR|JOB_SILENT); |
|
|
|
|
local = true; |
local = true; |
|
|
if (local && nLocal >= maxLocal && |
if (nJobs >= maxJobs && !(job->flags & JOB_SPECIAL)) { |
!(job->flags & JOB_SPECIAL) && |
/* we've hit the limit of concurrency, so put the job on hold |
maxLocal != 0 |
* until some other job finishes. |
) { |
|
/* |
|
* The job can only be run locally, but we've hit the limit of |
|
* local concurrency, so put the job on hold until some other |
|
* job finishes. Note that the special jobs (.BEGIN, .INTERRUPT |
|
* and .END) may be run locally even when the local limit has |
|
* been reached (e.g. when maxLocal == 0), though they will be |
|
* exported if at all possible. In addition, any target marked |
|
* with .NOEXPORT will be run locally if maxLocal is 0. |
|
*/ |
*/ |
jobFull = true; |
jobFull = true; |
|
|
|
|
} |
} |
job->flags |= JOB_RESTART; |
job->flags |= JOB_RESTART; |
Lst_AtEnd(&stoppedJobs, job); |
Lst_AtEnd(&stoppedJobs, job); |
} else { |
|
if (nLocal >= maxLocal && local) { |
|
/* |
|
* If we're running this job locally as a special case |
|
* (see above), at least say the table is full. |
|
*/ |
|
jobFull = true; |
|
if (DEBUG(JOB)) { |
|
(void)fprintf(stdout, |
|
"Local job queue is full.\n"); |
|
(void)fflush(stdout); |
|
} |
|
} |
|
JobExec(job, argv); |
|
} |
} |
return JOB_RUNNING; |
return JOB_RUNNING; |
} |
} |
|
|
lastNode = job->node; |
lastNode = job->node; |
} |
} |
/* |
/* |
* The only way there wouldn't be a newline |
* The only way there wouldn't be a newline after |
* after this line is if it were the last in |
* this line is if it were the last in the buffer. |
* the buffer. however, since the |
* however, since the non-printable comes after it, |
* non-printable comes after it, there must be |
* there must be a newline, so we don't print one. |
* a newline, so we don't print one. |
|
*/ |
*/ |
(void)fprintf(stdout, "%s", cp); |
(void)fprintf(stdout, "%s", cp); |
(void)fflush(stdout); |
(void)fflush(stdout); |
|
|
cp = ecp + strlen(SHELL_ECHO_OFF); |
cp = ecp + strlen(SHELL_ECHO_OFF); |
if (cp != endp) { |
if (cp != endp) { |
/* |
/* |
* Still more to print, look again after |
* Still more to print, look again after skipping |
* skipping the whitespace following the |
* the whitespace following the non-printable |
* non-printable command.... |
* command.... |
*/ |
*/ |
cp++; |
cp++; |
while (*cp == ' ' || *cp == '\t' || |
while (*cp == ' ' || *cp == '\t' || *cp == '\n') { |
*cp == '\n') { |
|
cp++; |
cp++; |
} |
} |
ecp = strstr(cp, SHELL_ECHO_OFF); |
ecp = strstr(cp, SHELL_ECHO_OFF); |
|
|
bool finish) /* true if this is the last time we'll be |
bool finish) /* true if this is the last time we'll be |
* called for this job */ |
* called for this job */ |
{ |
{ |
bool gotNL = false; /* true if got a newline */ |
bool gotNL = false; /* true if got a newline */ |
bool fbuf; /* true if our buffer filled up */ |
bool fbuf; /* true if our buffer filled up */ |
int nr; /* number of bytes read */ |
int nr; /* number of bytes read */ |
int i; /* auxiliary index into outBuf */ |
int i; /* auxiliary index into outBuf */ |
int max; /* limit for i (end of current data) */ |
int max; /* limit for i (end of current data) */ |
int nRead; /* (Temporary) number of bytes read */ |
int nRead; /* (Temporary) number of bytes read */ |
|
|
/* |
/* |
* Read as many bytes as will fit in the buffer. |
* Read as many bytes as will fit in the buffer. |
|
|
} |
} |
|
|
/* |
/* |
* If we hit the end-of-file (the job is dead), we must flush |
* If we hit the end-of-file (the job is dead), we must flush its |
* its remaining output, so pretend we read a newline if |
* remaining output, so pretend we read a newline if there's any |
* there's any output remaining in the buffer. Also clear the |
* output remaining in the buffer. |
* 'finish' flag so we stop looping. |
* Also clear the 'finish' flag so we stop looping. |
*/ |
*/ |
if (nr == 0 && job->curPos != 0) { |
if (nr == 0 && job->curPos != 0) { |
job->outBuf[job->curPos] = '\n'; |
job->outBuf[job->curPos] = '\n'; |
|
|
} |
} |
|
|
/* |
/* |
* Look for the last newline in the bytes we just got. If there |
* Look for the last newline in the bytes we just got. If there is |
* is one, break out of the loop with 'i' as its index and |
* one, break out of the loop with 'i' as its index and gotNL set |
* gotNL set true. |
* true. |
*/ |
*/ |
max = job->curPos + nr; |
max = job->curPos + nr; |
for (i = job->curPos + nr - 1; i >= job->curPos; i--) { |
for (i = job->curPos + nr - 1; i >= job->curPos; i--) { |
|
|
job->curPos += nr; |
job->curPos += nr; |
if (job->curPos == JOB_BUFSIZE) { |
if (job->curPos == JOB_BUFSIZE) { |
/* |
/* |
* If we've run out of buffer space, we have no |
* If we've run out of buffer space, we have no choice |
* choice but to print the stuff. sigh. |
* but to print the stuff. sigh. |
*/ |
*/ |
fbuf = true; |
fbuf = true; |
i = job->curPos; |
i = job->curPos; |
|
|
} |
} |
if (gotNL || fbuf) { |
if (gotNL || fbuf) { |
/* |
/* |
* Need to send the output to the screen. Null |
* Need to send the output to the screen. Null terminate it |
* terminate it first, overwriting the newline |
* first, overwriting the newline character if there was one. |
* character if there was one. So long as the line |
* So long as the line isn't one we should filter (according |
* isn't one we should filter (according to the shell |
* to the shell description), we print the line, preceded |
* description), we print the line, preceded by a |
* by a target banner if this target isn't the same as the |
* target banner if this target isn't the same as the |
* one for which we last printed something. |
* one for which we last printed something. The rest |
* The rest of the data in the buffer are then shifted down |
* of the data in the buffer are then shifted down to |
* to the start of the buffer and curPos is set accordingly. |
* the start of the buffer and curPos is set |
|
* accordingly. |
|
*/ |
*/ |
job->outBuf[i] = '\0'; |
job->outBuf[i] = '\0'; |
if (i >= job->curPos) { |
if (i >= job->curPos) { |
char *cp; |
char *cp; |
|
|
cp = JobOutput(job, job->outBuf, |
cp = JobOutput(job, job->outBuf, &job->outBuf[i], |
&job->outBuf[i], false); |
false); |
|
|
/* |
/* |
* There's still more in that thar buffer. This |
* There's still more in that thar buffer. This time, |
* time, though, we know there's no newline at |
* though, we know there's no newline at the end, so we |
* the end, so we add one of our own free will. |
* add one of our own free will. |
*/ |
*/ |
if (*cp != '\0') { |
if (*cp != '\0') { |
if (job->node != lastNode) { |
if (job->node != lastNode) { |
|
|
|
|
} else { |
} else { |
/* |
/* |
* We have written everything out, so we just |
* We have written everything out, so we just start over |
* start over from the start of the buffer. No |
* from the start of the buffer. No copying. No nothing. |
* copying. No nothing. |
|
*/ |
*/ |
job->curPos = 0; |
job->curPos = 0; |
} |
} |
} |
} |
if (finish) { |
if (finish) { |
/* |
/* |
* If the finish flag is true, we must loop until we |
* If the finish flag is true, we must loop until we hit |
* hit end-of-file on the pipe. This is guaranteed to |
* end-of-file on the pipe. This is guaranteed to happen |
* happen eventually since the other end of the pipe is |
* eventually since the other end of the pipe is now closed |
* now closed (we closed it explicitly and the child |
* (we closed it explicitly and the child has exited). When |
* has exited). When we do get an EOF, finish will be |
* we do get an EOF, finish will be set false and we'll fall |
* set false and we'll fall through and out. |
* through and out. |
*/ |
*/ |
goto end_loop; |
goto end_loop; |
} |
} |
|
|
void |
void |
Job_CatchChildren() |
Job_CatchChildren() |
{ |
{ |
pid_t pid; /* pid of dead child */ |
pid_t pid; /* pid of dead child */ |
Job *job; /* job descriptor for dead child */ |
Job *job; /* job descriptor for dead child */ |
LstNode jnode; /* list element for finding job */ |
LstNode jnode; /* list element for finding job */ |
int status; /* Exit/termination status */ |
int status; /* Exit/termination status */ |
|
|
/* |
/* |
* Don't even bother if we know there's no one around. |
* Don't even bother if we know there's no one around. |
*/ |
*/ |
if (nLocal == 0) { |
if (nJobs == 0) { |
return; |
return; |
} |
} |
|
|
|
|
(void)fflush(stdout); |
(void)fflush(stdout); |
} |
} |
jobFull = false; |
jobFull = false; |
nLocal--; |
|
} |
} |
|
|
JobFinish(job, &status); |
JobFinish(job, &status); |
|
|
|
|
int count = howmany(outputsn+1, NFDBITS) * sizeof(fd_mask); |
int count = howmany(outputsn+1, NFDBITS) * sizeof(fd_mask); |
fd_set *readfdsp = malloc(count); |
fd_set *readfdsp = malloc(count); |
|
|
(void)fflush(stdout); |
(void)fflush(stdout); |
if (readfdsp == NULL) |
if (readfdsp == NULL) |
return; |
return; |
|
|
*----------------------------------------------------------------------- |
*----------------------------------------------------------------------- |
*/ |
*/ |
void |
void |
Job_Init(int maxproc, /* the greatest number of jobs which may be |
Job_Init(int maxproc) |
* running at one time */ |
|
int maxlocal) /* the greatest number of local jobs which may |
|
* be running at once. */ |
|
{ |
{ |
GNode *begin; /* node for commands to do at the very start */ |
|
int tfd; |
int tfd; |
|
|
(void)strlcpy(tfile, TMPPAT, sizeof(tfile)); |
(void)strlcpy(tfile, TMPPAT, sizeof(tfile)); |
|
|
Static_Lst_Init(&jobs); |
Static_Lst_Init(&jobs); |
Static_Lst_Init(&stoppedJobs); |
Static_Lst_Init(&stoppedJobs); |
maxJobs = maxproc; |
maxJobs = maxproc; |
maxLocal = maxlocal; |
|
nJobs = 0; |
|
nLocal = 0; |
|
jobFull = false; |
jobFull = false; |
|
|
aborting = 0; |
aborting = 0; |
|
|
} |
} |
#endif |
#endif |
|
|
begin = Targ_FindNode(".BEGIN", TARG_NOCREATE); |
if ((begin_node->type & OP_DUMMY) == 0) { |
|
JobStart(begin_node, JOB_SPECIAL, (Job *)0); |
if (begin != NULL) { |
|
JobStart(begin, JOB_SPECIAL, (Job *)0); |
|
while (nJobs) { |
while (nJobs) { |
Job_CatchOutput(); |
Job_CatchOutput(); |
Job_CatchChildren(); |
Job_CatchChildren(); |
} |
} |
} |
} |
postCommands = Targ_FindNode(".END", TARG_CREATE); |
|
} |
} |
|
|
/*- |
/*- |
|
|
int signo) /* signal received */ |
int signo) /* signal received */ |
{ |
{ |
LstNode ln; /* element in job table */ |
LstNode ln; /* element in job table */ |
Job *job; /* job descriptor in that element */ |
Job *job; /* job descriptor in that element */ |
GNode *interrupt; /* the node describing the .INTERRUPT target */ |
|
|
|
aborting = ABORT_INTERRUPT; |
aborting = ABORT_INTERRUPT; |
|
|
|
|
} |
} |
|
|
if (runINTERRUPT && !touchFlag) { |
if (runINTERRUPT && !touchFlag) { |
interrupt = Targ_FindNode(".INTERRUPT", TARG_NOCREATE); |
if ((interrupt_node->type & OP_DUMMY) == 0) { |
if (interrupt != NULL) { |
|
ignoreErrors = false; |
ignoreErrors = false; |
|
|
JobStart(interrupt, JOB_IGNDOTS, (Job *)0); |
JobStart(interrupt_node, JOB_IGNDOTS, (Job *)0); |
while (nJobs) { |
while (nJobs) { |
Job_CatchOutput(); |
Job_CatchOutput(); |
Job_CatchChildren(); |
Job_CatchChildren(); |
|
|
int |
int |
Job_Finish(void) |
Job_Finish(void) |
{ |
{ |
if (postCommands != NULL && !Lst_IsEmpty(&postCommands->commands)) { |
if (end_node != NULL && !Lst_IsEmpty(&end_node->commands)) { |
if (errors) { |
if (errors) { |
Error("Errors reported so .END ignored"); |
Error("Errors reported so .END ignored"); |
} else { |
} else { |
JobStart(postCommands, JOB_SPECIAL | JOB_IGNDOTS, NULL); |
JobStart(end_node, JOB_SPECIAL | JOB_IGNDOTS, NULL); |
|
|
while (nJobs) { |
while (nJobs) { |
Job_CatchOutput(); |
Job_CatchOutput(); |
|
|
return errors; |
return errors; |
} |
} |
|
|
/*- |
|
*----------------------------------------------------------------------- |
|
* Job_End -- |
|
* Cleanup any memory used by the jobs module |
|
* |
|
* Side Effects: |
|
* Memory is freed |
|
*----------------------------------------------------------------------- |
|
*/ |
|
#ifdef CLEANUP |
#ifdef CLEANUP |
void |
void |
Job_End(void) |
Job_End(void) |
{ |
{ |
efree(shellArgv); |
|
} |
} |
#endif |
#endif |
|
|