version 1.125, 2012/09/21 08:18:40 |
version 1.126, 2012/10/02 10:29:30 |
|
|
* Job_Begin execute commands attached to the .BEGIN target |
* Job_Begin execute commands attached to the .BEGIN target |
* if any. |
* if any. |
* |
* |
* Job_End Should cleanup any memory used. |
|
* |
|
* can_start_job Return true if we can start job |
* can_start_job Return true if we can start job |
* |
* |
* Job_Empty Return true if the job table is completely |
* Job_Empty Return true if the job table is completely |
|
|
static Job *heldJobs; /* Jobs not running yet because of expensive */ |
static Job *heldJobs; /* Jobs not running yet because of expensive */ |
static pid_t mypid; |
static pid_t mypid; |
|
|
static volatile sig_atomic_t got_fatal; |
static volatile sig_atomic_t got_fatal, got_other; |
|
|
static volatile sig_atomic_t got_SIGINT, got_SIGHUP, got_SIGQUIT, got_SIGTERM, |
static volatile sig_atomic_t got_SIGINT, got_SIGHUP, got_SIGQUIT, got_SIGTERM, |
got_SIGINFO; |
got_SIGINFO, got_SIGTSTP, got_SIGTTOU, got_SIGTTIN, got_SIGCONT, |
|
got_SIGWINCH; |
|
|
static sigset_t sigset, emptyset; |
static sigset_t sigset, emptyset; |
|
|
static void handle_signal(int); |
static void handle_fatal_signal(int); |
|
static void pass_job_control_signal(int); |
static void handle_siginfo(void); |
static void handle_siginfo(void); |
static void postprocess_job(Job *, bool); |
static void postprocess_job(Job *, bool); |
static Job *prepare_job(GNode *); |
static Job *prepare_job(GNode *); |
|
|
static void setup_signal(int); |
static void setup_signal(int); |
static void notice_signal(int); |
static void notice_signal(int); |
static void setup_all_signals(void); |
static void setup_all_signals(void); |
static void really_kill(int, pid_t); |
static void setup_job_control_interrupts(void); |
|
|
|
static void |
|
print_error(Job *j, Job *k) |
|
{ |
|
const char *type; |
|
|
|
if (j->exit_type == JOB_EXIT_BAD) |
|
type = "Exit status"; |
|
else if (j->exit_type == JOB_SIGNALED) |
|
type = "Received signal"; |
|
else |
|
type = "Should not happen"; |
|
fprintf(stderr, " %s %d (", type, j->code); |
|
fprintf(stderr, "line %lu", |
|
j->location->lineno); |
|
if (j == k) |
|
fprintf(stderr, " of %s,", j->location->fname); |
|
else |
|
fputs(",", stderr); |
|
if ((j->flags & (JOB_SILENT | JOB_IS_EXPENSIVE)) == JOB_SILENT) |
|
fprintf(stderr, "\n target %s: %s", j->node->name, j->cmd); |
|
else |
|
fprintf(stderr, " target %s", j->node->name); |
|
fputs(")\n", stderr); |
|
free(j->cmd); |
|
} |
|
|
void |
void |
print_errors(void) |
print_errors(void) |
{ |
{ |
Job *j; |
Job *j, *k, *jnext; |
const char *previous = NULL; |
|
|
|
fprintf(stderr, "\nStop in %s%c\n", Var_Value(".CURDIR"), |
fprintf(stderr, "\nStop in %s%c\n", Var_Value(".CURDIR"), |
errorJobs ? ':' : '.'); |
errorJobs ? ':' : '.'); |
|
|
for (j = errorJobs; j != NULL; j = j->next) { |
while (errorJobs != NULL) { |
const char *type; |
k = errorJobs; |
|
errorJobs = NULL; |
if (j->exit_type == JOB_EXIT_BAD) |
for (j = k; j != NULL; j = jnext) { |
type = "Exit status"; |
jnext = j->next; |
else if (j->exit_type == JOB_SIGNALED) |
if (j->location->fname == k->location->fname) |
type = "Received signal"; |
print_error(j, k); |
else |
else { |
type = "Should not happen"; |
j->next = errorJobs; |
fprintf(stderr, " %s %d (", type, j->code); |
errorJobs = j; |
fprintf(stderr, "line %lu", |
} |
j->location->lineno); |
} |
if (j->location->fname == previous) |
|
fputs(",", stderr); |
|
else |
|
fprintf(stderr, " of %s,", j->location->fname); |
|
previous = j->location->fname; |
|
if ((j->flags & (JOB_SILENT | JOB_IS_EXPENSIVE)) == JOB_SILENT) |
|
fprintf(stderr, "\n target %s: %s", j->node->name, j->cmd); |
|
else |
|
fprintf(stderr, " target %s", j->node->name); |
|
fputs(")\n", stderr); |
|
free(j->cmd); |
|
} |
} |
} |
} |
|
|
|
|
static void |
static void |
notice_signal(int sig) |
notice_signal(int sig) |
{ |
{ |
|
|
switch(sig) { |
switch(sig) { |
case SIGINT: |
case SIGINT: |
got_SIGINT++; |
got_SIGINT++; |
|
|
break; |
break; |
case SIGCHLD: |
case SIGCHLD: |
break; |
break; |
|
case SIGTSTP: |
|
got_SIGTSTP++; |
|
got_other = 1; |
|
break; |
|
case SIGTTOU: |
|
got_SIGTTOU++; |
|
got_other = 1; |
|
break; |
|
case SIGTTIN: |
|
got_SIGTTIN++; |
|
got_other = 1; |
|
break; |
|
case SIGCONT: |
|
got_SIGCONT++; |
|
got_other = 1; |
|
break; |
|
case SIGWINCH: |
|
got_SIGWINCH++; |
|
got_other = 1; |
|
break; |
} |
} |
} |
} |
|
|
|
static void |
|
setup_job_control_interrupts(void) |
|
{ |
|
setup_signal(SIGTSTP); |
|
setup_signal(SIGTTOU); |
|
setup_signal(SIGTTIN); |
|
} |
|
|
void |
void |
setup_all_signals(void) |
setup_all_signals(void) |
{ |
{ |
|
|
/* Have to see SIGCHLD */ |
/* Have to see SIGCHLD */ |
setup_signal(SIGCHLD); |
setup_signal(SIGCHLD); |
got_fatal = 0; |
got_fatal = 0; |
|
setup_job_control_interrupts(); |
|
setup_signal(SIGWINCH); |
|
setup_signal(SIGCONT); |
|
got_other = 0; |
} |
} |
|
|
static void |
static void |
handle_siginfo(void) |
handle_siginfo(void) |
{ |
{ |
|
static BUFFER buf; |
|
static size_t length = 0; |
|
|
Job *job; |
Job *job; |
BUFFER buf; |
|
bool first = true; |
bool first = true; |
|
|
got_SIGINFO = 0; |
got_SIGINFO = 0; |
/* we have to store the info in a buffer, because status from all |
/* we have to store the info in a buffer, because status from all |
* makes running would get intermixed otherwise |
* makes running would get intermixed otherwise |
*/ |
*/ |
Buf_Init(&buf, 0); |
|
|
|
Buf_printf(&buf, "%s in %s: ", Var_Value("MAKE"), Var_Value(".CURDIR")); |
if (length == 0) { |
|
Buf_Init(&buf, 0); |
|
Buf_printf(&buf, "%s in %s: ", Var_Value("MAKE"), Var_Value(".CURDIR")); |
|
length = Buf_Size(&buf); |
|
} else |
|
Buf_Truncate(&buf, length); |
|
|
for (job = runningJobs; job != NULL ; job = job->next) { |
for (job = runningJobs; job != NULL ; job = job->next) { |
if (!first) |
if (!first) |
|
|
Buf_puts(&buf, first ? "nothing running\n" : "\n"); |
Buf_puts(&buf, first ? "nothing running\n" : "\n"); |
|
|
fputs(Buf_Retrieve(&buf), stderr); |
fputs(Buf_Retrieve(&buf), stderr); |
Buf_Destroy(&buf); |
|
} |
} |
|
|
void |
void |
|
|
{ |
{ |
if (got_SIGINFO) |
if (got_SIGINFO) |
handle_siginfo(); |
handle_siginfo(); |
|
while (got_other) { |
|
got_other = 0; |
|
if (got_SIGWINCH) { |
|
got_SIGWINCH=0; |
|
pass_job_control_signal(SIGWINCH); |
|
} |
|
if (got_SIGTTIN) { |
|
got_SIGTTIN=0; |
|
pass_job_control_signal(SIGTTIN); |
|
} |
|
if (got_SIGTTOU) { |
|
got_SIGTTOU=0; |
|
pass_job_control_signal(SIGTTOU); |
|
} |
|
if (got_SIGTSTP) { |
|
got_SIGTSTP=0; |
|
pass_job_control_signal(SIGTSTP); |
|
} |
|
if (got_SIGCONT) { |
|
got_SIGCONT=0; |
|
pass_job_control_signal(SIGCONT); |
|
} |
|
} |
while (got_fatal) { |
while (got_fatal) { |
got_fatal = 0; |
got_fatal = 0; |
aborting = ABORT_INTERRUPT; |
aborting = ABORT_INTERRUPT; |
|
|
if (got_SIGINT) { |
if (got_SIGINT) { |
got_SIGINT=0; |
got_SIGINT=0; |
handle_signal(SIGINT); |
handle_fatal_signal(SIGINT); |
} |
} |
if (got_SIGHUP) { |
if (got_SIGHUP) { |
got_SIGHUP=0; |
got_SIGHUP=0; |
handle_signal(SIGHUP); |
handle_fatal_signal(SIGHUP); |
} |
} |
if (got_SIGQUIT) { |
if (got_SIGQUIT) { |
got_SIGQUIT=0; |
got_SIGQUIT=0; |
handle_signal(SIGQUIT); |
handle_fatal_signal(SIGQUIT); |
} |
} |
if (got_SIGTERM) { |
if (got_SIGTERM) { |
got_SIGTERM=0; |
got_SIGTERM=0; |
handle_signal(SIGTERM); |
handle_fatal_signal(SIGTERM); |
} |
} |
} |
} |
} |
} |
|
|
aborting = ABORT_ERROR; |
aborting = ABORT_ERROR; |
|
|
if (aborting == ABORT_ERROR && DEBUG(QUICKDEATH)) |
if (aborting == ABORT_ERROR && DEBUG(QUICKDEATH)) |
handle_signal(SIGINT); |
handle_fatal_signal(SIGINT); |
if (aborting == ABORT_ERROR && Job_Empty()) |
if (aborting == ABORT_ERROR && Job_Empty()) |
Finish(); |
Finish(); |
} |
} |
|
|
Job_Make(GNode *gn) |
Job_Make(GNode *gn) |
{ |
{ |
Job *job; |
Job *job; |
bool finished; |
|
|
|
job = prepare_job(gn); |
job = prepare_job(gn); |
if (!job) |
if (!job) |
|
|
} |
} |
|
|
static void |
static void |
really_kill(pid_t pid, int sig) |
pass_job_control_signal(int signo) |
{ |
{ |
killpg(pid, sig); |
Job *job; |
if (killpg(pid, sig) == - 1 && errno == ESRCH) |
|
kill(pid, sig); |
debug_job_printf("pass_job_control_signal(%d) called.\n", signo); |
|
|
|
|
|
for (job = runningJobs; job != NULL; job = job->next) { |
|
debug_job_printf("pass_job_control_signal to " |
|
"child %ld running %s.\n", (long)job->pid, |
|
job->node->name); |
|
killpg(job->pid, signo); |
|
} |
|
/* after forwarding the signal, those should interrupt us */ |
|
if (signo == SIGTSTP || signo == SIGTTOU || signo == SIGTTIN) { |
|
sigprocmask(SIG_BLOCK, &sigset, NULL); |
|
signal(signo, SIG_DFL); |
|
kill(getpid(), signo); |
|
sigprocmask(SIG_SETMASK, &emptyset, NULL); |
|
} |
|
/* SIGWINCH is irrelevant for us, SIGCONT must put back normal |
|
* handling for other job control signals */ |
|
if (signo == SIGCONT) |
|
setup_job_control_interrupts(); |
} |
} |
|
|
/*- |
/*- |
*----------------------------------------------------------------------- |
*----------------------------------------------------------------------- |
* handle_signal -- |
* handle_fatal_signal -- |
* Handle the receipt of an interrupt. |
* Handle the receipt of a fatal interrupt |
* |
* |
* Side Effects: |
* Side Effects: |
* All children are killed. Another job may be started if the |
* All children are killed. Another job may be started if there |
* .INTERRUPT target was given. |
* is an interrupt target and the signal was SIGINT. |
*----------------------------------------------------------------------- |
*----------------------------------------------------------------------- |
*/ |
*/ |
static void |
static void |
handle_signal(int signo) |
handle_fatal_signal(int signo) |
{ |
{ |
Job *job; |
Job *job; |
|
|
debug_job_printf("handle_signal(%d) called.\n", signo); |
debug_job_printf("handle_fatal_signal(%d) called.\n", signo); |
|
|
|
|
for (job = runningJobs; job != NULL; job = job->next) { |
for (job = runningJobs; job != NULL; job = job->next) { |
|
|
if (!noExecute && eunlink(file) != -1) |
if (!noExecute && eunlink(file) != -1) |
Error("*** %s removed", file); |
Error("*** %s removed", file); |
} |
} |
debug_job_printf("handle_signal passing signal to " |
debug_job_printf("handle_fatal_signal: passing to " |
"child %ld running %s.\n", (long)job->pid, |
"child %ld running %s.\n", (long)job->pid, |
job->node->name); |
job->node->name); |
really_kill(job->pid, signo); |
killpg(job->pid, signo); |
} |
} |
|
|
if (signo == SIGINT && !touchFlag) { |
if (signo == SIGINT && !touchFlag) { |
|
|
/* die by that signal */ |
/* die by that signal */ |
sigprocmask(SIG_BLOCK, &sigset, NULL); |
sigprocmask(SIG_BLOCK, &sigset, NULL); |
signal(signo, SIG_DFL); |
signal(signo, SIG_DFL); |
really_kill(getpid(), signo); |
kill(getpid(), signo); |
sigprocmask(SIG_SETMASK, &emptyset, NULL); |
sigprocmask(SIG_SETMASK, &emptyset, NULL); |
/*NOTREACHED*/ |
/*NOTREACHED*/ |
} |
} |
|
|
} |
} |
} |
} |
|
|
#ifdef CLEANUP |
|
void |
|
Job_End(void) |
|
{ |
|
} |
|
#endif |
|
|
|
/*- |
/*- |
*----------------------------------------------------------------------- |
*----------------------------------------------------------------------- |
* Job_Wait -- |
* Job_Wait -- |
|
|
aborting = ABORT_ERROR; |
aborting = ABORT_ERROR; |
|
|
for (job = runningJobs; job != NULL; job = job->next) { |
for (job = runningJobs; job != NULL; job = job->next) { |
really_kill(job->pid, SIGINT); |
killpg(job->pid, SIGINT); |
really_kill(job->pid, SIGKILL); |
killpg(job->pid, SIGKILL); |
} |
} |
|
|
/* |
/* |