version 1.1, 1995/10/18 08:45:42 |
version 1.2, 1995/12/14 03:23:31 |
|
|
/* $NetBSD: job.c,v 1.11 1995/09/27 18:44:40 jtc Exp $ */ |
/* $NetBSD: job.c,v 1.13 1995/11/22 17:40:09 christos Exp $ */ |
|
|
/* |
/* |
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California. |
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California. |
|
|
#if 0 |
#if 0 |
static char sccsid[] = "@(#)job.c 5.15 (Berkeley) 3/1/91"; |
static char sccsid[] = "@(#)job.c 5.15 (Berkeley) 3/1/91"; |
#else |
#else |
static char rcsid[] = "$NetBSD: job.c,v 1.11 1995/09/27 18:44:40 jtc Exp $"; |
static char rcsid[] = "$NetBSD: job.c,v 1.13 1995/11/22 17:40:09 christos Exp $"; |
#endif |
#endif |
#endif /* not lint */ |
#endif /* not lint */ |
|
|
|
|
#include "dir.h" |
#include "dir.h" |
#include "job.h" |
#include "job.h" |
#include "pathnames.h" |
#include "pathnames.h" |
|
#ifdef REMOTE |
|
#include "rmt.h" |
|
# define STATIC |
|
#else |
|
# define STATIC static |
|
#endif |
|
|
extern int errno; |
extern int errno; |
|
|
|
|
#define ABORT_INTERRUPT 2 /* Because it was interrupted */ |
#define ABORT_INTERRUPT 2 /* Because it was interrupted */ |
#define ABORT_WAIT 3 /* Waiting for jobs to finish */ |
#define ABORT_WAIT 3 /* Waiting for jobs to finish */ |
|
|
|
/* |
|
* XXX: Avoid SunOS bug... FILENO() is fp->_file, and file |
|
* is a char! So when we go above 127 we turn negative! |
|
*/ |
|
#define FILENO(a) ((unsigned) fileno(a)) |
|
|
/* |
/* |
* post-make command processing. The node postCommands is really just the |
* post-make command processing. The node postCommands is really just the |
|
|
* 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. */ |
|
|
|
|
/* |
/* |
* Return values from JobStart. |
* Return values from JobStart. |
*/ |
*/ |
|
|
{ |
{ |
"sh", |
"sh", |
TRUE, "set -", "set -v", "set -", 5, |
TRUE, "set -", "set -v", "set -", 5, |
|
TRUE, "set -e", "set +e", |
|
#ifdef OLDBOURNESHELL |
FALSE, "echo \"%s\"\n", "sh -c '%s || exit 0'\n", |
FALSE, "echo \"%s\"\n", "sh -c '%s || exit 0'\n", |
|
#endif |
"v", "e", |
"v", "e", |
}, |
}, |
/* |
/* |
* UNKNOWN. |
* UNKNOWN. |
*/ |
*/ |
{ |
{ |
(char *)0, |
(char *) 0, |
FALSE, (char *)0, (char *)0, (char *)0, 0, |
FALSE, (char *) 0, (char *) 0, (char *) 0, 0, |
FALSE, (char *)0, (char *)0, |
FALSE, (char *) 0, (char *) 0, |
(char *)0, (char *)0, |
(char *) 0, (char *) 0, |
} |
} |
}; |
}; |
static Shell *commandShell = &shells[DEFSHELL];/* this is the shell to |
static Shell *commandShell = &shells[DEFSHELL];/* this is the shell to |
|
|
* commands in the Makefile. |
* commands in the Makefile. |
* It is set by the |
* It is set by the |
* Job_ParseShell function */ |
* Job_ParseShell function */ |
static char *shellPath = (char *) NULL, /* full pathname of |
static char *shellPath = NULL, /* full pathname of |
* executable image */ |
* executable image */ |
*shellName; /* last component of shell */ |
*shellName; /* last component of shell */ |
|
|
|
|
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 maxLocal; /* The most local ones we can have */ |
int nJobs; /* The number of children currently running */ |
STATIC int nJobs; /* The number of children currently running */ |
int nLocal; /* The number of local children */ |
STATIC int nLocal; /* The number of local children */ |
Lst jobs; /* The structures that describe them */ |
STATIC Lst jobs; /* The structures that describe them */ |
Boolean jobFull; /* Flag to tell when the job table is full. It |
STATIC Boolean jobFull; /* Flag to tell when the job table is full. It |
* is set TRUE when (1) the total number of |
* is set TRUE when (1) the total number of |
* running jobs equals the maximum allowed or |
* running jobs equals the maximum allowed or |
* (2) a job can only be run locally, but |
* (2) a job can only be run locally, but |
|
|
* the output channels of children */ |
* the output channels of children */ |
#endif |
#endif |
|
|
GNode *lastNode; /* The node for which output was most recently |
STATIC GNode *lastNode; /* The node for which output was most recently |
* produced. */ |
* produced. */ |
char *targFmt; /* Format string to use to head output from a |
STATIC char *targFmt; /* Format string to use to head output from a |
* job when it's not the most-recent job heard |
* job when it's not the most-recent job heard |
* from */ |
* from */ |
#define TARG_FMT "--- %s ---\n" /* Default format */ |
|
|
|
|
#ifdef REMOTE |
|
# define TARG_FMT "--- %s at %s ---\n" /* Default format */ |
|
# define MESSAGE(fp, gn) \ |
|
(void) fprintf(fp, targFmt, gn->name, gn->rem.hname); |
|
#else |
|
# define TARG_FMT "--- %s ---\n" /* Default format */ |
|
# define MESSAGE(fp, gn) \ |
|
(void) fprintf(fp, targFmt, gn->name); |
|
#endif |
|
|
/* |
/* |
* When JobStart attempts to run a job remotely but can't, and isn't allowed |
* 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 |
* 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 |
* been migrated home, the job is placed on the stoppedJobs queue to be run |
* when the next job finishes. |
* when the next job finishes. |
*/ |
*/ |
Lst stoppedJobs; /* Lst of Job structures describing |
STATIC Lst stoppedJobs; /* Lst of Job structures describing |
* jobs that were stopped due to concurrency |
* jobs that were stopped due to concurrency |
* limits or migration home */ |
* limits or migration home */ |
|
|
|
|
#if defined(USE_PGRP) && defined(SYSV) |
#if defined(USE_PGRP) && defined(SYSV) |
#define KILL(pid,sig) killpg (-(pid),(sig)) |
# define KILL(pid, sig) killpg(-(pid), (sig)) |
#else |
#else |
# if defined(USE_PGRP) |
# if defined(USE_PGRP) |
#define KILL(pid,sig) killpg ((pid),(sig)) |
# define KILL(pid, sig) killpg((pid), (sig)) |
# else |
# else |
#define KILL(pid,sig) kill ((pid),(sig)) |
# define KILL(pid, sig) kill((pid), (sig)) |
# endif |
# endif |
#endif |
#endif |
|
|
|
/* |
|
* Grmpf... There is no way to set bits of the wait structure |
|
* anymore with the stupid W*() macros. I liked the union wait |
|
* stuff much more. So, we devise our own macros... This is |
|
* really ugly, use dramamine sparingly. You have been warned. |
|
*/ |
|
#define W_SETMASKED(st, val, fun) \ |
|
{ \ |
|
int sh = (int) ~0; \ |
|
int mask = fun(sh); \ |
|
\ |
|
for (sh = 0; ((mask >> sh) & 1) == 0; sh++) \ |
|
continue; \ |
|
*(st) = (*(st) & ~mask) | ((val) << sh); \ |
|
} |
|
|
|
#define W_SETTERMSIG(st, val) W_SETMASKED(st, val, WTERMSIG) |
|
#define W_SETEXITSTATUS(st, val) W_SETMASKED(st, val, WEXITSTATUS) |
|
|
|
|
static int JobCondPassSig __P((ClientData, ClientData)); |
static int JobCondPassSig __P((ClientData, ClientData)); |
static void JobPassSig __P((int)); |
static void JobPassSig __P((int)); |
static int JobCmpPid __P((ClientData, ClientData)); |
static int JobCmpPid __P((ClientData, ClientData)); |
static int JobPrintCommand __P((ClientData, ClientData)); |
static int JobPrintCommand __P((ClientData, ClientData)); |
static int JobSaveCommand __P((ClientData, ClientData)); |
static int JobSaveCommand __P((ClientData, ClientData)); |
static void JobFinish __P((Job *, union wait)); |
static void JobClose __P((Job *)); |
|
#ifdef REMOTE |
|
static int JobCmpRmtID __P((Job *, int)); |
|
# ifdef RMT_WILL_WATCH |
|
static void JobLocalInput __P((int, Job *)); |
|
# endif |
|
#else |
|
static void JobFinish __P((Job *, int *)); |
static void JobExec __P((Job *, char **)); |
static void JobExec __P((Job *, char **)); |
|
#endif |
static void JobMakeArgv __P((Job *, char **)); |
static void JobMakeArgv __P((Job *, char **)); |
static void JobRestart __P((Job *)); |
static void JobRestart __P((Job *)); |
static int JobStart __P((GNode *, int, Job *)); |
static int JobStart __P((GNode *, int, Job *)); |
static void JobDoOutput __P((Job *, Boolean)); |
static void JobDoOutput __P((Job *, Boolean)); |
static Shell *JobMatchShell __P((char *)); |
static Shell *JobMatchShell __P((char *)); |
static void JobInterrupt __P((int)); |
static void JobInterrupt __P((int, int)); |
|
static void JobRestartJobs __P((void)); |
|
|
/*- |
/*- |
*----------------------------------------------------------------------- |
*----------------------------------------------------------------------- |
|
|
int signo = *(int *) signop; |
int signo = *(int *) signop; |
#ifdef RMT_WANTS_SIGNALS |
#ifdef RMT_WANTS_SIGNALS |
if (job->flags & JOB_REMOTE) { |
if (job->flags & JOB_REMOTE) { |
(void)Rmt_Signal(job, signo); |
(void) Rmt_Signal(job, signo); |
} else { |
} else { |
KILL(job->pid, signo); |
KILL(job->pid, signo); |
} |
} |
|
|
* Assume that sending the signal to job->pid will signal any remote |
* Assume that sending the signal to job->pid will signal any remote |
* job as well. |
* job as well. |
*/ |
*/ |
|
if (DEBUG(JOB)) { |
|
(void) fprintf(stdout, |
|
"JobCondPassSig passing signal %d to child %d.\n", |
|
signo, job->pid); |
|
(void) fflush(stdout); |
|
} |
KILL(job->pid, signo); |
KILL(job->pid, signo); |
#endif |
#endif |
return(0); |
return 0; |
} |
} |
|
|
/*- |
/*- |
|
|
JobPassSig(signo) |
JobPassSig(signo) |
int signo; /* The signal number we've received */ |
int signo; /* The signal number we've received */ |
{ |
{ |
int mask; |
sigset_t nmask, omask; |
|
|
Lst_ForEach(jobs, JobCondPassSig, (ClientData)(long)signo); |
if (DEBUG(JOB)) { |
|
(void) fprintf(stdout, "JobPassSig(%d) called.\n", signo); |
|
(void) fflush(stdout); |
|
} |
|
Lst_ForEach(jobs, JobCondPassSig, (ClientData) &signo); |
|
|
/* |
/* |
* Deal with proper cleanup based on the signal received. We only run |
* Deal with proper cleanup based on the signal received. We only run |
|
|
* three termination signals are more of a "get out *now*" command. |
* three termination signals are more of a "get out *now*" command. |
*/ |
*/ |
if (signo == SIGINT) { |
if (signo == SIGINT) { |
JobInterrupt(TRUE); |
JobInterrupt(TRUE, signo); |
} else if ((signo == SIGHUP) || (signo == SIGTERM) || (signo == SIGQUIT)) { |
} else if ((signo == SIGHUP) || (signo == SIGTERM) || (signo == SIGQUIT)) { |
JobInterrupt(FALSE); |
JobInterrupt(FALSE, signo); |
} |
} |
|
|
/* |
/* |
|
|
* This ensures that all our jobs get continued when we wake up before |
* This ensures that all our jobs get continued when we wake up before |
* we take any other signal. |
* we take any other signal. |
*/ |
*/ |
mask = sigblock(0); |
sigfillset(&nmask); |
(void) sigsetmask(~0 & ~(1 << (signo-1))); |
(void) sigprocmask(SIG_BLOCK, &nmask, &omask); |
signal(signo, SIG_DFL); |
|
|
|
kill(getpid(), signo); |
if (DEBUG(JOB)) { |
|
(void) fprintf(stdout, |
|
"JobPassSig passing signal to self, mask = %x.\n", |
|
~0 & ~(1 << (signo-1))); |
|
(void) fflush(stdout); |
|
} |
|
(void) signal(signo, SIG_DFL); |
|
|
|
(void) KILL(getpid(), signo); |
|
|
signo = SIGCONT; |
signo = SIGCONT; |
Lst_ForEach(jobs, JobCondPassSig, (ClientData) &signo); |
Lst_ForEach(jobs, JobCondPassSig, (ClientData) &signo); |
|
|
sigsetmask(mask); |
(void) sigprocmask(SIG_SETMASK, &omask, NULL); |
signal(signo, JobPassSig); |
(void) signal(signo, JobPassSig); |
|
|
} |
} |
|
|
|
|
*----------------------------------------------------------------------- |
*----------------------------------------------------------------------- |
*/ |
*/ |
static int |
static int |
JobCmpPid (job, pid) |
JobCmpPid(job, pid) |
ClientData job; /* job to examine */ |
ClientData job; /* job to examine */ |
ClientData pid; /* process id desired */ |
ClientData pid; /* process id desired */ |
{ |
{ |
return ( *(int *) pid - ((Job *) job)->pid); |
return( *(int *) pid - ((Job *) job)->pid); |
} |
} |
|
|
|
#ifdef REMOTE |
/*- |
/*- |
*----------------------------------------------------------------------- |
*----------------------------------------------------------------------- |
|
* JobCmpRmtID -- |
|
* Compare the rmtID of the job with the given rmtID and return 0 if they |
|
* are equal. |
|
* |
|
* Results: |
|
* 0 if the rmtID's match |
|
* |
|
* Side Effects: |
|
* None. |
|
*----------------------------------------------------------------------- |
|
*/ |
|
static int |
|
JobCmpRmtID(job, rmtID) |
|
ClientData job; /* job to examine */ |
|
ClientData rmtID; /* remote id desired */ |
|
{ |
|
return(*(int *) rmtID - *(int *) job->rmtID); |
|
} |
|
#endif |
|
|
|
/*- |
|
*----------------------------------------------------------------------- |
* JobPrintCommand -- |
* JobPrintCommand -- |
* Put out another command for the given job. If the command starts |
* Put out another command for the given job. If the command starts |
* with an @ or a - we process it specially. In the former case, |
* with an @ or a - we process it specially. In the former case, |
|
|
*----------------------------------------------------------------------- |
*----------------------------------------------------------------------- |
*/ |
*/ |
static int |
static int |
JobPrintCommand (cmdp, jobp) |
JobPrintCommand(cmdp, jobp) |
ClientData cmdp; /* command string to print */ |
ClientData cmdp; /* command string to print */ |
ClientData jobp; /* job for which to print it */ |
ClientData jobp; /* job for which to print it */ |
{ |
{ |
|
|
char *cmd = (char *) cmdp; |
char *cmd = (char *) cmdp; |
Job *job = (Job *) jobp; |
Job *job = (Job *) jobp; |
|
|
noSpecials = (noExecute && ! (job->node->type & OP_MAKE)); |
noSpecials = (noExecute && !(job->node->type & OP_MAKE)); |
|
|
if (strcmp (cmd, "...") == 0) { |
if (strcmp(cmd, "...") == 0) { |
job->node->type |= OP_SAVE_CMDS; |
job->node->type |= OP_SAVE_CMDS; |
if ((job->flags & JOB_IGNDOTS) == 0) { |
if ((job->flags & JOB_IGNDOTS) == 0) { |
job->tailCmds = Lst_Succ (Lst_Member (job->node->commands, |
job->tailCmds = Lst_Succ(Lst_Member(job->node->commands, |
(ClientData)cmd)); |
(ClientData)cmd)); |
return (1); |
return(1); |
} |
} |
return (0); |
return(0); |
} |
} |
|
|
#define DBPRINTF(fmt, arg) if (DEBUG(JOB)) printf (fmt, arg); fprintf (job->cmdFILE, fmt, arg) |
#define DBPRINTF(fmt, arg) if (DEBUG(JOB)) { \ |
|
(void) fprintf(stdout, fmt, arg); \ |
|
(void) fflush(stdout); \ |
|
} \ |
|
(void) fprintf(job->cmdFILE, fmt, arg); \ |
|
(void) fflush(job->cmdFILE); |
|
|
numCommands += 1; |
numCommands += 1; |
|
|
|
|
* For debugging, we replace each command with the result of expanding |
* For debugging, we replace each command with the result of expanding |
* the variables in the command. |
* the variables in the command. |
*/ |
*/ |
cmdNode = Lst_Member (job->node->commands, (ClientData)cmd); |
cmdNode = Lst_Member(job->node->commands, (ClientData)cmd); |
cmdStart = cmd = Var_Subst (NULL, cmd, job->node, FALSE); |
cmdStart = cmd = Var_Subst(NULL, cmd, job->node, FALSE); |
Lst_Replace (cmdNode, (ClientData)cmdStart); |
Lst_Replace(cmdNode, (ClientData)cmdStart); |
|
|
cmdTemplate = "%s\n"; |
cmdTemplate = "%s\n"; |
|
|
|
|
cmd++; |
cmd++; |
|
|
if (shutUp) { |
if (shutUp) { |
if (! (job->flags & JOB_SILENT) && !noSpecials && |
if (!(job->flags & JOB_SILENT) && !noSpecials && |
commandShell->hasEchoCtl) { |
commandShell->hasEchoCtl) { |
DBPRINTF ("%s\n", commandShell->echoOff); |
DBPRINTF("%s\n", commandShell->echoOff); |
} else { |
} else { |
shutUp = FALSE; |
shutUp = FALSE; |
} |
} |
} |
} |
|
|
if (errOff) { |
if (errOff) { |
if ( ! (job->flags & JOB_IGNERR) && !noSpecials) { |
if ( !(job->flags & JOB_IGNERR) && !noSpecials) { |
if (commandShell->hasErrCtl) { |
if (commandShell->hasErrCtl) { |
/* |
/* |
* we don't want the error-control commands showing |
* we don't want the error-control commands showing |
|
|
* string too, but why make it any more complex than |
* string too, but why make it any more complex than |
* it already is? |
* it already is? |
*/ |
*/ |
if (! (job->flags & JOB_SILENT) && !shutUp && |
if (!(job->flags & JOB_SILENT) && !shutUp && |
commandShell->hasEchoCtl) { |
commandShell->hasEchoCtl) { |
DBPRINTF ("%s\n", commandShell->echoOff); |
DBPRINTF("%s\n", commandShell->echoOff); |
DBPRINTF ("%s\n", commandShell->ignErr); |
DBPRINTF("%s\n", commandShell->ignErr); |
DBPRINTF ("%s\n", commandShell->echoOn); |
DBPRINTF("%s\n", commandShell->echoOn); |
} else { |
} else { |
DBPRINTF ("%s\n", commandShell->ignErr); |
DBPRINTF("%s\n", commandShell->ignErr); |
} |
} |
} else if (commandShell->ignErr && |
} else if (commandShell->ignErr && |
(*commandShell->ignErr != '\0')) |
(*commandShell->ignErr != '\0')) |
{ |
{ |
/* |
/* |
* The shell has no error control, so we need to be |
* The shell has no error control, so we need to be |
|
|
* to ignore errors. Set cmdTemplate to use the weirdness |
* to ignore errors. Set cmdTemplate to use the weirdness |
* instead of the simple "%s\n" template. |
* instead of the simple "%s\n" template. |
*/ |
*/ |
if (! (job->flags & JOB_SILENT) && !shutUp && |
if (!(job->flags & JOB_SILENT) && !shutUp && |
commandShell->hasEchoCtl) { |
commandShell->hasEchoCtl) { |
DBPRINTF ("%s\n", commandShell->echoOff); |
DBPRINTF("%s\n", commandShell->echoOff); |
DBPRINTF (commandShell->errCheck, cmd); |
DBPRINTF(commandShell->errCheck, cmd); |
shutUp = TRUE; |
shutUp = TRUE; |
} |
} |
cmdTemplate = commandShell->ignErr; |
cmdTemplate = commandShell->ignErr; |
/* |
/* |
* The error ignoration (hee hee) is already taken care |
* The error ignoration(hee hee) is already taken care |
* of by the ignErr template, so pretend error checking |
* of by the ignErr template, so pretend error checking |
* is still on. |
* is still on. |
*/ |
*/ |
|
|
} |
} |
} |
} |
|
|
DBPRINTF (cmdTemplate, cmd); |
DBPRINTF(cmdTemplate, cmd); |
|
|
if (errOff) { |
if (errOff) { |
/* |
/* |
|
|
* for the whole command... |
* for the whole command... |
*/ |
*/ |
if (!shutUp && !(job->flags & JOB_SILENT) && commandShell->hasEchoCtl){ |
if (!shutUp && !(job->flags & JOB_SILENT) && commandShell->hasEchoCtl){ |
DBPRINTF ("%s\n", commandShell->echoOff); |
DBPRINTF("%s\n", commandShell->echoOff); |
shutUp = TRUE; |
shutUp = TRUE; |
} |
} |
DBPRINTF ("%s\n", commandShell->errCheck); |
DBPRINTF("%s\n", commandShell->errCheck); |
} |
} |
if (shutUp) { |
if (shutUp) { |
DBPRINTF ("%s\n", commandShell->echoOn); |
DBPRINTF("%s\n", commandShell->echoOn); |
} |
} |
return (0); |
return 0; |
} |
} |
|
|
/*- |
/*- |
|
|
*----------------------------------------------------------------------- |
*----------------------------------------------------------------------- |
*/ |
*/ |
static int |
static int |
JobSaveCommand (cmd, gn) |
JobSaveCommand(cmd, gn) |
ClientData cmd; |
ClientData cmd; |
ClientData gn; |
ClientData gn; |
{ |
{ |
cmd = (ClientData) Var_Subst (NULL, (char *) cmd, (GNode *) gn, FALSE); |
cmd = (ClientData) Var_Subst(NULL, (char *) cmd, (GNode *) gn, FALSE); |
(void)Lst_AtEnd (postCommands->commands, cmd); |
(void) Lst_AtEnd(postCommands->commands, cmd); |
return (0); |
return(0); |
} |
} |
|
|
|
|
/*- |
/*- |
*----------------------------------------------------------------------- |
*----------------------------------------------------------------------- |
|
* JobClose -- |
|
* Called to close both input and output pipes when a job is finished. |
|
* |
|
* Results: |
|
* Nada |
|
* |
|
* Side Effects: |
|
* The file descriptors associated with the job are closed. |
|
* |
|
*----------------------------------------------------------------------- |
|
*/ |
|
static void |
|
JobClose(job) |
|
Job *job; |
|
{ |
|
if (usePipes) { |
|
#ifdef RMT_WILL_WATCH |
|
Rmt_Ignore(job->inPipe); |
|
#else |
|
FD_CLR(job->inPipe, &outputs); |
|
#endif |
|
if (job->outPipe != job->inPipe) { |
|
(void) close(job->outPipe); |
|
} |
|
JobDoOutput(job, TRUE); |
|
(void) close(job->inPipe); |
|
} else { |
|
(void) close(job->outFd); |
|
JobDoOutput(job, TRUE); |
|
} |
|
} |
|
|
|
/*- |
|
*----------------------------------------------------------------------- |
* JobFinish -- |
* JobFinish -- |
* Do final processing for the given job including updating |
* Do final processing for the given job including updating |
* parents and starting new jobs as available/necessary. Note |
* parents and starting new jobs as available/necessary. Note |
|
|
* 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 postCommands. |
* |
* |
* 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. |
* If we recognized an error (errors !=0), we set the aborting flag |
* If we recognized an error(errors !=0), we set the aborting flag |
* to ABORT_ERROR so no more jobs will be started. |
* to ABORT_ERROR so no more jobs will be started. |
*----------------------------------------------------------------------- |
*----------------------------------------------------------------------- |
*/ |
*/ |
/*ARGSUSED*/ |
/*ARGSUSED*/ |
static void |
static void |
JobFinish (job, status) |
JobFinish(job, status) |
Job *job; /* job to finish */ |
Job *job; /* job to finish */ |
union wait status; /* sub-why job went away */ |
int *status; /* sub-why job went away */ |
{ |
{ |
Boolean done; |
Boolean done; |
|
|
if ((WIFEXITED(status) && |
if ((WIFEXITED(*status) && |
(((status.w_retcode != 0) && !(job->flags & JOB_IGNERR)))) || |
(((WEXITSTATUS(*status) != 0) && !(job->flags & JOB_IGNERR)))) || |
(WIFSIGNALED(status) && (status.w_termsig != SIGCONT))) |
(WIFSIGNALED(*status) && (WTERMSIG(*status) != SIGCONT))) |
{ |
{ |
/* |
/* |
* If it exited non-zero and either we're doing things our |
* If it exited non-zero and either we're doing things our |
|
|
* cases, finish out the job's output before printing the exit |
* cases, finish out the job's output before printing the exit |
* status... |
* status... |
*/ |
*/ |
if (usePipes) { |
#ifdef REMOTE |
#ifdef RMT_WILL_WATCH |
KILL(job->pid, SIGCONT); |
Rmt_Ignore(job->inPipe); |
#endif |
#else |
JobClose(job); |
FD_CLR(job->inPipe, &outputs); |
|
#endif /* RMT_WILL_WATCH */ |
|
if (job->outPipe != job->inPipe) { |
|
(void)close (job->outPipe); |
|
} |
|
JobDoOutput (job, TRUE); |
|
(void)close (job->inPipe); |
|
} else { |
|
(void)close (job->outFd); |
|
JobDoOutput (job, TRUE); |
|
} |
|
|
|
if (job->cmdFILE != NULL && job->cmdFILE != stdout) { |
if (job->cmdFILE != NULL && job->cmdFILE != stdout) { |
fclose(job->cmdFILE); |
(void) fclose(job->cmdFILE); |
} |
} |
done = TRUE; |
done = TRUE; |
} else if (WIFEXITED(status) && status.w_retcode != 0) { |
#ifdef REMOTE |
|
if (job->flags & JOB_REMOTE) |
|
Rmt_Done(job->rmtID, job->node); |
|
#endif |
|
} else if (WIFEXITED(*status)) { |
/* |
/* |
* Deal with ignored errors in -B mode. We need to print a message |
* Deal with ignored errors in -B mode. We need to print a message |
* telling of the ignored error as well as setting status.w_status |
* telling of the ignored error as well as setting status.w_status |
* to 0 so the next command gets run. To do this, we set done to be |
* to 0 so the next command gets run. To do this, we set done to be |
* TRUE if in -B mode and the job exited non-zero. Note we don't |
* TRUE if in -B mode and the job exited non-zero. |
|
*/ |
|
done = WEXITSTATUS(*status) != 0; |
|
/* |
|
* Old comment said: "Note we don't |
* want to close down any of the streams until we know we're at the |
* want to close down any of the streams until we know we're at the |
* end. |
* end." |
|
* But we do. Otherwise when are we going to print the rest of the |
|
* stuff? |
*/ |
*/ |
done = TRUE; |
JobClose(job); |
|
#ifdef REMOTE |
|
if (job->flags & JOB_REMOTE) |
|
Rmt_Done(job->rmtID, job->node); |
|
#endif /* REMOTE */ |
} else { |
} else { |
/* |
/* |
* No need to close things down or anything. |
* No need to close things down or anything. |
|
|
} |
} |
|
|
if (done || |
if (done || |
WIFSTOPPED(status) || |
WIFSTOPPED(*status) || |
(WIFSIGNALED(status) && (status.w_termsig == SIGCONT)) || |
(WIFSIGNALED(*status) && (WTERMSIG(*status) == SIGCONT)) || |
DEBUG(JOB)) |
DEBUG(JOB)) |
{ |
{ |
FILE *out; |
FILE *out; |
|
|
if (!usePipes && (job->flags & JOB_IGNERR)) { |
if (compatMake && !usePipes && (job->flags & JOB_IGNERR)) { |
/* |
/* |
* If output is going to a file and this job is ignoring |
* If output is going to a file and this job is ignoring |
* errors, arrange to have the exit status sent to the |
* errors, arrange to have the exit status sent to the |
* output file as well. |
* output file as well. |
*/ |
*/ |
out = fdopen (job->outFd, "w"); |
out = fdopen(job->outFd, "w"); |
} else { |
} else { |
out = stdout; |
out = stdout; |
} |
} |
|
|
if (WIFEXITED(status)) { |
if (WIFEXITED(*status)) { |
if (status.w_retcode != 0) { |
if (DEBUG(JOB)) { |
|
(void) fprintf(stdout, "Process %d exited.\n", job->pid); |
|
(void) fflush(stdout); |
|
} |
|
if (WEXITSTATUS(*status) != 0) { |
if (usePipes && job->node != lastNode) { |
if (usePipes && job->node != lastNode) { |
fprintf (out, targFmt, job->node->name); |
MESSAGE(out, job->node); |
lastNode = job->node; |
lastNode = job->node; |
} |
} |
fprintf (out, "*** Error code %d%s\n", status.w_retcode, |
(void) fprintf(out, "*** Error code %d%s\n", |
(job->flags & JOB_IGNERR) ? " (ignored)" : ""); |
WEXITSTATUS(*status), |
|
(job->flags & JOB_IGNERR) ? "(ignored)" : ""); |
|
|
if (job->flags & JOB_IGNERR) { |
if (job->flags & JOB_IGNERR) { |
status.w_status = 0; |
*status = 0; |
} |
} |
} else if (DEBUG(JOB)) { |
} else if (DEBUG(JOB)) { |
if (usePipes && job->node != lastNode) { |
if (usePipes && job->node != lastNode) { |
fprintf (out, targFmt, job->node->name); |
MESSAGE(out, job->node); |
lastNode = job->node; |
lastNode = job->node; |
} |
} |
fprintf (out, "*** Completed successfully\n"); |
(void) fprintf(out, "*** Completed successfully\n"); |
} |
} |
} else if (WIFSTOPPED(status)) { |
} else if (WIFSTOPPED(*status)) { |
|
if (DEBUG(JOB)) { |
|
(void) fprintf(stdout, "Process %d stopped.\n", job->pid); |
|
(void) fflush(stdout); |
|
} |
if (usePipes && job->node != lastNode) { |
if (usePipes && job->node != lastNode) { |
fprintf (out, targFmt, job->node->name); |
MESSAGE(out, job->node); |
lastNode = job->node; |
lastNode = job->node; |
} |
} |
if (! (job->flags & JOB_REMIGRATE)) { |
if (!(job->flags & JOB_REMIGRATE)) { |
fprintf (out, "*** Stopped -- signal %d\n", status.w_stopsig); |
fprintf(out, "*** Stopped -- signal %d\n", WSTOPSIG(*status)); |
} |
} |
job->flags |= JOB_RESUME; |
job->flags |= JOB_RESUME; |
(void)Lst_AtEnd(stoppedJobs, (ClientData)job); |
(void)Lst_AtEnd(stoppedJobs, (ClientData)job); |
fflush(out); |
#ifdef REMOTE |
|
if (job->flags & JOB_REMIGRATE) |
|
JobRestart(job); |
|
#endif |
|
(void) fflush(out); |
return; |
return; |
} else if (status.w_termsig == SIGCONT) { |
} else if (WTERMSIG(*status) == SIGCONT) { |
/* |
/* |
* If the beastie has continued, shift the Job from the stopped |
* If the beastie has continued, shift the Job from the stopped |
* list to the running one (or re-stop it if concurrency is |
* list to the running one(or re-stop it if concurrency is |
* exceeded) and go and get another child. |
* exceeded) and go and get another child. |
*/ |
*/ |
if (job->flags & (JOB_RESUME|JOB_REMIGRATE|JOB_RESTART)) { |
if (job->flags & (JOB_RESUME|JOB_REMIGRATE|JOB_RESTART)) { |
if (usePipes && job->node != lastNode) { |
if (usePipes && job->node != lastNode) { |
fprintf (out, targFmt, job->node->name); |
MESSAGE(out, job->node); |
lastNode = job->node; |
lastNode = job->node; |
} |
} |
fprintf (out, "*** Continued\n"); |
(void) fprintf(out, "*** Continued\n"); |
} |
} |
if (! (job->flags & JOB_CONTINUING)) { |
if (!(job->flags & JOB_CONTINUING)) { |
JobRestart(job); |
if (DEBUG(JOB)) { |
} else { |
(void) fprintf(stdout, |
Lst_AtEnd(jobs, (ClientData)job); |
"Warning: process %d was not continuing.\n", |
nJobs += 1; |
job->pid); |
if (! (job->flags & JOB_REMOTE)) { |
(void) fflush(stdout); |
nLocal += 1; |
|
} |
} |
if (nJobs == maxJobs) { |
#ifdef notdef |
jobFull = TRUE; |
/* |
if (DEBUG(JOB)) { |
* We don't really want to restart a job from scratch just |
printf("Job queue is full.\n"); |
* because it continued, especially not without killing the |
} |
* continuing process! That's why this is ifdef'ed out. |
} |
* FD - 9/17/90 |
|
*/ |
|
JobRestart(job); |
|
#endif |
} |
} |
fflush(out); |
job->flags &= ~JOB_CONTINUING; |
return; |
Lst_AtEnd(jobs, (ClientData)job); |
|
nJobs += 1; |
|
if (!(job->flags & JOB_REMOTE)) { |
|
if (DEBUG(JOB)) { |
|
(void) fprintf(stdout, |
|
"Process %d is continuing locally.\n", |
|
job->pid); |
|
(void) fflush(stdout); |
|
} |
|
nLocal += 1; |
|
} |
|
if (nJobs == maxJobs) { |
|
jobFull = TRUE; |
|
if (DEBUG(JOB)) { |
|
(void) fprintf(stdout, "Job queue is full.\n"); |
|
(void) fflush(stdout); |
|
} |
|
} |
|
(void) fflush(out); |
|
return; |
} else { |
} else { |
if (usePipes && job->node != lastNode) { |
if (usePipes && job->node != lastNode) { |
fprintf (out, targFmt, job->node->name); |
MESSAGE(out, job->node); |
lastNode = job->node; |
lastNode = job->node; |
} |
} |
fprintf (out, "*** Signal %d\n", status.w_termsig); |
(void) fprintf(out, "*** Signal %d\n", WTERMSIG(*status)); |
} |
} |
|
|
fflush (out); |
(void) fflush(out); |
} |
} |
|
|
/* |
/* |
|
|
* try and restart the job on the next command. If JobStart says it's |
* try and restart the job on the next command. If JobStart says it's |
* ok, it's ok. If there's an error, this puppy is done. |
* ok, it's ok. If there's an error, this puppy is done. |
*/ |
*/ |
if ((status.w_status == 0) && |
if (compatMake && (WIFEXITED(*status) && |
!Lst_IsAtEnd (job->node->commands)) |
!Lst_IsAtEnd(job->node->commands))) { |
{ |
switch (JobStart(job->node, job->flags & JOB_IGNDOTS, job)) { |
switch (JobStart (job->node, |
case JOB_RUNNING: |
job->flags & JOB_IGNDOTS, |
done = FALSE; |
job)) |
break; |
{ |
case JOB_ERROR: |
case JOB_RUNNING: |
done = TRUE; |
done = FALSE; |
W_SETEXITSTATUS(status, 1); |
break; |
break; |
case JOB_ERROR: |
case JOB_FINISHED: |
done = TRUE; |
/* |
status.w_retcode = 1; |
* If we got back a JOB_FINISHED code, JobStart has already |
break; |
* called Make_Update and freed the job descriptor. We set |
case JOB_FINISHED: |
* done to false here to avoid fake cycles and double frees. |
/* |
* JobStart needs to do the update so we can proceed up the |
* If we got back a JOB_FINISHED code, JobStart has already |
* graph when given the -n flag.. |
* called Make_Update and freed the job descriptor. We set |
*/ |
* done to false here to avoid fake cycles and double frees. |
done = FALSE; |
* JobStart needs to do the update so we can proceed up the |
break; |
* graph when given the -n flag.. |
|
*/ |
|
done = FALSE; |
|
break; |
|
} |
} |
} else { |
} else { |
done = TRUE; |
done = TRUE; |
|
|
if (done && |
if (done && |
(aborting != ABORT_ERROR) && |
(aborting != ABORT_ERROR) && |
(aborting != ABORT_INTERRUPT) && |
(aborting != ABORT_INTERRUPT) && |
(status.w_status == 0)) |
(*status == 0)) |
{ |
{ |
/* |
/* |
* As long as we aren't aborting and the job didn't return a non-zero |
* As long as we aren't aborting and the job didn't return a non-zero |
|
|
* on the .END target. |
* on the .END target. |
*/ |
*/ |
if (job->tailCmds != NILLNODE) { |
if (job->tailCmds != NILLNODE) { |
Lst_ForEachFrom (job->node->commands, job->tailCmds, |
Lst_ForEachFrom(job->node->commands, job->tailCmds, |
JobSaveCommand, |
JobSaveCommand, |
(ClientData)job->node); |
(ClientData)job->node); |
} |
} |
job->node->made = MADE; |
job->node->made = MADE; |
Make_Update (job->node); |
Make_Update(job->node); |
free((Address)job); |
free((Address)job); |
} else if (status.w_status) { |
} else if (*status == 0) { |
errors += 1; |
errors += 1; |
free((Address)job); |
free((Address)job); |
} |
} |
|
|
while (!errors && !jobFull && !Lst_IsEmpty(stoppedJobs)) { |
JobRestartJobs(); |
JobRestart((Job *)Lst_DeQueue(stoppedJobs)); |
|
} |
|
|
|
/* |
/* |
* Set aborting if any error. |
* Set aborting if any error. |
|
|
/* |
/* |
* If we are aborting and the job table is now empty, we finish. |
* If we are aborting and the job table is now empty, we finish. |
*/ |
*/ |
(void) unlink (tfile); |
(void) eunlink(tfile); |
Finish (errors); |
Finish(errors); |
} |
} |
} |
} |
|
|
|
|
*----------------------------------------------------------------------- |
*----------------------------------------------------------------------- |
*/ |
*/ |
void |
void |
Job_Touch (gn, silent) |
Job_Touch(gn, silent) |
GNode *gn; /* the node of the file to touch */ |
GNode *gn; /* the node of the file to touch */ |
Boolean silent; /* TRUE if should not print messages */ |
Boolean silent; /* TRUE if should not print messages */ |
{ |
{ |
|
|
} |
} |
|
|
if (!silent) { |
if (!silent) { |
printf ("touch %s\n", gn->name); |
(void) fprintf(stdout, "touch %s\n", gn->name); |
|
(void) fflush(stdout); |
} |
} |
|
|
if (noExecute) { |
if (noExecute) { |
|
|
} |
} |
|
|
if (gn->type & OP_ARCHV) { |
if (gn->type & OP_ARCHV) { |
Arch_Touch (gn); |
Arch_Touch(gn); |
} else if (gn->type & OP_LIB) { |
} else if (gn->type & OP_LIB) { |
Arch_TouchLib (gn); |
Arch_TouchLib(gn); |
} else { |
} else { |
char *file = gn->path ? gn->path : gn->name; |
char *file = gn->path ? gn->path : gn->name; |
|
|
times[0].tv_sec = times[1].tv_sec = now; |
times[0].tv_sec = times[1].tv_sec = now; |
times[0].tv_usec = times[1].tv_usec = 0; |
times[0].tv_usec = times[1].tv_usec = 0; |
if (utimes(file, times) < 0){ |
if (utimes(file, times) < 0){ |
streamID = open (file, O_RDWR | O_CREAT, 0666); |
streamID = open(file, O_RDWR | O_CREAT, 0666); |
|
|
if (streamID >= 0) { |
if (streamID >= 0) { |
char c; |
char c; |
|
|
* modification time, then close the file. |
* modification time, then close the file. |
*/ |
*/ |
if (read(streamID, &c, 1) == 1) { |
if (read(streamID, &c, 1) == 1) { |
lseek(streamID, (off_t) 0, SEEK_SET); |
(void) lseek(streamID, 0L, L_SET); |
write(streamID, &c, 1); |
(void) write(streamID, &c, 1); |
} |
} |
|
|
(void)close (streamID); |
(void) close(streamID); |
} else |
} else { |
printf("*** couldn't touch %s: %s", file, strerror(errno)); |
(void) fprintf(stdout, "*** couldn't touch %s: %s", |
|
file, strerror(errno)); |
|
(void) fflush(stdout); |
|
} |
} |
} |
} |
} |
} |
} |
|
|
*----------------------------------------------------------------------- |
*----------------------------------------------------------------------- |
*/ |
*/ |
Boolean |
Boolean |
Job_CheckCommands (gn, abortProc) |
Job_CheckCommands(gn, abortProc) |
GNode *gn; /* The target whose commands need |
GNode *gn; /* The target whose commands need |
* verifying */ |
* verifying */ |
void (*abortProc) __P((char *, ...)); |
void (*abortProc) __P((char *, ...)); |
/* Function to abort with message */ |
/* Function to abort with message */ |
{ |
{ |
if (OP_NOP(gn->type) && Lst_IsEmpty (gn->commands) && |
if (OP_NOP(gn->type) && Lst_IsEmpty(gn->commands) && |
(gn->type & OP_LIB) == 0) { |
(gn->type & OP_LIB) == 0) { |
/* |
/* |
* No commands. Look for .DEFAULT rule from which we might infer |
* No commands. Look for .DEFAULT rule from which we might infer |
|
|
* .DEFAULT itself. |
* .DEFAULT itself. |
*/ |
*/ |
Make_HandleUse(DEFAULT, gn); |
Make_HandleUse(DEFAULT, gn); |
Var_Set (IMPSRC, Var_Value (TARGET, gn, &p1), gn); |
Var_Set(IMPSRC, Var_Value(TARGET, gn, &p1), gn); |
if (p1) |
if (p1) |
free(p1); |
free(p1); |
} else if (Dir_MTime (gn) == 0) { |
} else if (Dir_MTime(gn) == 0) { |
/* |
/* |
* The node wasn't the target of an operator we have no .DEFAULT |
* The node wasn't the target of an operator we have no .DEFAULT |
* rule to go on and the target doesn't already exist. There's |
* rule to go on and the target doesn't already exist. There's |
|
|
* given, we stop in our tracks, otherwise we just don't update |
* given, we stop in our tracks, otherwise we just don't update |
* this node's parents so they never get examined. |
* this node's parents so they never get examined. |
*/ |
*/ |
|
static const char msg[] = "make: don't know how to make"; |
|
|
if (gn->type & OP_OPTIONAL) { |
if (gn->type & OP_OPTIONAL) { |
printf ("make: don't know how to make %s (ignored)\n", |
(void) fprintf(stdout, "%s %s(ignored)\n", msg, gn->name); |
gn->name); |
(void) fflush(stdout); |
} else if (keepgoing) { |
} else if (keepgoing) { |
printf ("make: don't know how to make %s (continuing)\n", |
(void) fprintf(stdout, "%s %s(continuing)\n", msg, gn->name); |
gn->name); |
(void) fflush(stdout); |
return (FALSE); |
return FALSE; |
} else { |
} else { |
(*abortProc) ("make: don't know how to make %s. Stop", |
(*abortProc)("%s %s. Stop", msg, gn->name); |
gn->name); |
return FALSE; |
return(FALSE); |
|
} |
} |
} |
} |
} |
} |
return (TRUE); |
return TRUE; |
} |
} |
#ifdef RMT_WILL_WATCH |
#ifdef RMT_WILL_WATCH |
/*- |
/*- |
|
|
/*ARGSUSED*/ |
/*ARGSUSED*/ |
static void |
static void |
JobLocalInput(stream, job) |
JobLocalInput(stream, job) |
int stream; /* Stream that's ready (ignored) */ |
int stream; /* Stream that's ready(ignored) */ |
Job *job; /* Job to which the stream belongs */ |
Job *job; /* Job to which the stream belongs */ |
{ |
{ |
JobDoOutput(job, FALSE); |
JobDoOutput(job, FALSE); |
|
|
if (DEBUG(JOB)) { |
if (DEBUG(JOB)) { |
int i; |
int i; |
|
|
printf("Running %s %sly\n", job->node->name, |
(void) fprintf(stdout, "Running %s %sly\n", job->node->name, |
job->flags&JOB_REMOTE?"remote":"local"); |
job->flags&JOB_REMOTE?"remote":"local"); |
printf("\tCommand: "); |
(void) fprintf(stdout, "\tCommand: "); |
for (i = 0; argv[i] != (char *)NULL; i++) { |
for (i = 0; argv[i] != NULL; i++) { |
printf("%s ", argv[i]); |
(void) fprintf(stdout, "%s ", argv[i]); |
} |
} |
printf("\n"); |
(void) fprintf(stdout, "\n"); |
|
(void) fflush(stdout); |
} |
} |
|
|
/* |
/* |
* Some jobs produce no output and it's disconcerting to have |
* Some jobs produce no output and it's disconcerting to have |
* no feedback of their running (since they produce no output, the |
* no feedback of their running(since they produce no output, the |
* banner with their name in it never appears). This is an attempt to |
* banner with their name in it never appears). This is an attempt to |
* provide that feedback, even if nothing follows it. |
* provide that feedback, even if nothing follows it. |
*/ |
*/ |
if ((lastNode != job->node) && (job->flags & JOB_FIRST) && |
if ((lastNode != job->node) && (job->flags & JOB_FIRST) && |
!(job->flags & JOB_SILENT)) |
!(job->flags & JOB_SILENT)) { |
{ |
MESSAGE(stdout, job->node); |
printf(targFmt, job->node->name); |
|
lastNode = job->node; |
lastNode = job->node; |
} |
} |
|
|
|
|
} |
} |
#endif /* RMT_NO_EXEC */ |
#endif /* RMT_NO_EXEC */ |
|
|
if ((cpid = vfork()) == -1) { |
if ((cpid = vfork()) == -1) { |
Punt ("Cannot fork"); |
Punt("Cannot fork"); |
} else if (cpid == 0) { |
} else if (cpid == 0) { |
|
|
/* |
/* |
* Must duplicate the input stream down to the child's input and |
* Must duplicate the input stream down to the child's input and |
* reset it to the beginning (again). Since the stream was marked |
* reset it to the beginning(again). Since the stream was marked |
* close-on-exec, we must clear that bit in the new input. |
* close-on-exec, we must clear that bit in the new input. |
*/ |
*/ |
(void) dup2(fileno(job->cmdFILE), 0); |
if (dup2(FILENO(job->cmdFILE), 0) == -1) |
fcntl(0, F_SETFD, 0); |
Punt("Cannot dup2: %s", strerror(errno)); |
lseek(0, (off_t) 0, SEEK_SET); |
(void) fcntl(0, F_SETFD, 0); |
|
(void) lseek(0, 0, L_SET); |
|
|
if (usePipes) { |
if (usePipes) { |
/* |
/* |
* Set up the child's output to be routed through the pipe |
* Set up the child's output to be routed through the pipe |
* we've created for it. |
* we've created for it. |
*/ |
*/ |
(void) dup2 (job->outPipe, 1); |
if (dup2(job->outPipe, 1) == -1) |
|
Punt("Cannot dup2: %s", strerror(errno)); |
} else { |
} else { |
/* |
/* |
* We're capturing output in a file, so we duplicate the |
* We're capturing output in a file, so we duplicate the |
* descriptor to the temporary file into the standard |
* descriptor to the temporary file into the standard |
* output. |
* output. |
*/ |
*/ |
(void) dup2 (job->outFd, 1); |
if (dup2(job->outFd, 1) == -1) |
|
Punt("Cannot dup2: %s", strerror(errno)); |
} |
} |
/* |
/* |
* The output channels are marked close on exec. This bit was |
* The output channels are marked close on exec. This bit was |
* duplicated by the dup2 (on some systems), so we have to clear |
* duplicated by the dup2(on some systems), so we have to clear |
* it before routing the shell's error output to the same place as |
* it before routing the shell's error output to the same place as |
* its standard output. |
* its standard output. |
*/ |
*/ |
fcntl(1, F_SETFD, 0); |
(void) fcntl(1, F_SETFD, 0); |
(void) dup2 (1, 2); |
if (dup2(1, 2) == -1) |
|
Punt("Cannot dup2: %s", strerror(errno)); |
|
|
#ifdef USE_PGRP |
#ifdef USE_PGRP |
/* |
/* |
|
|
* we can kill it and all its descendants in one fell swoop, |
* we can kill it and all its descendants in one fell swoop, |
* by killing its process family, but not commit suicide. |
* by killing its process family, but not commit suicide. |
*/ |
*/ |
|
# if defined(SYSV) |
(void) setpgrp(0, getpid()); |
(void) setsid(); |
#endif USE_PGRP |
# else |
|
(void) setpgid(0, getpid()); |
|
# endif |
|
#endif /* USE_PGRP */ |
|
|
(void) execv (shellPath, argv); |
#ifdef REMOTE |
(void) write (2, "Could not execute shell\n", |
if (job->flags & JOB_REMOTE) { |
sizeof ("Could not execute shell")); |
Rmt_Exec(shellPath, argv, FALSE); |
_exit (1); |
} else |
|
#endif /* REMOTE */ |
|
(void) execv(shellPath, argv); |
|
|
|
(void) write(2, "Could not execute shell\n", |
|
sizeof("Could not execute shell")); |
|
_exit(1); |
} else { |
} else { |
|
#ifdef REMOTE |
|
long omask = sigblock(sigmask(SIGCHLD)); |
|
#endif |
job->pid = cpid; |
job->pid = cpid; |
|
|
if (usePipes && (job->flags & JOB_FIRST) ) { |
if (usePipes && (job->flags & JOB_FIRST) ) { |
|
|
} |
} |
|
|
if (job->flags & JOB_REMOTE) { |
if (job->flags & JOB_REMOTE) { |
|
#ifndef REMOTE |
job->rmtID = 0; |
job->rmtID = 0; |
|
#else |
|
job->rmtID = Rmt_LastID(job->pid); |
|
#endif /* REMOTE */ |
} else { |
} else { |
nLocal += 1; |
nLocal += 1; |
/* |
/* |
* XXX: Used to not happen if CUSTOMS. Why? |
* XXX: Used to not happen if REMOTE. Why? |
*/ |
*/ |
if (job->cmdFILE != stdout) { |
if (job->cmdFILE != NULL && job->cmdFILE != stdout) { |
fclose(job->cmdFILE); |
(void) fclose(job->cmdFILE); |
job->cmdFILE = NULL; |
job->cmdFILE = NULL; |
} |
} |
} |
} |
|
#ifdef REMOTE |
|
(void) sigsetmask(omask); |
|
#endif |
} |
} |
|
|
#ifdef RMT_NO_EXEC |
#ifdef RMT_NO_EXEC |
|
|
* Now the job is actually running, add it to the table. |
* Now the job is actually running, add it to the table. |
*/ |
*/ |
nJobs += 1; |
nJobs += 1; |
(void)Lst_AtEnd (jobs, (ClientData)job); |
(void) Lst_AtEnd(jobs, (ClientData)job); |
if (nJobs == maxJobs) { |
if (nJobs == maxJobs) { |
jobFull = TRUE; |
jobFull = TRUE; |
} |
} |
|
|
argc++; |
argc++; |
} |
} |
} |
} |
argv[argc] = (char *)NULL; |
argv[argc] = NULL; |
} |
} |
|
|
/*- |
/*- |
|
|
JobRestart(job) |
JobRestart(job) |
Job *job; /* Job to restart */ |
Job *job; /* Job to restart */ |
{ |
{ |
|
#ifdef REMOTE |
|
int host; |
|
#endif |
|
|
if (job->flags & JOB_REMIGRATE) { |
if (job->flags & JOB_REMIGRATE) { |
if (DEBUG(JOB)) { |
if ( |
printf("Remigrating %x\n", job->pid); |
#ifdef REMOTE |
|
verboseRemigrates || |
|
#endif |
|
DEBUG(JOB)) { |
|
(void) fprintf(stdout, "*** remigrating %x(%s)\n", |
|
job->pid, job->node->name); |
|
(void) fflush(stdout); |
} |
} |
if (nLocal != maxLocal) { |
|
|
#ifdef REMOTE |
|
if (!Rmt_ReExport(job->pid, job->node, &host)) { |
|
if (verboseRemigrates || DEBUG(JOB)) { |
|
(void) fprintf(stdout, "*** couldn't migrate...\n"); |
|
(void) fflush(stdout); |
|
} |
|
#endif |
|
if (nLocal != maxLocal) { |
/* |
/* |
* Job cannot be remigrated, but there's room on the local |
* Job cannot be remigrated, but there's room on the local |
* machine, so resume the job and note that another |
* machine, so resume the job and note that another |
* local job has started. |
* local job has started. |
*/ |
*/ |
if (DEBUG(JOB)) { |
if ( |
printf("resuming on local machine\n"); |
#ifdef REMOTE |
} |
verboseRemigrates || |
|
#endif |
|
DEBUG(JOB)) { |
|
(void) fprintf(stdout, "*** resuming on local machine\n"); |
|
(void) fflush(stdout); |
|
} |
KILL(job->pid, SIGCONT); |
KILL(job->pid, SIGCONT); |
nLocal +=1; |
nLocal +=1; |
|
#ifdef REMOTE |
|
job->flags &= ~(JOB_REMIGRATE|JOB_RESUME|JOB_REMOTE); |
|
job->flags |= JOB_CONTINUING; |
|
#else |
job->flags &= ~(JOB_REMIGRATE|JOB_RESUME); |
job->flags &= ~(JOB_REMIGRATE|JOB_RESUME); |
|
#endif |
} else { |
} else { |
/* |
/* |
* Job cannot be restarted. Mark the table as full and |
* Job cannot be restarted. Mark the table as full and |
* place the job back on the list of stopped jobs. |
* place the job back on the list of stopped jobs. |
*/ |
*/ |
if (DEBUG(JOB)) { |
if ( |
printf("holding\n"); |
#ifdef REMOTE |
} |
verboseRemigrates || |
|
#endif |
|
DEBUG(JOB)) { |
|
(void) fprintf(stdout, "*** holding\n"); |
|
(void) fflush(stdout); |
|
} |
(void)Lst_AtFront(stoppedJobs, (ClientData)job); |
(void)Lst_AtFront(stoppedJobs, (ClientData)job); |
jobFull = TRUE; |
jobFull = TRUE; |
if (DEBUG(JOB)) { |
if (DEBUG(JOB)) { |
printf("Job queue is full.\n"); |
(void) fprintf(stdout, "Job queue is full.\n"); |
|
(void) fflush(stdout); |
} |
} |
return; |
return; |
|
} |
|
#ifdef REMOTE |
|
} else { |
|
/* |
|
* Clear out the remigrate and resume flags. Set the continuing |
|
* flag so we know later on that the process isn't exiting just |
|
* because of a signal. |
|
*/ |
|
job->flags &= ~(JOB_REMIGRATE|JOB_RESUME); |
|
job->flags |= JOB_CONTINUING; |
|
job->rmtID = host; |
} |
} |
|
#endif |
|
|
(void)Lst_AtEnd(jobs, (ClientData)job); |
(void)Lst_AtEnd(jobs, (ClientData)job); |
nJobs += 1; |
nJobs += 1; |
if (nJobs == maxJobs) { |
if (nJobs == maxJobs) { |
jobFull = TRUE; |
jobFull = TRUE; |
if (DEBUG(JOB)) { |
if (DEBUG(JOB)) { |
printf("Job queue is full.\n"); |
(void) fprintf(stdout, "Job queue is full.\n"); |
|
(void) fflush(stdout); |
} |
} |
} |
} |
} else if (job->flags & JOB_RESTART) { |
} else if (job->flags & JOB_RESTART) { |
|
|
char *argv[4]; |
char *argv[4]; |
|
|
JobMakeArgv(job, argv); |
JobMakeArgv(job, argv); |
|
|
if (DEBUG(JOB)) { |
if (DEBUG(JOB)) { |
printf("Restarting %s...", job->node->name); |
(void) fprintf(stdout, "Restarting %s...", job->node->name); |
|
(void) fflush(stdout); |
} |
} |
if (((nLocal >= maxLocal) && ! (job->flags & JOB_SPECIAL))) { |
#ifdef REMOTE |
|
if ((job->node->type&OP_NOEXPORT) || |
|
(nLocal < maxLocal && runLocalFirst) |
|
# ifdef RMT_NO_EXEC |
|
|| !Rmt_Export(shellPath, argv, job) |
|
# else |
|
|| !Rmt_Begin(shellPath, argv, job->node) |
|
# endif |
|
#endif |
|
{ |
|
if (((nLocal >= maxLocal) && !(job->flags & JOB_SPECIAL))) { |
/* |
/* |
* Can't be exported and not allowed to run locally -- put it |
* Can't be exported and not allowed to run locally -- put it |
* back on the hold queue and mark the table full |
* back on the hold queue and mark the table full |
*/ |
*/ |
if (DEBUG(JOB)) { |
if (DEBUG(JOB)) { |
printf("holding\n"); |
(void) fprintf(stdout, "holding\n"); |
|
(void) fflush(stdout); |
} |
} |
(void)Lst_AtFront(stoppedJobs, (ClientData)job); |
(void)Lst_AtFront(stoppedJobs, (ClientData)job); |
jobFull = TRUE; |
jobFull = TRUE; |
if (DEBUG(JOB)) { |
if (DEBUG(JOB)) { |
printf("Job queue is full.\n"); |
(void) fprintf(stdout, "Job queue is full.\n"); |
|
(void) fflush(stdout); |
} |
} |
return; |
return; |
} else { |
} else { |
/* |
/* |
* Job may be run locally. |
* Job may be run locally. |
*/ |
*/ |
if (DEBUG(JOB)) { |
if (DEBUG(JOB)) { |
printf("running locally\n"); |
(void) fprintf(stdout, "running locally\n"); |
|
(void) fflush(stdout); |
} |
} |
job->flags &= ~JOB_REMOTE; |
job->flags &= ~JOB_REMOTE; |
|
} |
} |
} |
|
#ifdef REMOTE |
|
else { |
|
/* |
|
* Can be exported. Hooray! |
|
*/ |
|
if (DEBUG(JOB)) { |
|
(void) fprintf(stdout, "exporting\n"); |
|
(void) fflush(stdout); |
|
} |
|
job->flags |= JOB_REMOTE; |
|
} |
|
#endif |
JobExec(job, argv); |
JobExec(job, argv); |
} else { |
} else { |
/* |
/* |
|
|
* we don't know... |
* we don't know... |
*/ |
*/ |
if (DEBUG(JOB)) { |
if (DEBUG(JOB)) { |
printf("Resuming %s...", job->node->name); |
(void) fprintf(stdout, "Resuming %s...", job->node->name); |
|
(void) fflush(stdout); |
} |
} |
if (((job->flags & JOB_REMOTE) || |
if (((job->flags & JOB_REMOTE) || |
(nLocal < maxLocal) || |
(nLocal < maxLocal) || |
(((job->flags & JOB_SPECIAL)) && |
#ifdef REMOTE |
(maxLocal == 0))) && |
(((job->flags & JOB_SPECIAL) && |
(nJobs != maxJobs)) |
(job->node->type & OP_NOEXPORT)) && |
|
(maxLocal == 0))) && |
|
#else |
|
((job->flags & JOB_SPECIAL) && |
|
(maxLocal == 0))) && |
|
#endif |
|
(nJobs != maxJobs)) |
{ |
{ |
/* |
/* |
* If the job is remote, it's ok to resume it as long as the |
* If the job is remote, it's ok to resume it as long as the |
* maximum concurrency won't be exceeded. If it's local and |
* maximum concurrency won't be exceeded. If it's local and |
* we haven't reached the local concurrency limit already (or the |
* we haven't reached the local concurrency limit already(or the |
* job must be run locally and maxLocal is 0), it's also ok to |
* job must be run locally and maxLocal is 0), it's also ok to |
* resume it. |
* resume it. |
*/ |
*/ |
Boolean error; |
Boolean error; |
extern int errno; |
extern int errno; |
union wait status; |
int status; |
|
|
#ifdef RMT_WANTS_SIGNALS |
#ifdef RMT_WANTS_SIGNALS |
if (job->flags & JOB_REMOTE) { |
if (job->flags & JOB_REMOTE) { |
|
|
* actually put the thing in the job table. |
* actually put the thing in the job table. |
*/ |
*/ |
job->flags |= JOB_CONTINUING; |
job->flags |= JOB_CONTINUING; |
status.w_termsig = SIGCONT; |
W_SETTERMSIG(&status, SIGCONT); |
JobFinish(job, status); |
JobFinish(job, &status); |
|
|
job->flags &= ~(JOB_RESUME|JOB_CONTINUING); |
job->flags &= ~(JOB_RESUME|JOB_CONTINUING); |
if (DEBUG(JOB)) { |
if (DEBUG(JOB)) { |
printf("done\n"); |
(void) fprintf(stdout, "done\n"); |
|
(void) fflush(stdout); |
} |
} |
} else { |
} else { |
Error("couldn't resume %s: %s", |
Error("couldn't resume %s: %s", |
job->node->name, strerror(errno)); |
job->node->name, strerror(errno)); |
status.w_status = 0; |
status = 0; |
status.w_retcode = 1; |
W_SETEXITSTATUS(&status, 1); |
JobFinish(job, status); |
JobFinish(job, &status); |
} |
} |
} else { |
} else { |
/* |
/* |
|
|
* place the job back on the list of stopped jobs. |
* place the job back on the list of stopped jobs. |
*/ |
*/ |
if (DEBUG(JOB)) { |
if (DEBUG(JOB)) { |
printf("table full\n"); |
(void) fprintf(stdout, "table full\n"); |
|
(void) fflush(stdout); |
} |
} |
(void)Lst_AtFront(stoppedJobs, (ClientData)job); |
(void) Lst_AtFront(stoppedJobs, (ClientData)job); |
jobFull = TRUE; |
jobFull = TRUE; |
if (DEBUG(JOB)) { |
if (DEBUG(JOB)) { |
printf("Job queue is full.\n"); |
(void) fprintf(stdout, "Job queue is full.\n"); |
|
(void) fflush(stdout); |
} |
} |
} |
} |
} |
} |
|
|
*----------------------------------------------------------------------- |
*----------------------------------------------------------------------- |
*/ |
*/ |
static int |
static int |
JobStart (gn, flags, previous) |
JobStart(gn, flags, previous) |
GNode *gn; /* target to create */ |
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 */ |
|
|
Boolean local; /* Set true if the job was run locally */ |
Boolean local; /* Set true if the job was run locally */ |
Boolean noExec; /* Set true if we decide not to run the job */ |
Boolean noExec; /* Set true if we decide not to run the job */ |
|
|
if (previous != (Job *)NULL) { |
if (previous != NULL) { |
previous->flags &= ~ (JOB_FIRST|JOB_IGNERR|JOB_SILENT|JOB_REMOTE); |
previous->flags &= ~(JOB_FIRST|JOB_IGNERR|JOB_SILENT|JOB_REMOTE); |
job = previous; |
job = previous; |
} else { |
} else { |
job = (Job *) emalloc (sizeof (Job)); |
job = (Job *) emalloc(sizeof(Job)); |
if (job == (Job *)NULL) { |
if (job == NULL) { |
Punt("JobStart out of memory"); |
Punt("JobStart out of memory"); |
} |
} |
flags |= JOB_FIRST; |
flags |= JOB_FIRST; |
|
|
* are also added to the field. |
* are also added to the field. |
*/ |
*/ |
job->flags = 0; |
job->flags = 0; |
if (Targ_Ignore (gn)) { |
if (Targ_Ignore(gn)) { |
job->flags |= JOB_IGNERR; |
job->flags |= JOB_IGNERR; |
} |
} |
if (Targ_Silent (gn)) { |
if (Targ_Silent(gn)) { |
job->flags |= JOB_SILENT; |
job->flags |= JOB_SILENT; |
} |
} |
job->flags |= flags; |
job->flags |= flags; |
|
|
* Check the commands now so any attributes from .DEFAULT have a chance |
* Check the commands now so any attributes from .DEFAULT have a chance |
* to migrate to the node |
* to migrate to the node |
*/ |
*/ |
if (job->flags & JOB_FIRST) { |
if (!compatMake && job->flags & JOB_FIRST) { |
cmdsOK = Job_CheckCommands(gn, Error); |
cmdsOK = Job_CheckCommands(gn, Error); |
} else { |
} else { |
cmdsOK = TRUE; |
cmdsOK = TRUE; |
} |
} |
|
|
/* |
/* |
* If the -n flag wasn't given, we open up OUR (not the child's) |
* If the -n flag wasn't given, we open up OUR(not the child's) |
* temporary file to stuff commands in it. The thing is rd/wr so we don't |
* temporary file to stuff commands in it. The thing is rd/wr so we don't |
* need to reopen it to feed it to the shell. If the -n flag *was* given, |
* need to reopen it to feed it to the shell. If the -n flag *was* given, |
* we just set the file to be stdout. Cute, huh? |
* we just set the file to be stdout. Cute, huh? |
|
|
DieHorribly(); |
DieHorribly(); |
} |
} |
|
|
job->cmdFILE = fopen (tfile, "w+"); |
job->cmdFILE = fopen(tfile, "w+"); |
if (job->cmdFILE == (FILE *) NULL) { |
if (job->cmdFILE == NULL) { |
Punt ("Could not open %s", tfile); |
Punt("Could not open %s", tfile); |
} |
} |
fcntl(fileno(job->cmdFILE), F_SETFD, 1); |
(void) fcntl(FILENO(job->cmdFILE), F_SETFD, 1); |
/* |
/* |
* Send the commands to the command file, flush all its buffers then |
* Send the commands to the command file, flush all its buffers then |
* rewind and remove the thing. |
* rewind and remove the thing. |
|
|
if ((job->flags&JOB_FIRST) && (Lst_Open(gn->commands) != SUCCESS)){ |
if ((job->flags&JOB_FIRST) && (Lst_Open(gn->commands) != SUCCESS)){ |
cmdsOK = FALSE; |
cmdsOK = FALSE; |
} else { |
} else { |
LstNode ln = Lst_Next (gn->commands); |
LstNode ln = Lst_Next(gn->commands); |
|
|
if ((ln == NILLNODE) || |
if ((ln == NILLNODE) || |
JobPrintCommand ((char *)Lst_Datum (ln), job)) |
JobPrintCommand((ClientData) Lst_Datum(ln), |
|
(ClientData) job)) |
{ |
{ |
noExec = TRUE; |
noExec = TRUE; |
Lst_Close (gn->commands); |
Lst_Close(gn->commands); |
} |
} |
if (noExec && !(job->flags & JOB_FIRST)) { |
if (noExec && !(job->flags & JOB_FIRST)) { |
/* |
/* |
|
|
* Job_CatchChildren b/c it wasn't clear if there were |
* Job_CatchChildren b/c it wasn't clear if there were |
* more commands to execute or not... |
* more commands to execute or not... |
*/ |
*/ |
if (usePipes) { |
JobClose(job); |
#ifdef RMT_WILL_WATCH |
|
Rmt_Ignore(job->inPipe); |
|
#else |
|
FD_CLR(job->inPipe, &outputs); |
|
#endif |
|
if (job->outPipe != job->inPipe) { |
|
(void)close (job->outPipe); |
|
} |
|
JobDoOutput (job, TRUE); |
|
(void)close (job->inPipe); |
|
} else { |
|
(void)close (job->outFd); |
|
JobDoOutput (job, TRUE); |
|
} |
|
} |
} |
} |
} |
} else { |
} else { |
|
|
* We can do all the commands at once. hooray for sanity |
* We can do all the commands at once. hooray for sanity |
*/ |
*/ |
numCommands = 0; |
numCommands = 0; |
Lst_ForEach (gn->commands, JobPrintCommand, (ClientData)job); |
Lst_ForEach(gn->commands, JobPrintCommand, (ClientData)job); |
|
|
/* |
/* |
* If we didn't print out any commands to the shell script, |
* If we didn't print out any commands to the shell script, |
|
|
* in one fell swoop. This will still set up job->tailCmds correctly. |
* in one fell swoop. This will still set up job->tailCmds correctly. |
*/ |
*/ |
if (lastNode != gn) { |
if (lastNode != gn) { |
printf (targFmt, gn->name); |
MESSAGE(stdout, gn); |
lastNode = gn; |
lastNode = gn; |
} |
} |
job->cmdFILE = stdout; |
job->cmdFILE = stdout; |
|
|
* up the graph. |
* up the graph. |
*/ |
*/ |
job->cmdFILE = stdout; |
job->cmdFILE = stdout; |
Job_Touch (gn, job->flags&JOB_SILENT); |
Job_Touch(gn, job->flags&JOB_SILENT); |
noExec = TRUE; |
noExec = TRUE; |
} |
} |
|
|
|
|
* Unlink and close the command file if we opened one |
* Unlink and close the command file if we opened one |
*/ |
*/ |
if (job->cmdFILE != stdout) { |
if (job->cmdFILE != stdout) { |
(void) unlink (tfile); |
(void) eunlink(tfile); |
fclose(job->cmdFILE); |
if (job->cmdFILE != NULL) |
|
(void) fclose(job->cmdFILE); |
} else { |
} else { |
fflush (stdout); |
(void) fflush(stdout); |
} |
} |
|
|
/* |
/* |
|
|
if (job->tailCmds != NILLNODE) { |
if (job->tailCmds != NILLNODE) { |
Lst_ForEachFrom(job->node->commands, job->tailCmds, |
Lst_ForEachFrom(job->node->commands, job->tailCmds, |
JobSaveCommand, |
JobSaveCommand, |
(ClientData)job->node); |
(ClientData)job->node); |
} |
} |
Make_Update(job->node); |
Make_Update(job->node); |
} |
} |
|
|
return(JOB_ERROR); |
return(JOB_ERROR); |
} |
} |
} else { |
} else { |
fflush (job->cmdFILE); |
(void) fflush(job->cmdFILE); |
(void) unlink (tfile); |
(void) eunlink(tfile); |
} |
} |
|
|
/* |
/* |
|
|
* starting a job and then set up its temporary-file name. This is just |
* starting a job and then set up its temporary-file name. This is just |
* tfile with two extra digits tacked on -- jobno. |
* tfile with two extra digits tacked on -- jobno. |
*/ |
*/ |
if (job->flags & JOB_FIRST) { |
if (!compatMake || (job->flags & JOB_FIRST)) { |
if (usePipes) { |
if (usePipes) { |
int fd[2]; |
int fd[2]; |
(void) pipe(fd); |
if (pipe(fd) == -1) |
|
Punt("Cannot create pipe: %s", strerror(errno)); |
job->inPipe = fd[0]; |
job->inPipe = fd[0]; |
job->outPipe = fd[1]; |
job->outPipe = fd[1]; |
(void)fcntl (job->inPipe, F_SETFD, 1); |
(void) fcntl(job->inPipe, F_SETFD, 1); |
(void)fcntl (job->outPipe, F_SETFD, 1); |
(void) fcntl(job->outPipe, F_SETFD, 1); |
} else { |
} else { |
printf ("Remaking `%s'\n", gn->name); |
(void) fprintf(stdout, "Remaking `%s'\n", gn->name); |
fflush (stdout); |
(void) fflush(stdout); |
sprintf (job->outFile, "%s%02d", tfile, jobno); |
sprintf(job->outFile, "%s%02d", tfile, jobno); |
jobno = (jobno + 1) % 100; |
jobno = (jobno + 1) % 100; |
job->outFd = open(job->outFile,O_WRONLY|O_CREAT|O_APPEND,0600); |
job->outFd = open(job->outFile,O_WRONLY|O_CREAT|O_APPEND,0600); |
(void)fcntl (job->outFd, F_SETFD, 1); |
(void) fcntl(job->outFd, F_SETFD, 1); |
} |
} |
} |
} |
|
|
local = TRUE; |
#ifdef REMOTE |
|
if (!(gn->type & OP_NOEXPORT) && !(runLocalFirst && nLocal < maxLocal)) { |
|
#ifdef RMT_NO_EXEC |
|
local = !Rmt_Export(shellPath, argv, job); |
|
#else |
|
local = !Rmt_Begin(shellPath, argv, job->node); |
|
#endif /* RMT_NO_EXEC */ |
|
if (!local) { |
|
job->flags |= JOB_REMOTE; |
|
} |
|
} else |
|
#endif |
|
local = TRUE; |
|
|
if (local && (((nLocal >= maxLocal) && |
if (local && (((nLocal >= maxLocal) && |
!(job->flags & JOB_SPECIAL) && |
!(job->flags & JOB_SPECIAL) && |
(maxLocal != 0)))) |
#ifdef REMOTE |
|
(!(gn->type & OP_NOEXPORT) || (maxLocal != 0)) |
|
#else |
|
(maxLocal != 0) |
|
#endif |
|
))) |
{ |
{ |
/* |
/* |
* The job can only be run locally, but we've hit the limit of |
* 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 |
* local concurrency, so put the job on hold until some other job |
* finishes. Note that the special jobs (.BEGIN, .INTERRUPT and .END) |
* finishes. Note that the special jobs(.BEGIN, .INTERRUPT and .END) |
* may be run locally even when the local limit has been reached |
* may be run locally even when the local limit has been reached |
* (e.g. when maxLocal == 0), though they will be exported if at |
*(e.g. when maxLocal == 0), though they will be exported if at |
* all possible. |
* all possible. In addition, any target marked with .NOEXPORT will |
|
* be run locally if maxLocal is 0. |
*/ |
*/ |
jobFull = TRUE; |
jobFull = TRUE; |
|
|
if (DEBUG(JOB)) { |
if (DEBUG(JOB)) { |
printf("Can only run job locally.\n"); |
(void) fprintf(stdout, "Can only run job locally.\n"); |
|
(void) fflush(stdout); |
} |
} |
job->flags |= JOB_RESTART; |
job->flags |= JOB_RESTART; |
(void)Lst_AtEnd(stoppedJobs, (ClientData)job); |
(void) Lst_AtEnd(stoppedJobs, (ClientData)job); |
} else { |
} else { |
if ((nLocal >= maxLocal) && local) { |
if ((nLocal >= maxLocal) && local) { |
/* |
/* |
* If we're running this job locally as a special case (see above), |
* If we're running this job locally as a special case(see above), |
* at least say the table is full. |
* at least say the table is full. |
*/ |
*/ |
jobFull = TRUE; |
jobFull = TRUE; |
if (DEBUG(JOB)) { |
if (DEBUG(JOB)) { |
printf("Local job queue is full.\n"); |
(void) fprintf(stdout, "Local job queue is full.\n"); |
|
(void) fflush(stdout); |
} |
} |
} |
} |
JobExec(job, argv); |
JobExec(job, argv); |
|
|
return(JOB_RUNNING); |
return(JOB_RUNNING); |
} |
} |
|
|
|
static char * |
|
JobOutput(job, cp, endp, msg) |
|
register Job *job; |
|
register char *cp, *endp; |
|
int msg; |
|
{ |
|
register char *ecp; |
|
|
|
if (commandShell->noPrint) { |
|
ecp = Str_FindSubstring(cp, commandShell->noPrint); |
|
while (ecp != NULL) { |
|
if (cp != ecp) { |
|
*ecp = '\0'; |
|
if (msg && job->node != lastNode) { |
|
MESSAGE(stdout, job->node); |
|
lastNode = job->node; |
|
} |
|
/* |
|
* The only way there wouldn't be a newline after |
|
* this line is if it were the last in the buffer. |
|
* however, since the non-printable comes after it, |
|
* there must be a newline, so we don't print one. |
|
*/ |
|
(void) fprintf(stdout, "%s", cp); |
|
(void) fflush(stdout); |
|
} |
|
cp = ecp + commandShell->noPLen; |
|
if (cp != endp) { |
|
/* |
|
* Still more to print, look again after skipping |
|
* the whitespace following the non-printable |
|
* command.... |
|
*/ |
|
cp++; |
|
while (*cp == ' ' || *cp == '\t' || *cp == '\n') { |
|
cp++; |
|
} |
|
ecp = Str_FindSubstring(cp, commandShell->noPrint); |
|
} else { |
|
return cp; |
|
} |
|
} |
|
} |
|
return cp; |
|
} |
|
|
/*- |
/*- |
*----------------------------------------------------------------------- |
*----------------------------------------------------------------------- |
* JobDoOutput -- |
* JobDoOutput -- |
|
|
* In both cases, however, we keep our figurative eye out for the |
* In both cases, however, we keep our figurative eye out for the |
* 'noPrint' line for the shell from which the output came. If |
* 'noPrint' line for the shell from which the output came. If |
* we recognize a line, we don't print it. If the command is not |
* we recognize a line, we don't print it. If the command is not |
* alone on the line (the character after it is not \0 or \n), we |
* alone on the line(the character after it is not \0 or \n), we |
* do print whatever follows it. |
* do print whatever follows it. |
* |
* |
* Results: |
* Results: |
|
|
* curPos may be shifted as may the contents of outBuf. |
* curPos may be shifted as may the contents of outBuf. |
*----------------------------------------------------------------------- |
*----------------------------------------------------------------------- |
*/ |
*/ |
static void |
STATIC void |
JobDoOutput (job, finish) |
JobDoOutput(job, finish) |
register Job *job; /* the job whose output needs printing */ |
register Job *job; /* the job whose output needs printing */ |
Boolean finish; /* TRUE if this is the last time we'll be |
Boolean finish; /* TRUE if this is the last time we'll be |
* called for this job */ |
* called for this job */ |
{ |
{ |
Boolean gotNL = FALSE; /* true if got a newline */ |
Boolean gotNL = FALSE; /* true if got a newline */ |
|
Boolean fbuf; /* true if our buffer filled up */ |
register int nr; /* number of bytes read */ |
register int nr; /* number of bytes read */ |
register int i; /* auxiliary index into outBuf */ |
register int i; /* auxiliary index into outBuf */ |
register int max; /* limit for i (end of current data) */ |
register int max; /* limit for i(end of current data) */ |
int nRead; /* (Temporary) number of bytes read */ |
int nRead; /*(Temporary) number of bytes read */ |
|
|
FILE *oFILE; /* Stream pointer to shell's output file */ |
FILE *oFILE; /* Stream pointer to shell's output file */ |
char inLine[132]; |
char inLine[132]; |
|
|
* Read as many bytes as will fit in the buffer. |
* Read as many bytes as will fit in the buffer. |
*/ |
*/ |
end_loop: |
end_loop: |
|
gotNL = FALSE; |
|
fbuf = FALSE; |
|
|
nRead = read (job->inPipe, &job->outBuf[job->curPos], |
nRead = read(job->inPipe, &job->outBuf[job->curPos], |
JOB_BUFSIZE - job->curPos); |
JOB_BUFSIZE - job->curPos); |
if (nRead < 0) { |
if (nRead < 0) { |
if (DEBUG(JOB)) { |
if (DEBUG(JOB)) { |
|
|
} |
} |
|
|
/* |
/* |
* If we hit the end-of-file (the job is dead), we must flush its |
* If we hit the end-of-file(the job is dead), we must flush its |
* remaining output, so pretend we read a newline if there's any |
* remaining output, so pretend we read a newline if there's any |
* output remaining in the buffer. |
* output remaining in the buffer. |
* Also clear the 'finish' flag so we stop looping. |
* Also clear the 'finish' flag so we stop looping. |
|
|
* If we've run out of buffer space, we have no choice |
* If we've run out of buffer space, we have no choice |
* but to print the stuff. sigh. |
* but to print the stuff. sigh. |
*/ |
*/ |
gotNL = TRUE; |
fbuf = TRUE; |
i = job->curPos; |
i = job->curPos; |
} |
} |
} |
} |
if (gotNL) { |
if (gotNL || fbuf) { |
/* |
/* |
* Need to send the output to the screen. Null terminate it |
* Need to send the output to the screen. Null terminate it |
* first, overwriting the newline character if there was one. |
* first, overwriting the newline character if there was one. |
* So long as the line isn't one we should filter (according |
* So long as the line isn't one we should filter(according |
* to the shell description), we print the line, preceeded |
* to the shell description), we print the line, preceeded |
* by a target banner if this target isn't the same as the |
* by a 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. |
|
|
*/ |
*/ |
job->outBuf[i] = '\0'; |
job->outBuf[i] = '\0'; |
if (i >= job->curPos) { |
if (i >= job->curPos) { |
register char *cp, *ecp; |
char *cp; |
|
|
|
cp = JobOutput(job, job->outBuf, &job->outBuf[i]); |
|
|
cp = job->outBuf; |
|
if (commandShell->noPrint) { |
|
ecp = Str_FindSubstring(job->outBuf, |
|
commandShell->noPrint); |
|
while (ecp != (char *)NULL) { |
|
if (cp != ecp) { |
|
*ecp = '\0'; |
|
if (job->node != lastNode) { |
|
printf (targFmt, job->node->name); |
|
lastNode = job->node; |
|
} |
|
/* |
|
* The only way there wouldn't be a newline after |
|
* this line is if it were the last in the buffer. |
|
* however, since the non-printable comes after it, |
|
* there must be a newline, so we don't print one. |
|
*/ |
|
printf ("%s", cp); |
|
} |
|
cp = ecp + commandShell->noPLen; |
|
if (cp != &job->outBuf[i]) { |
|
/* |
|
* Still more to print, look again after skipping |
|
* the whitespace following the non-printable |
|
* command.... |
|
*/ |
|
cp++; |
|
while (*cp == ' ' || *cp == '\t' || *cp == '\n') { |
|
cp++; |
|
} |
|
ecp = Str_FindSubstring (cp, |
|
commandShell->noPrint); |
|
} else { |
|
break; |
|
} |
|
} |
|
} |
|
|
|
/* |
/* |
* There's still more in that thar buffer. This time, though, |
* There's still more in that thar buffer. This time, though, |
* we know there's no newline at the end, so we add one of |
* we know there's no newline at the end, so we add one of |
|
|
*/ |
*/ |
if (*cp != '\0') { |
if (*cp != '\0') { |
if (job->node != lastNode) { |
if (job->node != lastNode) { |
printf (targFmt, job->node->name); |
MESSAGE(stdout, job->node); |
lastNode = job->node; |
lastNode = job->node; |
} |
} |
printf ("%s\n", cp); |
(void) fprintf(stdout, "%s%s", cp, gotNL ? "\n" : ""); |
|
(void) fflush(stdout); |
} |
} |
|
|
fflush (stdout); |
|
} |
} |
if (i < max - 1) { |
if (i < max - 1) { |
/* shift the remaining characters down */ |
/* shift the remaining characters down */ |
memcpy ( job->outBuf, &job->outBuf[i + 1], max - (i + 1)); |
(void) memcpy(job->outBuf, &job->outBuf[i + 1], max -(i + 1)); |
job->curPos = max - (i + 1); |
job->curPos = max -(i + 1); |
|
|
} else { |
} else { |
/* |
/* |
|
|
/* |
/* |
* If the finish flag is true, we must loop until we hit |
* If the finish flag is true, we must loop until we hit |
* end-of-file on the pipe. This is guaranteed to happen eventually |
* end-of-file on the pipe. This is guaranteed to happen eventually |
* since the other end of the pipe is now closed (we closed it |
* since the other end of the pipe is now closed(we closed it |
* explicitly and the child has exited). When we do get an EOF, |
* explicitly and the child has exited). When we do get an EOF, |
* finish will be set FALSE and we'll fall through and out. |
* finish will be set FALSE and we'll fall through and out. |
*/ |
*/ |
|
|
* Change to read in blocks and do FindSubString type things as for |
* Change to read in blocks and do FindSubString type things as for |
* pipes? That would allow for "@echo -n..." |
* pipes? That would allow for "@echo -n..." |
*/ |
*/ |
oFILE = fopen (job->outFile, "r"); |
oFILE = fopen(job->outFile, "r"); |
if (oFILE != (FILE *) NULL) { |
if (oFILE != NULL) { |
printf ("Results of making %s:\n", job->node->name); |
(void) fprintf(stdout, "Results of making %s:\n", job->node->name); |
while (fgets (inLine, sizeof(inLine), oFILE) != NULL) { |
(void) fflush(stdout); |
register char *cp, *ecp, *endp; |
while (fgets(inLine, sizeof(inLine), oFILE) != NULL) { |
|
register char *cp, *endp, *oendp; |
|
|
cp = inLine; |
cp = inLine; |
endp = inLine + strlen(inLine); |
oendp = endp = inLine + strlen(inLine); |
if (endp[-1] == '\n') { |
if (endp[-1] == '\n') { |
*--endp = '\0'; |
*--endp = '\0'; |
} |
} |
if (commandShell->noPrint) { |
cp = JobOutput(job, inLine, endp, FALSE); |
ecp = Str_FindSubstring(cp, commandShell->noPrint); |
|
while (ecp != (char *)NULL) { |
|
if (cp != ecp) { |
|
*ecp = '\0'; |
|
/* |
|
* The only way there wouldn't be a newline after |
|
* this line is if it were the last in the buffer. |
|
* however, since the non-printable comes after it, |
|
* there must be a newline, so we don't print one. |
|
*/ |
|
printf ("%s", cp); |
|
} |
|
cp = ecp + commandShell->noPLen; |
|
if (cp != endp) { |
|
/* |
|
* Still more to print, look again after skipping |
|
* the whitespace following the non-printable |
|
* command.... |
|
*/ |
|
cp++; |
|
while (*cp == ' ' || *cp == '\t' || *cp == '\n') { |
|
cp++; |
|
} |
|
ecp = Str_FindSubstring(cp, commandShell->noPrint); |
|
} else { |
|
break; |
|
} |
|
} |
|
} |
|
|
|
/* |
/* |
* There's still more in that thar buffer. This time, though, |
* There's still more in that thar buffer. This time, though, |
* we know there's no newline at the end, so we add one of |
* we know there's no newline at the end, so we add one of |
* our own free will. |
* our own free will. |
*/ |
*/ |
if (*cp != '\0') { |
(void) fprintf(stdout, "%s", cp); |
printf ("%s\n", cp); |
(void) fflush(stdout); |
|
if (endp != oendp) { |
|
(void) fprintf(stdout, "\n"); |
|
(void) fflush(stdout); |
} |
} |
} |
} |
fclose (oFILE); |
(void) fclose(oFILE); |
(void) unlink (job->outFile); |
(void) eunlink(job->outFile); |
} |
} |
} |
} |
fflush(stdout); |
|
} |
} |
|
|
/*- |
/*- |
|
|
*----------------------------------------------------------------------- |
*----------------------------------------------------------------------- |
*/ |
*/ |
void |
void |
Job_CatchChildren (block) |
Job_CatchChildren(block) |
Boolean block; /* TRUE if should block on the wait. */ |
Boolean block; /* TRUE if should block on the wait. */ |
{ |
{ |
int pid; /* pid of dead child */ |
int pid; /* pid of dead child */ |
register Job *job; /* job descriptor for dead child */ |
register Job *job; /* job descriptor for dead child */ |
LstNode jnode; /* list element for finding job */ |
LstNode jnode; /* list element for finding job */ |
union wait 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. |
|
|
return; |
return; |
} |
} |
|
|
while ((pid = wait3((int *)&status, (block?0:WNOHANG)|WUNTRACED, |
while ((pid = waitpid((pid_t) -1, &status, |
(struct rusage *)0)) > 0) |
(block?0:WNOHANG)|WUNTRACED)) > 0) |
{ |
{ |
if (DEBUG(JOB)) |
if (DEBUG(JOB)) { |
printf("Process %d exited or stopped.\n", pid); |
(void) fprintf(stdout, "Process %d exited or stopped.\n", pid); |
|
(void) fflush(stdout); |
|
} |
|
|
|
|
jnode = Lst_Find (jobs, (ClientData)&pid, JobCmpPid); |
jnode = Lst_Find(jobs, (ClientData)&pid, JobCmpPid); |
|
|
if (jnode == NILLNODE) { |
if (jnode == NILLNODE) { |
if (WIFSIGNALED(status) && (status.w_termsig == SIGCONT)) { |
if (WIFSIGNALED(status) && (WTERMSIG(status) == SIGCONT)) { |
jnode = Lst_Find(stoppedJobs, (ClientData) &pid, JobCmpPid); |
jnode = Lst_Find(stoppedJobs, (ClientData) &pid, JobCmpPid); |
if (jnode == NILLNODE) { |
if (jnode == NILLNODE) { |
Error("Resumed child (%d) not in table", pid); |
Error("Resumed child(%d) not in table", pid); |
continue; |
continue; |
} |
} |
job = (Job *)Lst_Datum(jnode); |
job = (Job *)Lst_Datum(jnode); |
(void)Lst_Remove(stoppedJobs, jnode); |
(void) Lst_Remove(stoppedJobs, jnode); |
} else { |
} else { |
Error ("Child (%d) not in table?", pid); |
Error("Child(%d) not in table?", pid); |
continue; |
continue; |
} |
} |
} else { |
} else { |
job = (Job *) Lst_Datum (jnode); |
job = (Job *) Lst_Datum(jnode); |
(void)Lst_Remove (jobs, jnode); |
(void) Lst_Remove(jobs, jnode); |
nJobs -= 1; |
nJobs -= 1; |
if (jobFull && DEBUG(JOB)) { |
if (jobFull && DEBUG(JOB)) { |
printf("Job queue is no longer full.\n"); |
(void) fprintf(stdout, "Job queue is no longer full.\n"); |
|
(void) fflush(stdout); |
} |
} |
jobFull = FALSE; |
jobFull = FALSE; |
|
#ifdef REMOTE |
|
if (!(job->flags & JOB_REMOTE)) { |
|
if (DEBUG(JOB)) { |
|
(void) fprintf(stdout, |
|
"Job queue has one fewer local process.\n"); |
|
(void) fflush(stdout); |
|
} |
|
nLocal -= 1; |
|
} |
|
#else |
nLocal -= 1; |
nLocal -= 1; |
|
#endif |
} |
} |
|
|
JobFinish (job, status); |
JobFinish(job, &status); |
} |
} |
} |
} |
|
|
|
|
* Job_CatchOutput -- |
* Job_CatchOutput -- |
* Catch the output from our children, if we're using |
* Catch the output from our children, if we're using |
* pipes do so. Otherwise just block time until we get a |
* pipes do so. Otherwise just block time until we get a |
* signal (most likely a SIGCHLD) since there's no point in |
* signal(most likely a SIGCHLD) since there's no point in |
* just spinning when there's nothing to do and the reaping |
* just spinning when there's nothing to do and the reaping |
* of a child can wait for a while. |
* of a child can wait for a while. |
* |
* |
|
|
* ----------------------------------------------------------------------- |
* ----------------------------------------------------------------------- |
*/ |
*/ |
void |
void |
Job_CatchOutput () |
Job_CatchOutput() |
{ |
{ |
int nfds; |
int nfds; |
struct timeval timeout; |
struct timeval timeout; |
|
|
int pnJobs; /* Previous nJobs */ |
int pnJobs; /* Previous nJobs */ |
#endif |
#endif |
|
|
fflush(stdout); |
(void) fflush(stdout); |
#ifdef RMT_WILL_WATCH |
#ifdef RMT_WILL_WATCH |
pnJobs = nJobs; |
pnJobs = nJobs; |
|
|
|
|
* NOTE: IT IS THE RESPONSIBILITY OF Rmt_Wait TO CALL Job_CatchChildren |
* NOTE: IT IS THE RESPONSIBILITY OF Rmt_Wait TO CALL Job_CatchChildren |
* IN A TIMELY FASHION TO CATCH ANY LOCALLY RUNNING JOBS THAT EXIT. |
* IN A TIMELY FASHION TO CATCH ANY LOCALLY RUNNING JOBS THAT EXIT. |
* It may use the variable nLocal to determine if it needs to call |
* It may use the variable nLocal to determine if it needs to call |
* Job_CatchChildren (if nLocal is 0, there's nothing for which to |
* Job_CatchChildren(if nLocal is 0, there's nothing for which to |
* wait...) |
* wait...) |
*/ |
*/ |
while (nJobs != 0 && pnJobs == nJobs) { |
while (nJobs != 0 && pnJobs == nJobs) { |
|
|
timeout.tv_sec = SEL_SEC; |
timeout.tv_sec = SEL_SEC; |
timeout.tv_usec = SEL_USEC; |
timeout.tv_usec = SEL_USEC; |
|
|
if ((nfds = select (FD_SETSIZE, &readfds, (fd_set *) 0, (fd_set *) 0, &timeout)) < 0) |
if ((nfds = select(FD_SETSIZE, &readfds, (fd_set *) 0, |
{ |
(fd_set *) 0, &timeout)) <= 0) |
return; |
return; |
} else { |
else { |
if (Lst_Open (jobs) == FAILURE) { |
if (Lst_Open(jobs) == FAILURE) { |
Punt ("Cannot open job table"); |
Punt("Cannot open job table"); |
} |
} |
while (nfds && (ln = Lst_Next (jobs)) != NILLNODE) { |
while (nfds && (ln = Lst_Next(jobs)) != NILLNODE) { |
job = (Job *) Lst_Datum (ln); |
job = (Job *) Lst_Datum(ln); |
if (FD_ISSET(job->inPipe, &readfds)) { |
if (FD_ISSET(job->inPipe, &readfds)) { |
JobDoOutput (job, FALSE); |
JobDoOutput(job, FALSE); |
nfds -= 1; |
nfds -= 1; |
} |
} |
} |
} |
Lst_Close (jobs); |
Lst_Close(jobs); |
} |
} |
} |
} |
#endif /* RMT_WILL_WATCH */ |
#endif /* RMT_WILL_WATCH */ |
|
|
*----------------------------------------------------------------------- |
*----------------------------------------------------------------------- |
*/ |
*/ |
void |
void |
Job_Make (gn) |
Job_Make(gn) |
GNode *gn; |
GNode *gn; |
{ |
{ |
(void)JobStart (gn, 0, (Job *)NULL); |
(void) JobStart(gn, 0, NULL); |
} |
} |
|
|
/*- |
/*- |
|
|
*----------------------------------------------------------------------- |
*----------------------------------------------------------------------- |
*/ |
*/ |
void |
void |
Job_Init (maxproc, maxlocal) |
Job_Init(maxproc, maxlocal) |
int maxproc; /* the greatest number of jobs which may be |
int maxproc; /* the greatest number of jobs which may be |
* running at one time */ |
* running at one time */ |
int maxlocal; /* the greatest number of local jobs which may |
int maxlocal; /* the greatest number of local jobs which may |
|
|
{ |
{ |
GNode *begin; /* node for commands to do at the very start */ |
GNode *begin; /* node for commands to do at the very start */ |
|
|
sprintf (tfile, "/tmp/make%05d", getpid()); |
(void) sprintf(tfile, "/tmp/make%05d", getpid()); |
|
|
jobs = Lst_Init (FALSE); |
jobs = Lst_Init(FALSE); |
stoppedJobs = Lst_Init(FALSE); |
stoppedJobs = Lst_Init(FALSE); |
maxJobs = maxproc; |
maxJobs = maxproc; |
maxLocal = maxlocal; |
maxLocal = maxlocal; |
|
|
|
|
lastNode = NILGNODE; |
lastNode = NILGNODE; |
|
|
if (maxJobs == 1) { |
if (maxJobs == 1 |
|
#ifdef REMOTE |
|
|| noMessages |
|
#endif |
|
) { |
/* |
/* |
* If only one job can run at a time, there's no need for a banner, |
* If only one job can run at a time, there's no need for a banner, |
* no is there? |
* no is there? |
|
|
targFmt = TARG_FMT; |
targFmt = TARG_FMT; |
} |
} |
|
|
if (shellPath == (char *) NULL) { |
if (shellPath == NULL) { |
/* |
/* |
* The user didn't specify a shell to use, so we are using the |
* The user didn't specify a shell to use, so we are using the |
* default one... Both the absolute path and the last component |
* default one... Both the absolute path and the last component |
|
|
* All default shells are located in _PATH_DEFSHELLDIR. |
* All default shells are located in _PATH_DEFSHELLDIR. |
*/ |
*/ |
shellName = commandShell->name; |
shellName = commandShell->name; |
shellPath = str_concat (_PATH_DEFSHELLDIR, shellName, STR_ADDSLASH); |
shellPath = str_concat(_PATH_DEFSHELLDIR, shellName, STR_ADDSLASH); |
} |
} |
|
|
if (commandShell->exit == (char *)NULL) { |
if (commandShell->exit == NULL) { |
commandShell->exit = ""; |
commandShell->exit = ""; |
} |
} |
if (commandShell->echo == (char *)NULL) { |
if (commandShell->echo == NULL) { |
commandShell->echo = ""; |
commandShell->echo = ""; |
} |
} |
|
|
|
|
* Catch the four signals that POSIX specifies if they aren't ignored. |
* Catch the four signals that POSIX specifies if they aren't ignored. |
* JobPassSig will take care of calling JobInterrupt if appropriate. |
* JobPassSig will take care of calling JobInterrupt if appropriate. |
*/ |
*/ |
if (signal (SIGINT, SIG_IGN) != SIG_IGN) { |
if (signal(SIGINT, SIG_IGN) != SIG_IGN) { |
signal (SIGINT, JobPassSig); |
(void) signal(SIGINT, JobPassSig); |
} |
} |
if (signal (SIGHUP, SIG_IGN) != SIG_IGN) { |
if (signal(SIGHUP, SIG_IGN) != SIG_IGN) { |
signal (SIGHUP, JobPassSig); |
(void) signal(SIGHUP, JobPassSig); |
} |
} |
if (signal (SIGQUIT, SIG_IGN) != SIG_IGN) { |
if (signal(SIGQUIT, SIG_IGN) != SIG_IGN) { |
signal (SIGQUIT, JobPassSig); |
(void) signal(SIGQUIT, JobPassSig); |
} |
} |
if (signal (SIGTERM, SIG_IGN) != SIG_IGN) { |
if (signal(SIGTERM, SIG_IGN) != SIG_IGN) { |
signal (SIGTERM, JobPassSig); |
(void) signal(SIGTERM, JobPassSig); |
} |
} |
/* |
/* |
* There are additional signals that need to be caught and passed if |
* There are additional signals that need to be caught and passed if |
* either the export system wants to be told directly of signals or if |
* either the export system wants to be told directly of signals or if |
* we're giving each job its own process group (since then it won't get |
* we're giving each job its own process group(since then it won't get |
* signals from the terminal driver as we own the terminal) |
* signals from the terminal driver as we own the terminal) |
*/ |
*/ |
#if defined(RMT_WANTS_SIGNALS) || defined(USE_PGRP) |
#if defined(RMT_WANTS_SIGNALS) || defined(USE_PGRP) |
if (signal (SIGTSTP, SIG_IGN) != SIG_IGN) { |
if (signal(SIGTSTP, SIG_IGN) != SIG_IGN) { |
signal (SIGTSTP, JobPassSig); |
(void) signal(SIGTSTP, JobPassSig); |
} |
} |
if (signal (SIGTTOU, SIG_IGN) != SIG_IGN) { |
if (signal(SIGTTOU, SIG_IGN) != SIG_IGN) { |
signal (SIGTTOU, JobPassSig); |
(void) signal(SIGTTOU, JobPassSig); |
} |
} |
if (signal (SIGTTIN, SIG_IGN) != SIG_IGN) { |
if (signal(SIGTTIN, SIG_IGN) != SIG_IGN) { |
signal (SIGTTIN, JobPassSig); |
(void) signal(SIGTTIN, JobPassSig); |
} |
} |
if (signal (SIGWINCH, SIG_IGN) != SIG_IGN) { |
if (signal(SIGWINCH, SIG_IGN) != SIG_IGN) { |
signal (SIGWINCH, JobPassSig); |
(void) signal(SIGWINCH, JobPassSig); |
} |
} |
#endif |
#endif |
|
|
begin = Targ_FindNode (".BEGIN", TARG_NOCREATE); |
begin = Targ_FindNode(".BEGIN", TARG_NOCREATE); |
|
|
if (begin != NILGNODE) { |
if (begin != NILGNODE) { |
JobStart (begin, JOB_SPECIAL, (Job *)0); |
JobStart(begin, JOB_SPECIAL, (Job *)0); |
while (nJobs) { |
while (nJobs) { |
Job_CatchOutput(); |
Job_CatchOutput(); |
#ifndef RMT_WILL_WATCH |
#ifndef RMT_WILL_WATCH |
Job_CatchChildren (!usePipes); |
Job_CatchChildren(!usePipes); |
#endif /* RMT_WILL_WATCH */ |
#endif /* RMT_WILL_WATCH */ |
} |
} |
} |
} |
postCommands = Targ_FindNode (".END", TARG_CREATE); |
postCommands = Targ_FindNode(".END", TARG_CREATE); |
} |
} |
|
|
/*- |
/*- |
|
|
*----------------------------------------------------------------------- |
*----------------------------------------------------------------------- |
*/ |
*/ |
Boolean |
Boolean |
Job_Full () |
Job_Full() |
{ |
{ |
return (aborting || jobFull); |
return(aborting || jobFull); |
} |
} |
|
|
/*- |
/*- |
|
|
* ----------------------------------------------------------------------- |
* ----------------------------------------------------------------------- |
*/ |
*/ |
Boolean |
Boolean |
Job_Empty () |
Job_Empty() |
{ |
{ |
if (nJobs == 0) { |
if (nJobs == 0) { |
if (!Lst_IsEmpty(stoppedJobs) && !aborting) { |
if (!Lst_IsEmpty(stoppedJobs) && !aborting) { |
|
|
* it...Try and restart the stopped jobs. |
* it...Try and restart the stopped jobs. |
*/ |
*/ |
jobFull = FALSE; |
jobFull = FALSE; |
while (!jobFull && !Lst_IsEmpty(stoppedJobs)) { |
JobRestartJobs(); |
JobRestart((Job *)Lst_DeQueue(stoppedJobs)); |
|
} |
|
return(FALSE); |
return(FALSE); |
} else { |
} else { |
return(TRUE); |
return(TRUE); |
|
|
*----------------------------------------------------------------------- |
*----------------------------------------------------------------------- |
*/ |
*/ |
static Shell * |
static Shell * |
JobMatchShell (name) |
JobMatchShell(name) |
char *name; /* Final component of shell path */ |
char *name; /* Final component of shell path */ |
{ |
{ |
register Shell *sh; /* Pointer into shells table */ |
register Shell *sh; /* Pointer into shells table */ |
|
|
*cp2; |
*cp2; |
char *eoname; |
char *eoname; |
|
|
eoname = name + strlen (name); |
eoname = name + strlen(name); |
|
|
match = (Shell *) NULL; |
match = NULL; |
|
|
for (sh = shells; sh->name != NULL; sh++) { |
for (sh = shells; sh->name != NULL; sh++) { |
for (cp1 = eoname - strlen (sh->name), cp2 = sh->name; |
for (cp1 = eoname - strlen(sh->name), cp2 = sh->name; |
*cp1 != '\0' && *cp1 == *cp2; |
*cp1 != '\0' && *cp1 == *cp2; |
cp1++, cp2++) { |
cp1++, cp2++) { |
continue; |
continue; |
} |
} |
if (*cp1 != *cp2) { |
if (*cp1 != *cp2) { |
continue; |
continue; |
} else if (match == (Shell *) NULL || |
} else if (match == NULL || strlen(match->name) < strlen(sh->name)) { |
strlen (match->name) < strlen (sh->name)) { |
match = sh; |
match = sh; |
|
} |
} |
} |
} |
return (match == (Shell *) NULL ? sh : match); |
return(match == NULL ? sh : match); |
} |
} |
|
|
/*- |
/*- |
|
|
*----------------------------------------------------------------------- |
*----------------------------------------------------------------------- |
*/ |
*/ |
ReturnStatus |
ReturnStatus |
Job_ParseShell (line) |
Job_ParseShell(line) |
char *line; /* The shell spec */ |
char *line; /* The shell spec */ |
{ |
{ |
char **words; |
char **words; |
|
|
Shell newShell; |
Shell newShell; |
Boolean fullSpec = FALSE; |
Boolean fullSpec = FALSE; |
|
|
while (isspace (*line)) { |
while (isspace(*line)) { |
line++; |
line++; |
} |
} |
words = brk_string (line, &wordCount, TRUE); |
words = brk_string(line, &wordCount, TRUE); |
|
|
memset ((Address)&newShell, 0, sizeof(newShell)); |
memset((Address)&newShell, 0, sizeof(newShell)); |
|
|
/* |
/* |
* Parse the specification by keyword |
* Parse the specification by keyword |
*/ |
*/ |
for (path = (char *)NULL, argc = wordCount - 1, argv = words + 1; |
for (path = NULL, argc = wordCount - 1, argv = words + 1; |
argc != 0; |
argc != 0; |
argc--, argv++) { |
argc--, argv++) { |
if (strncmp (*argv, "path=", 5) == 0) { |
if (strncmp(*argv, "path=", 5) == 0) { |
path = &argv[0][5]; |
path = &argv[0][5]; |
} else if (strncmp (*argv, "name=", 5) == 0) { |
} else if (strncmp(*argv, "name=", 5) == 0) { |
newShell.name = &argv[0][5]; |
newShell.name = &argv[0][5]; |
} else { |
} else { |
if (strncmp (*argv, "quiet=", 6) == 0) { |
if (strncmp(*argv, "quiet=", 6) == 0) { |
newShell.echoOff = &argv[0][6]; |
newShell.echoOff = &argv[0][6]; |
} else if (strncmp (*argv, "echo=", 5) == 0) { |
} else if (strncmp(*argv, "echo=", 5) == 0) { |
newShell.echoOn = &argv[0][5]; |
newShell.echoOn = &argv[0][5]; |
} else if (strncmp (*argv, "filter=", 7) == 0) { |
} else if (strncmp(*argv, "filter=", 7) == 0) { |
newShell.noPrint = &argv[0][7]; |
newShell.noPrint = &argv[0][7]; |
newShell.noPLen = strlen(newShell.noPrint); |
newShell.noPLen = strlen(newShell.noPrint); |
} else if (strncmp (*argv, "echoFlag=", 9) == 0) { |
} else if (strncmp(*argv, "echoFlag=", 9) == 0) { |
newShell.echo = &argv[0][9]; |
newShell.echo = &argv[0][9]; |
} else if (strncmp (*argv, "errFlag=", 8) == 0) { |
} else if (strncmp(*argv, "errFlag=", 8) == 0) { |
newShell.exit = &argv[0][8]; |
newShell.exit = &argv[0][8]; |
} else if (strncmp (*argv, "hasErrCtl=", 10) == 0) { |
} else if (strncmp(*argv, "hasErrCtl=", 10) == 0) { |
char c = argv[0][10]; |
char c = argv[0][10]; |
newShell.hasErrCtl = !((c != 'Y') && (c != 'y') && |
newShell.hasErrCtl = !((c != 'Y') && (c != 'y') && |
(c != 'T') && (c != 't')); |
(c != 'T') && (c != 't')); |
} else if (strncmp (*argv, "check=", 6) == 0) { |
} else if (strncmp(*argv, "check=", 6) == 0) { |
newShell.errCheck = &argv[0][6]; |
newShell.errCheck = &argv[0][6]; |
} else if (strncmp (*argv, "ignore=", 7) == 0) { |
} else if (strncmp(*argv, "ignore=", 7) == 0) { |
newShell.ignErr = &argv[0][7]; |
newShell.ignErr = &argv[0][7]; |
} else { |
} else { |
Parse_Error (PARSE_FATAL, "Unknown keyword \"%s\"", |
Parse_Error(PARSE_FATAL, "Unknown keyword \"%s\"", |
*argv); |
*argv); |
return (FAILURE); |
return(FAILURE); |
} |
} |
fullSpec = TRUE; |
fullSpec = TRUE; |
} |
} |
} |
} |
|
|
if (path == (char *)NULL) { |
if (path == NULL) { |
/* |
/* |
* If no path was given, the user wants one of the pre-defined shells, |
* If no path was given, the user wants one of the pre-defined shells, |
* yes? So we find the one s/he wants with the help of JobMatchShell |
* yes? So we find the one s/he wants with the help of JobMatchShell |
* and set things up the right way. shellPath will be set up by |
* and set things up the right way. shellPath will be set up by |
* Job_Init. |
* Job_Init. |
*/ |
*/ |
if (newShell.name == (char *)NULL) { |
if (newShell.name == NULL) { |
Parse_Error (PARSE_FATAL, "Neither path nor name specified"); |
Parse_Error(PARSE_FATAL, "Neither path nor name specified"); |
return (FAILURE); |
return(FAILURE); |
} else { |
} else { |
commandShell = JobMatchShell (newShell.name); |
commandShell = JobMatchShell(newShell.name); |
shellName = newShell.name; |
shellName = newShell.name; |
} |
} |
} else { |
} else { |
/* |
/* |
* The user provided a path. If s/he gave nothing else (fullSpec is |
* The user provided a path. If s/he gave nothing else(fullSpec is |
* FALSE), try and find a matching shell in the ones we know of. |
* FALSE), try and find a matching shell in the ones we know of. |
* Else we just take the specification at its word and copy it |
* Else we just take the specification at its word and copy it |
* to a new location. In either case, we need to record the |
* to a new location. In either case, we need to record the |
* path the user gave for the shell. |
* path the user gave for the shell. |
*/ |
*/ |
shellPath = path; |
shellPath = path; |
path = strrchr (path, '/'); |
path = strrchr(path, '/'); |
if (path == (char *)NULL) { |
if (path == NULL) { |
path = shellPath; |
path = shellPath; |
} else { |
} else { |
path += 1; |
path += 1; |
} |
} |
if (newShell.name != (char *)NULL) { |
if (newShell.name != NULL) { |
shellName = newShell.name; |
shellName = newShell.name; |
} else { |
} else { |
shellName = path; |
shellName = path; |
} |
} |
if (!fullSpec) { |
if (!fullSpec) { |
commandShell = JobMatchShell (shellName); |
commandShell = JobMatchShell(shellName); |
} else { |
} else { |
commandShell = (Shell *) emalloc(sizeof(Shell)); |
commandShell = (Shell *) emalloc(sizeof(Shell)); |
*commandShell = newShell; |
*commandShell = newShell; |
|
|
} |
} |
|
|
if (!commandShell->hasErrCtl) { |
if (!commandShell->hasErrCtl) { |
if (commandShell->errCheck == (char *)NULL) { |
if (commandShell->errCheck == NULL) { |
commandShell->errCheck = ""; |
commandShell->errCheck = ""; |
} |
} |
if (commandShell->ignErr == (char *)NULL) { |
if (commandShell->ignErr == NULL) { |
commandShell->ignErr = "%s\n"; |
commandShell->ignErr = "%s\n"; |
} |
} |
} |
} |
|
|
* Do not free up the words themselves, since they might be in use by the |
* Do not free up the words themselves, since they might be in use by the |
* shell specification... |
* shell specification... |
*/ |
*/ |
free (words); |
free(words); |
return SUCCESS; |
return SUCCESS; |
} |
} |
|
|
|
|
*----------------------------------------------------------------------- |
*----------------------------------------------------------------------- |
*/ |
*/ |
static void |
static void |
JobInterrupt (runINTERRUPT) |
JobInterrupt(runINTERRUPT, signo) |
int runINTERRUPT; /* Non-zero if commands for the .INTERRUPT |
int runINTERRUPT; /* Non-zero if commands for the .INTERRUPT |
* target should be executed */ |
* target should be executed */ |
|
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 */ |
|
|
|
|
aborting = ABORT_INTERRUPT; |
aborting = ABORT_INTERRUPT; |
|
|
(void)Lst_Open (jobs); |
(void) Lst_Open(jobs); |
while ((ln = Lst_Next (jobs)) != NILLNODE) { |
while ((ln = Lst_Next(jobs)) != NILLNODE) { |
job = (Job *) Lst_Datum (ln); |
job = (Job *) Lst_Datum(ln); |
|
|
if (!Targ_Precious (job->node)) { |
if (!Targ_Precious(job->node)) { |
char *file = (job->node->path == (char *)NULL ? |
char *file = (job->node->path == NULL ? |
job->node->name : |
job->node->name : |
job->node->path); |
job->node->path); |
struct stat st; |
if (!noExecute && eunlink(file) != -1) { |
if (!noExecute && lstat(file, &st) != -1 && !S_ISDIR(st.st_mode) && |
Error("*** %s removed", file); |
unlink(file) != -1) { |
|
Error ("*** %s removed", file); |
|
} |
} |
} |
} |
#ifdef RMT_WANTS_SIGNALS |
#ifdef RMT_WANTS_SIGNALS |
|
|
/* |
/* |
* If job is remote, let the Rmt module do the killing. |
* If job is remote, let the Rmt module do the killing. |
*/ |
*/ |
if (!Rmt_Signal(job, SIGINT)) { |
if (!Rmt_Signal(job, signo)) { |
/* |
/* |
* If couldn't kill the thing, finish it out now with an |
* If couldn't kill the thing, finish it out now with an |
* error code, since no exit report will come in likely. |
* error code, since no exit report will come in likely. |
*/ |
*/ |
union wait status; |
int status; |
|
|
status.w_status = 0; |
status.w_status = 0; |
status.w_retcode = 1; |
status.w_retcode = 1; |
JobFinish(job, status); |
JobFinish(job, &status); |
} |
} |
} else if (job->pid) { |
} else if (job->pid) { |
KILL(job->pid, SIGINT); |
KILL(job->pid, signo); |
} |
} |
#else |
#else |
if (job->pid) { |
if (job->pid) { |
|
if (DEBUG(JOB)) { |
|
(void) fprintf(stdout, |
|
"JobInterrupt passing signal to child %d.\n", |
|
job->pid); |
|
(void) fflush(stdout); |
|
} |
|
KILL(job->pid, signo); |
|
} |
|
#endif /* RMT_WANTS_SIGNALS */ |
|
} |
|
|
|
#ifdef REMOTE |
|
(void)Lst_Open(stoppedJobs); |
|
while ((ln = Lst_Next(stoppedJobs)) != NILLNODE) { |
|
job = (Job *) Lst_Datum(ln); |
|
|
|
if (job->flags & JOB_RESTART) { |
|
if (DEBUG(JOB)) { |
|
(void) fprintf(stdout, "%s%s", |
|
"JobInterrupt skipping job on stopped queue", |
|
"-- it was waiting to be restarted.\n"); |
|
(void) fflush(stdout); |
|
} |
|
continue; |
|
} |
|
if (!Targ_Precious(job->node)) { |
|
char *file = (job->node->path == NULL ? |
|
job->node->name : |
|
job->node->path); |
|
if (eunlink(file) == 0) { |
|
Error("*** %s removed", file); |
|
} |
|
} |
|
/* |
|
* Resume the thing so it will take the signal. |
|
*/ |
|
if (DEBUG(JOB)) { |
|
(void) fprintf(stdout, |
|
"JobInterrupt passing CONT to stopped child %d.\n", |
|
job->pid); |
|
(void) fflush(stdout); |
|
} |
|
KILL(job->pid, SIGCONT); |
|
#ifdef RMT_WANTS_SIGNALS |
|
if (job->flags & JOB_REMOTE) { |
|
/* |
|
* If job is remote, let the Rmt module do the killing. |
|
*/ |
|
if (!Rmt_Signal(job, SIGINT)) { |
|
/* |
|
* If couldn't kill the thing, finish it out now with an |
|
* error code, since no exit report will come in likely. |
|
*/ |
|
int status; |
|
status.w_status = 0; |
|
status.w_retcode = 1; |
|
JobFinish(job, &status); |
|
} |
|
} else if (job->pid) { |
|
if (DEBUG(JOB)) { |
|
(void) fprintf(stdout, |
|
"JobInterrupt passing interrupt to stopped child %d.\n", |
|
job->pid); |
|
(void) fflush(stdout); |
|
} |
KILL(job->pid, SIGINT); |
KILL(job->pid, SIGINT); |
} |
} |
#endif /* RMT_WANTS_SIGNALS */ |
#endif /* RMT_WANTS_SIGNALS */ |
} |
} |
Lst_Close (jobs); |
#endif |
|
Lst_Close(stoppedJobs); |
|
|
if (runINTERRUPT && !touchFlag) { |
if (runINTERRUPT && !touchFlag) { |
interrupt = Targ_FindNode (".INTERRUPT", TARG_NOCREATE); |
interrupt = Targ_FindNode(".INTERRUPT", TARG_NOCREATE); |
if (interrupt != NILGNODE) { |
if (interrupt != NILGNODE) { |
ignoreErrors = FALSE; |
ignoreErrors = FALSE; |
|
|
JobStart (interrupt, JOB_IGNDOTS, (Job *)0); |
JobStart(interrupt, JOB_IGNDOTS, (Job *)0); |
while (nJobs) { |
while (nJobs) { |
Job_CatchOutput(); |
Job_CatchOutput(); |
#ifndef RMT_WILL_WATCH |
#ifndef RMT_WILL_WATCH |
Job_CatchChildren (!usePipes); |
Job_CatchChildren(!usePipes); |
#endif /* RMT_WILL_WATCH */ |
#endif /* RMT_WILL_WATCH */ |
} |
} |
} |
} |
} |
} |
(void) unlink (tfile); |
(void) eunlink(tfile); |
exit (0); |
exit(signo); |
} |
} |
|
|
/* |
/* |
|
|
* Number of errors reported. |
* Number of errors reported. |
* |
* |
* Side Effects: |
* Side Effects: |
* The process' temporary file (tfile) is removed if it still |
* The process' temporary file(tfile) is removed if it still |
* existed. |
* existed. |
*----------------------------------------------------------------------- |
*----------------------------------------------------------------------- |
*/ |
*/ |
int |
int |
Job_End () |
Job_End() |
{ |
{ |
if (postCommands != NILGNODE && !Lst_IsEmpty (postCommands->commands)) { |
if (postCommands != NILGNODE && !Lst_IsEmpty(postCommands->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, |
JobStart(postCommands, JOB_SPECIAL | JOB_IGNDOTS, NULL); |
(Job *)0); |
|
|
|
while (nJobs) { |
while (nJobs) { |
Job_CatchOutput(); |
Job_CatchOutput(); |
#ifndef RMT_WILL_WATCH |
#ifndef RMT_WILL_WATCH |
Job_CatchChildren (!usePipes); |
Job_CatchChildren(!usePipes); |
#endif /* RMT_WILL_WATCH */ |
#endif /* RMT_WILL_WATCH */ |
} |
} |
} |
} |
} |
} |
(void) unlink (tfile); |
(void) eunlink(tfile); |
return(errors); |
return(errors); |
} |
} |
|
|
|
|
*----------------------------------------------------------------------- |
*----------------------------------------------------------------------- |
*/ |
*/ |
void |
void |
Job_AbortAll () |
Job_AbortAll() |
{ |
{ |
LstNode ln; /* element in job table */ |
LstNode ln; /* element in job table */ |
Job *job; /* the job descriptor in that element */ |
Job *job; /* the job descriptor in that element */ |
int foo; |
int foo; |
|
|
|
|
|
|
if (nJobs) { |
if (nJobs) { |
|
|
(void)Lst_Open (jobs); |
(void) Lst_Open(jobs); |
while ((ln = Lst_Next (jobs)) != NILLNODE) { |
while ((ln = Lst_Next(jobs)) != NILLNODE) { |
job = (Job *) Lst_Datum (ln); |
job = (Job *) Lst_Datum(ln); |
|
|
/* |
/* |
* kill the child process with increasingly drastic signals to make |
* kill the child process with increasingly drastic signals to make |
|
|
/* |
/* |
* Catch as many children as want to report in at first, then give up |
* Catch as many children as want to report in at first, then give up |
*/ |
*/ |
while (wait3(&foo, WNOHANG, (struct rusage *)0) > 0) |
while (waitpid((pid_t) -1, &foo, WNOHANG) > 0) |
continue; |
continue; |
(void) unlink (tfile); |
(void) eunlink(tfile); |
|
} |
|
|
|
#ifdef REMOTE |
|
/*- |
|
*----------------------------------------------------------------------- |
|
* JobFlagForMigration -- |
|
* Handle the eviction of a child. Called from RmtStatusChange. |
|
* Flags the child as remigratable and then suspends it. |
|
* |
|
* Results: |
|
* none. |
|
* |
|
* Side Effects: |
|
* The job descriptor is flagged for remigration. |
|
* |
|
*----------------------------------------------------------------------- |
|
*/ |
|
void |
|
JobFlagForMigration(hostID) |
|
int hostID; /* ID of host we used, for matching children. */ |
|
{ |
|
register Job *job; /* job descriptor for dead child */ |
|
LstNode jnode; /* list element for finding job */ |
|
|
|
if (DEBUG(JOB)) { |
|
(void) fprintf(stdout, "JobFlagForMigration(%d) called.\n", hostID); |
|
(void) fflush(stdout); |
|
} |
|
jnode = Lst_Find(jobs, (ClientData)hostID, JobCmpRmtID); |
|
|
|
if (jnode == NILLNODE) { |
|
jnode = Lst_Find(stoppedJobs, (ClientData)hostID, JobCmpRmtID); |
|
if (jnode == NILLNODE) { |
|
if (DEBUG(JOB)) { |
|
Error("Evicting host(%d) not in table", hostID); |
|
} |
|
return; |
|
} |
|
} |
|
job = (Job *) Lst_Datum(jnode); |
|
|
|
if (DEBUG(JOB)) { |
|
(void) fprintf(stdout, |
|
"JobFlagForMigration(%d) found job '%s'.\n", hostID, |
|
job->node->name); |
|
(void) fflush(stdout); |
|
} |
|
|
|
KILL(job->pid, SIGSTOP); |
|
|
|
job->flags |= JOB_REMIGRATE; |
|
} |
|
|
|
#endif |
|
|
|
/*- |
|
*----------------------------------------------------------------------- |
|
* JobRestartJobs -- |
|
* Tries to restart stopped jobs if there are slots available. |
|
* Note that this tries to restart them regardless of pending errors. |
|
* It's not good to leave stopped jobs lying around! |
|
* |
|
* Results: |
|
* None. |
|
* |
|
* Side Effects: |
|
* Resumes(and possibly migrates) jobs. |
|
* |
|
*----------------------------------------------------------------------- |
|
*/ |
|
static void |
|
JobRestartJobs() |
|
{ |
|
while (!jobFull && !Lst_IsEmpty(stoppedJobs)) { |
|
if (DEBUG(JOB)) { |
|
(void) fprintf(stdout, |
|
"Job queue is not full. Restarting a stopped job.\n"); |
|
(void) fflush(stdout); |
|
} |
|
JobRestart((Job *)Lst_DeQueue(stoppedJobs)); |
|
} |
} |
} |