xref: /freebsd/contrib/bmake/job.c (revision e2eeea75eb8b6dd50c1298067a0655880d186734)
1*e2eeea75SSimon J. Gerraty /*	$NetBSD: job.c,v 1.326 2020/11/16 18:28:27 rillig Exp $	*/
23955d011SMarcel Moolenaar 
33955d011SMarcel Moolenaar /*
43955d011SMarcel Moolenaar  * Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
53955d011SMarcel Moolenaar  * All rights reserved.
63955d011SMarcel Moolenaar  *
73955d011SMarcel Moolenaar  * This code is derived from software contributed to Berkeley by
83955d011SMarcel Moolenaar  * Adam de Boor.
93955d011SMarcel Moolenaar  *
103955d011SMarcel Moolenaar  * Redistribution and use in source and binary forms, with or without
113955d011SMarcel Moolenaar  * modification, are permitted provided that the following conditions
123955d011SMarcel Moolenaar  * are met:
133955d011SMarcel Moolenaar  * 1. Redistributions of source code must retain the above copyright
143955d011SMarcel Moolenaar  *    notice, this list of conditions and the following disclaimer.
153955d011SMarcel Moolenaar  * 2. Redistributions in binary form must reproduce the above copyright
163955d011SMarcel Moolenaar  *    notice, this list of conditions and the following disclaimer in the
173955d011SMarcel Moolenaar  *    documentation and/or other materials provided with the distribution.
183955d011SMarcel Moolenaar  * 3. Neither the name of the University nor the names of its contributors
193955d011SMarcel Moolenaar  *    may be used to endorse or promote products derived from this software
203955d011SMarcel Moolenaar  *    without specific prior written permission.
213955d011SMarcel Moolenaar  *
223955d011SMarcel Moolenaar  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
233955d011SMarcel Moolenaar  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
243955d011SMarcel Moolenaar  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
253955d011SMarcel Moolenaar  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
263955d011SMarcel Moolenaar  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
273955d011SMarcel Moolenaar  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
283955d011SMarcel Moolenaar  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
293955d011SMarcel Moolenaar  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
303955d011SMarcel Moolenaar  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
313955d011SMarcel Moolenaar  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
323955d011SMarcel Moolenaar  * SUCH DAMAGE.
333955d011SMarcel Moolenaar  */
343955d011SMarcel Moolenaar 
353955d011SMarcel Moolenaar /*
363955d011SMarcel Moolenaar  * Copyright (c) 1988, 1989 by Adam de Boor
373955d011SMarcel Moolenaar  * Copyright (c) 1989 by Berkeley Softworks
383955d011SMarcel Moolenaar  * All rights reserved.
393955d011SMarcel Moolenaar  *
403955d011SMarcel Moolenaar  * This code is derived from software contributed to Berkeley by
413955d011SMarcel Moolenaar  * Adam de Boor.
423955d011SMarcel Moolenaar  *
433955d011SMarcel Moolenaar  * Redistribution and use in source and binary forms, with or without
443955d011SMarcel Moolenaar  * modification, are permitted provided that the following conditions
453955d011SMarcel Moolenaar  * are met:
463955d011SMarcel Moolenaar  * 1. Redistributions of source code must retain the above copyright
473955d011SMarcel Moolenaar  *    notice, this list of conditions and the following disclaimer.
483955d011SMarcel Moolenaar  * 2. Redistributions in binary form must reproduce the above copyright
493955d011SMarcel Moolenaar  *    notice, this list of conditions and the following disclaimer in the
503955d011SMarcel Moolenaar  *    documentation and/or other materials provided with the distribution.
513955d011SMarcel Moolenaar  * 3. All advertising materials mentioning features or use of this software
523955d011SMarcel Moolenaar  *    must display the following acknowledgement:
533955d011SMarcel Moolenaar  *	This product includes software developed by the University of
543955d011SMarcel Moolenaar  *	California, Berkeley and its contributors.
553955d011SMarcel Moolenaar  * 4. Neither the name of the University nor the names of its contributors
563955d011SMarcel Moolenaar  *    may be used to endorse or promote products derived from this software
573955d011SMarcel Moolenaar  *    without specific prior written permission.
583955d011SMarcel Moolenaar  *
593955d011SMarcel Moolenaar  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
603955d011SMarcel Moolenaar  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
613955d011SMarcel Moolenaar  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
623955d011SMarcel Moolenaar  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
633955d011SMarcel Moolenaar  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
643955d011SMarcel Moolenaar  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
653955d011SMarcel Moolenaar  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
663955d011SMarcel Moolenaar  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
673955d011SMarcel Moolenaar  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
683955d011SMarcel Moolenaar  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
693955d011SMarcel Moolenaar  * SUCH DAMAGE.
703955d011SMarcel Moolenaar  */
713955d011SMarcel Moolenaar 
723955d011SMarcel Moolenaar /*-
733955d011SMarcel Moolenaar  * job.c --
743955d011SMarcel Moolenaar  *	handle the creation etc. of our child processes.
753955d011SMarcel Moolenaar  *
763955d011SMarcel Moolenaar  * Interface:
77956e45f6SSimon J. Gerraty  *	Job_Init	Called to initialize this module. In addition,
78956e45f6SSimon J. Gerraty  *			any commands attached to the .BEGIN target
79956e45f6SSimon J. Gerraty  *			are executed before this function returns.
80956e45f6SSimon J. Gerraty  *			Hence, the makefiles must have been parsed
81956e45f6SSimon J. Gerraty  *			before this function is called.
82956e45f6SSimon J. Gerraty  *
83956e45f6SSimon J. Gerraty  *	Job_End		Clean up any memory used.
84956e45f6SSimon J. Gerraty  *
853955d011SMarcel Moolenaar  *	Job_Make	Start the creation of the given target.
863955d011SMarcel Moolenaar  *
87956e45f6SSimon J. Gerraty  *	Job_CatchChildren
88956e45f6SSimon J. Gerraty  *			Check for and handle the termination of any
893955d011SMarcel Moolenaar  *			children. This must be called reasonably
903955d011SMarcel Moolenaar  *			frequently to keep the whole make going at
913955d011SMarcel Moolenaar  *			a decent clip, since job table entries aren't
923955d011SMarcel Moolenaar  *			removed until their process is caught this way.
933955d011SMarcel Moolenaar  *
94956e45f6SSimon J. Gerraty  *	Job_CatchOutput
95956e45f6SSimon J. Gerraty  *			Print any output our children have produced.
963955d011SMarcel Moolenaar  *			Should also be called fairly frequently to
973955d011SMarcel Moolenaar  *			keep the user informed of what's going on.
983955d011SMarcel Moolenaar  *			If no output is waiting, it will block for
993955d011SMarcel Moolenaar  *			a time given by the SEL_* constants, below,
1003955d011SMarcel Moolenaar  *			or until output is ready.
1013955d011SMarcel Moolenaar  *
1023955d011SMarcel Moolenaar  *	Job_ParseShell	Given the line following a .SHELL target, parse
1033955d011SMarcel Moolenaar  *			the line as a shell specification. Returns
1042c3632d1SSimon J. Gerraty  *			FALSE if the spec was incorrect.
1053955d011SMarcel Moolenaar  *
1063955d011SMarcel Moolenaar  *	Job_Finish	Perform any final processing which needs doing.
1073955d011SMarcel Moolenaar  *			This includes the execution of any commands
1083955d011SMarcel Moolenaar  *			which have been/were attached to the .END
1093955d011SMarcel Moolenaar  *			target. It should only be called when the
1103955d011SMarcel Moolenaar  *			job table is empty.
1113955d011SMarcel Moolenaar  *
1123955d011SMarcel Moolenaar  *	Job_AbortAll	Abort all currently running jobs. It doesn't
1133955d011SMarcel Moolenaar  *			handle output or do anything for the jobs,
1143955d011SMarcel Moolenaar  *			just kills them. It should only be called in
115956e45f6SSimon J. Gerraty  *			an emergency.
1163955d011SMarcel Moolenaar  *
117956e45f6SSimon J. Gerraty  *	Job_CheckCommands
118956e45f6SSimon J. Gerraty  *			Verify that the commands for a target are
1193955d011SMarcel Moolenaar  *			ok. Provide them if necessary and possible.
1203955d011SMarcel Moolenaar  *
1213955d011SMarcel Moolenaar  *	Job_Touch	Update a target without really updating it.
1223955d011SMarcel Moolenaar  *
1233955d011SMarcel Moolenaar  *	Job_Wait	Wait for all currently-running jobs to finish.
1243955d011SMarcel Moolenaar  */
1253955d011SMarcel Moolenaar 
1263955d011SMarcel Moolenaar #ifdef HAVE_CONFIG_H
1273955d011SMarcel Moolenaar # include "config.h"
1283955d011SMarcel Moolenaar #endif
1293955d011SMarcel Moolenaar #include <sys/types.h>
1303955d011SMarcel Moolenaar #include <sys/stat.h>
1313955d011SMarcel Moolenaar #include <sys/file.h>
1323955d011SMarcel Moolenaar #include <sys/time.h>
1333955d011SMarcel Moolenaar #include "wait.h"
1343955d011SMarcel Moolenaar 
1353955d011SMarcel Moolenaar #include <errno.h>
1363955d011SMarcel Moolenaar #if !defined(USE_SELECT) && defined(HAVE_POLL_H)
1373955d011SMarcel Moolenaar #include <poll.h>
1383955d011SMarcel Moolenaar #else
1393955d011SMarcel Moolenaar #ifndef USE_SELECT			/* no poll.h */
1403955d011SMarcel Moolenaar # define USE_SELECT
1413955d011SMarcel Moolenaar #endif
1423955d011SMarcel Moolenaar #if defined(HAVE_SYS_SELECT_H)
1433955d011SMarcel Moolenaar # include <sys/select.h>
1443955d011SMarcel Moolenaar #endif
1453955d011SMarcel Moolenaar #endif
1463955d011SMarcel Moolenaar #include <signal.h>
1473955d011SMarcel Moolenaar #include <utime.h>
1483955d011SMarcel Moolenaar #if defined(HAVE_SYS_SOCKET_H)
1493955d011SMarcel Moolenaar # include <sys/socket.h>
1503955d011SMarcel Moolenaar #endif
1513955d011SMarcel Moolenaar 
1523955d011SMarcel Moolenaar #include "make.h"
1533955d011SMarcel Moolenaar #include "dir.h"
1543955d011SMarcel Moolenaar #include "job.h"
1553955d011SMarcel Moolenaar #include "pathnames.h"
1563955d011SMarcel Moolenaar #include "trace.h"
157956e45f6SSimon J. Gerraty 
158956e45f6SSimon J. Gerraty /*	"@(#)job.c	8.2 (Berkeley) 3/19/94"	*/
159*e2eeea75SSimon J. Gerraty MAKE_RCSID("$NetBSD: job.c,v 1.326 2020/11/16 18:28:27 rillig Exp $");
160956e45f6SSimon J. Gerraty 
161956e45f6SSimon J. Gerraty /* A shell defines how the commands are run.  All commands for a target are
162956e45f6SSimon J. Gerraty  * written into a single file, which is then given to the shell to execute
163956e45f6SSimon J. Gerraty  * the commands from it.  The commands are written to the file using a few
164956e45f6SSimon J. Gerraty  * templates for echo control and error control.
165956e45f6SSimon J. Gerraty  *
166956e45f6SSimon J. Gerraty  * The name of the shell is the basename for the predefined shells, such as
167956e45f6SSimon J. Gerraty  * "sh", "csh", "bash".  For custom shells, it is the full pathname, and its
168956e45f6SSimon J. Gerraty  * basename is used to select the type of shell; the longest match wins.
169956e45f6SSimon J. Gerraty  * So /usr/pkg/bin/bash has type sh, /usr/local/bin/tcsh has type csh.
170956e45f6SSimon J. Gerraty  *
171956e45f6SSimon J. Gerraty  * The echoing of command lines is controlled using hasEchoCtl, echoOff,
172956e45f6SSimon J. Gerraty  * echoOn, noPrint and noPrintLen.  When echoOff is executed by the shell, it
173956e45f6SSimon J. Gerraty  * still outputs something, but this something is not interesting, therefore
174956e45f6SSimon J. Gerraty  * it is filtered out using noPrint and noPrintLen.
175956e45f6SSimon J. Gerraty  *
176956e45f6SSimon J. Gerraty  * The error checking for individual commands is controlled using hasErrCtl,
177956e45f6SSimon J. Gerraty  * errOnOrEcho, errOffOrExecIgnore and errExit.
178956e45f6SSimon J. Gerraty  *
179956e45f6SSimon J. Gerraty  * If a shell doesn't have error control, errOnOrEcho becomes a printf template
180956e45f6SSimon J. Gerraty  * for echoing the command, should echoing be on; errOffOrExecIgnore becomes
181956e45f6SSimon J. Gerraty  * another printf template for executing the command while ignoring the return
182956e45f6SSimon J. Gerraty  * status. Finally errExit is a printf template for running the command and
183956e45f6SSimon J. Gerraty  * causing the shell to exit on error. If any of these strings are empty when
184956e45f6SSimon J. Gerraty  * hasErrCtl is FALSE, the command will be executed anyway as is, and if it
185956e45f6SSimon J. Gerraty  * causes an error, so be it. Any templates set up to echo the command will
186956e45f6SSimon J. Gerraty  * escape any '$ ` \ "' characters in the command string to avoid common
187956e45f6SSimon J. Gerraty  * problems with echo "%s\n" as a template.
188956e45f6SSimon J. Gerraty  *
189956e45f6SSimon J. Gerraty  * The command-line flags "echo" and "exit" also control the behavior.  The
190956e45f6SSimon J. Gerraty  * "echo" flag causes the shell to start echoing commands right away.  The
191956e45f6SSimon J. Gerraty  * "exit" flag causes the shell to exit when an error is detected in one of
192956e45f6SSimon J. Gerraty  * the commands.
193956e45f6SSimon J. Gerraty  */
194956e45f6SSimon J. Gerraty typedef struct Shell {
195956e45f6SSimon J. Gerraty 
196956e45f6SSimon J. Gerraty     /* The name of the shell. For Bourne and C shells, this is used only to
197956e45f6SSimon J. Gerraty      * find the shell description when used as the single source of a .SHELL
198956e45f6SSimon J. Gerraty      * target. For user-defined shells, this is the full path of the shell. */
199956e45f6SSimon J. Gerraty     const char *name;
200956e45f6SSimon J. Gerraty 
201956e45f6SSimon J. Gerraty     Boolean hasEchoCtl;		/* True if both echoOff and echoOn defined */
202956e45f6SSimon J. Gerraty     const char *echoOff;	/* command to turn off echo */
203956e45f6SSimon J. Gerraty     const char *echoOn;		/* command to turn it back on again */
204956e45f6SSimon J. Gerraty     const char *noPrint;	/* text to skip when printing output from
205956e45f6SSimon J. Gerraty 				 * shell. This is usually the same as echoOff */
206956e45f6SSimon J. Gerraty     size_t noPrintLen;		/* length of noPrint command */
207956e45f6SSimon J. Gerraty 
208956e45f6SSimon J. Gerraty     Boolean hasErrCtl;		/* set if can control error checking for
209956e45f6SSimon J. Gerraty 				 * individual commands */
210956e45f6SSimon J. Gerraty     /* XXX: split into errOn and echoCmd */
211956e45f6SSimon J. Gerraty     const char *errOnOrEcho;	/* template to turn on error checking */
212956e45f6SSimon J. Gerraty     /* XXX: split into errOff and execIgnore */
213956e45f6SSimon J. Gerraty     const char *errOffOrExecIgnore; /* template to turn off error checking */
214956e45f6SSimon J. Gerraty     const char *errExit;	/* template to use for testing exit code */
215956e45f6SSimon J. Gerraty 
216956e45f6SSimon J. Gerraty     /* string literal that results in a newline character when it appears
217956e45f6SSimon J. Gerraty      * outside of any 'quote' or "quote" characters */
218956e45f6SSimon J. Gerraty     const char *newline;
219956e45f6SSimon J. Gerraty     char commentChar;		/* character used by shell for comment lines */
220956e45f6SSimon J. Gerraty 
221956e45f6SSimon J. Gerraty     /*
222956e45f6SSimon J. Gerraty      * command-line flags
223956e45f6SSimon J. Gerraty      */
224956e45f6SSimon J. Gerraty     const char *echo;		/* echo commands */
225956e45f6SSimon J. Gerraty     const char *exit;		/* exit on error */
226956e45f6SSimon J. Gerraty } Shell;
2273955d011SMarcel Moolenaar 
2283955d011SMarcel Moolenaar /*
2299a4bc556SSimon J. Gerraty  * FreeBSD: traditionally .MAKE is not required to
2309a4bc556SSimon J. Gerraty  * pass jobs queue to sub-makes.
2319a4bc556SSimon J. Gerraty  * Use .MAKE.ALWAYS_PASS_JOB_QUEUE=no to disable.
2329a4bc556SSimon J. Gerraty  */
2339a4bc556SSimon J. Gerraty #define MAKE_ALWAYS_PASS_JOB_QUEUE ".MAKE.ALWAYS_PASS_JOB_QUEUE"
2349a4bc556SSimon J. Gerraty static int Always_pass_job_queue = TRUE;
2352d395cb5SSimon J. Gerraty /*
2362d395cb5SSimon J. Gerraty  * FreeBSD: aborting entire parallel make isn't always
2372d395cb5SSimon J. Gerraty  * desired. When doing tinderbox for example, failure of
2382d395cb5SSimon J. Gerraty  * one architecture should not stop all.
2392d395cb5SSimon J. Gerraty  * We still want to bail on interrupt though.
2402d395cb5SSimon J. Gerraty  */
2412d395cb5SSimon J. Gerraty #define MAKE_JOB_ERROR_TOKEN "MAKE_JOB_ERROR_TOKEN"
2422d395cb5SSimon J. Gerraty static int Job_error_token = TRUE;
2439a4bc556SSimon J. Gerraty 
2449a4bc556SSimon J. Gerraty /*
2453955d011SMarcel Moolenaar  * error handling variables
2463955d011SMarcel Moolenaar  */
2473955d011SMarcel Moolenaar static int errors = 0;		/* number of errors reported */
248956e45f6SSimon J. Gerraty typedef enum AbortReason {	/* why is the make aborting? */
249956e45f6SSimon J. Gerraty     ABORT_NONE,
250956e45f6SSimon J. Gerraty     ABORT_ERROR,		/* Because of an error */
251956e45f6SSimon J. Gerraty     ABORT_INTERRUPT,		/* Because it was interrupted */
252956e45f6SSimon J. Gerraty     ABORT_WAIT			/* Waiting for jobs to finish */
253956e45f6SSimon J. Gerraty } AbortReason;
254956e45f6SSimon J. Gerraty static AbortReason aborting = ABORT_NONE;
2553955d011SMarcel Moolenaar #define JOB_TOKENS	"+EI+"	/* Token to requeue for each abort state */
2563955d011SMarcel Moolenaar 
2573955d011SMarcel Moolenaar /*
2583955d011SMarcel Moolenaar  * this tracks the number of tokens currently "out" to build jobs.
2593955d011SMarcel Moolenaar  */
2603955d011SMarcel Moolenaar int jobTokensRunning = 0;
2613955d011SMarcel Moolenaar 
262956e45f6SSimon J. Gerraty /* The number of commands actually printed to the shell commands file for
263956e45f6SSimon J. Gerraty  * the current job.  Should this number be 0, no shell will be executed. */
264956e45f6SSimon J. Gerraty static int numCommands;
2653955d011SMarcel Moolenaar 
266956e45f6SSimon J. Gerraty typedef enum JobStartResult {
267956e45f6SSimon J. Gerraty     JOB_RUNNING,		/* Job is running */
268956e45f6SSimon J. Gerraty     JOB_ERROR,			/* Error in starting the job */
269956e45f6SSimon J. Gerraty     JOB_FINISHED		/* The job is already finished */
270956e45f6SSimon J. Gerraty } JobStartResult;
2713955d011SMarcel Moolenaar 
2723955d011SMarcel Moolenaar /*
2733955d011SMarcel Moolenaar  * Descriptions for various shells.
2743955d011SMarcel Moolenaar  *
2753955d011SMarcel Moolenaar  * The build environment may set DEFSHELL_INDEX to one of
2763955d011SMarcel Moolenaar  * DEFSHELL_INDEX_SH, DEFSHELL_INDEX_KSH, or DEFSHELL_INDEX_CSH, to
277956e45f6SSimon J. Gerraty  * select one of the predefined shells as the default shell.
2783955d011SMarcel Moolenaar  *
2793955d011SMarcel Moolenaar  * Alternatively, the build environment may set DEFSHELL_CUSTOM to the
2803955d011SMarcel Moolenaar  * name or the full path of a sh-compatible shell, which will be used as
2813955d011SMarcel Moolenaar  * the default shell.
2823955d011SMarcel Moolenaar  *
2833955d011SMarcel Moolenaar  * ".SHELL" lines in Makefiles can choose the default shell from the
284956e45f6SSimon J. Gerraty  * set defined here, or add additional shells.
2853955d011SMarcel Moolenaar  */
2863955d011SMarcel Moolenaar 
2873955d011SMarcel Moolenaar #ifdef DEFSHELL_CUSTOM
2883955d011SMarcel Moolenaar #define DEFSHELL_INDEX_CUSTOM 0
2893955d011SMarcel Moolenaar #define DEFSHELL_INDEX_SH     1
2903955d011SMarcel Moolenaar #define DEFSHELL_INDEX_KSH    2
2913955d011SMarcel Moolenaar #define DEFSHELL_INDEX_CSH    3
2923955d011SMarcel Moolenaar #else /* !DEFSHELL_CUSTOM */
2933955d011SMarcel Moolenaar #define DEFSHELL_INDEX_SH     0
2943955d011SMarcel Moolenaar #define DEFSHELL_INDEX_KSH    1
2953955d011SMarcel Moolenaar #define DEFSHELL_INDEX_CSH    2
2963955d011SMarcel Moolenaar #endif /* !DEFSHELL_CUSTOM */
2973955d011SMarcel Moolenaar 
2983955d011SMarcel Moolenaar #ifndef DEFSHELL_INDEX
2993955d011SMarcel Moolenaar #define DEFSHELL_INDEX 0	/* DEFSHELL_INDEX_CUSTOM or DEFSHELL_INDEX_SH */
3003955d011SMarcel Moolenaar #endif /* !DEFSHELL_INDEX */
3013955d011SMarcel Moolenaar 
3023955d011SMarcel Moolenaar static Shell    shells[] = {
3033955d011SMarcel Moolenaar #ifdef DEFSHELL_CUSTOM
3043955d011SMarcel Moolenaar     /*
3053955d011SMarcel Moolenaar      * An sh-compatible shell with a non-standard name.
3063955d011SMarcel Moolenaar      *
3073955d011SMarcel Moolenaar      * Keep this in sync with the "sh" description below, but avoid
3083955d011SMarcel Moolenaar      * non-portable features that might not be supplied by all
3093955d011SMarcel Moolenaar      * sh-compatible shells.
3103955d011SMarcel Moolenaar      */
3113955d011SMarcel Moolenaar {
312956e45f6SSimon J. Gerraty     DEFSHELL_CUSTOM,		/* .name */
313956e45f6SSimon J. Gerraty     FALSE,			/* .hasEchoCtl */
314956e45f6SSimon J. Gerraty     "",				/* .echoOff */
315956e45f6SSimon J. Gerraty     "",				/* .echoOn */
316956e45f6SSimon J. Gerraty     "",				/* .noPrint */
317956e45f6SSimon J. Gerraty     0,				/* .noPrintLen */
318956e45f6SSimon J. Gerraty     FALSE,			/* .hasErrCtl */
319956e45f6SSimon J. Gerraty     "echo \"%s\"\n",		/* .errOnOrEcho */
320956e45f6SSimon J. Gerraty     "%s\n",			/* .errOffOrExecIgnore */
321956e45f6SSimon J. Gerraty     "{ %s \n} || exit $?\n",	/* .errExit */
322956e45f6SSimon J. Gerraty     "'\n'",			/* .newline */
323956e45f6SSimon J. Gerraty     '#',			/* .commentChar */
324956e45f6SSimon J. Gerraty     "",				/* .echo */
325956e45f6SSimon J. Gerraty     "",				/* .exit */
3263955d011SMarcel Moolenaar },
3273955d011SMarcel Moolenaar #endif /* DEFSHELL_CUSTOM */
3283955d011SMarcel Moolenaar     /*
3293955d011SMarcel Moolenaar      * SH description. Echo control is also possible and, under
3303955d011SMarcel Moolenaar      * sun UNIX anyway, one can even control error checking.
3313955d011SMarcel Moolenaar      */
3323955d011SMarcel Moolenaar {
333956e45f6SSimon J. Gerraty     "sh",			/* .name */
334956e45f6SSimon J. Gerraty     FALSE,			/* .hasEchoCtl */
335956e45f6SSimon J. Gerraty     "",				/* .echoOff */
336956e45f6SSimon J. Gerraty     "",				/* .echoOn */
337956e45f6SSimon J. Gerraty     "",				/* .noPrint */
338956e45f6SSimon J. Gerraty     0,				/* .noPrintLen */
339956e45f6SSimon J. Gerraty     FALSE,			/* .hasErrCtl */
340956e45f6SSimon J. Gerraty     "echo \"%s\"\n", 		/* .errOnOrEcho */
341956e45f6SSimon J. Gerraty     "%s\n",			/* .errOffOrExecIgnore */
342956e45f6SSimon J. Gerraty     "{ %s \n} || exit $?\n",	/* .errExit */
343956e45f6SSimon J. Gerraty     "'\n'",			/* .newline */
344956e45f6SSimon J. Gerraty     '#',			/* .commentChar*/
3453955d011SMarcel Moolenaar #if defined(MAKE_NATIVE) && defined(__NetBSD__)
346956e45f6SSimon J. Gerraty     "q",			/* .echo */
3473955d011SMarcel Moolenaar #else
348956e45f6SSimon J. Gerraty     "",				/* .echo */
3493955d011SMarcel Moolenaar #endif
350956e45f6SSimon J. Gerraty     "",				/* .exit */
3513955d011SMarcel Moolenaar },
3523955d011SMarcel Moolenaar     /*
3533955d011SMarcel Moolenaar      * KSH description.
3543955d011SMarcel Moolenaar      */
3553955d011SMarcel Moolenaar {
356956e45f6SSimon J. Gerraty     "ksh",			/* .name */
357956e45f6SSimon J. Gerraty     TRUE,			/* .hasEchoCtl */
358956e45f6SSimon J. Gerraty     "set +v",			/* .echoOff */
359956e45f6SSimon J. Gerraty     "set -v",			/* .echoOn */
360956e45f6SSimon J. Gerraty     "set +v",			/* .noPrint */
361956e45f6SSimon J. Gerraty     6,				/* .noPrintLen */
362956e45f6SSimon J. Gerraty     FALSE,			/* .hasErrCtl */
363956e45f6SSimon J. Gerraty     "echo \"%s\"\n",		/* .errOnOrEcho */
364956e45f6SSimon J. Gerraty     "%s\n",			/* .errOffOrExecIgnore */
365956e45f6SSimon J. Gerraty     "{ %s \n} || exit $?\n",	/* .errExit */
366956e45f6SSimon J. Gerraty     "'\n'",			/* .newline */
367956e45f6SSimon J. Gerraty     '#',			/* .commentChar */
368956e45f6SSimon J. Gerraty     "v",			/* .echo */
369956e45f6SSimon J. Gerraty     "",				/* .exit */
3703955d011SMarcel Moolenaar },
3713955d011SMarcel Moolenaar     /*
3723955d011SMarcel Moolenaar      * CSH description. The csh can do echo control by playing
3733955d011SMarcel Moolenaar      * with the setting of the 'echo' shell variable. Sadly,
3743955d011SMarcel Moolenaar      * however, it is unable to do error control nicely.
3753955d011SMarcel Moolenaar      */
3763955d011SMarcel Moolenaar {
377956e45f6SSimon J. Gerraty     "csh",			/* .name */
378956e45f6SSimon J. Gerraty     TRUE,			/* .hasEchoCtl */
379956e45f6SSimon J. Gerraty     "unset verbose",		/* .echoOff */
380956e45f6SSimon J. Gerraty     "set verbose",		/* .echoOn */
381956e45f6SSimon J. Gerraty     "unset verbose", 		/* .noPrint */
382956e45f6SSimon J. Gerraty     13,				/* .noPrintLen */
383956e45f6SSimon J. Gerraty     FALSE, 			/* .hasErrCtl */
384956e45f6SSimon J. Gerraty     "echo \"%s\"\n", 		/* .errOnOrEcho */
385956e45f6SSimon J. Gerraty     /* XXX: Mismatch between errOn and execIgnore */
386956e45f6SSimon J. Gerraty     "csh -c \"%s || exit 0\"\n", /* .errOffOrExecIgnore */
387956e45f6SSimon J. Gerraty     "", 			/* .errExit */
388956e45f6SSimon J. Gerraty     "'\\\n'",			/* .newline */
389956e45f6SSimon J. Gerraty     '#',			/* .commentChar */
390956e45f6SSimon J. Gerraty     "v", 			/* .echo */
391956e45f6SSimon J. Gerraty     "e",			/* .exit */
3923955d011SMarcel Moolenaar }
3933955d011SMarcel Moolenaar };
394956e45f6SSimon J. Gerraty 
395956e45f6SSimon J. Gerraty /* This is the shell to which we pass all commands in the Makefile.
396956e45f6SSimon J. Gerraty  * It is set by the Job_ParseShell function. */
397956e45f6SSimon J. Gerraty static Shell *commandShell = &shells[DEFSHELL_INDEX];
398956e45f6SSimon J. Gerraty const char *shellPath = NULL;	/* full pathname of executable image */
399956e45f6SSimon J. Gerraty const char *shellName = NULL;	/* last component of shellPath */
40051ee2c1cSSimon J. Gerraty char *shellErrFlag = NULL;
4012c3632d1SSimon J. Gerraty static char *shellArgv = NULL;	/* Custom shell args */
4023955d011SMarcel Moolenaar 
4033955d011SMarcel Moolenaar 
404956e45f6SSimon J. Gerraty static Job *job_table;		/* The structures that describe them */
405956e45f6SSimon J. Gerraty static Job *job_table_end;	/* job_table + maxJobs */
406956e45f6SSimon J. Gerraty static unsigned int wantToken;	/* we want a token */
407*e2eeea75SSimon J. Gerraty static Boolean lurking_children = FALSE;
408*e2eeea75SSimon J. Gerraty static Boolean make_suspended = FALSE;	/* Whether we've seen a SIGTSTP (etc) */
4093955d011SMarcel Moolenaar 
4103955d011SMarcel Moolenaar /*
4113955d011SMarcel Moolenaar  * Set of descriptors of pipes connected to
4123955d011SMarcel Moolenaar  * the output channels of children
4133955d011SMarcel Moolenaar  */
4143955d011SMarcel Moolenaar static struct pollfd *fds = NULL;
4153955d011SMarcel Moolenaar static Job **jobfds = NULL;
416956e45f6SSimon J. Gerraty static nfds_t nfds = 0;
4173955d011SMarcel Moolenaar static void watchfd(Job *);
4183955d011SMarcel Moolenaar static void clearfd(Job *);
4193955d011SMarcel Moolenaar static int readyfd(Job *);
4203955d011SMarcel Moolenaar 
421956e45f6SSimon J. Gerraty static GNode *lastNode;		/* The node for which output was most recently
4223955d011SMarcel Moolenaar 				 * produced. */
4233955d011SMarcel Moolenaar static char *targPrefix = NULL; /* What we print at the start of TARG_FMT */
4243955d011SMarcel Moolenaar static Job tokenWaitJob;	/* token wait pseudo-job */
4253955d011SMarcel Moolenaar 
4263955d011SMarcel Moolenaar static Job childExitJob;	/* child exit pseudo-job */
4273955d011SMarcel Moolenaar #define	CHILD_EXIT	"."
4283955d011SMarcel Moolenaar #define	DO_JOB_RESUME	"R"
4293955d011SMarcel Moolenaar 
430956e45f6SSimon J. Gerraty enum { npseudojobs = 2 };	/* number of pseudo-jobs */
43149caa483SSimon J. Gerraty 
4323955d011SMarcel Moolenaar #define TARG_FMT  "%s %s ---\n" /* Default format */
4333955d011SMarcel Moolenaar #define MESSAGE(fp, gn) \
434956e45f6SSimon J. Gerraty 	if (opts.maxJobs != 1 && targPrefix && *targPrefix) \
4353955d011SMarcel Moolenaar 	    (void)fprintf(fp, TARG_FMT, targPrefix, gn->name)
4363955d011SMarcel Moolenaar 
4373955d011SMarcel Moolenaar static sigset_t caught_signals;	/* Set of signals we handle */
4383955d011SMarcel Moolenaar 
4393955d011SMarcel Moolenaar static void JobDoOutput(Job *, Boolean);
4403955d011SMarcel Moolenaar static void JobInterrupt(int, int) MAKE_ATTR_DEAD;
4413955d011SMarcel Moolenaar static void JobRestartJobs(void);
4423955d011SMarcel Moolenaar static void JobSigReset(void);
4433955d011SMarcel Moolenaar 
44449caa483SSimon J. Gerraty static unsigned
44549caa483SSimon J. Gerraty nfds_per_job(void)
44649caa483SSimon J. Gerraty {
44749caa483SSimon J. Gerraty #if defined(USE_FILEMON) && !defined(USE_FILEMON_DEV)
44849caa483SSimon J. Gerraty     if (useMeta)
44949caa483SSimon J. Gerraty 	return 2;
45049caa483SSimon J. Gerraty #endif
45149caa483SSimon J. Gerraty     return 1;
45249caa483SSimon J. Gerraty }
45349caa483SSimon J. Gerraty 
4543955d011SMarcel Moolenaar static void
4553955d011SMarcel Moolenaar job_table_dump(const char *where)
4563955d011SMarcel Moolenaar {
4573955d011SMarcel Moolenaar     Job *job;
4583955d011SMarcel Moolenaar 
459956e45f6SSimon J. Gerraty     debug_printf("job table @ %s\n", where);
4603955d011SMarcel Moolenaar     for (job = job_table; job < job_table_end; job++) {
461956e45f6SSimon J. Gerraty 	debug_printf("job %d, status %d, flags %d, pid %d\n",
462*e2eeea75SSimon J. Gerraty 		     (int)(job - job_table), job->status, job->flags, job->pid);
4633955d011SMarcel Moolenaar     }
4643955d011SMarcel Moolenaar }
4653955d011SMarcel Moolenaar 
4663955d011SMarcel Moolenaar /*
46745447996SSimon J. Gerraty  * Delete the target of a failed, interrupted, or otherwise
46845447996SSimon J. Gerraty  * unsuccessful job unless inhibited by .PRECIOUS.
46945447996SSimon J. Gerraty  */
47045447996SSimon J. Gerraty static void
47145447996SSimon J. Gerraty JobDeleteTarget(GNode *gn)
47245447996SSimon J. Gerraty {
473956e45f6SSimon J. Gerraty     const char *file;
474956e45f6SSimon J. Gerraty 
475956e45f6SSimon J. Gerraty     if (gn->type & OP_JOIN)
476956e45f6SSimon J. Gerraty 	return;
477956e45f6SSimon J. Gerraty     if (gn->type & OP_PHONY)
478956e45f6SSimon J. Gerraty 	return;
479956e45f6SSimon J. Gerraty     if (Targ_Precious(gn))
480956e45f6SSimon J. Gerraty 	return;
481956e45f6SSimon J. Gerraty     if (opts.noExecute)
482956e45f6SSimon J. Gerraty 	return;
483956e45f6SSimon J. Gerraty 
484956e45f6SSimon J. Gerraty     file = GNode_Path(gn);
485956e45f6SSimon J. Gerraty     if (eunlink(file) != -1)
48645447996SSimon J. Gerraty 	Error("*** %s removed", file);
48745447996SSimon J. Gerraty }
48845447996SSimon J. Gerraty 
48945447996SSimon J. Gerraty /*
4903955d011SMarcel Moolenaar  * JobSigLock/JobSigUnlock
4913955d011SMarcel Moolenaar  *
4923955d011SMarcel Moolenaar  * Signal lock routines to get exclusive access. Currently used to
4933955d011SMarcel Moolenaar  * protect `jobs' and `stoppedJobs' list manipulations.
4943955d011SMarcel Moolenaar  */
4953955d011SMarcel Moolenaar static void JobSigLock(sigset_t *omaskp)
4963955d011SMarcel Moolenaar {
4973955d011SMarcel Moolenaar 	if (sigprocmask(SIG_BLOCK, &caught_signals, omaskp) != 0) {
4983955d011SMarcel Moolenaar 		Punt("JobSigLock: sigprocmask: %s", strerror(errno));
4993955d011SMarcel Moolenaar 		sigemptyset(omaskp);
5003955d011SMarcel Moolenaar 	}
5013955d011SMarcel Moolenaar }
5023955d011SMarcel Moolenaar 
5033955d011SMarcel Moolenaar static void JobSigUnlock(sigset_t *omaskp)
5043955d011SMarcel Moolenaar {
5053955d011SMarcel Moolenaar 	(void)sigprocmask(SIG_SETMASK, omaskp, NULL);
5063955d011SMarcel Moolenaar }
5073955d011SMarcel Moolenaar 
5083955d011SMarcel Moolenaar static void
5093955d011SMarcel Moolenaar JobCreatePipe(Job *job, int minfd)
5103955d011SMarcel Moolenaar {
511e1cee40dSSimon J. Gerraty     int i, fd, flags;
512956e45f6SSimon J. Gerraty     int pipe_fds[2];
5133955d011SMarcel Moolenaar 
514956e45f6SSimon J. Gerraty     if (pipe(pipe_fds) == -1)
5153955d011SMarcel Moolenaar 	Punt("Cannot create pipe: %s", strerror(errno));
5163955d011SMarcel Moolenaar 
51774d2e02bSSimon J. Gerraty     for (i = 0; i < 2; i++) {
51874d2e02bSSimon J. Gerraty 	/* Avoid using low numbered fds */
519956e45f6SSimon J. Gerraty 	fd = fcntl(pipe_fds[i], F_DUPFD, minfd);
52074d2e02bSSimon J. Gerraty 	if (fd != -1) {
521956e45f6SSimon J. Gerraty 	    close(pipe_fds[i]);
522956e45f6SSimon J. Gerraty 	    pipe_fds[i] = fd;
52374d2e02bSSimon J. Gerraty 	}
52474d2e02bSSimon J. Gerraty     }
52574d2e02bSSimon J. Gerraty 
526956e45f6SSimon J. Gerraty     job->inPipe = pipe_fds[0];
527956e45f6SSimon J. Gerraty     job->outPipe = pipe_fds[1];
528956e45f6SSimon J. Gerraty 
5293955d011SMarcel Moolenaar     /* Set close-on-exec flag for both */
530956e45f6SSimon J. Gerraty     if (fcntl(job->inPipe, F_SETFD, FD_CLOEXEC) == -1)
531e1cee40dSSimon J. Gerraty 	Punt("Cannot set close-on-exec: %s", strerror(errno));
532956e45f6SSimon J. Gerraty     if (fcntl(job->outPipe, F_SETFD, FD_CLOEXEC) == -1)
533e1cee40dSSimon J. Gerraty 	Punt("Cannot set close-on-exec: %s", strerror(errno));
5343955d011SMarcel Moolenaar 
5353955d011SMarcel Moolenaar     /*
5363955d011SMarcel Moolenaar      * We mark the input side of the pipe non-blocking; we poll(2) the
5373955d011SMarcel Moolenaar      * pipe when we're waiting for a job token, but we might lose the
5383955d011SMarcel Moolenaar      * race for the token when a new one becomes available, so the read
5393955d011SMarcel Moolenaar      * from the pipe should not block.
5403955d011SMarcel Moolenaar      */
541956e45f6SSimon J. Gerraty     flags = fcntl(job->inPipe, F_GETFL, 0);
542e1cee40dSSimon J. Gerraty     if (flags == -1)
543e1cee40dSSimon J. Gerraty 	Punt("Cannot get flags: %s", strerror(errno));
544e1cee40dSSimon J. Gerraty     flags |= O_NONBLOCK;
545956e45f6SSimon J. Gerraty     if (fcntl(job->inPipe, F_SETFL, flags) == -1)
546e1cee40dSSimon J. Gerraty 	Punt("Cannot set flags: %s", strerror(errno));
5473955d011SMarcel Moolenaar }
5483955d011SMarcel Moolenaar 
549956e45f6SSimon J. Gerraty /* Pass the signal to each running job. */
5503955d011SMarcel Moolenaar static void
5513955d011SMarcel Moolenaar JobCondPassSig(int signo)
5523955d011SMarcel Moolenaar {
5533955d011SMarcel Moolenaar     Job *job;
5543955d011SMarcel Moolenaar 
555956e45f6SSimon J. Gerraty     DEBUG1(JOB, "JobCondPassSig(%d) called.\n", signo);
5563955d011SMarcel Moolenaar 
5573955d011SMarcel Moolenaar     for (job = job_table; job < job_table_end; job++) {
558*e2eeea75SSimon J. Gerraty 	if (job->status != JOB_ST_RUNNING)
5593955d011SMarcel Moolenaar 	    continue;
560956e45f6SSimon J. Gerraty 	DEBUG2(JOB, "JobCondPassSig passing signal %d to child %d.\n",
5613955d011SMarcel Moolenaar 	       signo, job->pid);
5623955d011SMarcel Moolenaar 	KILLPG(job->pid, signo);
5633955d011SMarcel Moolenaar     }
5643955d011SMarcel Moolenaar }
5653955d011SMarcel Moolenaar 
566956e45f6SSimon J. Gerraty /* SIGCHLD handler.
5673955d011SMarcel Moolenaar  *
568956e45f6SSimon J. Gerraty  * Sends a token on the child exit pipe to wake us up from select()/poll(). */
5693955d011SMarcel Moolenaar static void
5703955d011SMarcel Moolenaar JobChildSig(int signo MAKE_ATTR_UNUSED)
5713955d011SMarcel Moolenaar {
5723cbdda60SSimon J. Gerraty     while (write(childExitJob.outPipe, CHILD_EXIT, 1) == -1 && errno == EAGAIN)
5733cbdda60SSimon J. Gerraty 	continue;
5743955d011SMarcel Moolenaar }
5753955d011SMarcel Moolenaar 
5763955d011SMarcel Moolenaar 
577956e45f6SSimon J. Gerraty /* Resume all stopped jobs. */
5783955d011SMarcel Moolenaar static void
5793955d011SMarcel Moolenaar JobContinueSig(int signo MAKE_ATTR_UNUSED)
5803955d011SMarcel Moolenaar {
5813955d011SMarcel Moolenaar     /*
582956e45f6SSimon J. Gerraty      * Defer sending SIGCONT to our stopped children until we return
5833955d011SMarcel Moolenaar      * from the signal handler.
5843955d011SMarcel Moolenaar      */
5853cbdda60SSimon J. Gerraty     while (write(childExitJob.outPipe, DO_JOB_RESUME, 1) == -1 &&
5863cbdda60SSimon J. Gerraty 	errno == EAGAIN)
5873cbdda60SSimon J. Gerraty 	continue;
5883955d011SMarcel Moolenaar }
5893955d011SMarcel Moolenaar 
590956e45f6SSimon J. Gerraty /* Pass a signal on to all jobs, then resend to ourselves.
591956e45f6SSimon J. Gerraty  * We die by the same signal. */
5923955d011SMarcel Moolenaar MAKE_ATTR_DEAD static void
5933955d011SMarcel Moolenaar JobPassSig_int(int signo)
5943955d011SMarcel Moolenaar {
5953955d011SMarcel Moolenaar     /* Run .INTERRUPT target then exit */
5963955d011SMarcel Moolenaar     JobInterrupt(TRUE, signo);
5973955d011SMarcel Moolenaar }
5983955d011SMarcel Moolenaar 
599956e45f6SSimon J. Gerraty /* Pass a signal on to all jobs, then resend to ourselves.
600956e45f6SSimon J. Gerraty  * We die by the same signal. */
6013955d011SMarcel Moolenaar MAKE_ATTR_DEAD static void
6023955d011SMarcel Moolenaar JobPassSig_term(int signo)
6033955d011SMarcel Moolenaar {
6043955d011SMarcel Moolenaar     /* Dont run .INTERRUPT target then exit */
6053955d011SMarcel Moolenaar     JobInterrupt(FALSE, signo);
6063955d011SMarcel Moolenaar }
6073955d011SMarcel Moolenaar 
6083955d011SMarcel Moolenaar static void
6093955d011SMarcel Moolenaar JobPassSig_suspend(int signo)
6103955d011SMarcel Moolenaar {
6113955d011SMarcel Moolenaar     sigset_t nmask, omask;
6123955d011SMarcel Moolenaar     struct sigaction act;
6133955d011SMarcel Moolenaar 
6143955d011SMarcel Moolenaar     /* Suppress job started/continued messages */
615*e2eeea75SSimon J. Gerraty     make_suspended = TRUE;
6163955d011SMarcel Moolenaar 
6173955d011SMarcel Moolenaar     /* Pass the signal onto every job */
6183955d011SMarcel Moolenaar     JobCondPassSig(signo);
6193955d011SMarcel Moolenaar 
6203955d011SMarcel Moolenaar     /*
6213955d011SMarcel Moolenaar      * Send ourselves the signal now we've given the message to everyone else.
6223955d011SMarcel Moolenaar      * Note we block everything else possible while we're getting the signal.
6233955d011SMarcel Moolenaar      * This ensures that all our jobs get continued when we wake up before
6243955d011SMarcel Moolenaar      * we take any other signal.
6253955d011SMarcel Moolenaar      */
6263955d011SMarcel Moolenaar     sigfillset(&nmask);
6273955d011SMarcel Moolenaar     sigdelset(&nmask, signo);
6283955d011SMarcel Moolenaar     (void)sigprocmask(SIG_SETMASK, &nmask, &omask);
6293955d011SMarcel Moolenaar 
6303955d011SMarcel Moolenaar     act.sa_handler = SIG_DFL;
6313955d011SMarcel Moolenaar     sigemptyset(&act.sa_mask);
6323955d011SMarcel Moolenaar     act.sa_flags = 0;
6333955d011SMarcel Moolenaar     (void)sigaction(signo, &act, NULL);
6343955d011SMarcel Moolenaar 
635956e45f6SSimon J. Gerraty     if (DEBUG(JOB))
636956e45f6SSimon J. Gerraty 	debug_printf("JobPassSig passing signal %d to self.\n", signo);
6373955d011SMarcel Moolenaar 
6383955d011SMarcel Moolenaar     (void)kill(getpid(), signo);
6393955d011SMarcel Moolenaar 
6403955d011SMarcel Moolenaar     /*
6413955d011SMarcel Moolenaar      * We've been continued.
6423955d011SMarcel Moolenaar      *
6433955d011SMarcel Moolenaar      * A whole host of signals continue to happen!
6443955d011SMarcel Moolenaar      * SIGCHLD for any processes that actually suspended themselves.
6453955d011SMarcel Moolenaar      * SIGCHLD for any processes that exited while we were alseep.
6463955d011SMarcel Moolenaar      * The SIGCONT that actually caused us to wakeup.
6473955d011SMarcel Moolenaar      *
6483955d011SMarcel Moolenaar      * Since we defer passing the SIGCONT on to our children until
6493955d011SMarcel Moolenaar      * the main processing loop, we can be sure that all the SIGCHLD
6503955d011SMarcel Moolenaar      * events will have happened by then - and that the waitpid() will
6513955d011SMarcel Moolenaar      * collect the child 'suspended' events.
6523955d011SMarcel Moolenaar      * For correct sequencing we just need to ensure we process the
653956e45f6SSimon J. Gerraty      * waitpid() before passing on the SIGCONT.
6543955d011SMarcel Moolenaar      *
6553955d011SMarcel Moolenaar      * In any case nothing else is needed here.
6563955d011SMarcel Moolenaar      */
6573955d011SMarcel Moolenaar 
6583955d011SMarcel Moolenaar     /* Restore handler and signal mask */
6593955d011SMarcel Moolenaar     act.sa_handler = JobPassSig_suspend;
6603955d011SMarcel Moolenaar     (void)sigaction(signo, &act, NULL);
6613955d011SMarcel Moolenaar     (void)sigprocmask(SIG_SETMASK, &omask, NULL);
6623955d011SMarcel Moolenaar }
6633955d011SMarcel Moolenaar 
6643955d011SMarcel Moolenaar static Job *
665*e2eeea75SSimon J. Gerraty JobFindPid(int pid, JobStatus status, Boolean isJobs)
6663955d011SMarcel Moolenaar {
6673955d011SMarcel Moolenaar     Job *job;
6683955d011SMarcel Moolenaar 
6693955d011SMarcel Moolenaar     for (job = job_table; job < job_table_end; job++) {
670*e2eeea75SSimon J. Gerraty 	if (job->status == status && job->pid == pid)
6713955d011SMarcel Moolenaar 	    return job;
6723955d011SMarcel Moolenaar     }
6733955d011SMarcel Moolenaar     if (DEBUG(JOB) && isJobs)
6743955d011SMarcel Moolenaar 	job_table_dump("no pid");
6753955d011SMarcel Moolenaar     return NULL;
6763955d011SMarcel Moolenaar }
6773955d011SMarcel Moolenaar 
678956e45f6SSimon J. Gerraty /* Parse leading '@', '-' and '+', which control the exact execution mode. */
679956e45f6SSimon J. Gerraty static void
680956e45f6SSimon J. Gerraty ParseRunOptions(
681956e45f6SSimon J. Gerraty 	char **pp,
682956e45f6SSimon J. Gerraty 	Boolean *out_shutUp, Boolean *out_errOff, Boolean *out_runAlways)
683956e45f6SSimon J. Gerraty {
684956e45f6SSimon J. Gerraty     char *p = *pp;
685956e45f6SSimon J. Gerraty     *out_shutUp = FALSE;
686956e45f6SSimon J. Gerraty     *out_errOff = FALSE;
687956e45f6SSimon J. Gerraty     *out_runAlways = FALSE;
688956e45f6SSimon J. Gerraty 
689956e45f6SSimon J. Gerraty     for (;;) {
690956e45f6SSimon J. Gerraty 	if (*p == '@')
691956e45f6SSimon J. Gerraty 	    *out_shutUp = !DEBUG(LOUD);
692956e45f6SSimon J. Gerraty 	else if (*p == '-')
693956e45f6SSimon J. Gerraty 	    *out_errOff = TRUE;
694956e45f6SSimon J. Gerraty 	else if (*p == '+')
695956e45f6SSimon J. Gerraty 	    *out_runAlways = TRUE;
696956e45f6SSimon J. Gerraty 	else
697956e45f6SSimon J. Gerraty 	    break;
698956e45f6SSimon J. Gerraty 	p++;
699956e45f6SSimon J. Gerraty     }
700956e45f6SSimon J. Gerraty 
701956e45f6SSimon J. Gerraty     pp_skip_whitespace(&p);
702956e45f6SSimon J. Gerraty 
703956e45f6SSimon J. Gerraty     *pp = p;
704956e45f6SSimon J. Gerraty }
705956e45f6SSimon J. Gerraty 
706956e45f6SSimon J. Gerraty /* Escape a string for a double-quoted string literal in sh, csh and ksh. */
707956e45f6SSimon J. Gerraty static char *
708956e45f6SSimon J. Gerraty EscapeShellDblQuot(const char *cmd)
709956e45f6SSimon J. Gerraty {
710956e45f6SSimon J. Gerraty     size_t i, j;
711956e45f6SSimon J. Gerraty 
712956e45f6SSimon J. Gerraty     /* Worst that could happen is every char needs escaping. */
713956e45f6SSimon J. Gerraty     char *esc = bmake_malloc(strlen(cmd) * 2 + 1);
714956e45f6SSimon J. Gerraty     for (i = 0, j = 0; cmd[i] != '\0'; i++, j++) {
715956e45f6SSimon J. Gerraty 	if (cmd[i] == '$' || cmd[i] == '`' || cmd[i] == '\\' || cmd[i] == '"')
716956e45f6SSimon J. Gerraty 	    esc[j++] = '\\';
717956e45f6SSimon J. Gerraty 	esc[j] = cmd[i];
718956e45f6SSimon J. Gerraty     }
719956e45f6SSimon J. Gerraty     esc[j] = '\0';
720956e45f6SSimon J. Gerraty 
721956e45f6SSimon J. Gerraty     return esc;
722956e45f6SSimon J. Gerraty }
723956e45f6SSimon J. Gerraty 
724*e2eeea75SSimon J. Gerraty static void
725*e2eeea75SSimon J. Gerraty JobPrintf(Job *job, const char *fmt, const char *arg)
726*e2eeea75SSimon J. Gerraty {
727*e2eeea75SSimon J. Gerraty     if (DEBUG(JOB))
728*e2eeea75SSimon J. Gerraty 	debug_printf(fmt, arg);
729*e2eeea75SSimon J. Gerraty 
730*e2eeea75SSimon J. Gerraty     (void)fprintf(job->cmdFILE, fmt, arg);
731*e2eeea75SSimon J. Gerraty     (void)fflush(job->cmdFILE);
732*e2eeea75SSimon J. Gerraty }
733*e2eeea75SSimon J. Gerraty 
734*e2eeea75SSimon J. Gerraty static void
735*e2eeea75SSimon J. Gerraty JobPrintln(Job *job, const char *line)
736*e2eeea75SSimon J. Gerraty {
737*e2eeea75SSimon J. Gerraty     JobPrintf(job, "%s\n", line);
738*e2eeea75SSimon J. Gerraty }
739*e2eeea75SSimon J. Gerraty 
7403955d011SMarcel Moolenaar /*-
7413955d011SMarcel Moolenaar  *-----------------------------------------------------------------------
7423955d011SMarcel Moolenaar  * JobPrintCommand  --
7433955d011SMarcel Moolenaar  *	Put out another command for the given job. If the command starts
7443955d011SMarcel Moolenaar  *	with an @ or a - we process it specially. In the former case,
7453955d011SMarcel Moolenaar  *	so long as the -s and -n flags weren't given to make, we stick
7463955d011SMarcel Moolenaar  *	a shell-specific echoOff command in the script. In the latter,
7473955d011SMarcel Moolenaar  *	we ignore errors for the entire job, unless the shell has error
7483955d011SMarcel Moolenaar  *	control.
7493955d011SMarcel Moolenaar  *	If the command is just "..." we take all future commands for this
7503955d011SMarcel Moolenaar  *	job to be commands to be executed once the entire graph has been
7513955d011SMarcel Moolenaar  *	made and return non-zero to signal that the end of the commands
752*e2eeea75SSimon J. Gerraty  *	was reached. These commands are later attached to the .END
7533955d011SMarcel Moolenaar  *	node and executed by Job_End when all things are done.
7543955d011SMarcel Moolenaar  *
7553955d011SMarcel Moolenaar  * Side Effects:
7563955d011SMarcel Moolenaar  *	If the command begins with a '-' and the shell has no error control,
7573955d011SMarcel Moolenaar  *	the JOB_IGNERR flag is set in the job descriptor.
7583955d011SMarcel Moolenaar  *	numCommands is incremented if the command is actually printed.
7593955d011SMarcel Moolenaar  *-----------------------------------------------------------------------
7603955d011SMarcel Moolenaar  */
761956e45f6SSimon J. Gerraty static void
762956e45f6SSimon J. Gerraty JobPrintCommand(Job *job, char *cmd)
7633955d011SMarcel Moolenaar {
764956e45f6SSimon J. Gerraty     const char *const cmdp = cmd;
7653955d011SMarcel Moolenaar     Boolean noSpecials;		/* true if we shouldn't worry about
7663955d011SMarcel Moolenaar 				 * inserting special commands into
7673955d011SMarcel Moolenaar 				 * the input stream. */
768956e45f6SSimon J. Gerraty     Boolean shutUp;		/* true if we put a no echo command
7693955d011SMarcel Moolenaar 				 * into the command file */
770956e45f6SSimon J. Gerraty     Boolean errOff;		/* true if we turned error checking
7713955d011SMarcel Moolenaar 				 * off before printing the command
7723955d011SMarcel Moolenaar 				 * and need to turn it back on */
773956e45f6SSimon J. Gerraty     Boolean runAlways;
7743955d011SMarcel Moolenaar     const char *cmdTemplate;	/* Template to use when printing the
7753955d011SMarcel Moolenaar 				 * command */
7763955d011SMarcel Moolenaar     char *cmdStart;		/* Start of expanded command */
7773955d011SMarcel Moolenaar     char *escCmd = NULL;	/* Command with quotes/backticks escaped */
7783955d011SMarcel Moolenaar 
779956e45f6SSimon J. Gerraty     noSpecials = !GNode_ShouldExecute(job->node);
7803955d011SMarcel Moolenaar 
781956e45f6SSimon J. Gerraty     numCommands++;
7823955d011SMarcel Moolenaar 
783956e45f6SSimon J. Gerraty     Var_Subst(cmd, job->node, VARE_WANTRES, &cmd);
784956e45f6SSimon J. Gerraty     /* TODO: handle errors */
785956e45f6SSimon J. Gerraty     cmdStart = cmd;
7863955d011SMarcel Moolenaar 
7873955d011SMarcel Moolenaar     cmdTemplate = "%s\n";
7883955d011SMarcel Moolenaar 
789956e45f6SSimon J. Gerraty     ParseRunOptions(&cmd, &shutUp, &errOff, &runAlways);
790956e45f6SSimon J. Gerraty 
791956e45f6SSimon J. Gerraty     if (runAlways && noSpecials) {
7923955d011SMarcel Moolenaar 	/*
7933955d011SMarcel Moolenaar 	 * We're not actually executing anything...
7943955d011SMarcel Moolenaar 	 * but this one needs to be - use compat mode just for it.
7953955d011SMarcel Moolenaar 	 */
796956e45f6SSimon J. Gerraty 	Compat_RunCommand(cmdp, job->node);
797e1cee40dSSimon J. Gerraty 	free(cmdStart);
798956e45f6SSimon J. Gerraty 	return;
7993955d011SMarcel Moolenaar     }
8003955d011SMarcel Moolenaar 
8013955d011SMarcel Moolenaar     /*
8023955d011SMarcel Moolenaar      * If the shell doesn't have error control the alternate echo'ing will
8033955d011SMarcel Moolenaar      * be done (to avoid showing additional error checking code)
8043955d011SMarcel Moolenaar      * and this will need the characters '$ ` \ "' escaped
8053955d011SMarcel Moolenaar      */
8063955d011SMarcel Moolenaar 
807956e45f6SSimon J. Gerraty     if (!commandShell->hasErrCtl)
808956e45f6SSimon J. Gerraty 	escCmd = EscapeShellDblQuot(cmd);
8093955d011SMarcel Moolenaar 
8103955d011SMarcel Moolenaar     if (shutUp) {
8113955d011SMarcel Moolenaar 	if (!(job->flags & JOB_SILENT) && !noSpecials &&
812*e2eeea75SSimon J. Gerraty 	    (commandShell->hasEchoCtl)) {
813*e2eeea75SSimon J. Gerraty 	    JobPrintln(job, commandShell->echoOff);
8143955d011SMarcel Moolenaar 	} else {
8153955d011SMarcel Moolenaar 	    if (commandShell->hasErrCtl)
8163955d011SMarcel Moolenaar 		shutUp = FALSE;
8173955d011SMarcel Moolenaar 	}
8183955d011SMarcel Moolenaar     }
8193955d011SMarcel Moolenaar 
8203955d011SMarcel Moolenaar     if (errOff) {
8213955d011SMarcel Moolenaar 	if (!noSpecials) {
8223955d011SMarcel Moolenaar 	    if (commandShell->hasErrCtl) {
8233955d011SMarcel Moolenaar 		/*
8243955d011SMarcel Moolenaar 		 * we don't want the error-control commands showing
8253955d011SMarcel Moolenaar 		 * up either, so we turn off echoing while executing
8263955d011SMarcel Moolenaar 		 * them. We could put another field in the shell
8273955d011SMarcel Moolenaar 		 * structure to tell JobDoOutput to look for this
8283955d011SMarcel Moolenaar 		 * string too, but why make it any more complex than
8293955d011SMarcel Moolenaar 		 * it already is?
8303955d011SMarcel Moolenaar 		 */
8313955d011SMarcel Moolenaar 		if (!(job->flags & JOB_SILENT) && !shutUp &&
832*e2eeea75SSimon J. Gerraty 		    (commandShell->hasEchoCtl)) {
833*e2eeea75SSimon J. Gerraty 		    JobPrintln(job, commandShell->echoOff);
834*e2eeea75SSimon J. Gerraty 		    JobPrintln(job, commandShell->errOffOrExecIgnore);
835*e2eeea75SSimon J. Gerraty 		    JobPrintln(job,  commandShell->echoOn);
8363955d011SMarcel Moolenaar 		} else {
837*e2eeea75SSimon J. Gerraty 		    JobPrintln(job, commandShell->errOffOrExecIgnore);
8383955d011SMarcel Moolenaar 		}
839956e45f6SSimon J. Gerraty 	    } else if (commandShell->errOffOrExecIgnore &&
840*e2eeea75SSimon J. Gerraty 		       commandShell->errOffOrExecIgnore[0] != '\0') {
8413955d011SMarcel Moolenaar 		/*
8423955d011SMarcel Moolenaar 		 * The shell has no error control, so we need to be
8433955d011SMarcel Moolenaar 		 * weird to get it to ignore any errors from the command.
8443955d011SMarcel Moolenaar 		 * If echoing is turned on, we turn it off and use the
845956e45f6SSimon J. Gerraty 		 * errOnOrEcho template to echo the command. Leave echoing
8463955d011SMarcel Moolenaar 		 * off so the user doesn't see the weirdness we go through
8473955d011SMarcel Moolenaar 		 * to ignore errors. Set cmdTemplate to use the weirdness
8483955d011SMarcel Moolenaar 		 * instead of the simple "%s\n" template.
8493955d011SMarcel Moolenaar 		 */
850db29cad8SSimon J. Gerraty 		job->flags |= JOB_IGNERR;
8513955d011SMarcel Moolenaar 		if (!(job->flags & JOB_SILENT) && !shutUp) {
8523955d011SMarcel Moolenaar 		    if (commandShell->hasEchoCtl) {
853*e2eeea75SSimon J. Gerraty 			JobPrintln(job, commandShell->echoOff);
8543955d011SMarcel Moolenaar 		    }
855*e2eeea75SSimon J. Gerraty 		    JobPrintf(job, commandShell->errOnOrEcho, escCmd);
8563955d011SMarcel Moolenaar 		    shutUp = TRUE;
8573955d011SMarcel Moolenaar 		} else {
858*e2eeea75SSimon J. Gerraty 		    if (!shutUp)
859*e2eeea75SSimon J. Gerraty 			JobPrintf(job, commandShell->errOnOrEcho, escCmd);
8603955d011SMarcel Moolenaar 		}
861956e45f6SSimon J. Gerraty 		cmdTemplate = commandShell->errOffOrExecIgnore;
8623955d011SMarcel Moolenaar 		/*
8633955d011SMarcel Moolenaar 		 * The error ignoration (hee hee) is already taken care
864956e45f6SSimon J. Gerraty 		 * of by the errOffOrExecIgnore template, so pretend error
865956e45f6SSimon J. Gerraty 		 * checking is still on.
8663955d011SMarcel Moolenaar 		 */
8673955d011SMarcel Moolenaar 		errOff = FALSE;
8683955d011SMarcel Moolenaar 	    } else {
8693955d011SMarcel Moolenaar 		errOff = FALSE;
8703955d011SMarcel Moolenaar 	    }
8713955d011SMarcel Moolenaar 	} else {
8723955d011SMarcel Moolenaar 	    errOff = FALSE;
8733955d011SMarcel Moolenaar 	}
8743955d011SMarcel Moolenaar     } else {
8753955d011SMarcel Moolenaar 
8763955d011SMarcel Moolenaar 	/*
8773955d011SMarcel Moolenaar 	 * If errors are being checked and the shell doesn't have error control
878956e45f6SSimon J. Gerraty 	 * but does supply an errExit template, then set up commands to run
8793955d011SMarcel Moolenaar 	 * through it.
8803955d011SMarcel Moolenaar 	 */
8813955d011SMarcel Moolenaar 
882956e45f6SSimon J. Gerraty 	if (!commandShell->hasErrCtl && commandShell->errExit &&
883956e45f6SSimon J. Gerraty 	    commandShell->errExit[0] != '\0') {
8843955d011SMarcel Moolenaar 	    if (!(job->flags & JOB_SILENT) && !shutUp) {
885*e2eeea75SSimon J. Gerraty 		if (commandShell->hasEchoCtl)
886*e2eeea75SSimon J. Gerraty 		    JobPrintln(job, commandShell->echoOff);
887*e2eeea75SSimon J. Gerraty 		JobPrintf(job, commandShell->errOnOrEcho, escCmd);
8883955d011SMarcel Moolenaar 		shutUp = TRUE;
8893955d011SMarcel Moolenaar 	    }
8903955d011SMarcel Moolenaar 	    /* If it's a comment line or blank, treat as an ignored error */
891*e2eeea75SSimon J. Gerraty 	    if (escCmd[0] == commandShell->commentChar ||
892*e2eeea75SSimon J. Gerraty 		(escCmd[0] == '\0'))
893956e45f6SSimon J. Gerraty 		cmdTemplate = commandShell->errOffOrExecIgnore;
8943955d011SMarcel Moolenaar 	    else
895956e45f6SSimon J. Gerraty 		cmdTemplate = commandShell->errExit;
8963955d011SMarcel Moolenaar 	    errOff = FALSE;
8973955d011SMarcel Moolenaar 	}
8983955d011SMarcel Moolenaar     }
8993955d011SMarcel Moolenaar 
9003955d011SMarcel Moolenaar     if (DEBUG(SHELL) && strcmp(shellName, "sh") == 0 &&
901*e2eeea75SSimon J. Gerraty 	!(job->flags & JOB_TRACED)) {
902*e2eeea75SSimon J. Gerraty 	JobPrintln(job, "set -x");
9033955d011SMarcel Moolenaar 	job->flags |= JOB_TRACED;
9043955d011SMarcel Moolenaar     }
9053955d011SMarcel Moolenaar 
906*e2eeea75SSimon J. Gerraty     JobPrintf(job, cmdTemplate, cmd);
9073955d011SMarcel Moolenaar     free(cmdStart);
9083955d011SMarcel Moolenaar     free(escCmd);
9093955d011SMarcel Moolenaar     if (errOff) {
9103955d011SMarcel Moolenaar 	/*
9113955d011SMarcel Moolenaar 	 * If echoing is already off, there's no point in issuing the
9123955d011SMarcel Moolenaar 	 * echoOff command. Otherwise we issue it and pretend it was on
9133955d011SMarcel Moolenaar 	 * for the whole command...
9143955d011SMarcel Moolenaar 	 */
9153955d011SMarcel Moolenaar 	if (!shutUp && !(job->flags & JOB_SILENT) && commandShell->hasEchoCtl) {
916*e2eeea75SSimon J. Gerraty 	    JobPrintln(job, commandShell->echoOff);
9173955d011SMarcel Moolenaar 	    shutUp = TRUE;
9183955d011SMarcel Moolenaar 	}
919*e2eeea75SSimon J. Gerraty 	JobPrintln(job, commandShell->errOnOrEcho);
9203955d011SMarcel Moolenaar     }
921*e2eeea75SSimon J. Gerraty     if (shutUp && commandShell->hasEchoCtl)
922*e2eeea75SSimon J. Gerraty 	JobPrintln(job, commandShell->echoOn);
9233955d011SMarcel Moolenaar }
9243955d011SMarcel Moolenaar 
925956e45f6SSimon J. Gerraty /* Print all commands to the shell file that is later executed.
9263955d011SMarcel Moolenaar  *
927956e45f6SSimon J. Gerraty  * The special command "..." stops printing and saves the remaining commands
928956e45f6SSimon J. Gerraty  * to be executed later. */
929956e45f6SSimon J. Gerraty static void
930956e45f6SSimon J. Gerraty JobPrintCommands(Job *job)
9313955d011SMarcel Moolenaar {
932956e45f6SSimon J. Gerraty     StringListNode *ln;
933956e45f6SSimon J. Gerraty 
934956e45f6SSimon J. Gerraty     for (ln = job->node->commands->first; ln != NULL; ln = ln->next) {
935956e45f6SSimon J. Gerraty 	const char *cmd = ln->datum;
936956e45f6SSimon J. Gerraty 
937956e45f6SSimon J. Gerraty 	if (strcmp(cmd, "...") == 0) {
938956e45f6SSimon J. Gerraty 	    job->node->type |= OP_SAVE_CMDS;
939956e45f6SSimon J. Gerraty 	    job->tailCmds = ln->next;
940956e45f6SSimon J. Gerraty 	    break;
941956e45f6SSimon J. Gerraty 	}
942*e2eeea75SSimon J. Gerraty 
943956e45f6SSimon J. Gerraty 	JobPrintCommand(job, ln->datum);
944956e45f6SSimon J. Gerraty     }
945956e45f6SSimon J. Gerraty }
946956e45f6SSimon J. Gerraty 
947956e45f6SSimon J. Gerraty /* Save the delayed commands, to be executed when everything else is done. */
948956e45f6SSimon J. Gerraty static void
949956e45f6SSimon J. Gerraty JobSaveCommands(Job *job)
950956e45f6SSimon J. Gerraty {
951956e45f6SSimon J. Gerraty     StringListNode *node;
952956e45f6SSimon J. Gerraty 
953956e45f6SSimon J. Gerraty     for (node = job->tailCmds; node != NULL; node = node->next) {
954956e45f6SSimon J. Gerraty 	const char *cmd = node->datum;
955956e45f6SSimon J. Gerraty 	char *expanded_cmd;
956956e45f6SSimon J. Gerraty 	/* XXX: This Var_Subst is only intended to expand the dynamic
957956e45f6SSimon J. Gerraty 	 * variables such as .TARGET, .IMPSRC.  It is not intended to
958956e45f6SSimon J. Gerraty 	 * expand the other variables as well; see deptgt-end.mk. */
959956e45f6SSimon J. Gerraty 	(void)Var_Subst(cmd, job->node, VARE_WANTRES, &expanded_cmd);
960956e45f6SSimon J. Gerraty 	/* TODO: handle errors */
961956e45f6SSimon J. Gerraty 	Lst_Append(Targ_GetEndNode()->commands, expanded_cmd);
962956e45f6SSimon J. Gerraty     }
9633955d011SMarcel Moolenaar }
9643955d011SMarcel Moolenaar 
9653955d011SMarcel Moolenaar 
966956e45f6SSimon J. Gerraty /* Called to close both input and output pipes when a job is finished. */
9673955d011SMarcel Moolenaar static void
968*e2eeea75SSimon J. Gerraty JobClosePipes(Job *job)
9693955d011SMarcel Moolenaar {
9703955d011SMarcel Moolenaar     clearfd(job);
9713955d011SMarcel Moolenaar     (void)close(job->outPipe);
9723955d011SMarcel Moolenaar     job->outPipe = -1;
9733955d011SMarcel Moolenaar 
9743955d011SMarcel Moolenaar     JobDoOutput(job, TRUE);
9753955d011SMarcel Moolenaar     (void)close(job->inPipe);
9763955d011SMarcel Moolenaar     job->inPipe = -1;
9773955d011SMarcel Moolenaar }
9783955d011SMarcel Moolenaar 
979*e2eeea75SSimon J. Gerraty /* Do final processing for the given job including updating parent nodes and
980*e2eeea75SSimon J. Gerraty  * starting new jobs as available/necessary.
981*e2eeea75SSimon J. Gerraty  *
982*e2eeea75SSimon J. Gerraty  * Deferred commands for the job are placed on the .END node.
983*e2eeea75SSimon J. Gerraty  *
984*e2eeea75SSimon J. Gerraty  * If there was a serious error (errors != 0; not an ignored one), no more
985*e2eeea75SSimon J. Gerraty  * jobs will be started.
9863955d011SMarcel Moolenaar  *
9873955d011SMarcel Moolenaar  * Input:
9883955d011SMarcel Moolenaar  *	job		job to finish
9893955d011SMarcel Moolenaar  *	status		sub-why job went away
9903955d011SMarcel Moolenaar  */
9913955d011SMarcel Moolenaar static void
9923955d011SMarcel Moolenaar JobFinish (Job *job, WAIT_T status)
9933955d011SMarcel Moolenaar {
9943955d011SMarcel Moolenaar     Boolean done, return_job_token;
9953955d011SMarcel Moolenaar 
996956e45f6SSimon J. Gerraty     DEBUG3(JOB, "JobFinish: %d [%s], status %d\n",
9973955d011SMarcel Moolenaar 	   job->pid, job->node->name, status);
9983955d011SMarcel Moolenaar 
9993955d011SMarcel Moolenaar     if ((WIFEXITED(status) &&
1000*e2eeea75SSimon J. Gerraty 	 ((WEXITSTATUS(status) != 0 && !(job->flags & JOB_IGNERR)))) ||
10013955d011SMarcel Moolenaar 	WIFSIGNALED(status))
10023955d011SMarcel Moolenaar     {
10033955d011SMarcel Moolenaar 	/*
10043955d011SMarcel Moolenaar 	 * If it exited non-zero and either we're doing things our
10053955d011SMarcel Moolenaar 	 * way or we're not ignoring errors, the job is finished.
10063955d011SMarcel Moolenaar 	 * Similarly, if the shell died because of a signal
10073955d011SMarcel Moolenaar 	 * the job is also finished. In these
10083955d011SMarcel Moolenaar 	 * cases, finish out the job's output before printing the exit
10093955d011SMarcel Moolenaar 	 * status...
10103955d011SMarcel Moolenaar 	 */
1011*e2eeea75SSimon J. Gerraty 	JobClosePipes(job);
10123955d011SMarcel Moolenaar 	if (job->cmdFILE != NULL && job->cmdFILE != stdout) {
10133955d011SMarcel Moolenaar 	   (void)fclose(job->cmdFILE);
10143955d011SMarcel Moolenaar 	   job->cmdFILE = NULL;
10153955d011SMarcel Moolenaar 	}
10163955d011SMarcel Moolenaar 	done = TRUE;
10173955d011SMarcel Moolenaar     } else if (WIFEXITED(status)) {
10183955d011SMarcel Moolenaar 	/*
10193955d011SMarcel Moolenaar 	 * Deal with ignored errors in -B mode. We need to print a message
1020*e2eeea75SSimon J. Gerraty 	 * telling of the ignored error as well as to run the next command.
1021*e2eeea75SSimon J. Gerraty 	 *
10223955d011SMarcel Moolenaar 	 */
10233955d011SMarcel Moolenaar 	done = WEXITSTATUS(status) != 0;
1024*e2eeea75SSimon J. Gerraty 	JobClosePipes(job);
10253955d011SMarcel Moolenaar     } else {
10263955d011SMarcel Moolenaar 	/*
10273955d011SMarcel Moolenaar 	 * No need to close things down or anything.
10283955d011SMarcel Moolenaar 	 */
10293955d011SMarcel Moolenaar 	done = FALSE;
10303955d011SMarcel Moolenaar     }
10313955d011SMarcel Moolenaar 
10323955d011SMarcel Moolenaar     if (done) {
10333955d011SMarcel Moolenaar 	if (WIFEXITED(status)) {
1034956e45f6SSimon J. Gerraty 	    DEBUG2(JOB, "Process %d [%s] exited.\n",
10353955d011SMarcel Moolenaar 		   job->pid, job->node->name);
10363955d011SMarcel Moolenaar 	    if (WEXITSTATUS(status) != 0) {
10373955d011SMarcel Moolenaar 		if (job->node != lastNode) {
10383955d011SMarcel Moolenaar 		    MESSAGE(stdout, job->node);
10393955d011SMarcel Moolenaar 		    lastNode = job->node;
10403955d011SMarcel Moolenaar 		}
10413955d011SMarcel Moolenaar #ifdef USE_META
10423955d011SMarcel Moolenaar 		if (useMeta) {
10433955d011SMarcel Moolenaar 		    meta_job_error(job, job->node, job->flags, WEXITSTATUS(status));
10443955d011SMarcel Moolenaar 		}
10453955d011SMarcel Moolenaar #endif
1046*e2eeea75SSimon J. Gerraty 		if (!shouldDieQuietly(job->node, -1))
10473955d011SMarcel Moolenaar 		    (void)printf("*** [%s] Error code %d%s\n",
10483955d011SMarcel Moolenaar 				 job->node->name,
10493955d011SMarcel Moolenaar 				 WEXITSTATUS(status),
10503955d011SMarcel Moolenaar 				 (job->flags & JOB_IGNERR) ? " (ignored)" : "");
10513955d011SMarcel Moolenaar 		if (job->flags & JOB_IGNERR) {
10523955d011SMarcel Moolenaar 		    WAIT_STATUS(status) = 0;
10533955d011SMarcel Moolenaar 		} else {
105445447996SSimon J. Gerraty 		    if (deleteOnError) {
105545447996SSimon J. Gerraty 			JobDeleteTarget(job->node);
105645447996SSimon J. Gerraty 		    }
10573955d011SMarcel Moolenaar 		    PrintOnError(job->node, NULL);
10583955d011SMarcel Moolenaar 		}
10593955d011SMarcel Moolenaar 	    } else if (DEBUG(JOB)) {
10603955d011SMarcel Moolenaar 		if (job->node != lastNode) {
10613955d011SMarcel Moolenaar 		    MESSAGE(stdout, job->node);
10623955d011SMarcel Moolenaar 		    lastNode = job->node;
10633955d011SMarcel Moolenaar 		}
10643955d011SMarcel Moolenaar 		(void)printf("*** [%s] Completed successfully\n",
10653955d011SMarcel Moolenaar 				job->node->name);
10663955d011SMarcel Moolenaar 	    }
10673955d011SMarcel Moolenaar 	} else {
10683955d011SMarcel Moolenaar 	    if (job->node != lastNode) {
10693955d011SMarcel Moolenaar 		MESSAGE(stdout, job->node);
10703955d011SMarcel Moolenaar 		lastNode = job->node;
10713955d011SMarcel Moolenaar 	    }
10723955d011SMarcel Moolenaar 	    (void)printf("*** [%s] Signal %d\n",
10733955d011SMarcel Moolenaar 			job->node->name, WTERMSIG(status));
107445447996SSimon J. Gerraty 	    if (deleteOnError) {
107545447996SSimon J. Gerraty 		JobDeleteTarget(job->node);
107645447996SSimon J. Gerraty 	    }
10773955d011SMarcel Moolenaar 	}
10783955d011SMarcel Moolenaar 	(void)fflush(stdout);
10793955d011SMarcel Moolenaar     }
10803955d011SMarcel Moolenaar 
10813955d011SMarcel Moolenaar #ifdef USE_META
10823955d011SMarcel Moolenaar     if (useMeta) {
1083*e2eeea75SSimon J. Gerraty 	int meta_status = meta_job_finish(job);
1084*e2eeea75SSimon J. Gerraty 	if (meta_status != 0 && status == 0)
1085*e2eeea75SSimon J. Gerraty 	    status = meta_status;
10863955d011SMarcel Moolenaar     }
10873955d011SMarcel Moolenaar #endif
10883955d011SMarcel Moolenaar 
10893955d011SMarcel Moolenaar     return_job_token = FALSE;
10903955d011SMarcel Moolenaar 
10913955d011SMarcel Moolenaar     Trace_Log(JOBEND, job);
10923955d011SMarcel Moolenaar     if (!(job->flags & JOB_SPECIAL)) {
1093*e2eeea75SSimon J. Gerraty 	    if (WAIT_STATUS(status) != 0 ||
1094*e2eeea75SSimon J. Gerraty 		(aborting == ABORT_ERROR) || aborting == ABORT_INTERRUPT)
10953955d011SMarcel Moolenaar 	    return_job_token = TRUE;
10963955d011SMarcel Moolenaar     }
10973955d011SMarcel Moolenaar 
1098*e2eeea75SSimon J. Gerraty     if (aborting != ABORT_ERROR && aborting != ABORT_INTERRUPT &&
10993955d011SMarcel Moolenaar 	(WAIT_STATUS(status) == 0)) {
11003955d011SMarcel Moolenaar 	/*
11013955d011SMarcel Moolenaar 	 * As long as we aren't aborting and the job didn't return a non-zero
11023955d011SMarcel Moolenaar 	 * status that we shouldn't ignore, we call Make_Update to update
1103956e45f6SSimon J. Gerraty 	 * the parents.
11043955d011SMarcel Moolenaar 	 */
1105956e45f6SSimon J. Gerraty 	JobSaveCommands(job);
11063955d011SMarcel Moolenaar 	job->node->made = MADE;
11073955d011SMarcel Moolenaar 	if (!(job->flags & JOB_SPECIAL))
11083955d011SMarcel Moolenaar 	    return_job_token = TRUE;
11093955d011SMarcel Moolenaar 	Make_Update(job->node);
1110*e2eeea75SSimon J. Gerraty 	job->status = JOB_ST_FREE;
11113955d011SMarcel Moolenaar     } else if (WAIT_STATUS(status)) {
1112956e45f6SSimon J. Gerraty 	errors++;
1113*e2eeea75SSimon J. Gerraty 	job->status = JOB_ST_FREE;
11143955d011SMarcel Moolenaar     }
11153955d011SMarcel Moolenaar 
1116*e2eeea75SSimon J. Gerraty     if (errors > 0 && !opts.keepgoing && aborting != ABORT_INTERRUPT)
1117*e2eeea75SSimon J. Gerraty 	aborting = ABORT_ERROR;	/* Prevent more jobs from getting started. */
11183955d011SMarcel Moolenaar 
11193955d011SMarcel Moolenaar     if (return_job_token)
11203955d011SMarcel Moolenaar 	Job_TokenReturn();
11213955d011SMarcel Moolenaar 
1122*e2eeea75SSimon J. Gerraty     if (aborting == ABORT_ERROR && jobTokensRunning == 0)
11233955d011SMarcel Moolenaar 	Finish(errors);
11243955d011SMarcel Moolenaar }
1125*e2eeea75SSimon J. Gerraty 
1126*e2eeea75SSimon J. Gerraty static void
1127*e2eeea75SSimon J. Gerraty TouchRegular(GNode *gn)
1128*e2eeea75SSimon J. Gerraty {
1129*e2eeea75SSimon J. Gerraty     const char *file = GNode_Path(gn);
1130*e2eeea75SSimon J. Gerraty     struct utimbuf times = { now, now };
1131*e2eeea75SSimon J. Gerraty     int fd;
1132*e2eeea75SSimon J. Gerraty     char c;
1133*e2eeea75SSimon J. Gerraty 
1134*e2eeea75SSimon J. Gerraty     if (utime(file, &times) >= 0)
1135*e2eeea75SSimon J. Gerraty 	return;
1136*e2eeea75SSimon J. Gerraty 
1137*e2eeea75SSimon J. Gerraty     fd = open(file, O_RDWR | O_CREAT, 0666);
1138*e2eeea75SSimon J. Gerraty     if (fd < 0) {
1139*e2eeea75SSimon J. Gerraty 	(void)fprintf(stderr, "*** couldn't touch %s: %s\n",
1140*e2eeea75SSimon J. Gerraty 		      file, strerror(errno));
1141*e2eeea75SSimon J. Gerraty 	(void)fflush(stderr);
1142*e2eeea75SSimon J. Gerraty 	return;                /* XXX: What about propagating the error? */
1143*e2eeea75SSimon J. Gerraty     }
1144*e2eeea75SSimon J. Gerraty 
1145*e2eeea75SSimon J. Gerraty     /* Last resort: update the file's time stamps in the traditional way.
1146*e2eeea75SSimon J. Gerraty      * XXX: This doesn't work for empty files, which are sometimes used
1147*e2eeea75SSimon J. Gerraty      * as marker files. */
1148*e2eeea75SSimon J. Gerraty     if (read(fd, &c, 1) == 1) {
1149*e2eeea75SSimon J. Gerraty 	(void)lseek(fd, 0, SEEK_SET);
1150*e2eeea75SSimon J. Gerraty 	while (write(fd, &c, 1) == -1 && errno == EAGAIN)
1151*e2eeea75SSimon J. Gerraty 	    continue;
1152*e2eeea75SSimon J. Gerraty     }
1153*e2eeea75SSimon J. Gerraty     (void)close(fd);		/* XXX: What about propagating the error? */
11543955d011SMarcel Moolenaar }
11553955d011SMarcel Moolenaar 
1156956e45f6SSimon J. Gerraty /* Touch the given target. Called by JobStart when the -t flag was given.
11573955d011SMarcel Moolenaar  *
1158956e45f6SSimon J. Gerraty  * The modification date of the file is changed.
1159956e45f6SSimon J. Gerraty  * If the file did not exist, it is created. */
11603955d011SMarcel Moolenaar void
11613955d011SMarcel Moolenaar Job_Touch(GNode *gn, Boolean silent)
11623955d011SMarcel Moolenaar {
11633955d011SMarcel Moolenaar     if (gn->type & (OP_JOIN|OP_USE|OP_USEBEFORE|OP_EXEC|OP_OPTIONAL|
11643955d011SMarcel Moolenaar 	OP_SPECIAL|OP_PHONY)) {
1165*e2eeea75SSimon J. Gerraty 	/* These are "virtual" targets and should not really be created. */
11663955d011SMarcel Moolenaar 	return;
11673955d011SMarcel Moolenaar     }
11683955d011SMarcel Moolenaar 
1169956e45f6SSimon J. Gerraty     if (!silent || !GNode_ShouldExecute(gn)) {
11703955d011SMarcel Moolenaar 	(void)fprintf(stdout, "touch %s\n", gn->name);
11713955d011SMarcel Moolenaar 	(void)fflush(stdout);
11723955d011SMarcel Moolenaar     }
11733955d011SMarcel Moolenaar 
1174*e2eeea75SSimon J. Gerraty     if (!GNode_ShouldExecute(gn))
11753955d011SMarcel Moolenaar 	return;
11763955d011SMarcel Moolenaar 
11773955d011SMarcel Moolenaar     if (gn->type & OP_ARCHV) {
11783955d011SMarcel Moolenaar 	Arch_Touch(gn);
1179*e2eeea75SSimon J. Gerraty 	return;
1180*e2eeea75SSimon J. Gerraty     }
1181*e2eeea75SSimon J. Gerraty 
1182*e2eeea75SSimon J. Gerraty     if (gn->type & OP_LIB) {
11833955d011SMarcel Moolenaar 	Arch_TouchLib(gn);
1184*e2eeea75SSimon J. Gerraty 	return;
11853955d011SMarcel Moolenaar     }
11863955d011SMarcel Moolenaar 
1187*e2eeea75SSimon J. Gerraty     TouchRegular(gn);
11883955d011SMarcel Moolenaar }
11893955d011SMarcel Moolenaar 
1190956e45f6SSimon J. Gerraty /* Make sure the given node has all the commands it needs.
1191956e45f6SSimon J. Gerraty  *
1192956e45f6SSimon J. Gerraty  * The node will have commands from the .DEFAULT rule added to it if it
1193956e45f6SSimon J. Gerraty  * needs them.
11943955d011SMarcel Moolenaar  *
11953955d011SMarcel Moolenaar  * Input:
11963955d011SMarcel Moolenaar  *	gn		The target whose commands need verifying
11973955d011SMarcel Moolenaar  *	abortProc	Function to abort with message
11983955d011SMarcel Moolenaar  *
11993955d011SMarcel Moolenaar  * Results:
12003955d011SMarcel Moolenaar  *	TRUE if the commands list is/was ok.
12013955d011SMarcel Moolenaar  */
12023955d011SMarcel Moolenaar Boolean
12033955d011SMarcel Moolenaar Job_CheckCommands(GNode *gn, void (*abortProc)(const char *, ...))
12043955d011SMarcel Moolenaar {
1205956e45f6SSimon J. Gerraty     if (GNode_IsTarget(gn))
1206956e45f6SSimon J. Gerraty 	return TRUE;
1207956e45f6SSimon J. Gerraty     if (!Lst_IsEmpty(gn->commands))
1208956e45f6SSimon J. Gerraty 	return TRUE;
1209956e45f6SSimon J. Gerraty     if ((gn->type & OP_LIB) && !Lst_IsEmpty(gn->children))
1210956e45f6SSimon J. Gerraty 	return TRUE;
1211956e45f6SSimon J. Gerraty 
12123955d011SMarcel Moolenaar     /*
12133955d011SMarcel Moolenaar      * No commands. Look for .DEFAULT rule from which we might infer
1214*e2eeea75SSimon J. Gerraty      * commands.
12153955d011SMarcel Moolenaar      */
1216*e2eeea75SSimon J. Gerraty     if (defaultNode != NULL && !Lst_IsEmpty(defaultNode->commands) &&
1217*e2eeea75SSimon J. Gerraty 	!(gn->type & OP_SPECIAL)) {
12183955d011SMarcel Moolenaar 	/*
1219*e2eeea75SSimon J. Gerraty 	 * The traditional Make only looks for a .DEFAULT if the node was
1220*e2eeea75SSimon J. Gerraty 	 * never the target of an operator, so that's what we do too.
1221*e2eeea75SSimon J. Gerraty 	 *
1222*e2eeea75SSimon J. Gerraty 	 * The .DEFAULT node acts like a transformation rule, in that
12233955d011SMarcel Moolenaar 	 * gn also inherits any attributes or sources attached to
12243955d011SMarcel Moolenaar 	 * .DEFAULT itself.
12253955d011SMarcel Moolenaar 	 */
1226*e2eeea75SSimon J. Gerraty 	Make_HandleUse(defaultNode, gn);
1227956e45f6SSimon J. Gerraty 	Var_Set(IMPSRC, GNode_VarTarget(gn), gn);
1228956e45f6SSimon J. Gerraty 	return TRUE;
1229956e45f6SSimon J. Gerraty     }
1230956e45f6SSimon J. Gerraty 
1231*e2eeea75SSimon J. Gerraty     Dir_UpdateMTime(gn, FALSE);
1232*e2eeea75SSimon J. Gerraty     if (gn->mtime != 0 || (gn->type & OP_SPECIAL))
1233956e45f6SSimon J. Gerraty 	return TRUE;
1234956e45f6SSimon J. Gerraty 
12353955d011SMarcel Moolenaar     /*
1236956e45f6SSimon J. Gerraty      * The node wasn't the target of an operator.  We have no .DEFAULT
12373955d011SMarcel Moolenaar      * rule to go on and the target doesn't already exist. There's
12383955d011SMarcel Moolenaar      * nothing more we can do for this branch. If the -k flag wasn't
12393955d011SMarcel Moolenaar      * given, we stop in our tracks, otherwise we just don't update
12403955d011SMarcel Moolenaar      * this node's parents so they never get examined.
12413955d011SMarcel Moolenaar      */
12423955d011SMarcel Moolenaar 
12433955d011SMarcel Moolenaar     if (gn->flags & FROM_DEPEND) {
12441748de26SSimon J. Gerraty 	if (!Job_RunTarget(".STALE", gn->fname))
12451748de26SSimon J. Gerraty 	    fprintf(stdout, "%s: %s, %d: ignoring stale %s for %s\n",
12461748de26SSimon J. Gerraty 		    progname, gn->fname, gn->lineno, makeDependfile,
12471748de26SSimon J. Gerraty 		    gn->name);
12483955d011SMarcel Moolenaar 	return TRUE;
12493955d011SMarcel Moolenaar     }
12503955d011SMarcel Moolenaar 
12513955d011SMarcel Moolenaar     if (gn->type & OP_OPTIONAL) {
1252956e45f6SSimon J. Gerraty 	(void)fprintf(stdout, "%s: don't know how to make %s (%s)\n",
1253956e45f6SSimon J. Gerraty 		      progname, gn->name, "ignored");
12543955d011SMarcel Moolenaar 	(void)fflush(stdout);
12553955d011SMarcel Moolenaar 	return TRUE;
12563955d011SMarcel Moolenaar     }
12573955d011SMarcel Moolenaar 
1258956e45f6SSimon J. Gerraty     if (opts.keepgoing) {
1259956e45f6SSimon J. Gerraty 	(void)fprintf(stdout, "%s: don't know how to make %s (%s)\n",
1260956e45f6SSimon J. Gerraty 		      progname, gn->name, "continuing");
1261956e45f6SSimon J. Gerraty 	(void)fflush(stdout);
1262956e45f6SSimon J. Gerraty 	return FALSE;
1263956e45f6SSimon J. Gerraty     }
1264956e45f6SSimon J. Gerraty 
1265956e45f6SSimon J. Gerraty     abortProc("%s: don't know how to make %s. Stop", progname, gn->name);
1266956e45f6SSimon J. Gerraty     return FALSE;
1267956e45f6SSimon J. Gerraty }
1268956e45f6SSimon J. Gerraty 
1269956e45f6SSimon J. Gerraty /* Execute the shell for the given job.
12703955d011SMarcel Moolenaar  *
1271*e2eeea75SSimon J. Gerraty  * See Job_CatchOutput for handling the output of the shell. */
12723955d011SMarcel Moolenaar static void
12733955d011SMarcel Moolenaar JobExec(Job *job, char **argv)
12743955d011SMarcel Moolenaar {
12753955d011SMarcel Moolenaar     int cpid;			/* ID of new child */
12763955d011SMarcel Moolenaar     sigset_t mask;
12773955d011SMarcel Moolenaar 
12783955d011SMarcel Moolenaar     job->flags &= ~JOB_TRACED;
12793955d011SMarcel Moolenaar 
12803955d011SMarcel Moolenaar     if (DEBUG(JOB)) {
12813955d011SMarcel Moolenaar 	int i;
12823955d011SMarcel Moolenaar 
1283*e2eeea75SSimon J. Gerraty 	debug_printf("Running %s\n", job->node->name);
1284956e45f6SSimon J. Gerraty 	debug_printf("\tCommand: ");
12853955d011SMarcel Moolenaar 	for (i = 0; argv[i] != NULL; i++) {
1286956e45f6SSimon J. Gerraty 	    debug_printf("%s ", argv[i]);
12873955d011SMarcel Moolenaar 	}
1288956e45f6SSimon J. Gerraty 	debug_printf("\n");
12893955d011SMarcel Moolenaar     }
12903955d011SMarcel Moolenaar 
12913955d011SMarcel Moolenaar     /*
12923955d011SMarcel Moolenaar      * Some jobs produce no output and it's disconcerting to have
12933955d011SMarcel Moolenaar      * no feedback of their running (since they produce no output, the
12943955d011SMarcel Moolenaar      * banner with their name in it never appears). This is an attempt to
12953955d011SMarcel Moolenaar      * provide that feedback, even if nothing follows it.
12963955d011SMarcel Moolenaar      */
12973955d011SMarcel Moolenaar     if ((lastNode != job->node) && !(job->flags & JOB_SILENT)) {
12983955d011SMarcel Moolenaar 	MESSAGE(stdout, job->node);
12993955d011SMarcel Moolenaar 	lastNode = job->node;
13003955d011SMarcel Moolenaar     }
13013955d011SMarcel Moolenaar 
13023955d011SMarcel Moolenaar     /* No interruptions until this job is on the `jobs' list */
13033955d011SMarcel Moolenaar     JobSigLock(&mask);
13043955d011SMarcel Moolenaar 
13053955d011SMarcel Moolenaar     /* Pre-emptively mark job running, pid still zero though */
1306*e2eeea75SSimon J. Gerraty     job->status = JOB_ST_RUNNING;
13073955d011SMarcel Moolenaar 
13083955d011SMarcel Moolenaar     cpid = vFork();
13093955d011SMarcel Moolenaar     if (cpid == -1)
13103955d011SMarcel Moolenaar 	Punt("Cannot vfork: %s", strerror(errno));
13113955d011SMarcel Moolenaar 
13123955d011SMarcel Moolenaar     if (cpid == 0) {
13133955d011SMarcel Moolenaar 	/* Child */
13143955d011SMarcel Moolenaar 	sigset_t tmask;
13153955d011SMarcel Moolenaar 
13163955d011SMarcel Moolenaar #ifdef USE_META
13173955d011SMarcel Moolenaar 	if (useMeta) {
13183955d011SMarcel Moolenaar 	    meta_job_child(job);
13193955d011SMarcel Moolenaar 	}
13203955d011SMarcel Moolenaar #endif
13213955d011SMarcel Moolenaar 	/*
13223955d011SMarcel Moolenaar 	 * Reset all signal handlers; this is necessary because we also
13233955d011SMarcel Moolenaar 	 * need to unblock signals before we exec(2).
13243955d011SMarcel Moolenaar 	 */
13253955d011SMarcel Moolenaar 	JobSigReset();
13263955d011SMarcel Moolenaar 
13273955d011SMarcel Moolenaar 	/* Now unblock signals */
13283955d011SMarcel Moolenaar 	sigemptyset(&tmask);
13293955d011SMarcel Moolenaar 	JobSigUnlock(&tmask);
13303955d011SMarcel Moolenaar 
13313955d011SMarcel Moolenaar 	/*
13323955d011SMarcel Moolenaar 	 * Must duplicate the input stream down to the child's input and
13333955d011SMarcel Moolenaar 	 * reset it to the beginning (again). Since the stream was marked
13343955d011SMarcel Moolenaar 	 * close-on-exec, we must clear that bit in the new input.
13353955d011SMarcel Moolenaar 	 */
1336956e45f6SSimon J. Gerraty 	if (dup2(fileno(job->cmdFILE), 0) == -1)
1337956e45f6SSimon J. Gerraty 	    execDie("dup2", "job->cmdFILE");
1338956e45f6SSimon J. Gerraty 	if (fcntl(0, F_SETFD, 0) == -1)
1339956e45f6SSimon J. Gerraty 	    execDie("fcntl clear close-on-exec", "stdin");
1340*e2eeea75SSimon J. Gerraty 	if (lseek(0, 0, SEEK_SET) == -1)
1341956e45f6SSimon J. Gerraty 	    execDie("lseek to 0", "stdin");
13423955d011SMarcel Moolenaar 
1343db29cad8SSimon J. Gerraty 	if (Always_pass_job_queue ||
1344db29cad8SSimon J. Gerraty 	    (job->node->type & (OP_MAKE | OP_SUBMAKE))) {
13453955d011SMarcel Moolenaar 	    /*
13463955d011SMarcel Moolenaar 	     * Pass job token pipe to submakes.
13473955d011SMarcel Moolenaar 	     */
1348956e45f6SSimon J. Gerraty 	    if (fcntl(tokenWaitJob.inPipe, F_SETFD, 0) == -1)
1349956e45f6SSimon J. Gerraty 		execDie("clear close-on-exec", "tokenWaitJob.inPipe");
1350956e45f6SSimon J. Gerraty 	    if (fcntl(tokenWaitJob.outPipe, F_SETFD, 0) == -1)
1351956e45f6SSimon J. Gerraty 		execDie("clear close-on-exec", "tokenWaitJob.outPipe");
13523955d011SMarcel Moolenaar 	}
13533955d011SMarcel Moolenaar 
13543955d011SMarcel Moolenaar 	/*
13553955d011SMarcel Moolenaar 	 * Set up the child's output to be routed through the pipe
13563955d011SMarcel Moolenaar 	 * we've created for it.
13573955d011SMarcel Moolenaar 	 */
1358956e45f6SSimon J. Gerraty 	if (dup2(job->outPipe, 1) == -1)
1359956e45f6SSimon J. Gerraty 	    execDie("dup2", "job->outPipe");
1360956e45f6SSimon J. Gerraty 
13613955d011SMarcel Moolenaar 	/*
13623955d011SMarcel Moolenaar 	 * The output channels are marked close on exec. This bit was
13633955d011SMarcel Moolenaar 	 * duplicated by the dup2(on some systems), so we have to clear
13643955d011SMarcel Moolenaar 	 * it before routing the shell's error output to the same place as
13653955d011SMarcel Moolenaar 	 * its standard output.
13663955d011SMarcel Moolenaar 	 */
1367956e45f6SSimon J. Gerraty 	if (fcntl(1, F_SETFD, 0) == -1)
1368956e45f6SSimon J. Gerraty 	    execDie("clear close-on-exec", "stdout");
1369956e45f6SSimon J. Gerraty 	if (dup2(1, 2) == -1)
1370956e45f6SSimon J. Gerraty 	    execDie("dup2", "1, 2");
13713955d011SMarcel Moolenaar 
13723955d011SMarcel Moolenaar 	/*
13733955d011SMarcel Moolenaar 	 * We want to switch the child into a different process family so
13743955d011SMarcel Moolenaar 	 * we can kill it and all its descendants in one fell swoop,
13753955d011SMarcel Moolenaar 	 * by killing its process family, but not commit suicide.
13763955d011SMarcel Moolenaar 	 */
13773955d011SMarcel Moolenaar #if defined(HAVE_SETPGID)
13783955d011SMarcel Moolenaar 	(void)setpgid(0, getpid());
13793955d011SMarcel Moolenaar #else
13803955d011SMarcel Moolenaar #if defined(HAVE_SETSID)
13813955d011SMarcel Moolenaar 	/* XXX: dsl - I'm sure this should be setpgrp()... */
13823955d011SMarcel Moolenaar 	(void)setsid();
13833955d011SMarcel Moolenaar #else
13843955d011SMarcel Moolenaar 	(void)setpgrp(0, getpid());
13853955d011SMarcel Moolenaar #endif
13863955d011SMarcel Moolenaar #endif
13873955d011SMarcel Moolenaar 
13883955d011SMarcel Moolenaar 	Var_ExportVars();
13893955d011SMarcel Moolenaar 
13903955d011SMarcel Moolenaar 	(void)execv(shellPath, argv);
1391956e45f6SSimon J. Gerraty 	execDie("exec", shellPath);
13923955d011SMarcel Moolenaar     }
13933955d011SMarcel Moolenaar 
13943955d011SMarcel Moolenaar     /* Parent, continuing after the child exec */
13953955d011SMarcel Moolenaar     job->pid = cpid;
13963955d011SMarcel Moolenaar 
13973955d011SMarcel Moolenaar     Trace_Log(JOBSTART, job);
13983955d011SMarcel Moolenaar 
139949caa483SSimon J. Gerraty #ifdef USE_META
140049caa483SSimon J. Gerraty     if (useMeta) {
140149caa483SSimon J. Gerraty 	meta_job_parent(job, cpid);
140249caa483SSimon J. Gerraty     }
140349caa483SSimon J. Gerraty #endif
140449caa483SSimon J. Gerraty 
14053955d011SMarcel Moolenaar     /*
14063955d011SMarcel Moolenaar      * Set the current position in the buffer to the beginning
14073955d011SMarcel Moolenaar      * and mark another stream to watch in the outputs mask
14083955d011SMarcel Moolenaar      */
14093955d011SMarcel Moolenaar     job->curPos = 0;
14103955d011SMarcel Moolenaar 
14113955d011SMarcel Moolenaar     watchfd(job);
14123955d011SMarcel Moolenaar 
14133955d011SMarcel Moolenaar     if (job->cmdFILE != NULL && job->cmdFILE != stdout) {
14143955d011SMarcel Moolenaar 	(void)fclose(job->cmdFILE);
14153955d011SMarcel Moolenaar 	job->cmdFILE = NULL;
14163955d011SMarcel Moolenaar     }
14173955d011SMarcel Moolenaar 
14183955d011SMarcel Moolenaar     /*
14193955d011SMarcel Moolenaar      * Now the job is actually running, add it to the table.
14203955d011SMarcel Moolenaar      */
14213955d011SMarcel Moolenaar     if (DEBUG(JOB)) {
1422956e45f6SSimon J. Gerraty 	debug_printf("JobExec(%s): pid %d added to jobs table\n",
14233955d011SMarcel Moolenaar 		     job->node->name, job->pid);
14243955d011SMarcel Moolenaar 	job_table_dump("job started");
14253955d011SMarcel Moolenaar     }
14263955d011SMarcel Moolenaar     JobSigUnlock(&mask);
14273955d011SMarcel Moolenaar }
14283955d011SMarcel Moolenaar 
1429956e45f6SSimon J. Gerraty /* Create the argv needed to execute the shell for a given job. */
14303955d011SMarcel Moolenaar static void
14313955d011SMarcel Moolenaar JobMakeArgv(Job *job, char **argv)
14323955d011SMarcel Moolenaar {
14333955d011SMarcel Moolenaar     int argc;
14343955d011SMarcel Moolenaar     static char args[10];	/* For merged arguments */
14353955d011SMarcel Moolenaar 
14363955d011SMarcel Moolenaar     argv[0] = UNCONST(shellName);
14373955d011SMarcel Moolenaar     argc = 1;
14383955d011SMarcel Moolenaar 
1439956e45f6SSimon J. Gerraty     if ((commandShell->exit && commandShell->exit[0] != '-') ||
1440956e45f6SSimon J. Gerraty 	(commandShell->echo && commandShell->echo[0] != '-'))
14413955d011SMarcel Moolenaar     {
14423955d011SMarcel Moolenaar 	/*
14433955d011SMarcel Moolenaar 	 * At least one of the flags doesn't have a minus before it, so
14443955d011SMarcel Moolenaar 	 * merge them together. Have to do this because the *(&(@*#*&#$#
14453955d011SMarcel Moolenaar 	 * Bourne shell thinks its second argument is a file to source.
14463955d011SMarcel Moolenaar 	 * Grrrr. Note the ten-character limitation on the combined arguments.
14473955d011SMarcel Moolenaar 	 */
1448*e2eeea75SSimon J. Gerraty 	(void)snprintf(args, sizeof args, "-%s%s",
14493955d011SMarcel Moolenaar 		      ((job->flags & JOB_IGNERR) ? "" :
14503955d011SMarcel Moolenaar 		       (commandShell->exit ? commandShell->exit : "")),
14513955d011SMarcel Moolenaar 		      ((job->flags & JOB_SILENT) ? "" :
14523955d011SMarcel Moolenaar 		       (commandShell->echo ? commandShell->echo : "")));
14533955d011SMarcel Moolenaar 
14543955d011SMarcel Moolenaar 	if (args[1]) {
14553955d011SMarcel Moolenaar 	    argv[argc] = args;
14563955d011SMarcel Moolenaar 	    argc++;
14573955d011SMarcel Moolenaar 	}
14583955d011SMarcel Moolenaar     } else {
14593955d011SMarcel Moolenaar 	if (!(job->flags & JOB_IGNERR) && commandShell->exit) {
14603955d011SMarcel Moolenaar 	    argv[argc] = UNCONST(commandShell->exit);
14613955d011SMarcel Moolenaar 	    argc++;
14623955d011SMarcel Moolenaar 	}
14633955d011SMarcel Moolenaar 	if (!(job->flags & JOB_SILENT) && commandShell->echo) {
14643955d011SMarcel Moolenaar 	    argv[argc] = UNCONST(commandShell->echo);
14653955d011SMarcel Moolenaar 	    argc++;
14663955d011SMarcel Moolenaar 	}
14673955d011SMarcel Moolenaar     }
14683955d011SMarcel Moolenaar     argv[argc] = NULL;
14693955d011SMarcel Moolenaar }
14703955d011SMarcel Moolenaar 
14713955d011SMarcel Moolenaar /*-
14723955d011SMarcel Moolenaar  *-----------------------------------------------------------------------
14733955d011SMarcel Moolenaar  * JobStart  --
14743955d011SMarcel Moolenaar  *	Start a target-creation process going for the target described
14753955d011SMarcel Moolenaar  *	by the graph node gn.
14763955d011SMarcel Moolenaar  *
14773955d011SMarcel Moolenaar  * Input:
14783955d011SMarcel Moolenaar  *	gn		target to create
14793955d011SMarcel Moolenaar  *	flags		flags for the job to override normal ones.
14803955d011SMarcel Moolenaar  *	previous	The previous Job structure for this node, if any.
14813955d011SMarcel Moolenaar  *
14823955d011SMarcel Moolenaar  * Results:
14833955d011SMarcel Moolenaar  *	JOB_ERROR if there was an error in the commands, JOB_FINISHED
14843955d011SMarcel Moolenaar  *	if there isn't actually anything left to do for the job and
14853955d011SMarcel Moolenaar  *	JOB_RUNNING if the job has been started.
14863955d011SMarcel Moolenaar  *
14873955d011SMarcel Moolenaar  * Side Effects:
14883955d011SMarcel Moolenaar  *	A new Job node is created and added to the list of running
14893955d011SMarcel Moolenaar  *	jobs. PMake is forked and a child shell created.
14903955d011SMarcel Moolenaar  *
1491*e2eeea75SSimon J. Gerraty  * NB: The return value is ignored by everyone.
14923955d011SMarcel Moolenaar  *-----------------------------------------------------------------------
14933955d011SMarcel Moolenaar  */
1494956e45f6SSimon J. Gerraty static JobStartResult
1495*e2eeea75SSimon J. Gerraty JobStart(GNode *gn, JobFlags flags)
14963955d011SMarcel Moolenaar {
14973955d011SMarcel Moolenaar     Job *job;			/* new job descriptor */
14983955d011SMarcel Moolenaar     char *argv[10];		/* Argument vector to shell */
14993955d011SMarcel Moolenaar     Boolean cmdsOK;		/* true if the nodes commands were all right */
15003955d011SMarcel Moolenaar     Boolean noExec;		/* Set true if we decide not to run the job */
15013955d011SMarcel Moolenaar     int tfd;			/* File descriptor to the temp file */
15023955d011SMarcel Moolenaar 
15033955d011SMarcel Moolenaar     for (job = job_table; job < job_table_end; job++) {
1504*e2eeea75SSimon J. Gerraty 	if (job->status == JOB_ST_FREE)
15053955d011SMarcel Moolenaar 	    break;
15063955d011SMarcel Moolenaar     }
15073955d011SMarcel Moolenaar     if (job >= job_table_end)
15083955d011SMarcel Moolenaar 	Punt("JobStart no job slots vacant");
15093955d011SMarcel Moolenaar 
15103955d011SMarcel Moolenaar     memset(job, 0, sizeof *job);
15113955d011SMarcel Moolenaar     job->node = gn;
15123955d011SMarcel Moolenaar     job->tailCmds = NULL;
1513*e2eeea75SSimon J. Gerraty     job->status = JOB_ST_SET_UP;
15143955d011SMarcel Moolenaar 
1515*e2eeea75SSimon J. Gerraty     if (gn->type & OP_SPECIAL)
1516*e2eeea75SSimon J. Gerraty 	flags |= JOB_SPECIAL;
1517*e2eeea75SSimon J. Gerraty     if (Targ_Ignore(gn))
1518*e2eeea75SSimon J. Gerraty 	flags |= JOB_IGNERR;
1519*e2eeea75SSimon J. Gerraty     if (Targ_Silent(gn))
1520*e2eeea75SSimon J. Gerraty 	flags |= JOB_SILENT;
1521*e2eeea75SSimon J. Gerraty     job->flags = flags;
15223955d011SMarcel Moolenaar 
15233955d011SMarcel Moolenaar     /*
15243955d011SMarcel Moolenaar      * Check the commands now so any attributes from .DEFAULT have a chance
15253955d011SMarcel Moolenaar      * to migrate to the node
15263955d011SMarcel Moolenaar      */
15273955d011SMarcel Moolenaar     cmdsOK = Job_CheckCommands(gn, Error);
15283955d011SMarcel Moolenaar 
15293955d011SMarcel Moolenaar     job->inPollfd = NULL;
15303955d011SMarcel Moolenaar     /*
15313955d011SMarcel Moolenaar      * If the -n flag wasn't given, we open up OUR (not the child's)
15323955d011SMarcel Moolenaar      * temporary file to stuff commands in it. The thing is rd/wr so we don't
15333955d011SMarcel Moolenaar      * need to reopen it to feed it to the shell. If the -n flag *was* given,
15343955d011SMarcel Moolenaar      * we just set the file to be stdout. Cute, huh?
15353955d011SMarcel Moolenaar      */
1536956e45f6SSimon J. Gerraty     if (((gn->type & OP_MAKE) && !opts.noRecursiveExecute) ||
1537956e45f6SSimon J. Gerraty 	(!opts.noExecute && !opts.touchFlag)) {
15383955d011SMarcel Moolenaar 	/*
15393955d011SMarcel Moolenaar 	 * tfile is the name of a file into which all shell commands are
15403955d011SMarcel Moolenaar 	 * put. It is removed before the child shell is executed, unless
15413955d011SMarcel Moolenaar 	 * DEBUG(SCRIPT) is set.
15423955d011SMarcel Moolenaar 	 */
15433955d011SMarcel Moolenaar 	char *tfile;
15443955d011SMarcel Moolenaar 	sigset_t mask;
15453955d011SMarcel Moolenaar 	/*
15463955d011SMarcel Moolenaar 	 * We're serious here, but if the commands were bogus, we're
15473955d011SMarcel Moolenaar 	 * also dead...
15483955d011SMarcel Moolenaar 	 */
15493955d011SMarcel Moolenaar 	if (!cmdsOK) {
15503955d011SMarcel Moolenaar 	    PrintOnError(gn, NULL);	/* provide some clue */
15513955d011SMarcel Moolenaar 	    DieHorribly();
15523955d011SMarcel Moolenaar 	}
15533955d011SMarcel Moolenaar 
15543955d011SMarcel Moolenaar 	JobSigLock(&mask);
15553955d011SMarcel Moolenaar 	tfd = mkTempFile(TMPPAT, &tfile);
15563955d011SMarcel Moolenaar 	if (!DEBUG(SCRIPT))
15573955d011SMarcel Moolenaar 	    (void)eunlink(tfile);
15583955d011SMarcel Moolenaar 	JobSigUnlock(&mask);
15593955d011SMarcel Moolenaar 
15603955d011SMarcel Moolenaar 	job->cmdFILE = fdopen(tfd, "w+");
1561*e2eeea75SSimon J. Gerraty 	if (job->cmdFILE == NULL)
15623955d011SMarcel Moolenaar 	    Punt("Could not fdopen %s", tfile);
1563*e2eeea75SSimon J. Gerraty 
1564956e45f6SSimon J. Gerraty 	(void)fcntl(fileno(job->cmdFILE), F_SETFD, FD_CLOEXEC);
15653955d011SMarcel Moolenaar 	/*
15663955d011SMarcel Moolenaar 	 * Send the commands to the command file, flush all its buffers then
15673955d011SMarcel Moolenaar 	 * rewind and remove the thing.
15683955d011SMarcel Moolenaar 	 */
15693955d011SMarcel Moolenaar 	noExec = FALSE;
15703955d011SMarcel Moolenaar 
15713955d011SMarcel Moolenaar #ifdef USE_META
15723955d011SMarcel Moolenaar 	if (useMeta) {
15733955d011SMarcel Moolenaar 	    meta_job_start(job, gn);
1574*e2eeea75SSimon J. Gerraty 	    if (Targ_Silent(gn))	/* might have changed */
15753955d011SMarcel Moolenaar 		job->flags |= JOB_SILENT;
15763955d011SMarcel Moolenaar 	}
15773955d011SMarcel Moolenaar #endif
15783955d011SMarcel Moolenaar 	/*
15793955d011SMarcel Moolenaar 	 * We can do all the commands at once. hooray for sanity
15803955d011SMarcel Moolenaar 	 */
15813955d011SMarcel Moolenaar 	numCommands = 0;
1582956e45f6SSimon J. Gerraty 	JobPrintCommands(job);
15833955d011SMarcel Moolenaar 
15843955d011SMarcel Moolenaar 	/*
15853955d011SMarcel Moolenaar 	 * If we didn't print out any commands to the shell script,
15863955d011SMarcel Moolenaar 	 * there's not much point in executing the shell, is there?
15873955d011SMarcel Moolenaar 	 */
15883955d011SMarcel Moolenaar 	if (numCommands == 0) {
15893955d011SMarcel Moolenaar 	    noExec = TRUE;
15903955d011SMarcel Moolenaar 	}
15913955d011SMarcel Moolenaar 
15923955d011SMarcel Moolenaar 	free(tfile);
1593956e45f6SSimon J. Gerraty     } else if (!GNode_ShouldExecute(gn)) {
15943955d011SMarcel Moolenaar 	/*
15953955d011SMarcel Moolenaar 	 * Not executing anything -- just print all the commands to stdout
15963955d011SMarcel Moolenaar 	 * in one fell swoop. This will still set up job->tailCmds correctly.
15973955d011SMarcel Moolenaar 	 */
15983955d011SMarcel Moolenaar 	if (lastNode != gn) {
15993955d011SMarcel Moolenaar 	    MESSAGE(stdout, gn);
16003955d011SMarcel Moolenaar 	    lastNode = gn;
16013955d011SMarcel Moolenaar 	}
16023955d011SMarcel Moolenaar 	job->cmdFILE = stdout;
16033955d011SMarcel Moolenaar 	/*
16043955d011SMarcel Moolenaar 	 * Only print the commands if they're ok, but don't die if they're
16053955d011SMarcel Moolenaar 	 * not -- just let the user know they're bad and keep going. It
16063955d011SMarcel Moolenaar 	 * doesn't do any harm in this case and may do some good.
16073955d011SMarcel Moolenaar 	 */
1608956e45f6SSimon J. Gerraty 	if (cmdsOK)
1609956e45f6SSimon J. Gerraty 	    JobPrintCommands(job);
16103955d011SMarcel Moolenaar 	/*
16113955d011SMarcel Moolenaar 	 * Don't execute the shell, thank you.
16123955d011SMarcel Moolenaar 	 */
16133955d011SMarcel Moolenaar 	noExec = TRUE;
16143955d011SMarcel Moolenaar     } else {
16153955d011SMarcel Moolenaar 	/*
16163955d011SMarcel Moolenaar 	 * Just touch the target and note that no shell should be executed.
16173955d011SMarcel Moolenaar 	 * Set cmdFILE to stdout to make life easier. Check the commands, too,
16183955d011SMarcel Moolenaar 	 * but don't die if they're no good -- it does no harm to keep working
16193955d011SMarcel Moolenaar 	 * up the graph.
16203955d011SMarcel Moolenaar 	 */
16213955d011SMarcel Moolenaar 	job->cmdFILE = stdout;
16223955d011SMarcel Moolenaar 	Job_Touch(gn, job->flags & JOB_SILENT);
16233955d011SMarcel Moolenaar 	noExec = TRUE;
16243955d011SMarcel Moolenaar     }
16253955d011SMarcel Moolenaar     /* Just in case it isn't already... */
16263955d011SMarcel Moolenaar     (void)fflush(job->cmdFILE);
16273955d011SMarcel Moolenaar 
16283955d011SMarcel Moolenaar     /*
16293955d011SMarcel Moolenaar      * If we're not supposed to execute a shell, don't.
16303955d011SMarcel Moolenaar      */
16313955d011SMarcel Moolenaar     if (noExec) {
16323955d011SMarcel Moolenaar 	if (!(job->flags & JOB_SPECIAL))
16333955d011SMarcel Moolenaar 	    Job_TokenReturn();
16343955d011SMarcel Moolenaar 	/*
16353955d011SMarcel Moolenaar 	 * Unlink and close the command file if we opened one
16363955d011SMarcel Moolenaar 	 */
1637*e2eeea75SSimon J. Gerraty 	if (job->cmdFILE != NULL && job->cmdFILE != stdout) {
16383955d011SMarcel Moolenaar 	    (void)fclose(job->cmdFILE);
16393955d011SMarcel Moolenaar 	    job->cmdFILE = NULL;
16403955d011SMarcel Moolenaar 	}
16413955d011SMarcel Moolenaar 
16423955d011SMarcel Moolenaar 	/*
16433955d011SMarcel Moolenaar 	 * We only want to work our way up the graph if we aren't here because
16443955d011SMarcel Moolenaar 	 * the commands for the job were no good.
16453955d011SMarcel Moolenaar 	 */
1646956e45f6SSimon J. Gerraty 	if (cmdsOK && aborting == ABORT_NONE) {
1647956e45f6SSimon J. Gerraty 	    JobSaveCommands(job);
16483955d011SMarcel Moolenaar 	    job->node->made = MADE;
16493955d011SMarcel Moolenaar 	    Make_Update(job->node);
16503955d011SMarcel Moolenaar 	}
1651*e2eeea75SSimon J. Gerraty 	job->status = JOB_ST_FREE;
16523955d011SMarcel Moolenaar 	return cmdsOK ? JOB_FINISHED : JOB_ERROR;
16533955d011SMarcel Moolenaar     }
16543955d011SMarcel Moolenaar 
16553955d011SMarcel Moolenaar     /*
16563955d011SMarcel Moolenaar      * Set up the control arguments to the shell. This is based on the flags
16573955d011SMarcel Moolenaar      * set earlier for this job.
16583955d011SMarcel Moolenaar      */
16593955d011SMarcel Moolenaar     JobMakeArgv(job, argv);
16603955d011SMarcel Moolenaar 
16613955d011SMarcel Moolenaar     /* Create the pipe by which we'll get the shell's output.  */
16623955d011SMarcel Moolenaar     JobCreatePipe(job, 3);
16633955d011SMarcel Moolenaar 
16643955d011SMarcel Moolenaar     JobExec(job, argv);
16653841c287SSimon J. Gerraty     return JOB_RUNNING;
16663955d011SMarcel Moolenaar }
16673955d011SMarcel Moolenaar 
1668*e2eeea75SSimon J. Gerraty /* Print the output of the shell command, skipping the noPrint command of
1669*e2eeea75SSimon J. Gerraty  * the shell, if any. */
16703955d011SMarcel Moolenaar static char *
1671956e45f6SSimon J. Gerraty JobOutput(Job *job, char *cp, char *endp)
16723955d011SMarcel Moolenaar {
16733955d011SMarcel Moolenaar     char *ecp;
16743955d011SMarcel Moolenaar 
1675*e2eeea75SSimon J. Gerraty     if (commandShell->noPrint == NULL || commandShell->noPrint[0] == '\0')
1676*e2eeea75SSimon J. Gerraty 	return cp;
1677*e2eeea75SSimon J. Gerraty 
1678956e45f6SSimon J. Gerraty     while ((ecp = strstr(cp, commandShell->noPrint)) != NULL) {
1679*e2eeea75SSimon J. Gerraty 	if (ecp != cp) {
16803955d011SMarcel Moolenaar 	    *ecp = '\0';
16813955d011SMarcel Moolenaar 	    /*
16823955d011SMarcel Moolenaar 	     * The only way there wouldn't be a newline after
16833955d011SMarcel Moolenaar 	     * this line is if it were the last in the buffer.
16843955d011SMarcel Moolenaar 	     * however, since the non-printable comes after it,
16853955d011SMarcel Moolenaar 	     * there must be a newline, so we don't print one.
16863955d011SMarcel Moolenaar 	     */
16873955d011SMarcel Moolenaar 	    (void)fprintf(stdout, "%s", cp);
16883955d011SMarcel Moolenaar 	    (void)fflush(stdout);
16893955d011SMarcel Moolenaar 	}
1690956e45f6SSimon J. Gerraty 	cp = ecp + commandShell->noPrintLen;
16913955d011SMarcel Moolenaar 	if (cp != endp) {
16923955d011SMarcel Moolenaar 	    /*
16933955d011SMarcel Moolenaar 	     * Still more to print, look again after skipping
16943955d011SMarcel Moolenaar 	     * the whitespace following the non-printable
16953955d011SMarcel Moolenaar 	     * command....
16963955d011SMarcel Moolenaar 	     */
16973955d011SMarcel Moolenaar 	    cp++;
1698*e2eeea75SSimon J. Gerraty 	    pp_skip_whitespace(&cp);
16993955d011SMarcel Moolenaar 	} else {
17003955d011SMarcel Moolenaar 	    return cp;
17013955d011SMarcel Moolenaar 	}
17023955d011SMarcel Moolenaar     }
17033955d011SMarcel Moolenaar     return cp;
17043955d011SMarcel Moolenaar }
17053955d011SMarcel Moolenaar 
1706*e2eeea75SSimon J. Gerraty /*
1707*e2eeea75SSimon J. Gerraty  * This function is called whenever there is something to read on the pipe.
1708*e2eeea75SSimon J. Gerraty  * We collect more output from the given job and store it in the job's
1709*e2eeea75SSimon J. Gerraty  * outBuf. If this makes up a line, we print it tagged by the job's
1710*e2eeea75SSimon J. Gerraty  * identifier, as necessary.
1711*e2eeea75SSimon J. Gerraty  *
1712*e2eeea75SSimon J. Gerraty  * In the output of the shell, the 'noPrint' lines are removed. If the
1713*e2eeea75SSimon J. Gerraty  * command is not alone on the line (the character after it is not \0 or
1714*e2eeea75SSimon J. Gerraty  * \n), we do print whatever follows it.
17153955d011SMarcel Moolenaar  *
17163955d011SMarcel Moolenaar  * Input:
17173955d011SMarcel Moolenaar  *	job		the job whose output needs printing
17183955d011SMarcel Moolenaar  *	finish		TRUE if this is the last time we'll be called
17193955d011SMarcel Moolenaar  *			for this job
17203955d011SMarcel Moolenaar  */
1721956e45f6SSimon J. Gerraty static void
17223955d011SMarcel Moolenaar JobDoOutput(Job *job, Boolean finish)
17233955d011SMarcel Moolenaar {
17243955d011SMarcel Moolenaar     Boolean gotNL = FALSE;	/* true if got a newline */
17253955d011SMarcel Moolenaar     Boolean fbuf;		/* true if our buffer filled up */
1726956e45f6SSimon J. Gerraty     size_t nr;			/* number of bytes read */
1727956e45f6SSimon J. Gerraty     size_t i;			/* auxiliary index into outBuf */
1728956e45f6SSimon J. Gerraty     size_t max;			/* limit for i (end of current data) */
1729956e45f6SSimon J. Gerraty     ssize_t nRead;		/* (Temporary) number of bytes read */
17303955d011SMarcel Moolenaar 
17313955d011SMarcel Moolenaar     /*
17323955d011SMarcel Moolenaar      * Read as many bytes as will fit in the buffer.
17333955d011SMarcel Moolenaar      */
1734*e2eeea75SSimon J. Gerraty again:
17353955d011SMarcel Moolenaar     gotNL = FALSE;
17363955d011SMarcel Moolenaar     fbuf = FALSE;
17373955d011SMarcel Moolenaar 
17383955d011SMarcel Moolenaar     nRead = read(job->inPipe, &job->outBuf[job->curPos],
17393955d011SMarcel Moolenaar 		 JOB_BUFSIZE - job->curPos);
17403955d011SMarcel Moolenaar     if (nRead < 0) {
17413955d011SMarcel Moolenaar 	if (errno == EAGAIN)
17423955d011SMarcel Moolenaar 	    return;
17433955d011SMarcel Moolenaar 	if (DEBUG(JOB)) {
17443955d011SMarcel Moolenaar 	    perror("JobDoOutput(piperead)");
17453955d011SMarcel Moolenaar 	}
17463955d011SMarcel Moolenaar 	nr = 0;
17473955d011SMarcel Moolenaar     } else {
1748956e45f6SSimon J. Gerraty 	nr = (size_t)nRead;
17493955d011SMarcel Moolenaar     }
17503955d011SMarcel Moolenaar 
17513955d011SMarcel Moolenaar     /*
17523955d011SMarcel Moolenaar      * If we hit the end-of-file (the job is dead), we must flush its
17533955d011SMarcel Moolenaar      * remaining output, so pretend we read a newline if there's any
17543955d011SMarcel Moolenaar      * output remaining in the buffer.
17553955d011SMarcel Moolenaar      * Also clear the 'finish' flag so we stop looping.
17563955d011SMarcel Moolenaar      */
1757*e2eeea75SSimon J. Gerraty     if (nr == 0 && job->curPos != 0) {
17583955d011SMarcel Moolenaar 	job->outBuf[job->curPos] = '\n';
17593955d011SMarcel Moolenaar 	nr = 1;
17603955d011SMarcel Moolenaar 	finish = FALSE;
17613955d011SMarcel Moolenaar     } else if (nr == 0) {
17623955d011SMarcel Moolenaar 	finish = FALSE;
17633955d011SMarcel Moolenaar     }
17643955d011SMarcel Moolenaar 
17653955d011SMarcel Moolenaar     /*
17663955d011SMarcel Moolenaar      * Look for the last newline in the bytes we just got. If there is
17673955d011SMarcel Moolenaar      * one, break out of the loop with 'i' as its index and gotNL set
17683955d011SMarcel Moolenaar      * TRUE.
17693955d011SMarcel Moolenaar      */
17703955d011SMarcel Moolenaar     max = job->curPos + nr;
1771956e45f6SSimon J. Gerraty     for (i = job->curPos + nr - 1; i >= job->curPos && i != (size_t)-1; i--) {
17723955d011SMarcel Moolenaar 	if (job->outBuf[i] == '\n') {
17733955d011SMarcel Moolenaar 	    gotNL = TRUE;
17743955d011SMarcel Moolenaar 	    break;
17753955d011SMarcel Moolenaar 	} else if (job->outBuf[i] == '\0') {
17763955d011SMarcel Moolenaar 	    /*
17773955d011SMarcel Moolenaar 	     * Why?
17783955d011SMarcel Moolenaar 	     */
17793955d011SMarcel Moolenaar 	    job->outBuf[i] = ' ';
17803955d011SMarcel Moolenaar 	}
17813955d011SMarcel Moolenaar     }
17823955d011SMarcel Moolenaar 
17833955d011SMarcel Moolenaar     if (!gotNL) {
17843955d011SMarcel Moolenaar 	job->curPos += nr;
17853955d011SMarcel Moolenaar 	if (job->curPos == JOB_BUFSIZE) {
17863955d011SMarcel Moolenaar 	    /*
17873955d011SMarcel Moolenaar 	     * If we've run out of buffer space, we have no choice
17883955d011SMarcel Moolenaar 	     * but to print the stuff. sigh.
17893955d011SMarcel Moolenaar 	     */
17903955d011SMarcel Moolenaar 	    fbuf = TRUE;
17913955d011SMarcel Moolenaar 	    i = job->curPos;
17923955d011SMarcel Moolenaar 	}
17933955d011SMarcel Moolenaar     }
17943955d011SMarcel Moolenaar     if (gotNL || fbuf) {
17953955d011SMarcel Moolenaar 	/*
17963955d011SMarcel Moolenaar 	 * Need to send the output to the screen. Null terminate it
17973955d011SMarcel Moolenaar 	 * first, overwriting the newline character if there was one.
17983955d011SMarcel Moolenaar 	 * So long as the line isn't one we should filter (according
17993955d011SMarcel Moolenaar 	 * to the shell description), we print the line, preceded
18003955d011SMarcel Moolenaar 	 * by a target banner if this target isn't the same as the
18013955d011SMarcel Moolenaar 	 * one for which we last printed something.
18023955d011SMarcel Moolenaar 	 * The rest of the data in the buffer are then shifted down
18033955d011SMarcel Moolenaar 	 * to the start of the buffer and curPos is set accordingly.
18043955d011SMarcel Moolenaar 	 */
18053955d011SMarcel Moolenaar 	job->outBuf[i] = '\0';
18063955d011SMarcel Moolenaar 	if (i >= job->curPos) {
18073955d011SMarcel Moolenaar 	    char *cp;
18083955d011SMarcel Moolenaar 
1809956e45f6SSimon J. Gerraty 	    cp = JobOutput(job, job->outBuf, &job->outBuf[i]);
18103955d011SMarcel Moolenaar 
18113955d011SMarcel Moolenaar 	    /*
18123955d011SMarcel Moolenaar 	     * There's still more in that thar buffer. This time, though,
18133955d011SMarcel Moolenaar 	     * we know there's no newline at the end, so we add one of
18143955d011SMarcel Moolenaar 	     * our own free will.
18153955d011SMarcel Moolenaar 	     */
18163955d011SMarcel Moolenaar 	    if (*cp != '\0') {
1817956e45f6SSimon J. Gerraty 		if (!opts.beSilent && job->node != lastNode) {
18183955d011SMarcel Moolenaar 		    MESSAGE(stdout, job->node);
18193955d011SMarcel Moolenaar 		    lastNode = job->node;
18203955d011SMarcel Moolenaar 		}
18213955d011SMarcel Moolenaar #ifdef USE_META
18223955d011SMarcel Moolenaar 		if (useMeta) {
18233955d011SMarcel Moolenaar 		    meta_job_output(job, cp, gotNL ? "\n" : "");
18243955d011SMarcel Moolenaar 		}
18253955d011SMarcel Moolenaar #endif
18263955d011SMarcel Moolenaar 		(void)fprintf(stdout, "%s%s", cp, gotNL ? "\n" : "");
18273955d011SMarcel Moolenaar 		(void)fflush(stdout);
18283955d011SMarcel Moolenaar 	    }
18293955d011SMarcel Moolenaar 	}
18303955d011SMarcel Moolenaar 	/*
1831db29cad8SSimon J. Gerraty 	 * max is the last offset still in the buffer. Move any remaining
1832db29cad8SSimon J. Gerraty 	 * characters to the start of the buffer and update the end marker
1833db29cad8SSimon J. Gerraty 	 * curPos.
18343955d011SMarcel Moolenaar 	 */
1835db29cad8SSimon J. Gerraty 	if (i < max) {
1836db29cad8SSimon J. Gerraty 	    (void)memmove(job->outBuf, &job->outBuf[i + 1], max - (i + 1));
1837db29cad8SSimon J. Gerraty 	    job->curPos = max - (i + 1);
1838db29cad8SSimon J. Gerraty 	} else {
1839db29cad8SSimon J. Gerraty 	    assert(i == max);
18403955d011SMarcel Moolenaar 	    job->curPos = 0;
18413955d011SMarcel Moolenaar 	}
18423955d011SMarcel Moolenaar     }
18433955d011SMarcel Moolenaar     if (finish) {
18443955d011SMarcel Moolenaar 	/*
18453955d011SMarcel Moolenaar 	 * If the finish flag is true, we must loop until we hit
18463955d011SMarcel Moolenaar 	 * end-of-file on the pipe. This is guaranteed to happen
18473955d011SMarcel Moolenaar 	 * eventually since the other end of the pipe is now closed
18483955d011SMarcel Moolenaar 	 * (we closed it explicitly and the child has exited). When
18493955d011SMarcel Moolenaar 	 * we do get an EOF, finish will be set FALSE and we'll fall
18503955d011SMarcel Moolenaar 	 * through and out.
18513955d011SMarcel Moolenaar 	 */
1852*e2eeea75SSimon J. Gerraty 	goto again;
18533955d011SMarcel Moolenaar     }
18543955d011SMarcel Moolenaar }
18553955d011SMarcel Moolenaar 
18563955d011SMarcel Moolenaar static void
18573955d011SMarcel Moolenaar JobRun(GNode *targ)
18583955d011SMarcel Moolenaar {
1859956e45f6SSimon J. Gerraty #if 0
18603955d011SMarcel Moolenaar     /*
1861956e45f6SSimon J. Gerraty      * Unfortunately it is too complicated to run .BEGIN, .END, and
1862956e45f6SSimon J. Gerraty      * .INTERRUPT job in the parallel job module.  As of 2020-09-25,
1863956e45f6SSimon J. Gerraty      * unit-tests/deptgt-end-jobs.mk hangs in an endless loop.
1864956e45f6SSimon J. Gerraty      *
1865956e45f6SSimon J. Gerraty      * Running these jobs in compat mode also guarantees that these
1866956e45f6SSimon J. Gerraty      * jobs do not overlap with other unrelated jobs.
18673955d011SMarcel Moolenaar      */
1868956e45f6SSimon J. Gerraty     List *lst = Lst_New();
18692c3632d1SSimon J. Gerraty     Lst_Append(lst, targ);
18703955d011SMarcel Moolenaar     (void)Make_Run(lst);
18713955d011SMarcel Moolenaar     Lst_Destroy(lst, NULL);
18723955d011SMarcel Moolenaar     JobStart(targ, JOB_SPECIAL);
18733955d011SMarcel Moolenaar     while (jobTokensRunning) {
18743955d011SMarcel Moolenaar 	Job_CatchOutput();
18753955d011SMarcel Moolenaar     }
18763955d011SMarcel Moolenaar #else
18773955d011SMarcel Moolenaar     Compat_Make(targ, targ);
18783955d011SMarcel Moolenaar     if (targ->made == ERROR) {
18793955d011SMarcel Moolenaar 	PrintOnError(targ, "\n\nStop.");
18803955d011SMarcel Moolenaar 	exit(1);
18813955d011SMarcel Moolenaar     }
18823955d011SMarcel Moolenaar #endif
18833955d011SMarcel Moolenaar }
18843955d011SMarcel Moolenaar 
1885956e45f6SSimon J. Gerraty /* Handle the exit of a child. Called from Make_Make.
18863955d011SMarcel Moolenaar  *
18873955d011SMarcel Moolenaar  * The job descriptor is removed from the list of children.
18883955d011SMarcel Moolenaar  *
18893955d011SMarcel Moolenaar  * Notes:
18903955d011SMarcel Moolenaar  *	We do waits, blocking or not, according to the wisdom of our
18913955d011SMarcel Moolenaar  *	caller, until there are no more children to report. For each
18923955d011SMarcel Moolenaar  *	job, call JobFinish to finish things off.
18933955d011SMarcel Moolenaar  */
18943955d011SMarcel Moolenaar void
18953955d011SMarcel Moolenaar Job_CatchChildren(void)
18963955d011SMarcel Moolenaar {
18973955d011SMarcel Moolenaar     int pid;			/* pid of dead child */
18983955d011SMarcel Moolenaar     WAIT_T status;		/* Exit/termination status */
18993955d011SMarcel Moolenaar 
19003955d011SMarcel Moolenaar     /*
19013955d011SMarcel Moolenaar      * Don't even bother if we know there's no one around.
19023955d011SMarcel Moolenaar      */
19033955d011SMarcel Moolenaar     if (jobTokensRunning == 0)
19043955d011SMarcel Moolenaar 	return;
19053955d011SMarcel Moolenaar 
19063955d011SMarcel Moolenaar     while ((pid = waitpid((pid_t) -1, &status, WNOHANG | WUNTRACED)) > 0) {
1907956e45f6SSimon J. Gerraty 	DEBUG2(JOB, "Process %d exited/stopped status %x.\n", pid,
19083955d011SMarcel Moolenaar 	  WAIT_STATUS(status));
19093955d011SMarcel Moolenaar 	JobReapChild(pid, status, TRUE);
19103955d011SMarcel Moolenaar     }
19113955d011SMarcel Moolenaar }
19123955d011SMarcel Moolenaar 
19133955d011SMarcel Moolenaar /*
19143955d011SMarcel Moolenaar  * It is possible that wait[pid]() was called from elsewhere,
19153955d011SMarcel Moolenaar  * this lets us reap jobs regardless.
19163955d011SMarcel Moolenaar  */
19173955d011SMarcel Moolenaar void
19183955d011SMarcel Moolenaar JobReapChild(pid_t pid, WAIT_T status, Boolean isJobs)
19193955d011SMarcel Moolenaar {
19203955d011SMarcel Moolenaar     Job *job;			/* job descriptor for dead child */
19213955d011SMarcel Moolenaar 
19223955d011SMarcel Moolenaar     /*
19233955d011SMarcel Moolenaar      * Don't even bother if we know there's no one around.
19243955d011SMarcel Moolenaar      */
19253955d011SMarcel Moolenaar     if (jobTokensRunning == 0)
19263955d011SMarcel Moolenaar 	return;
19273955d011SMarcel Moolenaar 
19283955d011SMarcel Moolenaar     job = JobFindPid(pid, JOB_ST_RUNNING, isJobs);
19293955d011SMarcel Moolenaar     if (job == NULL) {
19303955d011SMarcel Moolenaar 	if (isJobs) {
19313955d011SMarcel Moolenaar 	    if (!lurking_children)
19323955d011SMarcel Moolenaar 		Error("Child (%d) status %x not in table?", pid, status);
19333955d011SMarcel Moolenaar 	}
19343955d011SMarcel Moolenaar 	return;				/* not ours */
19353955d011SMarcel Moolenaar     }
19363955d011SMarcel Moolenaar     if (WIFSTOPPED(status)) {
1937956e45f6SSimon J. Gerraty 	DEBUG2(JOB, "Process %d (%s) stopped.\n", job->pid, job->node->name);
19383955d011SMarcel Moolenaar 	if (!make_suspended) {
19393955d011SMarcel Moolenaar 	    switch (WSTOPSIG(status)) {
19403955d011SMarcel Moolenaar 	    case SIGTSTP:
19413955d011SMarcel Moolenaar 		(void)printf("*** [%s] Suspended\n", job->node->name);
19423955d011SMarcel Moolenaar 		break;
19433955d011SMarcel Moolenaar 	    case SIGSTOP:
19443955d011SMarcel Moolenaar 		(void)printf("*** [%s] Stopped\n", job->node->name);
19453955d011SMarcel Moolenaar 		break;
19463955d011SMarcel Moolenaar 	    default:
19473955d011SMarcel Moolenaar 		(void)printf("*** [%s] Stopped -- signal %d\n",
19483955d011SMarcel Moolenaar 			     job->node->name, WSTOPSIG(status));
19493955d011SMarcel Moolenaar 	    }
1950*e2eeea75SSimon J. Gerraty 	    job->suspended = TRUE;
19513955d011SMarcel Moolenaar 	}
19523955d011SMarcel Moolenaar 	(void)fflush(stdout);
19533955d011SMarcel Moolenaar 	return;
19543955d011SMarcel Moolenaar     }
19553955d011SMarcel Moolenaar 
1956*e2eeea75SSimon J. Gerraty     job->status = JOB_ST_FINISHED;
19573955d011SMarcel Moolenaar     job->exit_status = WAIT_STATUS(status);
19583955d011SMarcel Moolenaar 
19593955d011SMarcel Moolenaar     JobFinish(job, status);
19603955d011SMarcel Moolenaar }
19613955d011SMarcel Moolenaar 
1962956e45f6SSimon J. Gerraty /* Catch the output from our children, if we're using pipes do so. Otherwise
1963956e45f6SSimon J. Gerraty  * just block time until we get a signal(most likely a SIGCHLD) since there's
1964956e45f6SSimon J. Gerraty  * no point in just spinning when there's nothing to do and the reaping of a
1965956e45f6SSimon J. Gerraty  * child can wait for a while. */
19663955d011SMarcel Moolenaar void
19673955d011SMarcel Moolenaar Job_CatchOutput(void)
19683955d011SMarcel Moolenaar {
19693955d011SMarcel Moolenaar     int nready;
19703955d011SMarcel Moolenaar     Job *job;
1971956e45f6SSimon J. Gerraty     unsigned int i;
19723955d011SMarcel Moolenaar 
19733955d011SMarcel Moolenaar     (void)fflush(stdout);
19743955d011SMarcel Moolenaar 
19753955d011SMarcel Moolenaar     /* The first fd in the list is the job token pipe */
19761748de26SSimon J. Gerraty     do {
1977329d20deSMatt Macy 	nready = poll(fds + 1 - wantToken, nfds - 1 + wantToken, POLL_MSEC);
19781748de26SSimon J. Gerraty     } while (nready < 0 && errno == EINTR);
19793955d011SMarcel Moolenaar 
19801748de26SSimon J. Gerraty     if (nready < 0)
19811748de26SSimon J. Gerraty 	Punt("poll: %s", strerror(errno));
19821748de26SSimon J. Gerraty 
19831748de26SSimon J. Gerraty     if (nready > 0 && readyfd(&childExitJob)) {
19843955d011SMarcel Moolenaar 	char token = 0;
19851748de26SSimon J. Gerraty 	ssize_t count;
19861748de26SSimon J. Gerraty 	count = read(childExitJob.inPipe, &token, 1);
19871748de26SSimon J. Gerraty 	switch (count) {
19881748de26SSimon J. Gerraty 	case 0:
19891748de26SSimon J. Gerraty 	    Punt("unexpected eof on token pipe");
19901748de26SSimon J. Gerraty 	case -1:
19911748de26SSimon J. Gerraty 	    Punt("token pipe read: %s", strerror(errno));
19921748de26SSimon J. Gerraty 	case 1:
19933955d011SMarcel Moolenaar 	    if (token == DO_JOB_RESUME[0])
19943955d011SMarcel Moolenaar 		/* Complete relay requested from our SIGCONT handler */
19953955d011SMarcel Moolenaar 		JobRestartJobs();
19961748de26SSimon J. Gerraty 	    break;
19971748de26SSimon J. Gerraty 	default:
19981748de26SSimon J. Gerraty 	    abort();
19991748de26SSimon J. Gerraty 	}
2000*e2eeea75SSimon J. Gerraty 	nready--;
20013955d011SMarcel Moolenaar     }
20023955d011SMarcel Moolenaar 
20031748de26SSimon J. Gerraty     Job_CatchChildren();
20041748de26SSimon J. Gerraty     if (nready == 0)
20053955d011SMarcel Moolenaar 	return;
20063955d011SMarcel Moolenaar 
200749caa483SSimon J. Gerraty     for (i = npseudojobs * nfds_per_job(); i < nfds; i++) {
20083955d011SMarcel Moolenaar 	if (!fds[i].revents)
20093955d011SMarcel Moolenaar 	    continue;
20103955d011SMarcel Moolenaar 	job = jobfds[i];
2011*e2eeea75SSimon J. Gerraty 	if (job->status == JOB_ST_RUNNING)
20123955d011SMarcel Moolenaar 	    JobDoOutput(job, FALSE);
201349caa483SSimon J. Gerraty #if defined(USE_FILEMON) && !defined(USE_FILEMON_DEV)
201449caa483SSimon J. Gerraty 	/*
201549caa483SSimon J. Gerraty 	 * With meta mode, we may have activity on the job's filemon
201649caa483SSimon J. Gerraty 	 * descriptor too, which at the moment is any pollfd other than
201749caa483SSimon J. Gerraty 	 * job->inPollfd.
201849caa483SSimon J. Gerraty 	 */
201949caa483SSimon J. Gerraty 	if (useMeta && job->inPollfd != &fds[i]) {
202049caa483SSimon J. Gerraty 	    if (meta_job_event(job) <= 0) {
202149caa483SSimon J. Gerraty 		fds[i].events = 0; /* never mind */
202249caa483SSimon J. Gerraty 	    }
202349caa483SSimon J. Gerraty 	}
202449caa483SSimon J. Gerraty #endif
20251748de26SSimon J. Gerraty 	if (--nready == 0)
20261748de26SSimon J. Gerraty 	    return;
20273955d011SMarcel Moolenaar     }
20283955d011SMarcel Moolenaar }
20293955d011SMarcel Moolenaar 
2030956e45f6SSimon J. Gerraty /* Start the creation of a target. Basically a front-end for JobStart used by
2031956e45f6SSimon J. Gerraty  * the Make module. */
20323955d011SMarcel Moolenaar void
20333955d011SMarcel Moolenaar Job_Make(GNode *gn)
20343955d011SMarcel Moolenaar {
2035*e2eeea75SSimon J. Gerraty     (void)JobStart(gn, JOB_NONE);
20363955d011SMarcel Moolenaar }
20373955d011SMarcel Moolenaar 
20383955d011SMarcel Moolenaar void
20393955d011SMarcel Moolenaar Shell_Init(void)
20403955d011SMarcel Moolenaar {
20413955d011SMarcel Moolenaar     if (shellPath == NULL) {
20423955d011SMarcel Moolenaar 	/*
20433955d011SMarcel Moolenaar 	 * We are using the default shell, which may be an absolute
20443955d011SMarcel Moolenaar 	 * path if DEFSHELL_CUSTOM is defined.
20453955d011SMarcel Moolenaar 	 */
20463955d011SMarcel Moolenaar 	shellName = commandShell->name;
20473955d011SMarcel Moolenaar #ifdef DEFSHELL_CUSTOM
20483955d011SMarcel Moolenaar 	if (*shellName == '/') {
20493955d011SMarcel Moolenaar 	    shellPath = shellName;
20503955d011SMarcel Moolenaar 	    shellName = strrchr(shellPath, '/');
20513955d011SMarcel Moolenaar 	    shellName++;
20523955d011SMarcel Moolenaar 	} else
20533955d011SMarcel Moolenaar #endif
20542c3632d1SSimon J. Gerraty 	shellPath = str_concat3(_PATH_DEFSHELLDIR, "/", shellName);
20553955d011SMarcel Moolenaar     }
2056*e2eeea75SSimon J. Gerraty     Var_SetWithFlags(".SHELL", shellPath, VAR_CMDLINE, VAR_SET_READONLY);
20573955d011SMarcel Moolenaar     if (commandShell->exit == NULL) {
20583955d011SMarcel Moolenaar 	commandShell->exit = "";
20593955d011SMarcel Moolenaar     }
20603955d011SMarcel Moolenaar     if (commandShell->echo == NULL) {
20613955d011SMarcel Moolenaar 	commandShell->echo = "";
20623955d011SMarcel Moolenaar     }
2063956e45f6SSimon J. Gerraty     if (commandShell->hasErrCtl && commandShell->exit[0] != '\0') {
206451ee2c1cSSimon J. Gerraty 	if (shellErrFlag &&
206551ee2c1cSSimon J. Gerraty 	    strcmp(commandShell->exit, &shellErrFlag[1]) != 0) {
206651ee2c1cSSimon J. Gerraty 	    free(shellErrFlag);
206751ee2c1cSSimon J. Gerraty 	    shellErrFlag = NULL;
206851ee2c1cSSimon J. Gerraty 	}
206951ee2c1cSSimon J. Gerraty 	if (!shellErrFlag) {
2070956e45f6SSimon J. Gerraty 	    size_t n = strlen(commandShell->exit) + 2;
207151ee2c1cSSimon J. Gerraty 
207251ee2c1cSSimon J. Gerraty 	    shellErrFlag = bmake_malloc(n);
207351ee2c1cSSimon J. Gerraty 	    if (shellErrFlag) {
207451ee2c1cSSimon J. Gerraty 		snprintf(shellErrFlag, n, "-%s", commandShell->exit);
207551ee2c1cSSimon J. Gerraty 	    }
207651ee2c1cSSimon J. Gerraty 	}
207751ee2c1cSSimon J. Gerraty     } else if (shellErrFlag) {
207851ee2c1cSSimon J. Gerraty 	free(shellErrFlag);
207951ee2c1cSSimon J. Gerraty 	shellErrFlag = NULL;
208051ee2c1cSSimon J. Gerraty     }
20813955d011SMarcel Moolenaar }
20823955d011SMarcel Moolenaar 
2083956e45f6SSimon J. Gerraty /* Return the string literal that is used in the current command shell
2084956e45f6SSimon J. Gerraty  * to produce a newline character. */
20853955d011SMarcel Moolenaar const char *
20863955d011SMarcel Moolenaar Shell_GetNewline(void)
20873955d011SMarcel Moolenaar {
20883955d011SMarcel Moolenaar     return commandShell->newline;
20893955d011SMarcel Moolenaar }
20903955d011SMarcel Moolenaar 
20913955d011SMarcel Moolenaar void
20923955d011SMarcel Moolenaar Job_SetPrefix(void)
20933955d011SMarcel Moolenaar {
20943955d011SMarcel Moolenaar     if (targPrefix) {
20953955d011SMarcel Moolenaar 	free(targPrefix);
20963955d011SMarcel Moolenaar     } else if (!Var_Exists(MAKE_JOB_PREFIX, VAR_GLOBAL)) {
20973841c287SSimon J. Gerraty 	Var_Set(MAKE_JOB_PREFIX, "---", VAR_GLOBAL);
20983955d011SMarcel Moolenaar     }
20993955d011SMarcel Moolenaar 
2100956e45f6SSimon J. Gerraty     (void)Var_Subst("${" MAKE_JOB_PREFIX "}",
2101956e45f6SSimon J. Gerraty 		    VAR_GLOBAL, VARE_WANTRES, &targPrefix);
2102956e45f6SSimon J. Gerraty     /* TODO: handle errors */
21033955d011SMarcel Moolenaar }
21043955d011SMarcel Moolenaar 
21052c3632d1SSimon J. Gerraty /* Initialize the process module. */
21063955d011SMarcel Moolenaar void
21073955d011SMarcel Moolenaar Job_Init(void)
21083955d011SMarcel Moolenaar {
2109d191243dSSimon J. Gerraty     Job_SetPrefix();
21103955d011SMarcel Moolenaar     /* Allocate space for all the job info */
2111956e45f6SSimon J. Gerraty     job_table = bmake_malloc((size_t)opts.maxJobs * sizeof *job_table);
2112956e45f6SSimon J. Gerraty     memset(job_table, 0, (size_t)opts.maxJobs * sizeof *job_table);
2113956e45f6SSimon J. Gerraty     job_table_end = job_table + opts.maxJobs;
21143955d011SMarcel Moolenaar     wantToken =	0;
21153955d011SMarcel Moolenaar 
2116956e45f6SSimon J. Gerraty     aborting = ABORT_NONE;
21173955d011SMarcel Moolenaar     errors = 0;
21183955d011SMarcel Moolenaar 
21193955d011SMarcel Moolenaar     lastNode = NULL;
21203955d011SMarcel Moolenaar 
2121*e2eeea75SSimon J. Gerraty     Always_pass_job_queue = GetBooleanVar(MAKE_ALWAYS_PASS_JOB_QUEUE,
21229a4bc556SSimon J. Gerraty 				       Always_pass_job_queue);
21239a4bc556SSimon J. Gerraty 
2124*e2eeea75SSimon J. Gerraty     Job_error_token = GetBooleanVar(MAKE_JOB_ERROR_TOKEN, Job_error_token);
21252d395cb5SSimon J. Gerraty 
21262d395cb5SSimon J. Gerraty 
21273955d011SMarcel Moolenaar     /*
21283955d011SMarcel Moolenaar      * There is a non-zero chance that we already have children.
21293955d011SMarcel Moolenaar      * eg after 'make -f- <<EOF'
21303955d011SMarcel Moolenaar      * Since their termination causes a 'Child (pid) not in table' message,
21313955d011SMarcel Moolenaar      * Collect the status of any that are already dead, and suppress the
21323955d011SMarcel Moolenaar      * error message if there are any undead ones.
21333955d011SMarcel Moolenaar      */
21343955d011SMarcel Moolenaar     for (;;) {
21353955d011SMarcel Moolenaar 	int rval, status;
21363955d011SMarcel Moolenaar 	rval = waitpid((pid_t) -1, &status, WNOHANG);
21373955d011SMarcel Moolenaar 	if (rval > 0)
21383955d011SMarcel Moolenaar 	    continue;
21393955d011SMarcel Moolenaar 	if (rval == 0)
2140*e2eeea75SSimon J. Gerraty 	    lurking_children = TRUE;
21413955d011SMarcel Moolenaar 	break;
21423955d011SMarcel Moolenaar     }
21433955d011SMarcel Moolenaar 
21443955d011SMarcel Moolenaar     Shell_Init();
21453955d011SMarcel Moolenaar 
21463955d011SMarcel Moolenaar     JobCreatePipe(&childExitJob, 3);
21473955d011SMarcel Moolenaar 
214849caa483SSimon J. Gerraty     /* Preallocate enough for the maximum number of jobs.  */
2149*e2eeea75SSimon J. Gerraty     fds = bmake_malloc(sizeof *fds *
2150956e45f6SSimon J. Gerraty 	(npseudojobs + (size_t)opts.maxJobs) * nfds_per_job());
2151*e2eeea75SSimon J. Gerraty     jobfds = bmake_malloc(sizeof *jobfds *
2152956e45f6SSimon J. Gerraty 	(npseudojobs + (size_t)opts.maxJobs) * nfds_per_job());
21533955d011SMarcel Moolenaar 
21543955d011SMarcel Moolenaar     /* These are permanent entries and take slots 0 and 1 */
21553955d011SMarcel Moolenaar     watchfd(&tokenWaitJob);
21563955d011SMarcel Moolenaar     watchfd(&childExitJob);
21573955d011SMarcel Moolenaar 
21583955d011SMarcel Moolenaar     sigemptyset(&caught_signals);
21593955d011SMarcel Moolenaar     /*
21603955d011SMarcel Moolenaar      * Install a SIGCHLD handler.
21613955d011SMarcel Moolenaar      */
21623955d011SMarcel Moolenaar     (void)bmake_signal(SIGCHLD, JobChildSig);
21633955d011SMarcel Moolenaar     sigaddset(&caught_signals, SIGCHLD);
21643955d011SMarcel Moolenaar 
21653955d011SMarcel Moolenaar #define ADDSIG(s,h)				\
21663955d011SMarcel Moolenaar     if (bmake_signal(s, SIG_IGN) != SIG_IGN) {	\
21673955d011SMarcel Moolenaar 	sigaddset(&caught_signals, s);		\
21683955d011SMarcel Moolenaar 	(void)bmake_signal(s, h);		\
21693955d011SMarcel Moolenaar     }
21703955d011SMarcel Moolenaar 
21713955d011SMarcel Moolenaar     /*
21723955d011SMarcel Moolenaar      * Catch the four signals that POSIX specifies if they aren't ignored.
21733955d011SMarcel Moolenaar      * JobPassSig will take care of calling JobInterrupt if appropriate.
21743955d011SMarcel Moolenaar      */
21753955d011SMarcel Moolenaar     ADDSIG(SIGINT, JobPassSig_int)
21763955d011SMarcel Moolenaar     ADDSIG(SIGHUP, JobPassSig_term)
21773955d011SMarcel Moolenaar     ADDSIG(SIGTERM, JobPassSig_term)
21783955d011SMarcel Moolenaar     ADDSIG(SIGQUIT, JobPassSig_term)
21793955d011SMarcel Moolenaar 
21803955d011SMarcel Moolenaar     /*
21813955d011SMarcel Moolenaar      * There are additional signals that need to be caught and passed if
21823955d011SMarcel Moolenaar      * either the export system wants to be told directly of signals or if
21833955d011SMarcel Moolenaar      * we're giving each job its own process group (since then it won't get
21843955d011SMarcel Moolenaar      * signals from the terminal driver as we own the terminal)
21853955d011SMarcel Moolenaar      */
21863955d011SMarcel Moolenaar     ADDSIG(SIGTSTP, JobPassSig_suspend)
21873955d011SMarcel Moolenaar     ADDSIG(SIGTTOU, JobPassSig_suspend)
21883955d011SMarcel Moolenaar     ADDSIG(SIGTTIN, JobPassSig_suspend)
21893955d011SMarcel Moolenaar     ADDSIG(SIGWINCH, JobCondPassSig)
21903955d011SMarcel Moolenaar     ADDSIG(SIGCONT, JobContinueSig)
21913955d011SMarcel Moolenaar #undef ADDSIG
21923955d011SMarcel Moolenaar 
21931748de26SSimon J. Gerraty     (void)Job_RunTarget(".BEGIN", NULL);
2194956e45f6SSimon J. Gerraty     /* Create the .END node now, even though no code in the unit tests
2195956e45f6SSimon J. Gerraty      * depends on it.  See also Targ_GetEndNode in Compat_Run. */
2196956e45f6SSimon J. Gerraty     (void)Targ_GetEndNode();
21973955d011SMarcel Moolenaar }
21983955d011SMarcel Moolenaar 
21993955d011SMarcel Moolenaar static void JobSigReset(void)
22003955d011SMarcel Moolenaar {
22013955d011SMarcel Moolenaar #define DELSIG(s)					\
22023955d011SMarcel Moolenaar     if (sigismember(&caught_signals, s)) {		\
22033955d011SMarcel Moolenaar 	(void)bmake_signal(s, SIG_DFL);			\
22043955d011SMarcel Moolenaar     }
22053955d011SMarcel Moolenaar 
22063955d011SMarcel Moolenaar     DELSIG(SIGINT)
22073955d011SMarcel Moolenaar     DELSIG(SIGHUP)
22083955d011SMarcel Moolenaar     DELSIG(SIGQUIT)
22093955d011SMarcel Moolenaar     DELSIG(SIGTERM)
22103955d011SMarcel Moolenaar     DELSIG(SIGTSTP)
22113955d011SMarcel Moolenaar     DELSIG(SIGTTOU)
22123955d011SMarcel Moolenaar     DELSIG(SIGTTIN)
22133955d011SMarcel Moolenaar     DELSIG(SIGWINCH)
22143955d011SMarcel Moolenaar     DELSIG(SIGCONT)
22153955d011SMarcel Moolenaar #undef DELSIG
22163955d011SMarcel Moolenaar     (void)bmake_signal(SIGCHLD, SIG_DFL);
22173955d011SMarcel Moolenaar }
22183955d011SMarcel Moolenaar 
22192c3632d1SSimon J. Gerraty /* Find a shell in 'shells' given its name, or return NULL. */
22203955d011SMarcel Moolenaar static Shell *
2221956e45f6SSimon J. Gerraty FindShellByName(const char *name)
22223955d011SMarcel Moolenaar {
2223956e45f6SSimon J. Gerraty     Shell *sh = shells;
2224956e45f6SSimon J. Gerraty     const Shell *shellsEnd = sh + sizeof shells / sizeof shells[0];
22253955d011SMarcel Moolenaar 
2226956e45f6SSimon J. Gerraty     for (sh = shells; sh < shellsEnd; sh++) {
22273955d011SMarcel Moolenaar 	if (strcmp(name, sh->name) == 0)
22283841c287SSimon J. Gerraty 		return sh;
22293955d011SMarcel Moolenaar     }
22303955d011SMarcel Moolenaar     return NULL;
22313955d011SMarcel Moolenaar }
22323955d011SMarcel Moolenaar 
22333955d011SMarcel Moolenaar /*-
22343955d011SMarcel Moolenaar  *-----------------------------------------------------------------------
22353955d011SMarcel Moolenaar  * Job_ParseShell --
22363955d011SMarcel Moolenaar  *	Parse a shell specification and set up commandShell, shellPath
22373955d011SMarcel Moolenaar  *	and shellName appropriately.
22383955d011SMarcel Moolenaar  *
22393955d011SMarcel Moolenaar  * Input:
22403955d011SMarcel Moolenaar  *	line		The shell spec
22413955d011SMarcel Moolenaar  *
22423955d011SMarcel Moolenaar  * Results:
22432c3632d1SSimon J. Gerraty  *	FALSE if the specification was incorrect.
22443955d011SMarcel Moolenaar  *
22453955d011SMarcel Moolenaar  * Side Effects:
22463955d011SMarcel Moolenaar  *	commandShell points to a Shell structure (either predefined or
22473955d011SMarcel Moolenaar  *	created from the shell spec), shellPath is the full path of the
22483955d011SMarcel Moolenaar  *	shell described by commandShell, while shellName is just the
22493955d011SMarcel Moolenaar  *	final component of shellPath.
22503955d011SMarcel Moolenaar  *
22513955d011SMarcel Moolenaar  * Notes:
22523955d011SMarcel Moolenaar  *	A shell specification consists of a .SHELL target, with dependency
22533955d011SMarcel Moolenaar  *	operator, followed by a series of blank-separated words. Double
22543955d011SMarcel Moolenaar  *	quotes can be used to use blanks in words. A backslash escapes
22553955d011SMarcel Moolenaar  *	anything (most notably a double-quote and a space) and
22563955d011SMarcel Moolenaar  *	provides the functionality it does in C. Each word consists of
22573955d011SMarcel Moolenaar  *	keyword and value separated by an equal sign. There should be no
22583955d011SMarcel Moolenaar  *	unnecessary spaces in the word. The keywords are as follows:
22593955d011SMarcel Moolenaar  *	    name	Name of shell.
22603955d011SMarcel Moolenaar  *	    path	Location of shell.
22613955d011SMarcel Moolenaar  *	    quiet	Command to turn off echoing.
22623955d011SMarcel Moolenaar  *	    echo	Command to turn echoing on
22633955d011SMarcel Moolenaar  *	    filter	Result of turning off echoing that shouldn't be
22643955d011SMarcel Moolenaar  *			printed.
22653955d011SMarcel Moolenaar  *	    echoFlag	Flag to turn echoing on at the start
22663955d011SMarcel Moolenaar  *	    errFlag	Flag to turn error checking on at the start
22673955d011SMarcel Moolenaar  *	    hasErrCtl	True if shell has error checking control
22683955d011SMarcel Moolenaar  *	    newline	String literal to represent a newline char
22693955d011SMarcel Moolenaar  *	    check	Command to turn on error checking if hasErrCtl
22703955d011SMarcel Moolenaar  *			is TRUE or template of command to echo a command
22713955d011SMarcel Moolenaar  *			for which error checking is off if hasErrCtl is
22723955d011SMarcel Moolenaar  *			FALSE.
22733955d011SMarcel Moolenaar  *	    ignore	Command to turn off error checking if hasErrCtl
22743955d011SMarcel Moolenaar  *			is TRUE or template of command to execute a
22753955d011SMarcel Moolenaar  *			command so as to ignore any errors it returns if
22763955d011SMarcel Moolenaar  *			hasErrCtl is FALSE.
22773955d011SMarcel Moolenaar  *
22783955d011SMarcel Moolenaar  *-----------------------------------------------------------------------
22793955d011SMarcel Moolenaar  */
22802c3632d1SSimon J. Gerraty Boolean
22813955d011SMarcel Moolenaar Job_ParseShell(char *line)
22823955d011SMarcel Moolenaar {
22832c3632d1SSimon J. Gerraty     Words	wordsList;
22843955d011SMarcel Moolenaar     char	**words;
22853955d011SMarcel Moolenaar     char	**argv;
22862c3632d1SSimon J. Gerraty     size_t	argc;
22873955d011SMarcel Moolenaar     char	*path;
22883955d011SMarcel Moolenaar     Shell	newShell;
22893955d011SMarcel Moolenaar     Boolean	fullSpec = FALSE;
22903955d011SMarcel Moolenaar     Shell	*sh;
22913955d011SMarcel Moolenaar 
2292956e45f6SSimon J. Gerraty     pp_skip_whitespace(&line);
22933955d011SMarcel Moolenaar 
22942c3632d1SSimon J. Gerraty     free(shellArgv);
22953955d011SMarcel Moolenaar 
2296*e2eeea75SSimon J. Gerraty     memset(&newShell, 0, sizeof newShell);
22973955d011SMarcel Moolenaar 
22983955d011SMarcel Moolenaar     /*
22993955d011SMarcel Moolenaar      * Parse the specification by keyword
23003955d011SMarcel Moolenaar      */
23012c3632d1SSimon J. Gerraty     wordsList = Str_Words(line, TRUE);
23022c3632d1SSimon J. Gerraty     words = wordsList.words;
23032c3632d1SSimon J. Gerraty     argc = wordsList.len;
23042c3632d1SSimon J. Gerraty     path = wordsList.freeIt;
23053955d011SMarcel Moolenaar     if (words == NULL) {
23063955d011SMarcel Moolenaar 	Error("Unterminated quoted string [%s]", line);
23072c3632d1SSimon J. Gerraty 	return FALSE;
23083955d011SMarcel Moolenaar     }
23093955d011SMarcel Moolenaar     shellArgv = path;
23103955d011SMarcel Moolenaar 
23113955d011SMarcel Moolenaar     for (path = NULL, argv = words; argc != 0; argc--, argv++) {
2312956e45f6SSimon J. Gerraty 	char *arg = *argv;
2313956e45f6SSimon J. Gerraty 	if (strncmp(arg, "path=", 5) == 0) {
2314956e45f6SSimon J. Gerraty 	    path = arg + 5;
2315956e45f6SSimon J. Gerraty 	} else if (strncmp(arg, "name=", 5) == 0) {
2316956e45f6SSimon J. Gerraty 	    newShell.name = arg + 5;
23173955d011SMarcel Moolenaar 	} else {
2318956e45f6SSimon J. Gerraty 	    if (strncmp(arg, "quiet=", 6) == 0) {
2319956e45f6SSimon J. Gerraty 		newShell.echoOff = arg + 6;
2320956e45f6SSimon J. Gerraty 	    } else if (strncmp(arg, "echo=", 5) == 0) {
2321956e45f6SSimon J. Gerraty 		newShell.echoOn = arg + 5;
2322956e45f6SSimon J. Gerraty 	    } else if (strncmp(arg, "filter=", 7) == 0) {
2323956e45f6SSimon J. Gerraty 		newShell.noPrint = arg + 7;
2324956e45f6SSimon J. Gerraty 		newShell.noPrintLen = strlen(newShell.noPrint);
2325956e45f6SSimon J. Gerraty 	    } else if (strncmp(arg, "echoFlag=", 9) == 0) {
2326956e45f6SSimon J. Gerraty 		newShell.echo = arg + 9;
2327956e45f6SSimon J. Gerraty 	    } else if (strncmp(arg, "errFlag=", 8) == 0) {
2328956e45f6SSimon J. Gerraty 		newShell.exit = arg + 8;
2329956e45f6SSimon J. Gerraty 	    } else if (strncmp(arg, "hasErrCtl=", 10) == 0) {
2330956e45f6SSimon J. Gerraty 		char c = arg[10];
2331956e45f6SSimon J. Gerraty 		newShell.hasErrCtl = c == 'Y' || c == 'y' ||
2332956e45f6SSimon J. Gerraty 				     c == 'T' || c == 't';
2333956e45f6SSimon J. Gerraty 	    } else if (strncmp(arg, "newline=", 8) == 0) {
2334956e45f6SSimon J. Gerraty 		newShell.newline = arg + 8;
2335956e45f6SSimon J. Gerraty 	    } else if (strncmp(arg, "check=", 6) == 0) {
2336956e45f6SSimon J. Gerraty 		newShell.errOnOrEcho = arg + 6;
2337956e45f6SSimon J. Gerraty 	    } else if (strncmp(arg, "ignore=", 7) == 0) {
2338956e45f6SSimon J. Gerraty 		newShell.errOffOrExecIgnore = arg + 7;
2339956e45f6SSimon J. Gerraty 	    } else if (strncmp(arg, "errout=", 7) == 0) {
2340956e45f6SSimon J. Gerraty 		newShell.errExit = arg + 7;
2341956e45f6SSimon J. Gerraty 	    } else if (strncmp(arg, "comment=", 8) == 0) {
2342956e45f6SSimon J. Gerraty 		newShell.commentChar = arg[8];
23433955d011SMarcel Moolenaar 	    } else {
2344956e45f6SSimon J. Gerraty 		Parse_Error(PARSE_FATAL, "Unknown keyword \"%s\"", arg);
23453955d011SMarcel Moolenaar 		free(words);
23462c3632d1SSimon J. Gerraty 		return FALSE;
23473955d011SMarcel Moolenaar 	    }
23483955d011SMarcel Moolenaar 	    fullSpec = TRUE;
23493955d011SMarcel Moolenaar 	}
23503955d011SMarcel Moolenaar     }
23513955d011SMarcel Moolenaar 
23523955d011SMarcel Moolenaar     if (path == NULL) {
23533955d011SMarcel Moolenaar 	/*
23543955d011SMarcel Moolenaar 	 * If no path was given, the user wants one of the pre-defined shells,
2355956e45f6SSimon J. Gerraty 	 * yes? So we find the one s/he wants with the help of FindShellByName
23563955d011SMarcel Moolenaar 	 * and set things up the right way. shellPath will be set up by
23573955d011SMarcel Moolenaar 	 * Shell_Init.
23583955d011SMarcel Moolenaar 	 */
23593955d011SMarcel Moolenaar 	if (newShell.name == NULL) {
23603955d011SMarcel Moolenaar 	    Parse_Error(PARSE_FATAL, "Neither path nor name specified");
23613955d011SMarcel Moolenaar 	    free(words);
23622c3632d1SSimon J. Gerraty 	    return FALSE;
23633955d011SMarcel Moolenaar 	} else {
2364956e45f6SSimon J. Gerraty 	    if ((sh = FindShellByName(newShell.name)) == NULL) {
23653955d011SMarcel Moolenaar 		    Parse_Error(PARSE_WARNING, "%s: No matching shell",
23663955d011SMarcel Moolenaar 				newShell.name);
23673955d011SMarcel Moolenaar 		    free(words);
23682c3632d1SSimon J. Gerraty 		    return FALSE;
23693955d011SMarcel Moolenaar 	    }
23703955d011SMarcel Moolenaar 	    commandShell = sh;
23713955d011SMarcel Moolenaar 	    shellName = newShell.name;
23723955d011SMarcel Moolenaar 	    if (shellPath) {
23733955d011SMarcel Moolenaar 		/* Shell_Init has already been called!  Do it again. */
23743955d011SMarcel Moolenaar 		free(UNCONST(shellPath));
23753955d011SMarcel Moolenaar 		shellPath = NULL;
23763955d011SMarcel Moolenaar 		Shell_Init();
23773955d011SMarcel Moolenaar 	    }
23783955d011SMarcel Moolenaar 	}
23793955d011SMarcel Moolenaar     } else {
23803955d011SMarcel Moolenaar 	/*
23813955d011SMarcel Moolenaar 	 * The user provided a path. If s/he gave nothing else (fullSpec is
23823955d011SMarcel Moolenaar 	 * FALSE), try and find a matching shell in the ones we know of.
23833955d011SMarcel Moolenaar 	 * Else we just take the specification at its word and copy it
23843955d011SMarcel Moolenaar 	 * to a new location. In either case, we need to record the
23853955d011SMarcel Moolenaar 	 * path the user gave for the shell.
23863955d011SMarcel Moolenaar 	 */
23873955d011SMarcel Moolenaar 	shellPath = path;
23883955d011SMarcel Moolenaar 	path = strrchr(path, '/');
23893955d011SMarcel Moolenaar 	if (path == NULL) {
23903955d011SMarcel Moolenaar 	    path = UNCONST(shellPath);
23913955d011SMarcel Moolenaar 	} else {
2392956e45f6SSimon J. Gerraty 	    path++;
23933955d011SMarcel Moolenaar 	}
23943955d011SMarcel Moolenaar 	if (newShell.name != NULL) {
23953955d011SMarcel Moolenaar 	    shellName = newShell.name;
23963955d011SMarcel Moolenaar 	} else {
23973955d011SMarcel Moolenaar 	    shellName = path;
23983955d011SMarcel Moolenaar 	}
23993955d011SMarcel Moolenaar 	if (!fullSpec) {
2400956e45f6SSimon J. Gerraty 	    if ((sh = FindShellByName(shellName)) == NULL) {
24013955d011SMarcel Moolenaar 		    Parse_Error(PARSE_WARNING, "%s: No matching shell",
24023955d011SMarcel Moolenaar 				shellName);
24033955d011SMarcel Moolenaar 		    free(words);
24042c3632d1SSimon J. Gerraty 		    return FALSE;
24053955d011SMarcel Moolenaar 	    }
24063955d011SMarcel Moolenaar 	    commandShell = sh;
24073955d011SMarcel Moolenaar 	} else {
2408*e2eeea75SSimon J. Gerraty 	    commandShell = bmake_malloc(sizeof *commandShell);
24093955d011SMarcel Moolenaar 	    *commandShell = newShell;
24103955d011SMarcel Moolenaar 	}
241151ee2c1cSSimon J. Gerraty 	/* this will take care of shellErrFlag */
241251ee2c1cSSimon J. Gerraty 	Shell_Init();
24133955d011SMarcel Moolenaar     }
24143955d011SMarcel Moolenaar 
24153955d011SMarcel Moolenaar     if (commandShell->echoOn && commandShell->echoOff) {
24163955d011SMarcel Moolenaar 	commandShell->hasEchoCtl = TRUE;
24173955d011SMarcel Moolenaar     }
24183955d011SMarcel Moolenaar 
24193955d011SMarcel Moolenaar     if (!commandShell->hasErrCtl) {
2420956e45f6SSimon J. Gerraty 	if (commandShell->errOnOrEcho == NULL) {
2421956e45f6SSimon J. Gerraty 	    commandShell->errOnOrEcho = "";
24223955d011SMarcel Moolenaar 	}
2423956e45f6SSimon J. Gerraty 	if (commandShell->errOffOrExecIgnore == NULL) {
2424956e45f6SSimon J. Gerraty 	    commandShell->errOffOrExecIgnore = "%s\n";
24253955d011SMarcel Moolenaar 	}
24263955d011SMarcel Moolenaar     }
24273955d011SMarcel Moolenaar 
24283955d011SMarcel Moolenaar     /*
24293955d011SMarcel Moolenaar      * Do not free up the words themselves, since they might be in use by the
24303955d011SMarcel Moolenaar      * shell specification.
24313955d011SMarcel Moolenaar      */
24323955d011SMarcel Moolenaar     free(words);
24332c3632d1SSimon J. Gerraty     return TRUE;
24343955d011SMarcel Moolenaar }
24353955d011SMarcel Moolenaar 
2436956e45f6SSimon J. Gerraty /* Handle the receipt of an interrupt.
2437956e45f6SSimon J. Gerraty  *
2438956e45f6SSimon J. Gerraty  * All children are killed. Another job will be started if the .INTERRUPT
2439956e45f6SSimon J. Gerraty  * target is defined.
24403955d011SMarcel Moolenaar  *
24413955d011SMarcel Moolenaar  * Input:
24423955d011SMarcel Moolenaar  *	runINTERRUPT	Non-zero if commands for the .INTERRUPT target
24433955d011SMarcel Moolenaar  *			should be executed
24443955d011SMarcel Moolenaar  *	signo		signal received
24453955d011SMarcel Moolenaar  */
24463955d011SMarcel Moolenaar static void
24473955d011SMarcel Moolenaar JobInterrupt(int runINTERRUPT, int signo)
24483955d011SMarcel Moolenaar {
24493955d011SMarcel Moolenaar     Job		*job;		/* job descriptor in that element */
24503955d011SMarcel Moolenaar     GNode	*interrupt;	/* the node describing the .INTERRUPT target */
24513955d011SMarcel Moolenaar     sigset_t	mask;
24523955d011SMarcel Moolenaar     GNode	*gn;
24533955d011SMarcel Moolenaar 
24543955d011SMarcel Moolenaar     aborting = ABORT_INTERRUPT;
24553955d011SMarcel Moolenaar 
24563955d011SMarcel Moolenaar     JobSigLock(&mask);
24573955d011SMarcel Moolenaar 
24583955d011SMarcel Moolenaar     for (job = job_table; job < job_table_end; job++) {
2459*e2eeea75SSimon J. Gerraty 	if (job->status != JOB_ST_RUNNING)
24603955d011SMarcel Moolenaar 	    continue;
24613955d011SMarcel Moolenaar 
24623955d011SMarcel Moolenaar 	gn = job->node;
24633955d011SMarcel Moolenaar 
246445447996SSimon J. Gerraty 	JobDeleteTarget(gn);
24653955d011SMarcel Moolenaar 	if (job->pid) {
2466956e45f6SSimon J. Gerraty 	    DEBUG2(JOB, "JobInterrupt passing signal %d to child %d.\n",
24673955d011SMarcel Moolenaar 		   signo, job->pid);
24683955d011SMarcel Moolenaar 	    KILLPG(job->pid, signo);
24693955d011SMarcel Moolenaar 	}
24703955d011SMarcel Moolenaar     }
24713955d011SMarcel Moolenaar 
24723955d011SMarcel Moolenaar     JobSigUnlock(&mask);
24733955d011SMarcel Moolenaar 
2474956e45f6SSimon J. Gerraty     if (runINTERRUPT && !opts.touchFlag) {
2475956e45f6SSimon J. Gerraty 	interrupt = Targ_FindNode(".INTERRUPT");
24763955d011SMarcel Moolenaar 	if (interrupt != NULL) {
2477956e45f6SSimon J. Gerraty 	    opts.ignoreErrors = FALSE;
24783955d011SMarcel Moolenaar 	    JobRun(interrupt);
24793955d011SMarcel Moolenaar 	}
24803955d011SMarcel Moolenaar     }
2481*e2eeea75SSimon J. Gerraty     Trace_Log(MAKEINTR, NULL);
24823955d011SMarcel Moolenaar     exit(signo);
24833955d011SMarcel Moolenaar }
24843955d011SMarcel Moolenaar 
2485956e45f6SSimon J. Gerraty /* Do the final processing, i.e. run the commands attached to the .END target.
24863955d011SMarcel Moolenaar  *
2487956e45f6SSimon J. Gerraty  * Return the number of errors reported. */
24883955d011SMarcel Moolenaar int
24893955d011SMarcel Moolenaar Job_Finish(void)
24903955d011SMarcel Moolenaar {
2491956e45f6SSimon J. Gerraty     GNode *endNode = Targ_GetEndNode();
2492956e45f6SSimon J. Gerraty     if (!Lst_IsEmpty(endNode->commands) || !Lst_IsEmpty(endNode->children)) {
24933955d011SMarcel Moolenaar 	if (errors) {
24943955d011SMarcel Moolenaar 	    Error("Errors reported so .END ignored");
24953955d011SMarcel Moolenaar 	} else {
2496956e45f6SSimon J. Gerraty 	    JobRun(endNode);
24973955d011SMarcel Moolenaar 	}
24983955d011SMarcel Moolenaar     }
24993841c287SSimon J. Gerraty     return errors;
25003955d011SMarcel Moolenaar }
25013955d011SMarcel Moolenaar 
2502956e45f6SSimon J. Gerraty /* Clean up any memory used by the jobs module. */
25033955d011SMarcel Moolenaar void
25043955d011SMarcel Moolenaar Job_End(void)
25053955d011SMarcel Moolenaar {
25063955d011SMarcel Moolenaar #ifdef CLEANUP
25073955d011SMarcel Moolenaar     free(shellArgv);
25083955d011SMarcel Moolenaar #endif
25093955d011SMarcel Moolenaar }
25103955d011SMarcel Moolenaar 
2511956e45f6SSimon J. Gerraty /* Waits for all running jobs to finish and returns.
2512956e45f6SSimon J. Gerraty  * Sets 'aborting' to ABORT_WAIT to prevent other jobs from starting. */
25133955d011SMarcel Moolenaar void
25143955d011SMarcel Moolenaar Job_Wait(void)
25153955d011SMarcel Moolenaar {
25163955d011SMarcel Moolenaar     aborting = ABORT_WAIT;
25173955d011SMarcel Moolenaar     while (jobTokensRunning != 0) {
25183955d011SMarcel Moolenaar 	Job_CatchOutput();
25193955d011SMarcel Moolenaar     }
2520956e45f6SSimon J. Gerraty     aborting = ABORT_NONE;
25213955d011SMarcel Moolenaar }
25223955d011SMarcel Moolenaar 
2523956e45f6SSimon J. Gerraty /* Abort all currently running jobs without handling output or anything.
2524956e45f6SSimon J. Gerraty  * This function is to be called only in the event of a major error.
2525956e45f6SSimon J. Gerraty  * Most definitely NOT to be called from JobInterrupt.
25263955d011SMarcel Moolenaar  *
2527956e45f6SSimon J. Gerraty  * All children are killed, not just the firstborn. */
25283955d011SMarcel Moolenaar void
25293955d011SMarcel Moolenaar Job_AbortAll(void)
25303955d011SMarcel Moolenaar {
25313955d011SMarcel Moolenaar     Job		*job;	/* the job descriptor in that element */
25323955d011SMarcel Moolenaar     WAIT_T	foo;
25333955d011SMarcel Moolenaar 
25343955d011SMarcel Moolenaar     aborting = ABORT_ERROR;
25353955d011SMarcel Moolenaar 
25363955d011SMarcel Moolenaar     if (jobTokensRunning) {
25373955d011SMarcel Moolenaar 	for (job = job_table; job < job_table_end; job++) {
2538*e2eeea75SSimon J. Gerraty 	    if (job->status != JOB_ST_RUNNING)
25393955d011SMarcel Moolenaar 		continue;
25403955d011SMarcel Moolenaar 	    /*
25413955d011SMarcel Moolenaar 	     * kill the child process with increasingly drastic signals to make
25423955d011SMarcel Moolenaar 	     * darn sure it's dead.
25433955d011SMarcel Moolenaar 	     */
25443955d011SMarcel Moolenaar 	    KILLPG(job->pid, SIGINT);
25453955d011SMarcel Moolenaar 	    KILLPG(job->pid, SIGKILL);
25463955d011SMarcel Moolenaar 	}
25473955d011SMarcel Moolenaar     }
25483955d011SMarcel Moolenaar 
25493955d011SMarcel Moolenaar     /*
25503955d011SMarcel Moolenaar      * Catch as many children as want to report in at first, then give up
25513955d011SMarcel Moolenaar      */
25523955d011SMarcel Moolenaar     while (waitpid((pid_t) -1, &foo, WNOHANG) > 0)
25533955d011SMarcel Moolenaar 	continue;
25543955d011SMarcel Moolenaar }
25553955d011SMarcel Moolenaar 
2556956e45f6SSimon J. Gerraty /* Tries to restart stopped jobs if there are slots available.
2557956e45f6SSimon J. Gerraty  * Called in process context in response to a SIGCONT. */
25583955d011SMarcel Moolenaar static void
25593955d011SMarcel Moolenaar JobRestartJobs(void)
25603955d011SMarcel Moolenaar {
25613955d011SMarcel Moolenaar     Job *job;
25623955d011SMarcel Moolenaar 
25633955d011SMarcel Moolenaar     for (job = job_table; job < job_table_end; job++) {
2564*e2eeea75SSimon J. Gerraty 	if (job->status == JOB_ST_RUNNING &&
2565*e2eeea75SSimon J. Gerraty 	    (make_suspended || job->suspended)) {
2566956e45f6SSimon J. Gerraty 	    DEBUG1(JOB, "Restarting stopped job pid %d.\n", job->pid);
2567*e2eeea75SSimon J. Gerraty 	    if (job->suspended) {
25683955d011SMarcel Moolenaar 		    (void)printf("*** [%s] Continued\n", job->node->name);
25693955d011SMarcel Moolenaar 		    (void)fflush(stdout);
25703955d011SMarcel Moolenaar 	    }
2571*e2eeea75SSimon J. Gerraty 	    job->suspended = FALSE;
25723955d011SMarcel Moolenaar 	    if (KILLPG(job->pid, SIGCONT) != 0 && DEBUG(JOB)) {
2573956e45f6SSimon J. Gerraty 		debug_printf("Failed to send SIGCONT to %d\n", job->pid);
25743955d011SMarcel Moolenaar 	    }
25753955d011SMarcel Moolenaar 	}
2576*e2eeea75SSimon J. Gerraty 	if (job->status == JOB_ST_FINISHED)
25773955d011SMarcel Moolenaar 	    /* Job exit deferred after calling waitpid() in a signal handler */
25783955d011SMarcel Moolenaar 	    JobFinish(job, job->exit_status);
25793955d011SMarcel Moolenaar     }
2580*e2eeea75SSimon J. Gerraty     make_suspended = FALSE;
25813955d011SMarcel Moolenaar }
25823955d011SMarcel Moolenaar 
25833955d011SMarcel Moolenaar static void
25843955d011SMarcel Moolenaar watchfd(Job *job)
25853955d011SMarcel Moolenaar {
25863955d011SMarcel Moolenaar     if (job->inPollfd != NULL)
25873955d011SMarcel Moolenaar 	Punt("Watching watched job");
25883955d011SMarcel Moolenaar 
25893955d011SMarcel Moolenaar     fds[nfds].fd = job->inPipe;
25903955d011SMarcel Moolenaar     fds[nfds].events = POLLIN;
25913955d011SMarcel Moolenaar     jobfds[nfds] = job;
25923955d011SMarcel Moolenaar     job->inPollfd = &fds[nfds];
25933955d011SMarcel Moolenaar     nfds++;
259449caa483SSimon J. Gerraty #if defined(USE_FILEMON) && !defined(USE_FILEMON_DEV)
259549caa483SSimon J. Gerraty     if (useMeta) {
259649caa483SSimon J. Gerraty 	fds[nfds].fd = meta_job_fd(job);
259749caa483SSimon J. Gerraty 	fds[nfds].events = fds[nfds].fd == -1 ? 0 : POLLIN;
259849caa483SSimon J. Gerraty 	jobfds[nfds] = job;
259949caa483SSimon J. Gerraty 	nfds++;
260049caa483SSimon J. Gerraty     }
260149caa483SSimon J. Gerraty #endif
26023955d011SMarcel Moolenaar }
26033955d011SMarcel Moolenaar 
26043955d011SMarcel Moolenaar static void
26053955d011SMarcel Moolenaar clearfd(Job *job)
26063955d011SMarcel Moolenaar {
2607956e45f6SSimon J. Gerraty     size_t i;
26083955d011SMarcel Moolenaar     if (job->inPollfd == NULL)
26093955d011SMarcel Moolenaar 	Punt("Unwatching unwatched job");
2610956e45f6SSimon J. Gerraty     i = (size_t)(job->inPollfd - fds);
26113955d011SMarcel Moolenaar     nfds--;
261249caa483SSimon J. Gerraty #if defined(USE_FILEMON) && !defined(USE_FILEMON_DEV)
261349caa483SSimon J. Gerraty     if (useMeta) {
261449caa483SSimon J. Gerraty 	/*
261549caa483SSimon J. Gerraty 	 * Sanity check: there should be two fds per job, so the job's
261649caa483SSimon J. Gerraty 	 * pollfd number should be even.
261749caa483SSimon J. Gerraty 	 */
261849caa483SSimon J. Gerraty 	assert(nfds_per_job() == 2);
261949caa483SSimon J. Gerraty 	if (i % 2)
262049caa483SSimon J. Gerraty 	    Punt("odd-numbered fd with meta");
262149caa483SSimon J. Gerraty 	nfds--;
262249caa483SSimon J. Gerraty     }
262349caa483SSimon J. Gerraty #endif
26243955d011SMarcel Moolenaar     /*
26253955d011SMarcel Moolenaar      * Move last job in table into hole made by dead job.
26263955d011SMarcel Moolenaar      */
26273955d011SMarcel Moolenaar     if (nfds != i) {
26283955d011SMarcel Moolenaar 	fds[i] = fds[nfds];
26293955d011SMarcel Moolenaar 	jobfds[i] = jobfds[nfds];
26303955d011SMarcel Moolenaar 	jobfds[i]->inPollfd = &fds[i];
263149caa483SSimon J. Gerraty #if defined(USE_FILEMON) && !defined(USE_FILEMON_DEV)
263249caa483SSimon J. Gerraty 	if (useMeta) {
263349caa483SSimon J. Gerraty 	    fds[i + 1] = fds[nfds + 1];
263449caa483SSimon J. Gerraty 	    jobfds[i + 1] = jobfds[nfds + 1];
263549caa483SSimon J. Gerraty 	}
263649caa483SSimon J. Gerraty #endif
26373955d011SMarcel Moolenaar     }
26383955d011SMarcel Moolenaar     job->inPollfd = NULL;
26393955d011SMarcel Moolenaar }
26403955d011SMarcel Moolenaar 
26413955d011SMarcel Moolenaar static int
26423955d011SMarcel Moolenaar readyfd(Job *job)
26433955d011SMarcel Moolenaar {
26443955d011SMarcel Moolenaar     if (job->inPollfd == NULL)
26453955d011SMarcel Moolenaar 	Punt("Polling unwatched job");
26463955d011SMarcel Moolenaar     return (job->inPollfd->revents & POLLIN) != 0;
26473955d011SMarcel Moolenaar }
26483955d011SMarcel Moolenaar 
2649956e45f6SSimon J. Gerraty /* Put a token (back) into the job pipe.
2650956e45f6SSimon J. Gerraty  * This allows a make process to start a build job. */
26513955d011SMarcel Moolenaar static void
26523955d011SMarcel Moolenaar JobTokenAdd(void)
26533955d011SMarcel Moolenaar {
26543955d011SMarcel Moolenaar     char tok = JOB_TOKENS[aborting], tok1;
26553955d011SMarcel Moolenaar 
26562d395cb5SSimon J. Gerraty     if (!Job_error_token && aborting == ABORT_ERROR) {
26572d395cb5SSimon J. Gerraty 	if (jobTokensRunning == 0)
26582d395cb5SSimon J. Gerraty 	    return;
26592d395cb5SSimon J. Gerraty 	tok = '+';			/* no error token */
26602d395cb5SSimon J. Gerraty     }
26612d395cb5SSimon J. Gerraty 
26623955d011SMarcel Moolenaar     /* If we are depositing an error token flush everything else */
26633955d011SMarcel Moolenaar     while (tok != '+' && read(tokenWaitJob.inPipe, &tok1, 1) == 1)
26643955d011SMarcel Moolenaar 	continue;
26653955d011SMarcel Moolenaar 
2666956e45f6SSimon J. Gerraty     DEBUG3(JOB, "(%d) aborting %d, deposit token %c\n",
26672d395cb5SSimon J. Gerraty 	   getpid(), aborting, tok);
26683cbdda60SSimon J. Gerraty     while (write(tokenWaitJob.outPipe, &tok, 1) == -1 && errno == EAGAIN)
26693cbdda60SSimon J. Gerraty 	continue;
26703955d011SMarcel Moolenaar }
26713955d011SMarcel Moolenaar 
2672956e45f6SSimon J. Gerraty /* Prep the job token pipe in the root make process. */
26733955d011SMarcel Moolenaar void
26743955d011SMarcel Moolenaar Job_ServerStart(int max_tokens, int jp_0, int jp_1)
26753955d011SMarcel Moolenaar {
26763955d011SMarcel Moolenaar     int i;
26773955d011SMarcel Moolenaar     char jobarg[64];
26783955d011SMarcel Moolenaar 
26793955d011SMarcel Moolenaar     if (jp_0 >= 0 && jp_1 >= 0) {
26803955d011SMarcel Moolenaar 	/* Pipe passed in from parent */
26813955d011SMarcel Moolenaar 	tokenWaitJob.inPipe = jp_0;
26823955d011SMarcel Moolenaar 	tokenWaitJob.outPipe = jp_1;
2683be19d90bSSimon J. Gerraty 	(void)fcntl(jp_0, F_SETFD, FD_CLOEXEC);
2684be19d90bSSimon J. Gerraty 	(void)fcntl(jp_1, F_SETFD, FD_CLOEXEC);
26853955d011SMarcel Moolenaar 	return;
26863955d011SMarcel Moolenaar     }
26873955d011SMarcel Moolenaar 
26883955d011SMarcel Moolenaar     JobCreatePipe(&tokenWaitJob, 15);
26893955d011SMarcel Moolenaar 
2690*e2eeea75SSimon J. Gerraty     snprintf(jobarg, sizeof jobarg, "%d,%d",
26913955d011SMarcel Moolenaar 	    tokenWaitJob.inPipe, tokenWaitJob.outPipe);
26923955d011SMarcel Moolenaar 
26933955d011SMarcel Moolenaar     Var_Append(MAKEFLAGS, "-J", VAR_GLOBAL);
26943955d011SMarcel Moolenaar     Var_Append(MAKEFLAGS, jobarg, VAR_GLOBAL);
26953955d011SMarcel Moolenaar 
26963955d011SMarcel Moolenaar     /*
26973955d011SMarcel Moolenaar      * Preload the job pipe with one token per job, save the one
26983955d011SMarcel Moolenaar      * "extra" token for the primary job.
26993955d011SMarcel Moolenaar      *
27003955d011SMarcel Moolenaar      * XXX should clip maxJobs against PIPE_BUF -- if max_tokens is
27013955d011SMarcel Moolenaar      * larger than the write buffer size of the pipe, we will
27023955d011SMarcel Moolenaar      * deadlock here.
27033955d011SMarcel Moolenaar      */
27043955d011SMarcel Moolenaar     for (i = 1; i < max_tokens; i++)
27053955d011SMarcel Moolenaar 	JobTokenAdd();
27063955d011SMarcel Moolenaar }
27073955d011SMarcel Moolenaar 
2708956e45f6SSimon J. Gerraty /* Return a withdrawn token to the pool. */
27093955d011SMarcel Moolenaar void
27103955d011SMarcel Moolenaar Job_TokenReturn(void)
27113955d011SMarcel Moolenaar {
27123955d011SMarcel Moolenaar     jobTokensRunning--;
27133955d011SMarcel Moolenaar     if (jobTokensRunning < 0)
27143955d011SMarcel Moolenaar 	Punt("token botch");
27153955d011SMarcel Moolenaar     if (jobTokensRunning || JOB_TOKENS[aborting] != '+')
27163955d011SMarcel Moolenaar 	JobTokenAdd();
27173955d011SMarcel Moolenaar }
27183955d011SMarcel Moolenaar 
2719956e45f6SSimon J. Gerraty /* Attempt to withdraw a token from the pool.
27203955d011SMarcel Moolenaar  *
2721956e45f6SSimon J. Gerraty  * If pool is empty, set wantToken so that we wake up when a token is
2722956e45f6SSimon J. Gerraty  * released.
27233955d011SMarcel Moolenaar  *
2724956e45f6SSimon J. Gerraty  * Returns TRUE if a token was withdrawn, and FALSE if the pool is currently
2725956e45f6SSimon J. Gerraty  * empty. */
27263955d011SMarcel Moolenaar Boolean
27273955d011SMarcel Moolenaar Job_TokenWithdraw(void)
27283955d011SMarcel Moolenaar {
27293955d011SMarcel Moolenaar     char tok, tok1;
2730956e45f6SSimon J. Gerraty     ssize_t count;
27313955d011SMarcel Moolenaar 
27323955d011SMarcel Moolenaar     wantToken = 0;
2733956e45f6SSimon J. Gerraty     DEBUG3(JOB, "Job_TokenWithdraw(%d): aborting %d, running %d\n",
27343955d011SMarcel Moolenaar 	   getpid(), aborting, jobTokensRunning);
27353955d011SMarcel Moolenaar 
2736956e45f6SSimon J. Gerraty     if (aborting != ABORT_NONE || (jobTokensRunning >= opts.maxJobs))
27373955d011SMarcel Moolenaar 	return FALSE;
27383955d011SMarcel Moolenaar 
27393955d011SMarcel Moolenaar     count = read(tokenWaitJob.inPipe, &tok, 1);
27403955d011SMarcel Moolenaar     if (count == 0)
27413955d011SMarcel Moolenaar 	Fatal("eof on job pipe!");
27423955d011SMarcel Moolenaar     if (count < 0 && jobTokensRunning != 0) {
27433955d011SMarcel Moolenaar 	if (errno != EAGAIN) {
27443955d011SMarcel Moolenaar 	    Fatal("job pipe read: %s", strerror(errno));
27453955d011SMarcel Moolenaar 	}
2746956e45f6SSimon J. Gerraty 	DEBUG1(JOB, "(%d) blocked for token\n", getpid());
27473955d011SMarcel Moolenaar 	return FALSE;
27483955d011SMarcel Moolenaar     }
27493955d011SMarcel Moolenaar 
27503955d011SMarcel Moolenaar     if (count == 1 && tok != '+') {
27513955d011SMarcel Moolenaar 	/* make being abvorted - remove any other job tokens */
2752956e45f6SSimon J. Gerraty 	DEBUG2(JOB, "(%d) aborted by token %c\n", getpid(), tok);
27533955d011SMarcel Moolenaar 	while (read(tokenWaitJob.inPipe, &tok1, 1) == 1)
27543955d011SMarcel Moolenaar 	    continue;
27553955d011SMarcel Moolenaar 	/* And put the stopper back */
27563cbdda60SSimon J. Gerraty 	while (write(tokenWaitJob.outPipe, &tok, 1) == -1 && errno == EAGAIN)
27573cbdda60SSimon J. Gerraty 	    continue;
2758*e2eeea75SSimon J. Gerraty 	if (shouldDieQuietly(NULL, 1))
27593841c287SSimon J. Gerraty 	    exit(2);
27603955d011SMarcel Moolenaar 	Fatal("A failure has been detected in another branch of the parallel make");
27613955d011SMarcel Moolenaar     }
27623955d011SMarcel Moolenaar 
27633955d011SMarcel Moolenaar     if (count == 1 && jobTokensRunning == 0)
27643955d011SMarcel Moolenaar 	/* We didn't want the token really */
27653cbdda60SSimon J. Gerraty 	while (write(tokenWaitJob.outPipe, &tok, 1) == -1 && errno == EAGAIN)
27663cbdda60SSimon J. Gerraty 	    continue;
27673955d011SMarcel Moolenaar 
27683955d011SMarcel Moolenaar     jobTokensRunning++;
2769956e45f6SSimon J. Gerraty     DEBUG1(JOB, "(%d) withdrew token\n", getpid());
27703955d011SMarcel Moolenaar     return TRUE;
27713955d011SMarcel Moolenaar }
27723955d011SMarcel Moolenaar 
2773956e45f6SSimon J. Gerraty /* Run the named target if found. If a filename is specified, then set that
2774956e45f6SSimon J. Gerraty  * to the sources.
27751748de26SSimon J. Gerraty  *
2776956e45f6SSimon J. Gerraty  * Exits if the target fails. */
27771748de26SSimon J. Gerraty Boolean
27781748de26SSimon J. Gerraty Job_RunTarget(const char *target, const char *fname) {
2779956e45f6SSimon J. Gerraty     GNode *gn = Targ_FindNode(target);
27801748de26SSimon J. Gerraty     if (gn == NULL)
27811748de26SSimon J. Gerraty 	return FALSE;
27821748de26SSimon J. Gerraty 
27831748de26SSimon J. Gerraty     if (fname)
27843841c287SSimon J. Gerraty 	Var_Set(ALLSRC, fname, gn);
27851748de26SSimon J. Gerraty 
27861748de26SSimon J. Gerraty     JobRun(gn);
27871748de26SSimon J. Gerraty     if (gn->made == ERROR) {
27881748de26SSimon J. Gerraty 	PrintOnError(gn, "\n\nStop.");
27891748de26SSimon J. Gerraty 	exit(1);
27901748de26SSimon J. Gerraty     }
27911748de26SSimon J. Gerraty     return TRUE;
27921748de26SSimon J. Gerraty }
27931748de26SSimon J. Gerraty 
27943955d011SMarcel Moolenaar #ifdef USE_SELECT
27953955d011SMarcel Moolenaar int
27963955d011SMarcel Moolenaar emul_poll(struct pollfd *fd, int nfd, int timeout)
27973955d011SMarcel Moolenaar {
27983955d011SMarcel Moolenaar     fd_set rfds, wfds;
27993955d011SMarcel Moolenaar     int i, maxfd, nselect, npoll;
28003955d011SMarcel Moolenaar     struct timeval tv, *tvp;
28013955d011SMarcel Moolenaar     long usecs;
28023955d011SMarcel Moolenaar 
28033955d011SMarcel Moolenaar     FD_ZERO(&rfds);
28043955d011SMarcel Moolenaar     FD_ZERO(&wfds);
28053955d011SMarcel Moolenaar 
28063955d011SMarcel Moolenaar     maxfd = -1;
28073955d011SMarcel Moolenaar     for (i = 0; i < nfd; i++) {
28083955d011SMarcel Moolenaar 	fd[i].revents = 0;
28093955d011SMarcel Moolenaar 
28103955d011SMarcel Moolenaar 	if (fd[i].events & POLLIN)
28113955d011SMarcel Moolenaar 	    FD_SET(fd[i].fd, &rfds);
28123955d011SMarcel Moolenaar 
28133955d011SMarcel Moolenaar 	if (fd[i].events & POLLOUT)
28143955d011SMarcel Moolenaar 	    FD_SET(fd[i].fd, &wfds);
28153955d011SMarcel Moolenaar 
28163955d011SMarcel Moolenaar 	if (fd[i].fd > maxfd)
28173955d011SMarcel Moolenaar 	    maxfd = fd[i].fd;
28183955d011SMarcel Moolenaar     }
28193955d011SMarcel Moolenaar 
28203955d011SMarcel Moolenaar     if (maxfd >= FD_SETSIZE) {
28213955d011SMarcel Moolenaar 	Punt("Ran out of fd_set slots; "
28223955d011SMarcel Moolenaar 	     "recompile with a larger FD_SETSIZE.");
28233955d011SMarcel Moolenaar     }
28243955d011SMarcel Moolenaar 
28253955d011SMarcel Moolenaar     if (timeout < 0) {
28263955d011SMarcel Moolenaar 	tvp = NULL;
28273955d011SMarcel Moolenaar     } else {
28283955d011SMarcel Moolenaar 	usecs = timeout * 1000;
28293955d011SMarcel Moolenaar 	tv.tv_sec = usecs / 1000000;
28303955d011SMarcel Moolenaar 	tv.tv_usec = usecs % 1000000;
28313955d011SMarcel Moolenaar 	tvp = &tv;
28323955d011SMarcel Moolenaar     }
28333955d011SMarcel Moolenaar 
2834*e2eeea75SSimon J. Gerraty     nselect = select(maxfd + 1, &rfds, &wfds, NULL, tvp);
28353955d011SMarcel Moolenaar 
28363955d011SMarcel Moolenaar     if (nselect <= 0)
28373955d011SMarcel Moolenaar 	return nselect;
28383955d011SMarcel Moolenaar 
28393955d011SMarcel Moolenaar     npoll = 0;
28403955d011SMarcel Moolenaar     for (i = 0; i < nfd; i++) {
28413955d011SMarcel Moolenaar 	if (FD_ISSET(fd[i].fd, &rfds))
28423955d011SMarcel Moolenaar 	    fd[i].revents |= POLLIN;
28433955d011SMarcel Moolenaar 
28443955d011SMarcel Moolenaar 	if (FD_ISSET(fd[i].fd, &wfds))
28453955d011SMarcel Moolenaar 	    fd[i].revents |= POLLOUT;
28463955d011SMarcel Moolenaar 
28473955d011SMarcel Moolenaar 	if (fd[i].revents)
28483955d011SMarcel Moolenaar 	    npoll++;
28493955d011SMarcel Moolenaar     }
28503955d011SMarcel Moolenaar 
28513955d011SMarcel Moolenaar     return npoll;
28523955d011SMarcel Moolenaar }
28533955d011SMarcel Moolenaar #endif /* USE_SELECT */
2854