Annotation of src/usr.bin/make/job.c, Revision 1.125
1.125 ! espie 1: /* $OpenBSD: job.c,v 1.124 2012/09/21 07:55:20 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.124 espie 76: * Job_End Should cleanup any memory used.
1.40 espie 77: *
1.117 espie 78: * can_start_job Return true if we can start job
1.40 espie 79: *
1.41 espie 80: * Job_Empty Return true if the job table is completely
1.40 espie 81: * empty.
82: *
83: * Job_Finish Perform any final processing which needs doing.
84: * This includes the execution of any commands
85: * which have been/were attached to the .END
1.124 espie 86: * target.
1.40 espie 87: *
1.117 espie 88: * Job_AbortAll Abort all current jobs. It doesn't
1.40 espie 89: * handle output or do anything for the jobs,
1.124 espie 90: * just kills them.
1.1 deraadt 91: *
1.117 espie 92: * Job_Wait Wait for all running jobs to finish.
1.1 deraadt 93: */
94:
95: #include <sys/types.h>
96: #include <sys/wait.h>
1.41 espie 97: #include <ctype.h>
98: #include <errno.h>
1.1 deraadt 99: #include <fcntl.h>
1.41 espie 100: #include <signal.h>
1.69 espie 101: #include <stdarg.h>
1.1 deraadt 102: #include <stdio.h>
1.42 espie 103: #include <stdlib.h>
1.1 deraadt 104: #include <string.h>
1.41 espie 105: #include <unistd.h>
106: #include "config.h"
107: #include "defines.h"
1.1 deraadt 108: #include "job.h"
1.63 espie 109: #include "engine.h"
1.1 deraadt 110: #include "pathnames.h"
1.41 espie 111: #include "var.h"
112: #include "targ.h"
113: #include "error.h"
1.124 espie 114: #include "extern.h"
1.41 espie 115: #include "lst.h"
116: #include "gnode.h"
117: #include "memory.h"
118: #include "make.h"
1.124 espie 119: #include "buf.h"
1.78 espie 120:
1.40 espie 121: static int aborting = 0; /* why is the make aborting? */
122: #define ABORT_ERROR 1 /* Because of an error */
123: #define ABORT_INTERRUPT 2 /* Because it was interrupted */
124: #define ABORT_WAIT 3 /* Waiting for jobs to finish */
1.1 deraadt 125:
1.40 espie 126: static int maxJobs; /* The most children we can run at once */
1.124 espie 127: static int nJobs; /* Number of jobs already allocated */
128: static bool no_new_jobs; /* Mark recursive shit so we shouldn't start
129: * something else at the same time
130: */
131: Job *runningJobs; /* Jobs currently running a process */
132: Job *errorJobs; /* Jobs in error at end */
133: static Job *heldJobs; /* Jobs not running yet because of expensive */
134: static pid_t mypid;
1.1 deraadt 135:
1.124 espie 136: static volatile sig_atomic_t got_fatal;
1.112 espie 137:
1.124 espie 138: static volatile sig_atomic_t got_SIGINT, got_SIGHUP, got_SIGQUIT, got_SIGTERM,
139: got_SIGINFO;
1.112 espie 140:
1.124 espie 141: static sigset_t sigset, emptyset;
1.112 espie 142:
1.124 espie 143: static void handle_signal(int);
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);
160: static void really_kill(int, pid_t);
1.93 espie 161:
1.124 espie 162: void
163: print_errors(void)
1.101 espie 164: {
1.124 espie 165: Job *j;
1.125 ! espie 166: const char *previous = NULL;
1.124 espie 167:
168: fprintf(stderr, "\nStop in %s%c\n", Var_Value(".CURDIR"),
169: errorJobs ? ':' : '.');
1.1 deraadt 170:
1.124 espie 171: for (j = errorJobs; j != NULL; j = j->next) {
172: const char *type;
1.93 espie 173:
1.124 espie 174: if (j->exit_type == JOB_EXIT_BAD)
1.111 espie 175: type = "Exit status";
1.124 espie 176: else if (j->exit_type == JOB_SIGNALED)
1.111 espie 177: type = "Received signal";
1.124 espie 178: else
1.112 espie 179: type = "Should not happen";
1.124 espie 180: fprintf(stderr, " %s %d (", type, j->code);
181: fprintf(stderr, "line %lu",
182: j->location->lineno);
183: if (j->location->fname == previous)
184: fputs(",", stderr);
185: else
186: fprintf(stderr, " of %s,", j->location->fname);
187: previous = j->location->fname;
188: if ((j->flags & (JOB_SILENT | JOB_IS_EXPENSIVE)) == JOB_SILENT)
189: fprintf(stderr, "\n target %s: %s", j->node->name, j->cmd);
190: else
191: fprintf(stderr, " target %s", j->node->name);
192: fputs(")\n", stderr);
193: free(j->cmd);
1.101 espie 194: }
1.88 espie 195: }
196:
1.57 espie 197: static void
1.124 espie 198: setup_signal(int sig)
1.57 espie 199: {
1.124 espie 200: if (signal(sig, SIG_IGN) != SIG_IGN) {
201: (void)signal(sig, notice_signal);
202: sigaddset(&sigset, sig);
1.57 espie 203: }
204: }
205:
206: static void
1.124 espie 207: notice_signal(int sig)
1.1 deraadt 208: {
1.124 espie 209: switch(sig) {
1.117 espie 210: case SIGINT:
211: got_SIGINT++;
1.124 espie 212: got_fatal = 1;
213: break;
1.117 espie 214: case SIGHUP:
215: got_SIGHUP++;
1.124 espie 216: got_fatal = 1;
217: break;
1.117 espie 218: case SIGQUIT:
219: got_SIGQUIT++;
1.124 espie 220: got_fatal = 1;
221: break;
1.117 espie 222: case SIGTERM:
223: got_SIGTERM++;
1.124 espie 224: got_fatal = 1;
1.117 espie 225: break;
1.124 espie 226: case SIGINFO:
227: got_SIGINFO++;
1.117 espie 228: break;
1.124 espie 229: case SIGCHLD:
1.117 espie 230: break;
1.66 espie 231: }
1.124 espie 232: }
1.117 espie 233:
1.124 espie 234: void
235: setup_all_signals(void)
236: {
237: sigemptyset(&sigset);
238: sigemptyset(&emptyset);
239: /*
240: * Catch the four signals that POSIX specifies if they aren't ignored.
241: * handle_signal will take care of calling JobInterrupt if appropriate.
242: */
243: setup_signal(SIGINT);
244: setup_signal(SIGHUP);
245: setup_signal(SIGQUIT);
246: setup_signal(SIGTERM);
247: /* Display running jobs on SIGINFO */
248: setup_signal(SIGINFO);
249: /* Have to see SIGCHLD */
250: setup_signal(SIGCHLD);
251: got_fatal = 0;
1.1 deraadt 252: }
253:
1.124 espie 254: static void
255: handle_siginfo(void)
1.1 deraadt 256: {
1.124 espie 257: Job *job;
258: BUFFER buf;
259: bool first = true;
1.66 espie 260:
1.124 espie 261: got_SIGINFO = 0;
262: /* we have to store the info in a buffer, because status from all
263: * makes running would get intermixed otherwise
1.66 espie 264: */
1.124 espie 265: Buf_Init(&buf, 0);
266:
267: Buf_printf(&buf, "%s in %s: ", Var_Value("MAKE"), Var_Value(".CURDIR"));
268:
269: for (job = runningJobs; job != NULL ; job = job->next) {
270: if (!first)
271: Buf_puts(&buf, ", ");
272: first = false;
273: Buf_puts(&buf, job->node->name);
274: }
275: Buf_puts(&buf, first ? "nothing running\n" : "\n");
1.1 deraadt 276:
1.124 espie 277: fputs(Buf_Retrieve(&buf), stderr);
278: Buf_Destroy(&buf);
1.1 deraadt 279: }
280:
1.124 espie 281: void
282: handle_all_signals(void)
1.1 deraadt 283: {
1.124 espie 284: if (got_SIGINFO)
285: handle_siginfo();
286: while (got_fatal) {
287: got_fatal = 0;
288: aborting = ABORT_INTERRUPT;
289:
290: if (got_SIGINT) {
291: got_SIGINT=0;
292: handle_signal(SIGINT);
293: }
294: if (got_SIGHUP) {
295: got_SIGHUP=0;
296: handle_signal(SIGHUP);
297: }
298: if (got_SIGQUIT) {
299: got_SIGQUIT=0;
300: handle_signal(SIGQUIT);
301: }
302: if (got_SIGTERM) {
303: got_SIGTERM=0;
304: handle_signal(SIGTERM);
305: }
306: }
1.1 deraadt 307: }
308:
1.124 espie 309: void
310: debug_job_printf(const char *fmt, ...)
1.84 espie 311: {
312: if (DEBUG(JOB)) {
313: va_list va;
1.124 espie 314: (void)printf("[%ld] ", (long)mypid);
1.84 espie 315: va_start(va, fmt);
1.124 espie 316: (void)vprintf(fmt, va);
1.84 espie 317: fflush(stdout);
318: va_end(va);
319: }
320: }
1.119 espie 321:
1.1 deraadt 322: /*-
323: *-----------------------------------------------------------------------
1.124 espie 324: * postprocess_job --
325: * Do final processing for the given job including updating
1.119 espie 326: * parents and starting new jobs as available/necessary.
1.1 deraadt 327: *
328: * Side Effects:
1.6 millert 329: * If we got an error and are aborting (aborting == ABORT_ERROR) and
1.1 deraadt 330: * the job list is now empty, we are done for the day.
1.101 espie 331: * If we recognized an error we set the aborting flag
1.1 deraadt 332: * to ABORT_ERROR so no more jobs will be started.
333: *-----------------------------------------------------------------------
334: */
335: /*ARGSUSED*/
1.112 espie 336:
1.1 deraadt 337: static void
1.124 espie 338: postprocess_job(Job *job, bool okay)
1.2 deraadt 339: {
1.124 espie 340: if (okay &&
1.66 espie 341: aborting != ABORT_ERROR &&
1.124 espie 342: aborting != ABORT_INTERRUPT) {
1.66 espie 343: /* As long as we aren't aborting and the job didn't return a
344: * non-zero status that we shouldn't ignore, we call
1.101 espie 345: * Make_Update to update the parents. */
1.108 espie 346: job->node->built_status = MADE;
1.66 espie 347: Make_Update(job->node);
1.124 espie 348: free(job);
1.66 espie 349: }
1.1 deraadt 350:
1.124 espie 351: if (errorJobs != NULL && !keepgoing &&
1.117 espie 352: aborting != ABORT_INTERRUPT)
1.66 espie 353: aborting = ABORT_ERROR;
1.6 millert 354:
1.124 espie 355: if (aborting == ABORT_ERROR && DEBUG(QUICKDEATH))
356: handle_signal(SIGINT);
1.117 espie 357: if (aborting == ABORT_ERROR && Job_Empty())
1.124 espie 358: Finish();
1.1 deraadt 359: }
360:
1.124 espie 361: /* expensive jobs handling: in order to avoid forking an exponential number
362: * of jobs, make tries to figure out "recursive make" configurations.
363: * It may err on the side of caution.
364: * Basically, a command is "expensive" if it's likely to fork an extra
365: * level of make: either by looking at the command proper, or if it has
366: * some specific qualities ('+cmd' are likely to be recursive, as are
367: * .MAKE: commands). It's possible to explicitly say some targets are
368: * expensive or cheap with .EXPENSIVE or .CHEAP.
369: *
370: * While an expensive command is running, no_new_jobs
371: * is set, so jobs that would fork new processes are accumulated in the
372: * heldJobs list instead.
373: *
374: * This heuristics is also used on error exit: we display silent commands
375: * that failed, unless those ARE expensive commands: expensive commands
376: * are likely to not be failing by themselves, but to be the result of
377: * a cascade of failures in descendant makes.
378: */
379: void
380: determine_expensive_job(Job *job)
381: {
382: if (expensive_job(job)) {
383: job->flags |= JOB_IS_EXPENSIVE;
384: no_new_jobs = true;
385: } else
386: job->flags &= ~JOB_IS_EXPENSIVE;
387: if (DEBUG(EXPENSIVE))
388: fprintf(stderr, "[%ld] Target %s running %.50s: %s\n",
389: (long)mypid, job->node->name, job->cmd,
390: job->flags & JOB_IS_EXPENSIVE ? "expensive" : "cheap");
1.101 espie 391: }
392:
1.124 espie 393: static bool
394: expensive_job(Job *job)
1.1 deraadt 395: {
1.124 espie 396: if (job->node->type & OP_CHEAP)
397: return false;
398: if (job->node->type & (OP_EXPENSIVE | OP_MAKE))
399: return true;
400: return expensive_command(job->cmd);
1.96 espie 401: }
402:
1.117 espie 403: static bool
404: expensive_command(const char *s)
1.1 deraadt 405: {
1.117 espie 406: const char *p;
407: bool include = false;
408: bool expensive = false;
1.66 espie 409:
1.117 espie 410: /* okay, comments are cheap, always */
411: if (*s == '#')
412: return false;
1.124 espie 413: /* and commands we always execute are expensive */
414: if (*s == '+')
415: return true;
1.66 espie 416:
1.117 espie 417: for (p = s; *p != '\0'; p++) {
418: if (*p == ' ' || *p == '\t') {
419: include = false;
420: if (p[1] == '-' && p[2] == 'I')
421: include = true;
422: }
423: if (include)
424: continue;
425: /* KMP variant, avoid looking twice at the same
426: * letter.
427: */
428: if (*p != 'm')
429: continue;
430: if (p[1] != 'a')
431: continue;
432: p++;
433: if (p[1] != 'k')
434: continue;
435: p++;
436: if (p[1] != 'e')
437: continue;
438: p++;
439: expensive = true;
440: while (p[1] != '\0' && p[1] != ' ' && p[1] != '\t') {
1.124 espie 441: if (p[1] == '.' || p[1] == '/') {
1.117 espie 442: expensive = false;
443: break;
1.66 espie 444: }
1.117 espie 445: p++;
1.1 deraadt 446: }
1.117 espie 447: if (expensive)
448: return true;
1.1 deraadt 449: }
1.117 espie 450: return false;
451: }
452:
1.98 espie 453: static Job *
1.124 espie 454: prepare_job(GNode *gn)
1.78 espie 455: {
1.124 espie 456: /* a new job is prepared unless its commands are bogus (we don't
457: * have anything for it), or if we're in touch mode.
458: *
459: * Note that even in noexec mode, some commands may still run
460: * thanks to the +cmd construct.
1.1 deraadt 461: */
1.124 espie 462: if (node_find_valid_commands(gn)) {
463: if (touchFlag) {
464: Job_Touch(gn);
465: return NULL;
466: } else {
467: Job *job;
1.6 millert 468:
1.124 espie 469: job = emalloc(sizeof(Job));
470: if (job == NULL)
471: Punt("can't create job: out of memory");
1.66 espie 472:
1.124 espie 473: job_attach_node(job, gn);
474: return job;
475: }
1.66 espie 476: } else {
1.124 espie 477: node_failure(gn);
478: return NULL;
1.1 deraadt 479: }
1.124 espie 480: }
1.66 espie 481:
1.124 espie 482: static void
483: may_continue_job(Job *job)
484: {
485: if (no_new_jobs) {
486: if (DEBUG(EXPENSIVE))
487: fprintf(stderr, "[%ld] expensive -> hold %s\n",
488: (long)mypid, job->node->name);
489: job->next = heldJobs;
490: heldJobs = job;
491: } else
492: continue_job(job);
493: }
1.122 espie 494:
1.124 espie 495: static void
496: continue_job(Job *job)
497: {
498: bool finished = job_run_next(job);
499: if (finished)
500: remove_job(job, true);
501: else
502: determine_expensive_job(job);
1.98 espie 503: }
1.1 deraadt 504:
1.98 espie 505: /*-
506: *-----------------------------------------------------------------------
1.124 espie 507: * Job_Make --
1.98 espie 508: * Start a target-creation process going for the target described
509: * by the graph node gn.
510: *
511: * Side Effects:
1.124 espie 512: * A new Job node is created and its commands continued, which
513: * may fork the first command of that job.
1.98 espie 514: *-----------------------------------------------------------------------
515: */
1.124 espie 516: void
517: Job_Make(GNode *gn)
1.98 espie 518: {
519: Job *job;
1.124 espie 520: bool finished;
521:
522: job = prepare_job(gn);
1.98 espie 523: if (!job)
524: return;
1.124 espie 525: nJobs++;
526: may_continue_job(job);
1.1 deraadt 527: }
528:
1.101 espie 529: static void
1.124 espie 530: determine_job_next_step(Job *job)
1.2 deraadt 531: {
1.124 espie 532: bool okay;
533: if (job->flags & JOB_IS_EXPENSIVE) {
534: no_new_jobs = false;
535: if (DEBUG(EXPENSIVE))
536: fprintf(stderr, "[%ld] "
537: "Returning from expensive target %s, "
538: "allowing new jobs\n", (long)mypid,
539: job->node->name);
540: }
1.2 deraadt 541:
1.124 espie 542: okay = job->exit_type == JOB_EXIT_OKAY;
543: if (!okay || job->next_cmd == NULL)
544: remove_job(job, okay);
545: else
546: may_continue_job(job);
1.101 espie 547: }
548:
549: static void
1.124 espie 550: remove_job(Job *job, bool okay)
1.101 espie 551: {
1.124 espie 552: nJobs--;
553: postprocess_job(job, okay);
554: while (!no_new_jobs) {
555: if (heldJobs != NULL) {
556: job = heldJobs;
557: heldJobs = heldJobs->next;
558: if (DEBUG(EXPENSIVE))
559: fprintf(stderr, "[%ld] cheap -> release %s\n",
560: (long)mypid, job->node->name);
561: continue_job(job);
562: } else
563: break;
1.2 deraadt 564: }
565: }
1.111 espie 566:
1.124 espie 567: /*
568: * job = reap_finished_job(pid):
569: * retrieve and remove a job from runningJobs, based on its pid
1.1 deraadt 570: *
1.124 espie 571: * Note that we remove it right away, so that handle_signals()
572: * is accurate.
1.1 deraadt 573: */
1.124 espie 574: static Job *
575: reap_finished_job(pid_t pid)
1.66 espie 576: {
1.124 espie 577: Job **j, *job;
1.1 deraadt 578:
1.124 espie 579: for (j = &runningJobs; *j != NULL; j = &((*j)->next))
580: if ((*j)->pid == pid) {
581: job = *j;
582: *j = job->next;
583: return job;
1.66 espie 584: }
1.1 deraadt 585:
1.124 espie 586: return NULL;
1.101 espie 587: }
1.6 millert 588:
1.124 espie 589: /*
590: * classic waitpid handler: retrieve as many dead children as possible.
591: * returns true if succesful
592: */
593: static bool
594: reap_jobs(void)
1.117 espie 595: {
1.124 espie 596: pid_t pid; /* pid of dead child */
597: int status; /* Exit/termination status */
598: bool reaped = false;
1.117 espie 599: Job *job;
600:
601: while ((pid = waitpid(WAIT_ANY, &status, WNOHANG)) > 0) {
1.124 espie 602: reaped = true;
603: job = reap_finished_job(pid);
1.1 deraadt 604:
1.124 espie 605: if (job == NULL) {
606: Punt("Child (%ld) not in table?", (long)pid);
1.66 espie 607: } else {
1.124 espie 608: job_handle_status(job, status);
609: determine_job_next_step(job);
1.1 deraadt 610: }
611: }
1.124 espie 612: /* sanity check, should not happen */
613: if (pid == -1 && errno == ECHILD && runningJobs != NULL)
614: Punt("Process has no children, but runningJobs is not empty ?");
615: return reaped;
1.1 deraadt 616: }
617:
618: void
1.124 espie 619: handle_running_jobs(void)
1.1 deraadt 620: {
1.124 espie 621: sigset_t old;
1.66 espie 622:
1.124 espie 623: /* reaping children in the presence of caught signals */
1.91 espie 624:
1.124 espie 625: /* first, we make sure to hold on new signals, to synchronize
626: * reception of new stuff on sigsuspend
627: */
628: sigprocmask(SIG_BLOCK, &sigset, &old);
629: while (runningJobs != NULL) {
630: /* did we already have pending stuff that advances things ?
631: * then handle_all_signals() will not return
632: * or reap_jobs() will reap_jobs()
633: */
634: handle_all_signals();
635: if (reap_jobs())
636: break;
637: /* okay, so it's safe to suspend, we have nothing to do but
638: * wait...
639: */
640: sigsuspend(&emptyset);
1.1 deraadt 641: }
1.124 espie 642: sigprocmask(SIG_SETMASK, &old, NULL);
1.1 deraadt 643: }
644:
1.111 espie 645: void
1.124 espie 646: handle_one_job(Job *job)
1.111 espie 647: {
1.124 espie 648: int stat;
649: int status;
650: sigset_t old;
651:
652: sigprocmask(SIG_BLOCK, &sigset, &old);
653: while (1) {
654: handle_all_signals();
655: stat = waitpid(job->pid, &status, WNOHANG);
656: if (stat == job->pid)
657: break;
658: sigsuspend(&emptyset);
659: }
660: runningJobs = NULL;
661: job_handle_status(job, status);
662: sigprocmask(SIG_SETMASK, &old, NULL);
1.111 espie 663: }
664:
665: static void
666: loop_handle_running_jobs()
667: {
1.124 espie 668: while (runningJobs != NULL)
1.111 espie 669: handle_running_jobs();
670: }
1.1 deraadt 671:
672: void
1.83 espie 673: Job_Init(int maxproc)
1.1 deraadt 674: {
1.124 espie 675: runningJobs = NULL;
676: heldJobs = NULL;
677: errorJobs = NULL;
1.117 espie 678: maxJobs = maxproc;
1.124 espie 679: mypid = getpid();
680:
1.117 espie 681: nJobs = 0;
1.66 espie 682:
1.117 espie 683: aborting = 0;
1.124 espie 684: setup_all_signals();
1.1 deraadt 685: }
686:
1.41 espie 687: bool
1.117 espie 688: can_start_job(void)
1.1 deraadt 689: {
1.124 espie 690: if (aborting || nJobs >= maxJobs)
1.117 espie 691: return false;
1.119 espie 692: else
1.117 espie 693: return true;
1.1 deraadt 694: }
695:
1.41 espie 696: bool
1.56 espie 697: Job_Empty(void)
1.1 deraadt 698: {
1.124 espie 699: return runningJobs == NULL;
1.1 deraadt 700: }
701:
1.124 espie 702: static void
703: really_kill(pid_t pid, int sig)
704: {
705: killpg(pid, sig);
706: if (killpg(pid, sig) == - 1 && errno == ESRCH)
707: kill(pid, sig);
708: }
1.1 deraadt 709: /*-
710: *-----------------------------------------------------------------------
1.124 espie 711: * handle_signal --
1.1 deraadt 712: * Handle the receipt of an interrupt.
713: *
714: * Side Effects:
1.124 espie 715: * All children are killed. Another job may be started if the
1.1 deraadt 716: * .INTERRUPT target was given.
717: *-----------------------------------------------------------------------
718: */
719: static void
1.124 espie 720: handle_signal(int signo)
1.1 deraadt 721: {
1.124 espie 722: Job *job;
1.66 espie 723:
1.124 espie 724: debug_job_printf("handle_signal(%d) called.\n", signo);
1.66 espie 725:
726:
1.124 espie 727: for (job = runningJobs; job != NULL; job = job->next) {
1.66 espie 728: if (!Targ_Precious(job->node)) {
1.124 espie 729: const char *file = Var(TARGET_INDEX, job->node);
730:
731: if (!noExecute && eunlink(file) != -1)
1.66 espie 732: Error("*** %s removed", file);
733: }
1.124 espie 734: debug_job_printf("handle_signal passing signal to "
735: "child %ld running %s.\n", (long)job->pid,
736: job->node->name);
737: really_kill(job->pid, signo);
1.2 deraadt 738: }
1.1 deraadt 739:
1.124 espie 740: if (signo == SIGINT && !touchFlag) {
1.78 espie 741: if ((interrupt_node->type & OP_DUMMY) == 0) {
1.66 espie 742: ignoreErrors = false;
743:
1.124 espie 744: Job_Make(interrupt_node);
1.66 espie 745: }
1.1 deraadt 746: }
1.124 espie 747: loop_handle_running_jobs();
748: print_errors();
749:
750: /* die by that signal */
751: sigprocmask(SIG_BLOCK, &sigset, NULL);
752: signal(signo, SIG_DFL);
753: really_kill(getpid(), signo);
754: sigprocmask(SIG_SETMASK, &emptyset, NULL);
755: /*NOTREACHED*/
1.1 deraadt 756: }
757:
758: /*
759: *-----------------------------------------------------------------------
1.12 espie 760: * Job_Finish --
1.1 deraadt 761: * Do final processing such as the running of the commands
1.6 millert 762: * attached to the .END target.
1.1 deraadt 763: *
1.124 espie 764: * return true if fatal errors have happened.
1.1 deraadt 765: *-----------------------------------------------------------------------
766: */
1.124 espie 767: bool
1.56 espie 768: Job_Finish(void)
1.1 deraadt 769: {
1.124 espie 770: bool errors = errorJobs != NULL;
771:
1.116 espie 772: if ((end_node->type & OP_DUMMY) == 0) {
1.66 espie 773: if (errors) {
774: Error("Errors reported so .END ignored");
775: } else {
1.124 espie 776: Job_Make(end_node);
1.111 espie 777: loop_handle_running_jobs();
1.66 espie 778: }
1.1 deraadt 779: }
1.66 espie 780: return errors;
1.1 deraadt 781: }
782:
1.124 espie 783: void
784: Job_Begin(void)
785: {
786: if ((begin_node->type & OP_DUMMY) == 0) {
787: Job_Make(begin_node);
788: loop_handle_running_jobs();
789: }
790: }
791:
1.41 espie 792: #ifdef CLEANUP
1.12 espie 793: void
1.56 espie 794: Job_End(void)
1.12 espie 795: {
1.41 espie 796: }
1.13 espie 797: #endif
1.40 espie 798:
1.1 deraadt 799: /*-
800: *-----------------------------------------------------------------------
801: * Job_Wait --
802: * Waits for all running jobs to finish and returns. Sets 'aborting'
803: * to ABORT_WAIT to prevent other jobs from starting.
804: *
805: * Side Effects:
806: * Currently running jobs finish.
807: *
808: *-----------------------------------------------------------------------
809: */
810: void
1.56 espie 811: Job_Wait(void)
1.1 deraadt 812: {
1.66 espie 813: aborting = ABORT_WAIT;
1.111 espie 814: loop_handle_running_jobs();
1.66 espie 815: aborting = 0;
1.1 deraadt 816: }
817:
818: /*-
819: *-----------------------------------------------------------------------
820: * Job_AbortAll --
821: * Abort all currently running jobs without handling output or anything.
822: * This function is to be called only in the event of a major
1.124 espie 823: * error.
1.1 deraadt 824: *
825: * Side Effects:
1.124 espie 826: * All children are killed
1.1 deraadt 827: *-----------------------------------------------------------------------
828: */
829: void
1.56 espie 830: Job_AbortAll(void)
1.1 deraadt 831: {
1.66 espie 832: Job *job; /* the job descriptor in that element */
833: int foo;
1.6 millert 834:
1.66 espie 835: aborting = ABORT_ERROR;
1.6 millert 836:
1.124 espie 837: for (job = runningJobs; job != NULL; job = job->next) {
838: really_kill(job->pid, SIGINT);
839: really_kill(job->pid, SIGKILL);
1.1 deraadt 840: }
1.6 millert 841:
1.66 espie 842: /*
843: * Catch as many children as want to report in at first, then give up
844: */
1.117 espie 845: while (waitpid(WAIT_ANY, &foo, WNOHANG) > 0)
1.66 espie 846: continue;
1.2 deraadt 847: }