Annotation of src/usr.bin/make/job.c, Revision 1.126
1.126 ! espie 1: /* $OpenBSD: job.c,v 1.125 2012/09/21 08:18:40 espie Exp $ */
1.6 millert 2: /* $NetBSD: job.c,v 1.16 1996/11/06 17:59:08 christos Exp $ */
1.1 deraadt 3:
4: /*
1.124 espie 5: * Copyright (c) 2012 Marc Espie.
6: *
7: * Extensive code modifications for the OpenBSD project.
8: *
9: * Redistribution and use in source and binary forms, with or without
10: * modification, are permitted provided that the following conditions
11: * are met:
12: * 1. Redistributions of source code must retain the above copyright
13: * notice, this list of conditions and the following disclaimer.
14: * 2. Redistributions in binary form must reproduce the above copyright
15: * notice, this list of conditions and the following disclaimer in the
16: * documentation and/or other materials provided with the distribution.
17: *
18: * THIS SOFTWARE IS PROVIDED BY THE OPENBSD PROJECT AND CONTRIBUTORS
19: * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20: * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21: * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OPENBSD
22: * PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25: * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26: * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28: * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29: */
30: /*
1.1 deraadt 31: * Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
32: * Copyright (c) 1988, 1989 by Adam de Boor
33: * Copyright (c) 1989 by Berkeley Softworks
34: * All rights reserved.
35: *
36: * This code is derived from software contributed to Berkeley by
37: * Adam de Boor.
38: *
39: * Redistribution and use in source and binary forms, with or without
40: * modification, are permitted provided that the following conditions
41: * are met:
42: * 1. Redistributions of source code must retain the above copyright
43: * notice, this list of conditions and the following disclaimer.
44: * 2. Redistributions in binary form must reproduce the above copyright
45: * notice, this list of conditions and the following disclaimer in the
46: * documentation and/or other materials provided with the distribution.
1.55 millert 47: * 3. Neither the name of the University nor the names of its contributors
1.1 deraadt 48: * may be used to endorse or promote products derived from this software
49: * without specific prior written permission.
50: *
51: * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
52: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
53: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
54: * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
55: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
56: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
57: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
58: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
59: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
60: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
61: * SUCH DAMAGE.
62: */
63:
64: /*-
65: * job.c --
66: * handle the creation etc. of our child processes.
67: *
68: * Interface:
1.40 espie 69: * Job_Make Start the creation of the given target.
1.1 deraadt 70: *
1.124 espie 71: * Job_Init Called to initialize this module.
72: *
73: * Job_Begin execute commands attached to the .BEGIN target
74: * if any.
1.40 espie 75: *
1.117 espie 76: * can_start_job Return true if we can start job
1.40 espie 77: *
1.41 espie 78: * Job_Empty Return true if the job table is completely
1.40 espie 79: * empty.
80: *
81: * Job_Finish Perform any final processing which needs doing.
82: * This includes the execution of any commands
83: * which have been/were attached to the .END
1.124 espie 84: * target.
1.40 espie 85: *
1.117 espie 86: * Job_AbortAll Abort all current jobs. It doesn't
1.40 espie 87: * handle output or do anything for the jobs,
1.124 espie 88: * just kills them.
1.1 deraadt 89: *
1.117 espie 90: * Job_Wait Wait for all running jobs to finish.
1.1 deraadt 91: */
92:
93: #include <sys/types.h>
94: #include <sys/wait.h>
1.41 espie 95: #include <ctype.h>
96: #include <errno.h>
1.1 deraadt 97: #include <fcntl.h>
1.41 espie 98: #include <signal.h>
1.69 espie 99: #include <stdarg.h>
1.1 deraadt 100: #include <stdio.h>
1.42 espie 101: #include <stdlib.h>
1.1 deraadt 102: #include <string.h>
1.41 espie 103: #include <unistd.h>
104: #include "config.h"
105: #include "defines.h"
1.1 deraadt 106: #include "job.h"
1.63 espie 107: #include "engine.h"
1.1 deraadt 108: #include "pathnames.h"
1.41 espie 109: #include "var.h"
110: #include "targ.h"
111: #include "error.h"
1.124 espie 112: #include "extern.h"
1.41 espie 113: #include "lst.h"
114: #include "gnode.h"
115: #include "memory.h"
116: #include "make.h"
1.124 espie 117: #include "buf.h"
1.78 espie 118:
1.40 espie 119: static int aborting = 0; /* why is the make aborting? */
120: #define ABORT_ERROR 1 /* Because of an error */
121: #define ABORT_INTERRUPT 2 /* Because it was interrupted */
122: #define ABORT_WAIT 3 /* Waiting for jobs to finish */
1.1 deraadt 123:
1.40 espie 124: static int maxJobs; /* The most children we can run at once */
1.124 espie 125: static int nJobs; /* Number of jobs already allocated */
126: static bool no_new_jobs; /* Mark recursive shit so we shouldn't start
127: * something else at the same time
128: */
129: Job *runningJobs; /* Jobs currently running a process */
130: Job *errorJobs; /* Jobs in error at end */
131: static Job *heldJobs; /* Jobs not running yet because of expensive */
132: static pid_t mypid;
1.1 deraadt 133:
1.126 ! espie 134: static volatile sig_atomic_t got_fatal, got_other;
1.112 espie 135:
1.124 espie 136: static volatile sig_atomic_t got_SIGINT, got_SIGHUP, got_SIGQUIT, got_SIGTERM,
1.126 ! espie 137: got_SIGINFO, got_SIGTSTP, got_SIGTTOU, got_SIGTTIN, got_SIGCONT,
! 138: got_SIGWINCH;
1.112 espie 139:
1.124 espie 140: static sigset_t sigset, emptyset;
1.112 espie 141:
1.126 ! espie 142: static void handle_fatal_signal(int);
! 143: static void pass_job_control_signal(int);
1.124 espie 144: static void handle_siginfo(void);
145: static void postprocess_job(Job *, bool);
146: static Job *prepare_job(GNode *);
147: static void determine_job_next_step(Job *);
148: static void remove_job(Job *, bool);
149: static void may_continue_job(Job *);
150: static void continue_job(Job *);
151: static Job *reap_finished_job(pid_t);
152: static bool reap_jobs(void);
1.112 espie 153:
1.111 espie 154: static void loop_handle_running_jobs(void);
1.124 espie 155: static bool expensive_job(Job *);
156: static bool expensive_command(const char *);
157: static void setup_signal(int);
158: static void notice_signal(int);
159: static void setup_all_signals(void);
1.126 ! espie 160: static void setup_job_control_interrupts(void);
! 161:
! 162: static void
! 163: print_error(Job *j, Job *k)
! 164: {
! 165: const char *type;
! 166:
! 167: if (j->exit_type == JOB_EXIT_BAD)
! 168: type = "Exit status";
! 169: else if (j->exit_type == JOB_SIGNALED)
! 170: type = "Received signal";
! 171: else
! 172: type = "Should not happen";
! 173: fprintf(stderr, " %s %d (", type, j->code);
! 174: fprintf(stderr, "line %lu",
! 175: j->location->lineno);
! 176: if (j == k)
! 177: fprintf(stderr, " of %s,", j->location->fname);
! 178: else
! 179: fputs(",", stderr);
! 180: if ((j->flags & (JOB_SILENT | JOB_IS_EXPENSIVE)) == JOB_SILENT)
! 181: fprintf(stderr, "\n target %s: %s", j->node->name, j->cmd);
! 182: else
! 183: fprintf(stderr, " target %s", j->node->name);
! 184: fputs(")\n", stderr);
! 185: free(j->cmd);
! 186: }
1.93 espie 187:
1.124 espie 188: void
189: print_errors(void)
1.101 espie 190: {
1.126 ! espie 191: Job *j, *k, *jnext;
1.124 espie 192:
193: fprintf(stderr, "\nStop in %s%c\n", Var_Value(".CURDIR"),
194: errorJobs ? ':' : '.');
1.1 deraadt 195:
1.126 ! espie 196: while (errorJobs != NULL) {
! 197: k = errorJobs;
! 198: errorJobs = NULL;
! 199: for (j = k; j != NULL; j = jnext) {
! 200: jnext = j->next;
! 201: if (j->location->fname == k->location->fname)
! 202: print_error(j, k);
! 203: else {
! 204: j->next = errorJobs;
! 205: errorJobs = j;
! 206: }
! 207: }
1.101 espie 208: }
1.88 espie 209: }
210:
1.57 espie 211: static void
1.124 espie 212: setup_signal(int sig)
1.57 espie 213: {
1.124 espie 214: if (signal(sig, SIG_IGN) != SIG_IGN) {
215: (void)signal(sig, notice_signal);
216: sigaddset(&sigset, sig);
1.57 espie 217: }
218: }
219:
220: static void
1.124 espie 221: notice_signal(int sig)
1.1 deraadt 222: {
1.126 ! espie 223:
1.124 espie 224: switch(sig) {
1.117 espie 225: case SIGINT:
226: got_SIGINT++;
1.124 espie 227: got_fatal = 1;
228: break;
1.117 espie 229: case SIGHUP:
230: got_SIGHUP++;
1.124 espie 231: got_fatal = 1;
232: break;
1.117 espie 233: case SIGQUIT:
234: got_SIGQUIT++;
1.124 espie 235: got_fatal = 1;
236: break;
1.117 espie 237: case SIGTERM:
238: got_SIGTERM++;
1.124 espie 239: got_fatal = 1;
1.117 espie 240: break;
1.124 espie 241: case SIGINFO:
242: got_SIGINFO++;
1.117 espie 243: break;
1.124 espie 244: case SIGCHLD:
1.117 espie 245: break;
1.126 ! espie 246: case SIGTSTP:
! 247: got_SIGTSTP++;
! 248: got_other = 1;
! 249: break;
! 250: case SIGTTOU:
! 251: got_SIGTTOU++;
! 252: got_other = 1;
! 253: break;
! 254: case SIGTTIN:
! 255: got_SIGTTIN++;
! 256: got_other = 1;
! 257: break;
! 258: case SIGCONT:
! 259: got_SIGCONT++;
! 260: got_other = 1;
! 261: break;
! 262: case SIGWINCH:
! 263: got_SIGWINCH++;
! 264: got_other = 1;
! 265: break;
1.66 espie 266: }
1.124 espie 267: }
1.117 espie 268:
1.126 ! espie 269: static void
! 270: setup_job_control_interrupts(void)
! 271: {
! 272: setup_signal(SIGTSTP);
! 273: setup_signal(SIGTTOU);
! 274: setup_signal(SIGTTIN);
! 275: }
! 276:
1.124 espie 277: void
278: setup_all_signals(void)
279: {
280: sigemptyset(&sigset);
281: sigemptyset(&emptyset);
282: /*
283: * Catch the four signals that POSIX specifies if they aren't ignored.
284: * handle_signal will take care of calling JobInterrupt if appropriate.
285: */
286: setup_signal(SIGINT);
287: setup_signal(SIGHUP);
288: setup_signal(SIGQUIT);
289: setup_signal(SIGTERM);
290: /* Display running jobs on SIGINFO */
291: setup_signal(SIGINFO);
292: /* Have to see SIGCHLD */
293: setup_signal(SIGCHLD);
294: got_fatal = 0;
1.126 ! espie 295: setup_job_control_interrupts();
! 296: setup_signal(SIGWINCH);
! 297: setup_signal(SIGCONT);
! 298: got_other = 0;
1.1 deraadt 299: }
300:
1.124 espie 301: static void
302: handle_siginfo(void)
1.1 deraadt 303: {
1.126 ! espie 304: static BUFFER buf;
! 305: static size_t length = 0;
! 306:
1.124 espie 307: Job *job;
308: bool first = true;
1.66 espie 309:
1.124 espie 310: got_SIGINFO = 0;
311: /* we have to store the info in a buffer, because status from all
312: * makes running would get intermixed otherwise
1.66 espie 313: */
1.124 espie 314:
1.126 ! espie 315: if (length == 0) {
! 316: Buf_Init(&buf, 0);
! 317: Buf_printf(&buf, "%s in %s: ", Var_Value("MAKE"), Var_Value(".CURDIR"));
! 318: length = Buf_Size(&buf);
! 319: } else
! 320: Buf_Truncate(&buf, length);
1.124 espie 321:
322: for (job = runningJobs; job != NULL ; job = job->next) {
323: if (!first)
324: Buf_puts(&buf, ", ");
325: first = false;
326: Buf_puts(&buf, job->node->name);
327: }
328: Buf_puts(&buf, first ? "nothing running\n" : "\n");
1.1 deraadt 329:
1.124 espie 330: fputs(Buf_Retrieve(&buf), stderr);
1.1 deraadt 331: }
332:
1.124 espie 333: void
334: handle_all_signals(void)
1.1 deraadt 335: {
1.124 espie 336: if (got_SIGINFO)
337: handle_siginfo();
1.126 ! espie 338: while (got_other) {
! 339: got_other = 0;
! 340: if (got_SIGWINCH) {
! 341: got_SIGWINCH=0;
! 342: pass_job_control_signal(SIGWINCH);
! 343: }
! 344: if (got_SIGTTIN) {
! 345: got_SIGTTIN=0;
! 346: pass_job_control_signal(SIGTTIN);
! 347: }
! 348: if (got_SIGTTOU) {
! 349: got_SIGTTOU=0;
! 350: pass_job_control_signal(SIGTTOU);
! 351: }
! 352: if (got_SIGTSTP) {
! 353: got_SIGTSTP=0;
! 354: pass_job_control_signal(SIGTSTP);
! 355: }
! 356: if (got_SIGCONT) {
! 357: got_SIGCONT=0;
! 358: pass_job_control_signal(SIGCONT);
! 359: }
! 360: }
1.124 espie 361: while (got_fatal) {
362: got_fatal = 0;
363: aborting = ABORT_INTERRUPT;
364:
365: if (got_SIGINT) {
366: got_SIGINT=0;
1.126 ! espie 367: handle_fatal_signal(SIGINT);
1.124 espie 368: }
369: if (got_SIGHUP) {
370: got_SIGHUP=0;
1.126 ! espie 371: handle_fatal_signal(SIGHUP);
1.124 espie 372: }
373: if (got_SIGQUIT) {
374: got_SIGQUIT=0;
1.126 ! espie 375: handle_fatal_signal(SIGQUIT);
1.124 espie 376: }
377: if (got_SIGTERM) {
378: got_SIGTERM=0;
1.126 ! espie 379: handle_fatal_signal(SIGTERM);
1.124 espie 380: }
381: }
1.1 deraadt 382: }
383:
1.124 espie 384: void
385: debug_job_printf(const char *fmt, ...)
1.84 espie 386: {
387: if (DEBUG(JOB)) {
388: va_list va;
1.124 espie 389: (void)printf("[%ld] ", (long)mypid);
1.84 espie 390: va_start(va, fmt);
1.124 espie 391: (void)vprintf(fmt, va);
1.84 espie 392: fflush(stdout);
393: va_end(va);
394: }
395: }
1.119 espie 396:
1.1 deraadt 397: /*-
398: *-----------------------------------------------------------------------
1.124 espie 399: * postprocess_job --
400: * Do final processing for the given job including updating
1.119 espie 401: * parents and starting new jobs as available/necessary.
1.1 deraadt 402: *
403: * Side Effects:
1.6 millert 404: * If we got an error and are aborting (aborting == ABORT_ERROR) and
1.1 deraadt 405: * the job list is now empty, we are done for the day.
1.101 espie 406: * If we recognized an error we set the aborting flag
1.1 deraadt 407: * to ABORT_ERROR so no more jobs will be started.
408: *-----------------------------------------------------------------------
409: */
410: /*ARGSUSED*/
1.112 espie 411:
1.1 deraadt 412: static void
1.124 espie 413: postprocess_job(Job *job, bool okay)
1.2 deraadt 414: {
1.124 espie 415: if (okay &&
1.66 espie 416: aborting != ABORT_ERROR &&
1.124 espie 417: aborting != ABORT_INTERRUPT) {
1.66 espie 418: /* As long as we aren't aborting and the job didn't return a
419: * non-zero status that we shouldn't ignore, we call
1.101 espie 420: * Make_Update to update the parents. */
1.108 espie 421: job->node->built_status = MADE;
1.66 espie 422: Make_Update(job->node);
1.124 espie 423: free(job);
1.66 espie 424: }
1.1 deraadt 425:
1.124 espie 426: if (errorJobs != NULL && !keepgoing &&
1.117 espie 427: aborting != ABORT_INTERRUPT)
1.66 espie 428: aborting = ABORT_ERROR;
1.6 millert 429:
1.124 espie 430: if (aborting == ABORT_ERROR && DEBUG(QUICKDEATH))
1.126 ! espie 431: handle_fatal_signal(SIGINT);
1.117 espie 432: if (aborting == ABORT_ERROR && Job_Empty())
1.124 espie 433: Finish();
1.1 deraadt 434: }
435:
1.124 espie 436: /* expensive jobs handling: in order to avoid forking an exponential number
437: * of jobs, make tries to figure out "recursive make" configurations.
438: * It may err on the side of caution.
439: * Basically, a command is "expensive" if it's likely to fork an extra
440: * level of make: either by looking at the command proper, or if it has
441: * some specific qualities ('+cmd' are likely to be recursive, as are
442: * .MAKE: commands). It's possible to explicitly say some targets are
443: * expensive or cheap with .EXPENSIVE or .CHEAP.
444: *
445: * While an expensive command is running, no_new_jobs
446: * is set, so jobs that would fork new processes are accumulated in the
447: * heldJobs list instead.
448: *
449: * This heuristics is also used on error exit: we display silent commands
450: * that failed, unless those ARE expensive commands: expensive commands
451: * are likely to not be failing by themselves, but to be the result of
452: * a cascade of failures in descendant makes.
453: */
454: void
455: determine_expensive_job(Job *job)
456: {
457: if (expensive_job(job)) {
458: job->flags |= JOB_IS_EXPENSIVE;
459: no_new_jobs = true;
460: } else
461: job->flags &= ~JOB_IS_EXPENSIVE;
462: if (DEBUG(EXPENSIVE))
463: fprintf(stderr, "[%ld] Target %s running %.50s: %s\n",
464: (long)mypid, job->node->name, job->cmd,
465: job->flags & JOB_IS_EXPENSIVE ? "expensive" : "cheap");
1.101 espie 466: }
467:
1.124 espie 468: static bool
469: expensive_job(Job *job)
1.1 deraadt 470: {
1.124 espie 471: if (job->node->type & OP_CHEAP)
472: return false;
473: if (job->node->type & (OP_EXPENSIVE | OP_MAKE))
474: return true;
475: return expensive_command(job->cmd);
1.96 espie 476: }
477:
1.117 espie 478: static bool
479: expensive_command(const char *s)
1.1 deraadt 480: {
1.117 espie 481: const char *p;
482: bool include = false;
483: bool expensive = false;
1.66 espie 484:
1.117 espie 485: /* okay, comments are cheap, always */
486: if (*s == '#')
487: return false;
1.124 espie 488: /* and commands we always execute are expensive */
489: if (*s == '+')
490: return true;
1.66 espie 491:
1.117 espie 492: for (p = s; *p != '\0'; p++) {
493: if (*p == ' ' || *p == '\t') {
494: include = false;
495: if (p[1] == '-' && p[2] == 'I')
496: include = true;
497: }
498: if (include)
499: continue;
500: /* KMP variant, avoid looking twice at the same
501: * letter.
502: */
503: if (*p != 'm')
504: continue;
505: if (p[1] != 'a')
506: continue;
507: p++;
508: if (p[1] != 'k')
509: continue;
510: p++;
511: if (p[1] != 'e')
512: continue;
513: p++;
514: expensive = true;
515: while (p[1] != '\0' && p[1] != ' ' && p[1] != '\t') {
1.124 espie 516: if (p[1] == '.' || p[1] == '/') {
1.117 espie 517: expensive = false;
518: break;
1.66 espie 519: }
1.117 espie 520: p++;
1.1 deraadt 521: }
1.117 espie 522: if (expensive)
523: return true;
1.1 deraadt 524: }
1.117 espie 525: return false;
526: }
527:
1.98 espie 528: static Job *
1.124 espie 529: prepare_job(GNode *gn)
1.78 espie 530: {
1.124 espie 531: /* a new job is prepared unless its commands are bogus (we don't
532: * have anything for it), or if we're in touch mode.
533: *
534: * Note that even in noexec mode, some commands may still run
535: * thanks to the +cmd construct.
1.1 deraadt 536: */
1.124 espie 537: if (node_find_valid_commands(gn)) {
538: if (touchFlag) {
539: Job_Touch(gn);
540: return NULL;
541: } else {
542: Job *job;
1.6 millert 543:
1.124 espie 544: job = emalloc(sizeof(Job));
545: if (job == NULL)
546: Punt("can't create job: out of memory");
1.66 espie 547:
1.124 espie 548: job_attach_node(job, gn);
549: return job;
550: }
1.66 espie 551: } else {
1.124 espie 552: node_failure(gn);
553: return NULL;
1.1 deraadt 554: }
1.124 espie 555: }
1.66 espie 556:
1.124 espie 557: static void
558: may_continue_job(Job *job)
559: {
560: if (no_new_jobs) {
561: if (DEBUG(EXPENSIVE))
562: fprintf(stderr, "[%ld] expensive -> hold %s\n",
563: (long)mypid, job->node->name);
564: job->next = heldJobs;
565: heldJobs = job;
566: } else
567: continue_job(job);
568: }
1.122 espie 569:
1.124 espie 570: static void
571: continue_job(Job *job)
572: {
573: bool finished = job_run_next(job);
574: if (finished)
575: remove_job(job, true);
576: else
577: determine_expensive_job(job);
1.98 espie 578: }
1.1 deraadt 579:
1.98 espie 580: /*-
581: *-----------------------------------------------------------------------
1.124 espie 582: * Job_Make --
1.98 espie 583: * Start a target-creation process going for the target described
584: * by the graph node gn.
585: *
586: * Side Effects:
1.124 espie 587: * A new Job node is created and its commands continued, which
588: * may fork the first command of that job.
1.98 espie 589: *-----------------------------------------------------------------------
590: */
1.124 espie 591: void
592: Job_Make(GNode *gn)
1.98 espie 593: {
594: Job *job;
1.124 espie 595:
596: job = prepare_job(gn);
1.98 espie 597: if (!job)
598: return;
1.124 espie 599: nJobs++;
600: may_continue_job(job);
1.1 deraadt 601: }
602:
1.101 espie 603: static void
1.124 espie 604: determine_job_next_step(Job *job)
1.2 deraadt 605: {
1.124 espie 606: bool okay;
607: if (job->flags & JOB_IS_EXPENSIVE) {
608: no_new_jobs = false;
609: if (DEBUG(EXPENSIVE))
610: fprintf(stderr, "[%ld] "
611: "Returning from expensive target %s, "
612: "allowing new jobs\n", (long)mypid,
613: job->node->name);
614: }
1.2 deraadt 615:
1.124 espie 616: okay = job->exit_type == JOB_EXIT_OKAY;
617: if (!okay || job->next_cmd == NULL)
618: remove_job(job, okay);
619: else
620: may_continue_job(job);
1.101 espie 621: }
622:
623: static void
1.124 espie 624: remove_job(Job *job, bool okay)
1.101 espie 625: {
1.124 espie 626: nJobs--;
627: postprocess_job(job, okay);
628: while (!no_new_jobs) {
629: if (heldJobs != NULL) {
630: job = heldJobs;
631: heldJobs = heldJobs->next;
632: if (DEBUG(EXPENSIVE))
633: fprintf(stderr, "[%ld] cheap -> release %s\n",
634: (long)mypid, job->node->name);
635: continue_job(job);
636: } else
637: break;
1.2 deraadt 638: }
639: }
1.111 espie 640:
1.124 espie 641: /*
642: * job = reap_finished_job(pid):
643: * retrieve and remove a job from runningJobs, based on its pid
1.1 deraadt 644: *
1.124 espie 645: * Note that we remove it right away, so that handle_signals()
646: * is accurate.
1.1 deraadt 647: */
1.124 espie 648: static Job *
649: reap_finished_job(pid_t pid)
1.66 espie 650: {
1.124 espie 651: Job **j, *job;
1.1 deraadt 652:
1.124 espie 653: for (j = &runningJobs; *j != NULL; j = &((*j)->next))
654: if ((*j)->pid == pid) {
655: job = *j;
656: *j = job->next;
657: return job;
1.66 espie 658: }
1.1 deraadt 659:
1.124 espie 660: return NULL;
1.101 espie 661: }
1.6 millert 662:
1.124 espie 663: /*
664: * classic waitpid handler: retrieve as many dead children as possible.
665: * returns true if succesful
666: */
667: static bool
668: reap_jobs(void)
1.117 espie 669: {
1.124 espie 670: pid_t pid; /* pid of dead child */
671: int status; /* Exit/termination status */
672: bool reaped = false;
1.117 espie 673: Job *job;
674:
675: while ((pid = waitpid(WAIT_ANY, &status, WNOHANG)) > 0) {
1.124 espie 676: reaped = true;
677: job = reap_finished_job(pid);
1.1 deraadt 678:
1.124 espie 679: if (job == NULL) {
680: Punt("Child (%ld) not in table?", (long)pid);
1.66 espie 681: } else {
1.124 espie 682: job_handle_status(job, status);
683: determine_job_next_step(job);
1.1 deraadt 684: }
685: }
1.124 espie 686: /* sanity check, should not happen */
687: if (pid == -1 && errno == ECHILD && runningJobs != NULL)
688: Punt("Process has no children, but runningJobs is not empty ?");
689: return reaped;
1.1 deraadt 690: }
691:
692: void
1.124 espie 693: handle_running_jobs(void)
1.1 deraadt 694: {
1.124 espie 695: sigset_t old;
1.66 espie 696:
1.124 espie 697: /* reaping children in the presence of caught signals */
1.91 espie 698:
1.124 espie 699: /* first, we make sure to hold on new signals, to synchronize
700: * reception of new stuff on sigsuspend
701: */
702: sigprocmask(SIG_BLOCK, &sigset, &old);
703: while (runningJobs != NULL) {
704: /* did we already have pending stuff that advances things ?
705: * then handle_all_signals() will not return
706: * or reap_jobs() will reap_jobs()
707: */
708: handle_all_signals();
709: if (reap_jobs())
710: break;
711: /* okay, so it's safe to suspend, we have nothing to do but
712: * wait...
713: */
714: sigsuspend(&emptyset);
1.1 deraadt 715: }
1.124 espie 716: sigprocmask(SIG_SETMASK, &old, NULL);
1.1 deraadt 717: }
718:
1.111 espie 719: void
1.124 espie 720: handle_one_job(Job *job)
1.111 espie 721: {
1.124 espie 722: int stat;
723: int status;
724: sigset_t old;
725:
726: sigprocmask(SIG_BLOCK, &sigset, &old);
727: while (1) {
728: handle_all_signals();
729: stat = waitpid(job->pid, &status, WNOHANG);
730: if (stat == job->pid)
731: break;
732: sigsuspend(&emptyset);
733: }
734: runningJobs = NULL;
735: job_handle_status(job, status);
736: sigprocmask(SIG_SETMASK, &old, NULL);
1.111 espie 737: }
738:
739: static void
740: loop_handle_running_jobs()
741: {
1.124 espie 742: while (runningJobs != NULL)
1.111 espie 743: handle_running_jobs();
744: }
1.1 deraadt 745:
746: void
1.83 espie 747: Job_Init(int maxproc)
1.1 deraadt 748: {
1.124 espie 749: runningJobs = NULL;
750: heldJobs = NULL;
751: errorJobs = NULL;
1.117 espie 752: maxJobs = maxproc;
1.124 espie 753: mypid = getpid();
754:
1.117 espie 755: nJobs = 0;
1.66 espie 756:
1.117 espie 757: aborting = 0;
1.124 espie 758: setup_all_signals();
1.1 deraadt 759: }
760:
1.41 espie 761: bool
1.117 espie 762: can_start_job(void)
1.1 deraadt 763: {
1.124 espie 764: if (aborting || nJobs >= maxJobs)
1.117 espie 765: return false;
1.119 espie 766: else
1.117 espie 767: return true;
1.1 deraadt 768: }
769:
1.41 espie 770: bool
1.56 espie 771: Job_Empty(void)
1.1 deraadt 772: {
1.124 espie 773: return runningJobs == NULL;
1.1 deraadt 774: }
775:
1.124 espie 776: static void
1.126 ! espie 777: pass_job_control_signal(int signo)
1.124 espie 778: {
1.126 ! espie 779: Job *job;
! 780:
! 781: debug_job_printf("pass_job_control_signal(%d) called.\n", signo);
! 782:
! 783:
! 784: for (job = runningJobs; job != NULL; job = job->next) {
! 785: debug_job_printf("pass_job_control_signal to "
! 786: "child %ld running %s.\n", (long)job->pid,
! 787: job->node->name);
! 788: killpg(job->pid, signo);
! 789: }
! 790: /* after forwarding the signal, those should interrupt us */
! 791: if (signo == SIGTSTP || signo == SIGTTOU || signo == SIGTTIN) {
! 792: sigprocmask(SIG_BLOCK, &sigset, NULL);
! 793: signal(signo, SIG_DFL);
! 794: kill(getpid(), signo);
! 795: sigprocmask(SIG_SETMASK, &emptyset, NULL);
! 796: }
! 797: /* SIGWINCH is irrelevant for us, SIGCONT must put back normal
! 798: * handling for other job control signals */
! 799: if (signo == SIGCONT)
! 800: setup_job_control_interrupts();
1.124 espie 801: }
1.126 ! espie 802:
1.1 deraadt 803: /*-
804: *-----------------------------------------------------------------------
1.126 ! espie 805: * handle_fatal_signal --
! 806: * Handle the receipt of a fatal interrupt
1.1 deraadt 807: *
808: * Side Effects:
1.126 ! espie 809: * All children are killed. Another job may be started if there
! 810: * is an interrupt target and the signal was SIGINT.
1.1 deraadt 811: *-----------------------------------------------------------------------
812: */
813: static void
1.126 ! espie 814: handle_fatal_signal(int signo)
1.1 deraadt 815: {
1.124 espie 816: Job *job;
1.66 espie 817:
1.126 ! espie 818: debug_job_printf("handle_fatal_signal(%d) called.\n", signo);
1.66 espie 819:
820:
1.124 espie 821: for (job = runningJobs; job != NULL; job = job->next) {
1.66 espie 822: if (!Targ_Precious(job->node)) {
1.124 espie 823: const char *file = Var(TARGET_INDEX, job->node);
824:
825: if (!noExecute && eunlink(file) != -1)
1.66 espie 826: Error("*** %s removed", file);
827: }
1.126 ! espie 828: debug_job_printf("handle_fatal_signal: passing to "
1.124 espie 829: "child %ld running %s.\n", (long)job->pid,
830: job->node->name);
1.126 ! espie 831: killpg(job->pid, signo);
1.2 deraadt 832: }
1.1 deraadt 833:
1.124 espie 834: if (signo == SIGINT && !touchFlag) {
1.78 espie 835: if ((interrupt_node->type & OP_DUMMY) == 0) {
1.66 espie 836: ignoreErrors = false;
837:
1.124 espie 838: Job_Make(interrupt_node);
1.66 espie 839: }
1.1 deraadt 840: }
1.124 espie 841: loop_handle_running_jobs();
842: print_errors();
843:
844: /* die by that signal */
845: sigprocmask(SIG_BLOCK, &sigset, NULL);
846: signal(signo, SIG_DFL);
1.126 ! espie 847: kill(getpid(), signo);
1.124 espie 848: sigprocmask(SIG_SETMASK, &emptyset, NULL);
849: /*NOTREACHED*/
1.1 deraadt 850: }
851:
852: /*
853: *-----------------------------------------------------------------------
1.12 espie 854: * Job_Finish --
1.1 deraadt 855: * Do final processing such as the running of the commands
1.6 millert 856: * attached to the .END target.
1.1 deraadt 857: *
1.124 espie 858: * return true if fatal errors have happened.
1.1 deraadt 859: *-----------------------------------------------------------------------
860: */
1.124 espie 861: bool
1.56 espie 862: Job_Finish(void)
1.1 deraadt 863: {
1.124 espie 864: bool errors = errorJobs != NULL;
865:
1.116 espie 866: if ((end_node->type & OP_DUMMY) == 0) {
1.66 espie 867: if (errors) {
868: Error("Errors reported so .END ignored");
869: } else {
1.124 espie 870: Job_Make(end_node);
1.111 espie 871: loop_handle_running_jobs();
1.66 espie 872: }
1.1 deraadt 873: }
1.66 espie 874: return errors;
1.1 deraadt 875: }
876:
1.124 espie 877: void
878: Job_Begin(void)
879: {
880: if ((begin_node->type & OP_DUMMY) == 0) {
881: Job_Make(begin_node);
882: loop_handle_running_jobs();
883: }
884: }
885:
1.1 deraadt 886: /*-
887: *-----------------------------------------------------------------------
888: * Job_Wait --
889: * Waits for all running jobs to finish and returns. Sets 'aborting'
890: * to ABORT_WAIT to prevent other jobs from starting.
891: *
892: * Side Effects:
893: * Currently running jobs finish.
894: *
895: *-----------------------------------------------------------------------
896: */
897: void
1.56 espie 898: Job_Wait(void)
1.1 deraadt 899: {
1.66 espie 900: aborting = ABORT_WAIT;
1.111 espie 901: loop_handle_running_jobs();
1.66 espie 902: aborting = 0;
1.1 deraadt 903: }
904:
905: /*-
906: *-----------------------------------------------------------------------
907: * Job_AbortAll --
908: * Abort all currently running jobs without handling output or anything.
909: * This function is to be called only in the event of a major
1.124 espie 910: * error.
1.1 deraadt 911: *
912: * Side Effects:
1.124 espie 913: * All children are killed
1.1 deraadt 914: *-----------------------------------------------------------------------
915: */
916: void
1.56 espie 917: Job_AbortAll(void)
1.1 deraadt 918: {
1.66 espie 919: Job *job; /* the job descriptor in that element */
920: int foo;
1.6 millert 921:
1.66 espie 922: aborting = ABORT_ERROR;
1.6 millert 923:
1.124 espie 924: for (job = runningJobs; job != NULL; job = job->next) {
1.126 ! espie 925: killpg(job->pid, SIGINT);
! 926: killpg(job->pid, SIGKILL);
1.1 deraadt 927: }
1.6 millert 928:
1.66 espie 929: /*
930: * Catch as many children as want to report in at first, then give up
931: */
1.117 espie 932: while (waitpid(WAIT_ANY, &foo, WNOHANG) > 0)
1.66 espie 933: continue;
1.2 deraadt 934: }