Complete revamp of the way make handles job control signals.
- Send each type of signal to its own handler. - Only call JobFinish when a process exits, in particular don't 'fake up' 'exitstatus' for jobs being continued, nor call it for suspends. - When a job is stopped, use an entire variable to remember the fact, so we know we need to send a SIGCONT. Don't change any other state. - In order to report '*** [job3] Suspended' before we suspend ourselves we have to call waitpid() from the signal handler - where we don't want to process job termination events. Save the exit status and process later. The code now handles: - jobs that suspend themselves - jobs exiting while suspended - jobs that don't actually suspend at all Hoewever it still does printfs() from the signal handler, and I haven't yet stopped it thrashing the signal mask.
This commit is contained in:
parent
4887a60437
commit
56564e27f6
@ -1,4 +1,4 @@
|
||||
/* $NetBSD: job.c,v 1.115 2006/09/22 21:55:52 dsl Exp $ */
|
||||
/* $NetBSD: job.c,v 1.116 2006/09/23 20:51:28 dsl Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
|
||||
@ -70,14 +70,14 @@
|
||||
*/
|
||||
|
||||
#ifndef MAKE_NATIVE
|
||||
static char rcsid[] = "$NetBSD: job.c,v 1.115 2006/09/22 21:55:52 dsl Exp $";
|
||||
static char rcsid[] = "$NetBSD: job.c,v 1.116 2006/09/23 20:51:28 dsl Exp $";
|
||||
#else
|
||||
#include <sys/cdefs.h>
|
||||
#ifndef lint
|
||||
#if 0
|
||||
static char sccsid[] = "@(#)job.c 8.2 (Berkeley) 3/19/94";
|
||||
#else
|
||||
__RCSID("$NetBSD: job.c,v 1.115 2006/09/22 21:55:52 dsl Exp $");
|
||||
__RCSID("$NetBSD: job.c,v 1.116 2006/09/23 20:51:28 dsl Exp $");
|
||||
#endif
|
||||
#endif /* not lint */
|
||||
#endif
|
||||
@ -301,12 +301,12 @@ int exit_pipe[2] = { -1, -1 }; /* child exit signal pipe. */
|
||||
|
||||
static sigset_t caught_signals; /* Set of signals we handle */
|
||||
#if defined(USE_PGRP) && defined(SYSV)
|
||||
# define KILL(pid, sig) kill(-(pid), (sig))
|
||||
# define KILLPG(pid, sig) kill(-(pid), (sig))
|
||||
#else
|
||||
# if defined(USE_PGRP)
|
||||
# define KILL(pid, sig) killpg((pid), (sig))
|
||||
# define KILLPG(pid, sig) killpg((pid), (sig))
|
||||
# else
|
||||
# define KILL(pid, sig) kill((pid), (sig))
|
||||
# define KILLPG(pid, sig) kill((pid), (sig))
|
||||
# endif
|
||||
#endif
|
||||
|
||||
@ -323,20 +323,16 @@ static sigset_t caught_signals; /* Set of signals we handle */
|
||||
#define W_EXITCODE(ret, sig) ((ret << 8) | (sig))
|
||||
#endif
|
||||
|
||||
static void JobCondPassSig(int);
|
||||
static void JobPassSig(int);
|
||||
static void JobChildSig(int);
|
||||
#ifdef USE_PGRP
|
||||
static void JobContinueSig(int);
|
||||
#endif
|
||||
static Job *JobCmpPid(int, int);
|
||||
static Job *JobFindPid(int, int);
|
||||
static int JobPrintCommand(ClientData, ClientData);
|
||||
static int JobSaveCommand(ClientData, ClientData);
|
||||
static void JobClose(Job *);
|
||||
static void JobFinish(Job *, int *);
|
||||
static void JobExec(Job *, char **);
|
||||
static void JobMakeArgv(Job *, char **);
|
||||
static int JobRestart(Job *);
|
||||
static int JobStart(GNode *, int);
|
||||
static char *JobOutput(Job *, char *, char *, int);
|
||||
static void JobDoOutput(Job *, Boolean);
|
||||
@ -355,10 +351,10 @@ job_table_dump(const char *where)
|
||||
{
|
||||
Job *job;
|
||||
|
||||
fprintf(stderr, "job table @ %s\n", where);
|
||||
fprintf(stdout, "job table @ %s\n", where);
|
||||
for (job = job_table; job < job_table_end; job++) {
|
||||
fprintf(stderr, "job %d, status %d, flags %d, pid %d\n",
|
||||
(int)(job - job_table), job->job_status, job->flags, job->pid);
|
||||
fprintf(stdout, "job %d, status %d, flags %d, pid %d\n",
|
||||
(int)(job - job_table), job->job_state, job->flags, job->pid);
|
||||
}
|
||||
}
|
||||
|
||||
@ -399,8 +395,13 @@ JobCondPassSig(int signo)
|
||||
{
|
||||
Job *job;
|
||||
|
||||
if (DEBUG(JOB)) {
|
||||
(void)fprintf(stdout, "JobCondPassSig(%d) called.\n", signo);
|
||||
(void)fflush(stdout);
|
||||
}
|
||||
|
||||
for (job = job_table; job < job_table_end; job++) {
|
||||
if (job->job_status != JOB_ST_RUNNING)
|
||||
if (job->job_state != JOB_ST_RUNNING)
|
||||
continue;
|
||||
if (DEBUG(JOB)) {
|
||||
(void)fprintf(stdout,
|
||||
@ -408,7 +409,7 @@ JobCondPassSig(int signo)
|
||||
signo, job->pid);
|
||||
(void)fflush(stdout);
|
||||
}
|
||||
KILL(job->pid, signo);
|
||||
KILLPG(job->pid, signo);
|
||||
}
|
||||
}
|
||||
|
||||
@ -456,6 +457,10 @@ JobChildSig(int signo __unused)
|
||||
static void
|
||||
JobContinueSig(int signo __unused)
|
||||
{
|
||||
/*
|
||||
* Defer sending to SIGCONT to out stopped children until we return
|
||||
* from the signal handler.
|
||||
*/
|
||||
write(exit_pipe[1], DO_JOB_RESUME, 1);
|
||||
}
|
||||
#endif
|
||||
@ -478,38 +483,39 @@ JobContinueSig(int signo __unused)
|
||||
*-----------------------------------------------------------------------
|
||||
*/
|
||||
static void
|
||||
JobPassSig(int signo)
|
||||
JobPassSig_int(int signo)
|
||||
{
|
||||
/* Run .INTERRUPT target then exit */
|
||||
JobInterrupt(TRUE, signo);
|
||||
}
|
||||
|
||||
static void
|
||||
JobPassSig_term(int signo)
|
||||
{
|
||||
/* Dont run .INTERRUPT target then exit */
|
||||
JobInterrupt(FALSE, signo);
|
||||
}
|
||||
|
||||
static void
|
||||
JobPassSig_suspend(int signo)
|
||||
{
|
||||
sigset_t nmask, omask;
|
||||
struct sigaction act;
|
||||
int sigcount;
|
||||
|
||||
if (DEBUG(JOB)) {
|
||||
(void)fprintf(stdout, "JobPassSig(%d) called.\n", signo);
|
||||
(void)fflush(stdout);
|
||||
}
|
||||
/* Pass the signal onto every job */
|
||||
JobCondPassSig(signo);
|
||||
|
||||
/*
|
||||
* Deal with proper cleanup based on the signal received. We only run
|
||||
* the .INTERRUPT target if the signal was in fact an interrupt. The other
|
||||
* three termination signals are more of a "get out *now*" command.
|
||||
*/
|
||||
if (signo == SIGINT) {
|
||||
JobInterrupt(TRUE, signo);
|
||||
} else if ((signo == SIGHUP) || (signo == SIGTERM) || (signo == SIGQUIT)) {
|
||||
JobInterrupt(FALSE, signo);
|
||||
}
|
||||
|
||||
/*
|
||||
* Leave gracefully if SIGQUIT, rather than core dumping.
|
||||
*/
|
||||
if (signo == SIGQUIT) {
|
||||
Finish(0);
|
||||
}
|
||||
|
||||
if (signo == SIGTSTP) {
|
||||
Job_CatchChildren(FALSE);
|
||||
/* Give children a short chance to run and suspend themselves */
|
||||
for (sigcount = maxJobs; sigcount != 0; sigcount--) {
|
||||
if (poll(0, 0, 1) != -1 || errno != EINTR)
|
||||
break;
|
||||
}
|
||||
/* Then report any that have... (gets messages sequenced properly) */
|
||||
Job_CatchChildren(CATCH_DEFER);
|
||||
}
|
||||
|
||||
/*
|
||||
* Send ourselves the signal now we've given the message to everyone else.
|
||||
* Note we block everything else possible while we're getting the signal.
|
||||
@ -534,21 +540,32 @@ JobPassSig(int signo)
|
||||
(void)kill(getpid(), signo);
|
||||
|
||||
/*
|
||||
* If we didn't die, then likely as not all our children are suspended
|
||||
* so loop through and wake them up.
|
||||
* We've been continued.
|
||||
*
|
||||
* A whole host of signals continue to happen!
|
||||
* Any SIGCHLD for the suspends that we didn't catch above.
|
||||
* SIGCHLD for any processes that exited while we were alseep.
|
||||
* The SIGCONT that actually caused us to wakeup.
|
||||
*
|
||||
* Since we defer passing the SIGCONT on to our children until
|
||||
* the main processing loop, we can be sure that all the SIGCHLD
|
||||
* events will have happened by then - and that the waitpid() will
|
||||
* collect the child 'suspended' events.
|
||||
* For correct sequencing we just need to ensure we process the
|
||||
* waitpid() before passign on the SIGCONT.
|
||||
*
|
||||
* In any case nothing else is needed here.
|
||||
*/
|
||||
if (signo != SIGTSTP)
|
||||
JobCondPassSig(SIGCONT);
|
||||
|
||||
/* Restore handler and signal mask */
|
||||
act.sa_handler = JobPassSig;
|
||||
act.sa_handler = JobPassSig_suspend;
|
||||
(void)sigaction(signo, &act, NULL);
|
||||
(void)sigprocmask(SIG_SETMASK, &omask, NULL);
|
||||
}
|
||||
|
||||
/*-
|
||||
*-----------------------------------------------------------------------
|
||||
* JobCmpPid --
|
||||
* JobFindPid --
|
||||
* Compare the pid of the job with the given pid and return 0 if they
|
||||
* are equal. This function is called from Job_CatchChildren via
|
||||
* Lst_Find to find the job descriptor of the finished job.
|
||||
@ -565,14 +582,12 @@ JobPassSig(int signo)
|
||||
*-----------------------------------------------------------------------
|
||||
*/
|
||||
static Job *
|
||||
JobCmpPid(int pid, int status)
|
||||
JobFindPid(int pid, int status)
|
||||
{
|
||||
Job *job;
|
||||
|
||||
for (job = job_table; job < job_table_end; job++) {
|
||||
if (job->job_status != status)
|
||||
continue;
|
||||
if (job->pid == pid)
|
||||
if ((job->job_state == status) && job->pid == pid)
|
||||
return job;
|
||||
}
|
||||
if (DEBUG(JOB))
|
||||
@ -911,13 +926,19 @@ JobClose(Job *job)
|
||||
*/
|
||||
/*ARGSUSED*/
|
||||
static void
|
||||
JobFinish(Job *job, int *status)
|
||||
JobFinish(Job *job, int status)
|
||||
{
|
||||
Boolean done, return_job_token;
|
||||
|
||||
if ((WIFEXITED(*status) &&
|
||||
(((WEXITSTATUS(*status) != 0) && !(job->flags & JOB_IGNERR)))) ||
|
||||
WIFSIGNALED(*status))
|
||||
if (DEBUG(JOB)) {
|
||||
fprintf(stdout, "Jobfinish: %d [%s], status %d\n",
|
||||
job->pid, job->node->name, status);
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
if ((WIFEXITED(status) &&
|
||||
(((WEXITSTATUS(status) != 0) && !(job->flags & JOB_IGNERR)))) ||
|
||||
WIFSIGNALED(status))
|
||||
{
|
||||
/*
|
||||
* If it exited non-zero and either we're doing things our
|
||||
@ -933,14 +954,14 @@ JobFinish(Job *job, int *status)
|
||||
job->cmdFILE = NULL;
|
||||
}
|
||||
done = TRUE;
|
||||
} else if (WIFEXITED(*status)) {
|
||||
} else if (WIFEXITED(status)) {
|
||||
/*
|
||||
* 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
|
||||
* 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.
|
||||
*/
|
||||
done = WEXITSTATUS(*status) != 0;
|
||||
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
|
||||
@ -956,28 +977,24 @@ JobFinish(Job *job, int *status)
|
||||
done = FALSE;
|
||||
}
|
||||
|
||||
if (done || WIFSTOPPED(*status) ||
|
||||
(WIFSIGNALED(*status) && (WTERMSIG(*status) == SIGCONT))) {
|
||||
|
||||
if (WIFEXITED(*status)) {
|
||||
if (done) {
|
||||
if (WIFEXITED(status)) {
|
||||
if (DEBUG(JOB)) {
|
||||
(void)fprintf(stdout, "Process %d [%s] exited.\n",
|
||||
job->pid, job->node->name);
|
||||
(void)fflush(stdout);
|
||||
}
|
||||
if (WEXITSTATUS(*status) != 0) {
|
||||
if (WEXITSTATUS(status) != 0) {
|
||||
if (usePipes && job->node != lastNode) {
|
||||
MESSAGE(stdout, job->node);
|
||||
lastNode = job->node;
|
||||
}
|
||||
(void)printf("*** [%s] Error code %d%s\n",
|
||||
job->node->name,
|
||||
WEXITSTATUS(*status),
|
||||
WEXITSTATUS(status),
|
||||
(job->flags & JOB_IGNERR) ? "(ignored)" : "");
|
||||
|
||||
if (job->flags & JOB_IGNERR) {
|
||||
*status = 0;
|
||||
}
|
||||
if (job->flags & JOB_IGNERR)
|
||||
status = 0;
|
||||
} else if (DEBUG(JOB)) {
|
||||
if (usePipes && job->node != lastNode) {
|
||||
MESSAGE(stdout, job->node);
|
||||
@ -986,71 +1003,14 @@ JobFinish(Job *job, int *status)
|
||||
(void)printf("*** [%s] Completed successfully\n",
|
||||
job->node->name);
|
||||
}
|
||||
} else if (WIFSTOPPED(*status) && WSTOPSIG(*status) != SIGCONT) {
|
||||
if (DEBUG(JOB)) {
|
||||
(void)fprintf(stdout, "Process %d (%s) stopped.\n",
|
||||
job->pid, job->node->name);
|
||||
(void)fflush(stdout);
|
||||
}
|
||||
if (usePipes && job->node != lastNode) {
|
||||
MESSAGE(stdout, job->node);
|
||||
lastNode = job->node;
|
||||
}
|
||||
switch (WSTOPSIG(*status)) {
|
||||
case SIGTSTP:
|
||||
(void)printf("*** [%s] Suspended\n",
|
||||
job->node->name);
|
||||
break;
|
||||
case SIGSTOP:
|
||||
(void)printf("*** [%s] Stopped\n",
|
||||
job->node->name);
|
||||
break;
|
||||
default:
|
||||
(void)printf("*** [%s] Stopped -- signal %d\n",
|
||||
job->node->name, WSTOPSIG(*status));
|
||||
}
|
||||
job->flags |= JOB_RESUME;
|
||||
job->job_status = JOB_ST_STOPPED;
|
||||
(void)fflush(stdout);
|
||||
return;
|
||||
} else if (WIFSTOPPED(*status) && WSTOPSIG(*status) == SIGCONT) {
|
||||
/*
|
||||
* If the beastie has continued, shift the Job from the stopped
|
||||
* list to the running one.
|
||||
*/
|
||||
if (job->flags & JOB_RESUME) {
|
||||
if (usePipes && job->node != lastNode) {
|
||||
MESSAGE(stdout, job->node);
|
||||
lastNode = job->node;
|
||||
}
|
||||
(void)printf("*** [%s] Continued\n", job->node->name);
|
||||
}
|
||||
if (!(job->flags & JOB_CONTINUING)) {
|
||||
if (DEBUG(JOB)) {
|
||||
(void)fprintf(stdout,
|
||||
"Warning: process %d [%s] was not continuing.\n",
|
||||
job->pid, job->node->name);
|
||||
(void)fflush(stdout);
|
||||
}
|
||||
}
|
||||
job->flags &= ~JOB_CONTINUING;
|
||||
job->job_status = JOB_ST_RUNNING;
|
||||
if (DEBUG(JOB)) {
|
||||
(void)fprintf(stdout, "Process %d is continuing.\n",
|
||||
job->pid);
|
||||
(void)fflush(stdout);
|
||||
}
|
||||
(void)fflush(stdout);
|
||||
return;
|
||||
} else {
|
||||
if (usePipes && job->node != lastNode) {
|
||||
MESSAGE(stdout, job->node);
|
||||
lastNode = job->node;
|
||||
}
|
||||
(void)printf("*** [%s] Signal %d\n",
|
||||
job->node->name, WTERMSIG(*status));
|
||||
job->node->name, WTERMSIG(status));
|
||||
}
|
||||
|
||||
(void)fflush(stdout);
|
||||
}
|
||||
|
||||
@ -1058,13 +1018,13 @@ JobFinish(Job *job, int *status)
|
||||
|
||||
Trace_Log(JOBEND, job);
|
||||
if (!(job->flags & JOB_SPECIAL)) {
|
||||
if ((*status != 0) ||
|
||||
if ((status != 0) ||
|
||||
(aborting == ABORT_ERROR) ||
|
||||
(aborting == ABORT_INTERRUPT))
|
||||
return_job_token = TRUE;
|
||||
}
|
||||
|
||||
if ((aborting != ABORT_ERROR) && (aborting != ABORT_INTERRUPT) && (*status == 0)) {
|
||||
if ((aborting != ABORT_ERROR) && (aborting != ABORT_INTERRUPT) && (status == 0)) {
|
||||
/*
|
||||
* As long as we aren't aborting and the job didn't return a non-zero
|
||||
* status that we shouldn't ignore, we call Make_Update to update
|
||||
@ -1080,10 +1040,10 @@ JobFinish(Job *job, int *status)
|
||||
if (!(job->flags & JOB_SPECIAL))
|
||||
return_job_token = TRUE;
|
||||
Make_Update(job->node);
|
||||
job->job_status = JOB_ST_FREE;
|
||||
} else if (*status != 0) {
|
||||
job->job_state = JOB_ST_FREE;
|
||||
} else if (status != 0) {
|
||||
errors += 1;
|
||||
job->job_status = JOB_ST_FREE;
|
||||
job->job_state = JOB_ST_FREE;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1256,8 +1216,7 @@ Job_CheckCommands(GNode *gn, void (*abortProc)(const char *, ...))
|
||||
/*-
|
||||
*-----------------------------------------------------------------------
|
||||
* JobExec --
|
||||
* Execute the shell for the given job. Called from JobStart and
|
||||
* JobRestart.
|
||||
* Execute the shell for the given job. Called from JobStart
|
||||
*
|
||||
* Input:
|
||||
* job Job to execute
|
||||
@ -1305,9 +1264,15 @@ JobExec(Job *job, char **argv)
|
||||
/* No interruptions until this job is on the `jobs' list */
|
||||
JobSigLock(&mask);
|
||||
|
||||
if ((cpid = vfork()) == -1) {
|
||||
/* Pre-emptively mark job running, pid still zero though */
|
||||
job->job_state = JOB_ST_RUNNING;
|
||||
|
||||
cpid = vfork();
|
||||
if (cpid == -1)
|
||||
Punt("Cannot vfork: %s", strerror(errno));
|
||||
} else if (cpid == 0) {
|
||||
|
||||
if (cpid == 0) {
|
||||
/* Child */
|
||||
|
||||
/*
|
||||
* Reset all signal handlers; this is necessary because we also
|
||||
@ -1387,25 +1352,26 @@ JobExec(Job *job, char **argv)
|
||||
(void)execv(shellPath, argv);
|
||||
execError("exec", shellPath);
|
||||
_exit(1);
|
||||
} else {
|
||||
job->pid = cpid;
|
||||
}
|
||||
|
||||
Trace_Log(JOBSTART, job);
|
||||
/* Parent, continuing after the child exec */
|
||||
job->pid = cpid;
|
||||
|
||||
if (usePipes) {
|
||||
/*
|
||||
* Set the current position in the buffer to the beginning
|
||||
* and mark another stream to watch in the outputs mask
|
||||
*/
|
||||
job->curPos = 0;
|
||||
Trace_Log(JOBSTART, job);
|
||||
|
||||
watchfd(job);
|
||||
}
|
||||
if (usePipes) {
|
||||
/*
|
||||
* Set the current position in the buffer to the beginning
|
||||
* and mark another stream to watch in the outputs mask
|
||||
*/
|
||||
job->curPos = 0;
|
||||
|
||||
if (job->cmdFILE != NULL && job->cmdFILE != stdout) {
|
||||
(void)fclose(job->cmdFILE);
|
||||
job->cmdFILE = NULL;
|
||||
}
|
||||
watchfd(job);
|
||||
}
|
||||
|
||||
if (job->cmdFILE != NULL && job->cmdFILE != stdout) {
|
||||
(void)fclose(job->cmdFILE);
|
||||
job->cmdFILE = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1416,7 +1382,6 @@ JobExec(Job *job, char **argv)
|
||||
job->node->name, job->pid);
|
||||
job_table_dump("job started");
|
||||
}
|
||||
job->job_status = JOB_ST_RUNNING;
|
||||
JobSigUnlock(&mask);
|
||||
}
|
||||
|
||||
@ -1473,59 +1438,6 @@ JobMakeArgv(Job *job, char **argv)
|
||||
argv[argc] = NULL;
|
||||
}
|
||||
|
||||
/*-
|
||||
*-----------------------------------------------------------------------
|
||||
* JobRestart --
|
||||
* Restart a job that stopped for some reason.
|
||||
*
|
||||
* Input:
|
||||
* job Job to restart
|
||||
*
|
||||
* Results:
|
||||
* 1 if max number of running jobs has been reached, 0 otherwise.
|
||||
*
|
||||
*-----------------------------------------------------------------------
|
||||
*/
|
||||
static int
|
||||
JobRestart(Job *job)
|
||||
{
|
||||
Boolean error;
|
||||
int status;
|
||||
|
||||
/*
|
||||
* The job has stopped and needs to be restarted. Why it stopped,
|
||||
* we don't know...
|
||||
*/
|
||||
if (DEBUG(JOB)) {
|
||||
(void)fprintf(stdout, "Resuming %s...", job->node->name);
|
||||
(void)fflush(stdout);
|
||||
}
|
||||
|
||||
error = (KILL(job->pid, SIGCONT) != 0);
|
||||
|
||||
if (!error) {
|
||||
/*
|
||||
* Make sure the user knows we've continued the beast and
|
||||
* actually put the thing in the job table.
|
||||
*/
|
||||
job->flags |= JOB_CONTINUING;
|
||||
status = W_STOPCODE(SIGCONT);
|
||||
JobFinish(job, &status);
|
||||
|
||||
job->flags &= ~(JOB_RESUME|JOB_CONTINUING);
|
||||
if (DEBUG(JOB)) {
|
||||
(void)fprintf(stdout, "done\n");
|
||||
(void)fflush(stdout);
|
||||
}
|
||||
} else {
|
||||
Error("couldn't resume %s: %s",
|
||||
job->node->name, strerror(errno));
|
||||
status = W_EXITCODE(1, 0);
|
||||
JobFinish(job, &status);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*-
|
||||
*-----------------------------------------------------------------------
|
||||
* JobStart --
|
||||
@ -1561,13 +1473,13 @@ JobStart(GNode *gn, int flags)
|
||||
int tfd; /* File descriptor to the temp file */
|
||||
|
||||
for (job = job_table; job < job_table_end; job++) {
|
||||
if (job->job_status == JOB_ST_FREE)
|
||||
if (job->job_state == JOB_ST_FREE)
|
||||
break;
|
||||
}
|
||||
if (job >= job_table_end)
|
||||
Punt("JobStart out of memory");
|
||||
memset(job, 0, sizeof *job);
|
||||
job->job_status = JOB_ST_SETUP;
|
||||
job->job_state = JOB_ST_SETUP;
|
||||
if (gn->type & OP_SPECIAL)
|
||||
flags |= JOB_SPECIAL;
|
||||
|
||||
@ -1717,10 +1629,10 @@ JobStart(GNode *gn, int flags)
|
||||
job->node->made = MADE;
|
||||
Make_Update(job->node);
|
||||
}
|
||||
job->job_status = JOB_ST_FREE;
|
||||
job->job_state = JOB_ST_FREE;
|
||||
return(JOB_FINISHED);
|
||||
} else {
|
||||
job->job_status = JOB_ST_FREE;
|
||||
job->job_state = JOB_ST_FREE;
|
||||
return(JOB_ERROR);
|
||||
}
|
||||
} else {
|
||||
@ -2025,7 +1937,7 @@ JobRun(GNode *targ)
|
||||
JobStart(targ, JOB_SPECIAL);
|
||||
while (jobTokensRunning) {
|
||||
Job_CatchOutput();
|
||||
Job_CatchChildren(!usePipes);
|
||||
Job_CatchChildren(usePipes ? 0 : CATCH_BLOCK);
|
||||
}
|
||||
#else
|
||||
Compat_Make(targ, targ);
|
||||
@ -2058,8 +1970,9 @@ JobRun(GNode *targ)
|
||||
*
|
||||
*-----------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
void
|
||||
Job_CatchChildren(Boolean block)
|
||||
Job_CatchChildren(unsigned int flags)
|
||||
{
|
||||
int pid; /* pid of dead child */
|
||||
Job *job; /* job descriptor for dead child */
|
||||
@ -2068,37 +1981,52 @@ Job_CatchChildren(Boolean block)
|
||||
/*
|
||||
* Don't even bother if we know there's no one around.
|
||||
*/
|
||||
if (jobTokensRunning == 0) {
|
||||
if (jobTokensRunning == 0)
|
||||
return;
|
||||
}
|
||||
|
||||
while ((pid = waitpid((pid_t) -1, &status,
|
||||
(block?0:WNOHANG)|WUNTRACED)) > 0)
|
||||
{
|
||||
flags & CATCH_BLOCK ? WUNTRACED : WNOHANG | WUNTRACED)) > 0) {
|
||||
if (DEBUG(JOB)) {
|
||||
(void)fprintf(stdout, "Process %d exited or stopped %x.\n", pid,
|
||||
(void)fprintf(stdout, "Process %d exited/stopped status %x.\n", pid,
|
||||
status);
|
||||
(void)fflush(stdout);
|
||||
}
|
||||
|
||||
job = JobCmpPid(pid, JOB_ST_RUNNING);
|
||||
job = JobFindPid(pid, JOB_ST_RUNNING);
|
||||
if (job == NULL) {
|
||||
if (WIFSTOPPED(status) && (WSTOPSIG(status) == SIGCONT)) {
|
||||
job = JobCmpPid(pid, JOB_ST_STOPPED);
|
||||
if (job == NULL) {
|
||||
Error("Resumed child (%d) not in table", pid);
|
||||
continue;
|
||||
}
|
||||
job->job_status = JOB_ST_FINISHED;
|
||||
} else {
|
||||
Error("Child (%d) status %x not in table?", pid, status);
|
||||
continue;
|
||||
Error("Child (%d) status %x not in table?", pid, status);
|
||||
continue;
|
||||
}
|
||||
if (WIFSTOPPED(status)) {
|
||||
if (DEBUG(JOB)) {
|
||||
(void)fprintf(stdout, "Process %d (%s) stopped.\n",
|
||||
job->pid, job->node->name);
|
||||
(void)fflush(stdout);
|
||||
}
|
||||
} else {
|
||||
job->job_status = JOB_ST_FINISHED;
|
||||
switch (WSTOPSIG(status)) {
|
||||
case SIGTSTP:
|
||||
(void)printf("*** [%s] Suspended\n", job->node->name);
|
||||
break;
|
||||
case SIGSTOP:
|
||||
(void)printf("*** [%s] Stopped\n", job->node->name);
|
||||
break;
|
||||
default:
|
||||
(void)printf("*** [%s] Stopped -- signal %d\n",
|
||||
job->node->name, WSTOPSIG(status));
|
||||
}
|
||||
job->job_suspended = 1;
|
||||
(void)fflush(stdout);
|
||||
continue;
|
||||
}
|
||||
|
||||
JobFinish(job, &status);
|
||||
job->job_state = JOB_ST_FINISHED;
|
||||
job->exit_status = status;
|
||||
|
||||
if (flags & CATCH_DEFER)
|
||||
/* We don't want to process the termination now... */
|
||||
write(exit_pipe[1], DO_JOB_RESUME, 1);
|
||||
else
|
||||
JobFinish(job, status);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2133,17 +2061,18 @@ Job_CatchOutput(void)
|
||||
sigset_t mask;
|
||||
|
||||
if (readyfd(&childExitJob)) {
|
||||
char token;
|
||||
(void)read(childExitJob.inPipe, &token, 1);
|
||||
char token = 0;
|
||||
nready -= 1;
|
||||
(void)read(childExitJob.inPipe, &token, 1);
|
||||
if (token == DO_JOB_RESUME[0])
|
||||
/* Complete relay requested from our SIGCONT handler */
|
||||
JobRestartJobs();
|
||||
}
|
||||
|
||||
JobSigLock(&mask);
|
||||
|
||||
for (job = job_table; nready && job < job_table_end; job++) {
|
||||
if (job->job_status != JOB_ST_RUNNING)
|
||||
if (job->job_state != JOB_ST_RUNNING)
|
||||
continue;
|
||||
if (readyfd(job)) {
|
||||
JobDoOutput(job, FALSE);
|
||||
@ -2274,10 +2203,10 @@ Job_Init(void)
|
||||
* Catch the four signals that POSIX specifies if they aren't ignored.
|
||||
* JobPassSig will take care of calling JobInterrupt if appropriate.
|
||||
*/
|
||||
ADDSIG(SIGINT, JobPassSig)
|
||||
ADDSIG(SIGHUP, JobPassSig)
|
||||
ADDSIG(SIGTERM, JobPassSig)
|
||||
ADDSIG(SIGQUIT, JobPassSig)
|
||||
ADDSIG(SIGINT, JobPassSig_int)
|
||||
ADDSIG(SIGHUP, JobPassSig_term)
|
||||
ADDSIG(SIGTERM, JobPassSig_term)
|
||||
ADDSIG(SIGQUIT, JobPassSig_term)
|
||||
|
||||
/*
|
||||
* There are additional signals that need to be caught and passed if
|
||||
@ -2286,10 +2215,10 @@ Job_Init(void)
|
||||
* signals from the terminal driver as we own the terminal)
|
||||
*/
|
||||
#if defined(USE_PGRP)
|
||||
ADDSIG(SIGTSTP, JobPassSig)
|
||||
ADDSIG(SIGTTOU, JobPassSig)
|
||||
ADDSIG(SIGTTIN, JobPassSig)
|
||||
ADDSIG(SIGWINCH, JobPassSig)
|
||||
ADDSIG(SIGTSTP, JobPassSig_suspend)
|
||||
ADDSIG(SIGTTOU, JobPassSig_suspend)
|
||||
ADDSIG(SIGTTIN, JobPassSig_suspend)
|
||||
ADDSIG(SIGWINCH, JobCondPassSig)
|
||||
ADDSIG(SIGCONT, JobContinueSig)
|
||||
#endif
|
||||
#undef ADDSIG
|
||||
@ -2574,7 +2503,7 @@ JobInterrupt(int runINTERRUPT, int signo)
|
||||
JobSigLock(&mask);
|
||||
|
||||
for (job = job_table; job < job_table_end; job++) {
|
||||
if (job->job_status != JOB_ST_RUNNING)
|
||||
if (job->job_state != JOB_ST_RUNNING)
|
||||
continue;
|
||||
|
||||
gn = job->node;
|
||||
@ -2592,7 +2521,7 @@ JobInterrupt(int runINTERRUPT, int signo)
|
||||
signo, job->pid);
|
||||
(void)fflush(stdout);
|
||||
}
|
||||
KILL(job->pid, signo);
|
||||
KILLPG(job->pid, signo);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2676,7 +2605,7 @@ Job_Wait(void)
|
||||
aborting = ABORT_WAIT;
|
||||
while (jobTokensRunning != 0) {
|
||||
Job_CatchOutput();
|
||||
Job_CatchChildren(!usePipes);
|
||||
Job_CatchChildren(usePipes ? 0 : CATCH_BLOCK);
|
||||
}
|
||||
aborting = 0;
|
||||
}
|
||||
@ -2708,14 +2637,14 @@ Job_AbortAll(void)
|
||||
|
||||
JobSigLock(&mask);
|
||||
for (job = job_table; job < job_table_end; job++) {
|
||||
if (job->job_status != JOB_ST_RUNNING)
|
||||
if (job->job_state != JOB_ST_RUNNING)
|
||||
continue;
|
||||
/*
|
||||
* kill the child process with increasingly drastic signals to make
|
||||
* darn sure it's dead.
|
||||
*/
|
||||
KILL(job->pid, SIGINT);
|
||||
KILL(job->pid, SIGKILL);
|
||||
KILLPG(job->pid, SIGINT);
|
||||
KILLPG(job->pid, SIGKILL);
|
||||
}
|
||||
JobSigUnlock(&mask);
|
||||
}
|
||||
@ -2739,7 +2668,7 @@ Job_AbortAll(void)
|
||||
* None.
|
||||
*
|
||||
* Side Effects:
|
||||
* Resumes(and possibly migrates) jobs.
|
||||
* Resumes jobs.
|
||||
*
|
||||
*-----------------------------------------------------------------------
|
||||
*/
|
||||
@ -2751,14 +2680,23 @@ JobRestartJobs(void)
|
||||
|
||||
JobSigLock(&mask);
|
||||
for (job = job_table; job < job_table_end; job++) {
|
||||
if (job->job_status != JOB_ST_STOPPED)
|
||||
continue;
|
||||
if (DEBUG(JOB)) {
|
||||
(void)fprintf(stdout, "Restarting a stopped job.\n");
|
||||
if (job->job_state == JOB_ST_RUNNING && job->job_suspended) {
|
||||
job->job_suspended = 0;
|
||||
if (DEBUG(JOB)) {
|
||||
(void)fprintf(stdout, "Restarting stopped job pid %d.\n",
|
||||
job->pid);
|
||||
(void)fflush(stdout);
|
||||
}
|
||||
(void)printf("*** [%s] Continued\n", job->node->name);
|
||||
(void)fflush(stdout);
|
||||
if (KILLPG(job->pid, SIGCONT) != 0 && DEBUG(JOB)) {
|
||||
fprintf(stdout, "Failed to send SIGCONT to %d\n", job->pid);
|
||||
(void)fflush(stdout);
|
||||
}
|
||||
}
|
||||
if (JobRestart(job) != 0)
|
||||
break;
|
||||
if (job->job_state == JOB_ST_FINISHED)
|
||||
/* Job exit deferred after calling waitpid() in a signal handler */
|
||||
JobFinish(job, job->exit_status);
|
||||
}
|
||||
JobSigUnlock(&mask);
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* $NetBSD: job.h,v 1.29 2006/09/22 19:07:09 dsl Exp $ */
|
||||
/* $NetBSD: job.h,v 1.30 2006/09/23 20:51:28 dsl Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
|
||||
@ -151,12 +151,13 @@ typedef struct Job {
|
||||
* saved when the job has been run */
|
||||
FILE *cmdFILE; /* When creating the shell script, this is
|
||||
* where the commands go */
|
||||
short job_status; /* status of the job entry */
|
||||
int exit_status; /* from wait4() in signal handler */
|
||||
char job_state; /* status of the job entry */
|
||||
#define JOB_ST_FREE 0 /* Job is available */
|
||||
#define JOB_ST_SETUP 1 /* Job is allocated but otherwise invalid */
|
||||
#define JOB_ST_RUNNING 2 /* Job is running, pid valid */
|
||||
#define JOB_ST_STOPPED 3 /* Job is stopped (ie after ^Z) */
|
||||
#define JOB_ST_RUNNING 3 /* Job is running, pid valid */
|
||||
#define JOB_ST_FINISHED 4 /* Job is done (ie after SIGCHILD) */
|
||||
char job_suspended;
|
||||
short flags; /* Flags to control treatment of job */
|
||||
#define JOB_IGNERR 0x001 /* Ignore non-zero exits */
|
||||
#define JOB_SILENT 0x002 /* no output */
|
||||
@ -164,11 +165,6 @@ typedef struct Job {
|
||||
* if we can't export it and maxLocal is 0 */
|
||||
#define JOB_IGNDOTS 0x008 /* Ignore "..." lines when processing
|
||||
* commands */
|
||||
#define JOB_RESUME 0x100 /* Job needs to be resumed b/c it stopped,
|
||||
* for some reason */
|
||||
#define JOB_CONTINUING 0x200 /* We are in the process of resuming this job.
|
||||
* Used to avoid infinite recursion between
|
||||
* JobFinish and JobRestart */
|
||||
#define JOB_TRACED 0x400 /* we've sent 'set -x' */
|
||||
|
||||
union {
|
||||
@ -280,7 +276,9 @@ void Shell_Init(void);
|
||||
const char *Shell_GetNewline(void);
|
||||
void Job_Touch(GNode *, Boolean);
|
||||
Boolean Job_CheckCommands(GNode *, void (*abortProc )(const char *, ...));
|
||||
void Job_CatchChildren(Boolean);
|
||||
#define CATCH_BLOCK 1
|
||||
#define CATCH_DEFER 2
|
||||
void Job_CatchChildren(unsigned int);
|
||||
void Job_CatchOutput(void);
|
||||
void Job_Make(GNode *);
|
||||
void Job_Init(void);
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* $NetBSD: make.c,v 1.62 2006/03/31 21:05:34 dsl Exp $ */
|
||||
/* $NetBSD: make.c,v 1.63 2006/09/23 20:51:28 dsl Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 1988, 1989, 1990, 1993
|
||||
@ -69,14 +69,14 @@
|
||||
*/
|
||||
|
||||
#ifndef MAKE_NATIVE
|
||||
static char rcsid[] = "$NetBSD: make.c,v 1.62 2006/03/31 21:05:34 dsl Exp $";
|
||||
static char rcsid[] = "$NetBSD: make.c,v 1.63 2006/09/23 20:51:28 dsl Exp $";
|
||||
#else
|
||||
#include <sys/cdefs.h>
|
||||
#ifndef lint
|
||||
#if 0
|
||||
static char sccsid[] = "@(#)make.c 8.1 (Berkeley) 6/6/93";
|
||||
#else
|
||||
__RCSID("$NetBSD: make.c,v 1.62 2006/03/31 21:05:34 dsl Exp $");
|
||||
__RCSID("$NetBSD: make.c,v 1.63 2006/09/23 20:51:28 dsl Exp $");
|
||||
#endif
|
||||
#endif /* not lint */
|
||||
#endif
|
||||
@ -1230,7 +1230,7 @@ Make_Run(Lst targs)
|
||||
*/
|
||||
while (!Lst_IsEmpty(toBeMade) || jobTokensRunning > 0) {
|
||||
Job_CatchOutput();
|
||||
Job_CatchChildren(!usePipes);
|
||||
Job_CatchChildren(usePipes ? 0 : CATCH_BLOCK);
|
||||
(void)MakeStartJobs();
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user