xref: /dflybsd-src/contrib/bmake/job.c (revision 9e7ae5a0527a977cab412aede3a532cfe2903bbb)
1*6eef5f0cSAntonio Huete Jimenez /*	$NetBSD: job.c,v 1.455 2022/09/03 08:41:07 rillig Exp $	*/
201e196c8SJohn Marino 
301e196c8SJohn Marino /*
401e196c8SJohn Marino  * Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
501e196c8SJohn Marino  * All rights reserved.
601e196c8SJohn Marino  *
701e196c8SJohn Marino  * This code is derived from software contributed to Berkeley by
801e196c8SJohn Marino  * Adam de Boor.
901e196c8SJohn Marino  *
1001e196c8SJohn Marino  * Redistribution and use in source and binary forms, with or without
1101e196c8SJohn Marino  * modification, are permitted provided that the following conditions
1201e196c8SJohn Marino  * are met:
1301e196c8SJohn Marino  * 1. Redistributions of source code must retain the above copyright
1401e196c8SJohn Marino  *    notice, this list of conditions and the following disclaimer.
1501e196c8SJohn Marino  * 2. Redistributions in binary form must reproduce the above copyright
1601e196c8SJohn Marino  *    notice, this list of conditions and the following disclaimer in the
1701e196c8SJohn Marino  *    documentation and/or other materials provided with the distribution.
1801e196c8SJohn Marino  * 3. Neither the name of the University nor the names of its contributors
1901e196c8SJohn Marino  *    may be used to endorse or promote products derived from this software
2001e196c8SJohn Marino  *    without specific prior written permission.
2101e196c8SJohn Marino  *
2201e196c8SJohn Marino  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2301e196c8SJohn Marino  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2401e196c8SJohn Marino  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2501e196c8SJohn Marino  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2601e196c8SJohn Marino  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2701e196c8SJohn Marino  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2801e196c8SJohn Marino  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2901e196c8SJohn Marino  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
3001e196c8SJohn Marino  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3101e196c8SJohn Marino  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3201e196c8SJohn Marino  * SUCH DAMAGE.
3301e196c8SJohn Marino  */
3401e196c8SJohn Marino 
3501e196c8SJohn Marino /*
3601e196c8SJohn Marino  * Copyright (c) 1988, 1989 by Adam de Boor
3701e196c8SJohn Marino  * Copyright (c) 1989 by Berkeley Softworks
3801e196c8SJohn Marino  * All rights reserved.
3901e196c8SJohn Marino  *
4001e196c8SJohn Marino  * This code is derived from software contributed to Berkeley by
4101e196c8SJohn Marino  * Adam de Boor.
4201e196c8SJohn Marino  *
4301e196c8SJohn Marino  * Redistribution and use in source and binary forms, with or without
4401e196c8SJohn Marino  * modification, are permitted provided that the following conditions
4501e196c8SJohn Marino  * are met:
4601e196c8SJohn Marino  * 1. Redistributions of source code must retain the above copyright
4701e196c8SJohn Marino  *    notice, this list of conditions and the following disclaimer.
4801e196c8SJohn Marino  * 2. Redistributions in binary form must reproduce the above copyright
4901e196c8SJohn Marino  *    notice, this list of conditions and the following disclaimer in the
5001e196c8SJohn Marino  *    documentation and/or other materials provided with the distribution.
5101e196c8SJohn Marino  * 3. All advertising materials mentioning features or use of this software
5201e196c8SJohn Marino  *    must display the following acknowledgement:
5301e196c8SJohn Marino  *	This product includes software developed by the University of
5401e196c8SJohn Marino  *	California, Berkeley and its contributors.
5501e196c8SJohn Marino  * 4. Neither the name of the University nor the names of its contributors
5601e196c8SJohn Marino  *    may be used to endorse or promote products derived from this software
5701e196c8SJohn Marino  *    without specific prior written permission.
5801e196c8SJohn Marino  *
5901e196c8SJohn Marino  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
6001e196c8SJohn Marino  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
6101e196c8SJohn Marino  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
6201e196c8SJohn Marino  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
6301e196c8SJohn Marino  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
6401e196c8SJohn Marino  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
6501e196c8SJohn Marino  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
6601e196c8SJohn Marino  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
6701e196c8SJohn Marino  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
6801e196c8SJohn Marino  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
6901e196c8SJohn Marino  * SUCH DAMAGE.
7001e196c8SJohn Marino  */
7101e196c8SJohn Marino 
72a34d5fb1SAntonio Huete Jimenez /*
7301e196c8SJohn Marino  * job.c --
7401e196c8SJohn Marino  *	handle the creation etc. of our child processes.
7501e196c8SJohn Marino  *
7601e196c8SJohn Marino  * Interface:
77a34d5fb1SAntonio Huete Jimenez  *	Job_Init	Called to initialize this module. In addition,
78a34d5fb1SAntonio Huete Jimenez  *			the .BEGIN target is made including all of its
79a34d5fb1SAntonio Huete Jimenez  *			dependencies before this function returns.
80a34d5fb1SAntonio Huete Jimenez  *			Hence, the makefiles must have been parsed
81a34d5fb1SAntonio Huete Jimenez  *			before this function is called.
82a34d5fb1SAntonio Huete Jimenez  *
83a34d5fb1SAntonio Huete Jimenez  *	Job_End		Clean up any memory used.
84a34d5fb1SAntonio Huete Jimenez  *
8501e196c8SJohn Marino  *	Job_Make	Start the creation of the given target.
8601e196c8SJohn Marino  *
87a34d5fb1SAntonio Huete Jimenez  *	Job_CatchChildren
88a34d5fb1SAntonio Huete Jimenez  *			Check for and handle the termination of any
8901e196c8SJohn Marino  *			children. This must be called reasonably
9001e196c8SJohn Marino  *			frequently to keep the whole make going at
9101e196c8SJohn Marino  *			a decent clip, since job table entries aren't
9201e196c8SJohn Marino  *			removed until their process is caught this way.
9301e196c8SJohn Marino  *
94a34d5fb1SAntonio Huete Jimenez  *	Job_CatchOutput
95a34d5fb1SAntonio Huete Jimenez  *			Print any output our children have produced.
9601e196c8SJohn Marino  *			Should also be called fairly frequently to
9701e196c8SJohn Marino  *			keep the user informed of what's going on.
9801e196c8SJohn Marino  *			If no output is waiting, it will block for
9901e196c8SJohn Marino  *			a time given by the SEL_* constants, below,
10001e196c8SJohn Marino  *			or until output is ready.
10101e196c8SJohn Marino  *
102a34d5fb1SAntonio Huete Jimenez  *	Job_ParseShell	Given a special dependency line with target '.SHELL',
103a34d5fb1SAntonio Huete Jimenez  *			define the shell that is used for the creation
104a34d5fb1SAntonio Huete Jimenez  *			commands in jobs mode.
10501e196c8SJohn Marino  *
10601e196c8SJohn Marino  *	Job_Finish	Perform any final processing which needs doing.
10701e196c8SJohn Marino  *			This includes the execution of any commands
10801e196c8SJohn Marino  *			which have been/were attached to the .END
10901e196c8SJohn Marino  *			target. It should only be called when the
11001e196c8SJohn Marino  *			job table is empty.
11101e196c8SJohn Marino  *
112a34d5fb1SAntonio Huete Jimenez  *	Job_AbortAll	Abort all currently running jobs. Do not handle
113a34d5fb1SAntonio Huete Jimenez  *			output or do anything for the jobs, just kill them.
114a34d5fb1SAntonio Huete Jimenez  *			Should only be called in an emergency.
11501e196c8SJohn Marino  *
116a34d5fb1SAntonio Huete Jimenez  *	Job_CheckCommands
117a34d5fb1SAntonio Huete Jimenez  *			Verify that the commands for a target are
11801e196c8SJohn Marino  *			ok. Provide them if necessary and possible.
11901e196c8SJohn Marino  *
12001e196c8SJohn Marino  *	Job_Touch	Update a target without really updating it.
12101e196c8SJohn Marino  *
12201e196c8SJohn Marino  *	Job_Wait	Wait for all currently-running jobs to finish.
12301e196c8SJohn Marino  */
12401e196c8SJohn Marino 
12501e196c8SJohn Marino #ifdef HAVE_CONFIG_H
12601e196c8SJohn Marino # include "config.h"
12701e196c8SJohn Marino #endif
12801e196c8SJohn Marino #include <sys/types.h>
12901e196c8SJohn Marino #include <sys/stat.h>
13001e196c8SJohn Marino #include <sys/file.h>
13101e196c8SJohn Marino #include <sys/time.h>
13201e196c8SJohn Marino #include "wait.h"
13301e196c8SJohn Marino 
13401e196c8SJohn Marino #include <errno.h>
13501e196c8SJohn Marino #if !defined(USE_SELECT) && defined(HAVE_POLL_H)
13601e196c8SJohn Marino #include <poll.h>
13701e196c8SJohn Marino #else
13801e196c8SJohn Marino #ifndef USE_SELECT			/* no poll.h */
13901e196c8SJohn Marino # define USE_SELECT
14001e196c8SJohn Marino #endif
14101e196c8SJohn Marino #if defined(HAVE_SYS_SELECT_H)
14201e196c8SJohn Marino # include <sys/select.h>
14301e196c8SJohn Marino #endif
14401e196c8SJohn Marino #endif
14501e196c8SJohn Marino #include <signal.h>
14601e196c8SJohn Marino #include <utime.h>
14701e196c8SJohn Marino #if defined(HAVE_SYS_SOCKET_H)
14801e196c8SJohn Marino # include <sys/socket.h>
14901e196c8SJohn Marino #endif
15001e196c8SJohn Marino 
15101e196c8SJohn Marino #include "make.h"
15201e196c8SJohn Marino #include "dir.h"
15301e196c8SJohn Marino #include "job.h"
15401e196c8SJohn Marino #include "pathnames.h"
15501e196c8SJohn Marino #include "trace.h"
156a34d5fb1SAntonio Huete Jimenez 
157a34d5fb1SAntonio Huete Jimenez /*	"@(#)job.c	8.2 (Berkeley) 3/19/94"	*/
158*6eef5f0cSAntonio Huete Jimenez MAKE_RCSID("$NetBSD: job.c,v 1.455 2022/09/03 08:41:07 rillig Exp $");
159a34d5fb1SAntonio Huete Jimenez 
160a34d5fb1SAntonio Huete Jimenez /*
161a34d5fb1SAntonio Huete Jimenez  * A shell defines how the commands are run.  All commands for a target are
162a34d5fb1SAntonio Huete Jimenez  * written into a single file, which is then given to the shell to execute
163a34d5fb1SAntonio Huete Jimenez  * the commands from it.  The commands are written to the file using a few
164a34d5fb1SAntonio Huete Jimenez  * templates for echo control and error control.
165a34d5fb1SAntonio Huete Jimenez  *
166a34d5fb1SAntonio Huete Jimenez  * The name of the shell is the basename for the predefined shells, such as
167a34d5fb1SAntonio Huete Jimenez  * "sh", "csh", "bash".  For custom shells, it is the full pathname, and its
168a34d5fb1SAntonio Huete Jimenez  * basename is used to select the type of shell; the longest match wins.
169a34d5fb1SAntonio Huete Jimenez  * So /usr/pkg/bin/bash has type sh, /usr/local/bin/tcsh has type csh.
170a34d5fb1SAntonio Huete Jimenez  *
171a34d5fb1SAntonio Huete Jimenez  * The echoing of command lines is controlled using hasEchoCtl, echoOff,
172a34d5fb1SAntonio Huete Jimenez  * echoOn, noPrint and noPrintLen.  When echoOff is executed by the shell, it
173a34d5fb1SAntonio Huete Jimenez  * still outputs something, but this something is not interesting, therefore
174a34d5fb1SAntonio Huete Jimenez  * it is filtered out using noPrint and noPrintLen.
175a34d5fb1SAntonio Huete Jimenez  *
176a34d5fb1SAntonio Huete Jimenez  * The error checking for individual commands is controlled using hasErrCtl,
177a34d5fb1SAntonio Huete Jimenez  * errOn, errOff and runChkTmpl.
178a34d5fb1SAntonio Huete Jimenez  *
179a34d5fb1SAntonio Huete Jimenez  * In case a shell doesn't have error control, echoTmpl is a printf template
180a34d5fb1SAntonio Huete Jimenez  * for echoing the command, should echoing be on; runIgnTmpl is another
181a34d5fb1SAntonio Huete Jimenez  * printf template for executing the command while ignoring the return
182a34d5fb1SAntonio Huete Jimenez  * status. Finally runChkTmpl is a printf template for running the command and
183a34d5fb1SAntonio Huete Jimenez  * causing the shell to exit on error. If any of these strings are empty when
184*6eef5f0cSAntonio Huete Jimenez  * hasErrCtl is false, the command will be executed anyway as is, and if it
185a34d5fb1SAntonio Huete Jimenez  * causes an error, so be it. Any templates set up to echo the command will
186a34d5fb1SAntonio Huete Jimenez  * escape any '$ ` \ "' characters in the command string to avoid unwanted
187a34d5fb1SAntonio Huete Jimenez  * shell code injection, the escaped command is safe to use in double quotes.
188a34d5fb1SAntonio Huete Jimenez  *
189a34d5fb1SAntonio Huete Jimenez  * The command-line flags "echo" and "exit" also control the behavior.  The
190a34d5fb1SAntonio Huete Jimenez  * "echo" flag causes the shell to start echoing commands right away.  The
191a34d5fb1SAntonio Huete Jimenez  * "exit" flag causes the shell to exit when an error is detected in one of
192a34d5fb1SAntonio Huete Jimenez  * the commands.
193a34d5fb1SAntonio Huete Jimenez  */
194a34d5fb1SAntonio Huete Jimenez typedef struct Shell {
195a34d5fb1SAntonio Huete Jimenez 
196a34d5fb1SAntonio Huete Jimenez 	/*
197a34d5fb1SAntonio Huete Jimenez 	 * The name of the shell. For Bourne and C shells, this is used only
198a34d5fb1SAntonio Huete Jimenez 	 * to find the shell description when used as the single source of a
199a34d5fb1SAntonio Huete Jimenez 	 * .SHELL target. For user-defined shells, this is the full path of
200a34d5fb1SAntonio Huete Jimenez 	 * the shell.
201a34d5fb1SAntonio Huete Jimenez 	 */
202a34d5fb1SAntonio Huete Jimenez 	const char *name;
203a34d5fb1SAntonio Huete Jimenez 
204*6eef5f0cSAntonio Huete Jimenez 	bool hasEchoCtl;	/* whether both echoOff and echoOn are there */
205a34d5fb1SAntonio Huete Jimenez 	const char *echoOff;	/* command to turn echoing off */
206a34d5fb1SAntonio Huete Jimenez 	const char *echoOn;	/* command to turn echoing back on */
207a34d5fb1SAntonio Huete Jimenez 	const char *noPrint;	/* text to skip when printing output from the
208a34d5fb1SAntonio Huete Jimenez 				 * shell. This is usually the same as echoOff */
209a34d5fb1SAntonio Huete Jimenez 	size_t noPrintLen;	/* length of noPrint command */
210a34d5fb1SAntonio Huete Jimenez 
211*6eef5f0cSAntonio Huete Jimenez 	bool hasErrCtl;		/* whether error checking can be controlled
212a34d5fb1SAntonio Huete Jimenez 				 * for individual commands */
213a34d5fb1SAntonio Huete Jimenez 	const char *errOn;	/* command to turn on error checking */
214a34d5fb1SAntonio Huete Jimenez 	const char *errOff;	/* command to turn off error checking */
215a34d5fb1SAntonio Huete Jimenez 
216a34d5fb1SAntonio Huete Jimenez 	const char *echoTmpl;	/* template to echo a command */
217*6eef5f0cSAntonio Huete Jimenez 	const char *runIgnTmpl;	/* template to run a command without error
218*6eef5f0cSAntonio Huete Jimenez 				 * checking */
219*6eef5f0cSAntonio Huete Jimenez 	const char *runChkTmpl;	/* template to run a command with error
220*6eef5f0cSAntonio Huete Jimenez 				 * checking */
221a34d5fb1SAntonio Huete Jimenez 
222*6eef5f0cSAntonio Huete Jimenez 	/*
223*6eef5f0cSAntonio Huete Jimenez 	 * A string literal that results in a newline character when it
224*6eef5f0cSAntonio Huete Jimenez 	 * occurs outside of any 'quote' or "quote" characters.
225*6eef5f0cSAntonio Huete Jimenez 	 */
226a34d5fb1SAntonio Huete Jimenez 	const char *newline;
227a34d5fb1SAntonio Huete Jimenez 	char commentChar;	/* character used by shell for comment lines */
228a34d5fb1SAntonio Huete Jimenez 
229a34d5fb1SAntonio Huete Jimenez 	const char *echoFlag;	/* shell flag to echo commands */
230a34d5fb1SAntonio Huete Jimenez 	const char *errFlag;	/* shell flag to exit on error */
231a34d5fb1SAntonio Huete Jimenez } Shell;
232a34d5fb1SAntonio Huete Jimenez 
233a34d5fb1SAntonio Huete Jimenez typedef struct CommandFlags {
234a34d5fb1SAntonio Huete Jimenez 	/* Whether to echo the command before or instead of running it. */
235*6eef5f0cSAntonio Huete Jimenez 	bool echo;
236a34d5fb1SAntonio Huete Jimenez 
237a34d5fb1SAntonio Huete Jimenez 	/* Run the command even in -n or -N mode. */
238*6eef5f0cSAntonio Huete Jimenez 	bool always;
239a34d5fb1SAntonio Huete Jimenez 
240a34d5fb1SAntonio Huete Jimenez 	/*
241*6eef5f0cSAntonio Huete Jimenez 	 * true if we turned error checking off before writing the command to
242*6eef5f0cSAntonio Huete Jimenez 	 * the commands file and need to turn it back on
243a34d5fb1SAntonio Huete Jimenez 	 */
244*6eef5f0cSAntonio Huete Jimenez 	bool ignerr;
245a34d5fb1SAntonio Huete Jimenez } CommandFlags;
246a34d5fb1SAntonio Huete Jimenez 
247a34d5fb1SAntonio Huete Jimenez /*
248a34d5fb1SAntonio Huete Jimenez  * Write shell commands to a file.
249a34d5fb1SAntonio Huete Jimenez  *
250a34d5fb1SAntonio Huete Jimenez  * TODO: keep track of whether commands are echoed.
251a34d5fb1SAntonio Huete Jimenez  * TODO: keep track of whether error checking is active.
252a34d5fb1SAntonio Huete Jimenez  */
253a34d5fb1SAntonio Huete Jimenez typedef struct ShellWriter {
254a34d5fb1SAntonio Huete Jimenez 	FILE *f;
255a34d5fb1SAntonio Huete Jimenez 
256a34d5fb1SAntonio Huete Jimenez 	/* we've sent 'set -x' */
257*6eef5f0cSAntonio Huete Jimenez 	bool xtraced;
258a34d5fb1SAntonio Huete Jimenez 
259a34d5fb1SAntonio Huete Jimenez } ShellWriter;
26001e196c8SJohn Marino 
26101e196c8SJohn Marino /*
26201e196c8SJohn Marino  * error handling variables
26301e196c8SJohn Marino  */
264a34d5fb1SAntonio Huete Jimenez static int job_errors = 0;	/* number of errors reported */
265*6eef5f0cSAntonio Huete Jimenez static enum {			/* Why is the make aborting? */
266a34d5fb1SAntonio Huete Jimenez 	ABORT_NONE,
267*6eef5f0cSAntonio Huete Jimenez 	ABORT_ERROR,		/* Aborted because of an error */
268*6eef5f0cSAntonio Huete Jimenez 	ABORT_INTERRUPT,	/* Aborted because it was interrupted */
269a34d5fb1SAntonio Huete Jimenez 	ABORT_WAIT		/* Waiting for jobs to finish */
270*6eef5f0cSAntonio Huete Jimenez } aborting = ABORT_NONE;
27101e196c8SJohn Marino #define JOB_TOKENS "+EI+"	/* Token to requeue for each abort state */
27201e196c8SJohn Marino 
27301e196c8SJohn Marino /*
27401e196c8SJohn Marino  * this tracks the number of tokens currently "out" to build jobs.
27501e196c8SJohn Marino  */
27601e196c8SJohn Marino int jobTokensRunning = 0;
27701e196c8SJohn Marino 
278a34d5fb1SAntonio Huete Jimenez typedef enum JobStartResult {
279a34d5fb1SAntonio Huete Jimenez 	JOB_RUNNING,		/* Job is running */
280a34d5fb1SAntonio Huete Jimenez 	JOB_ERROR,		/* Error in starting the job */
281a34d5fb1SAntonio Huete Jimenez 	JOB_FINISHED		/* The job is already finished */
282a34d5fb1SAntonio Huete Jimenez } JobStartResult;
28301e196c8SJohn Marino 
28401e196c8SJohn Marino /*
28501e196c8SJohn Marino  * Descriptions for various shells.
28601e196c8SJohn Marino  *
28701e196c8SJohn Marino  * The build environment may set DEFSHELL_INDEX to one of
28801e196c8SJohn Marino  * DEFSHELL_INDEX_SH, DEFSHELL_INDEX_KSH, or DEFSHELL_INDEX_CSH, to
289a34d5fb1SAntonio Huete Jimenez  * select one of the predefined shells as the default shell.
29001e196c8SJohn Marino  *
29101e196c8SJohn Marino  * Alternatively, the build environment may set DEFSHELL_CUSTOM to the
29201e196c8SJohn Marino  * name or the full path of a sh-compatible shell, which will be used as
29301e196c8SJohn Marino  * the default shell.
29401e196c8SJohn Marino  *
29501e196c8SJohn Marino  * ".SHELL" lines in Makefiles can choose the default shell from the
296a34d5fb1SAntonio Huete Jimenez  * set defined here, or add additional shells.
29701e196c8SJohn Marino  */
29801e196c8SJohn Marino 
29901e196c8SJohn Marino #ifdef DEFSHELL_CUSTOM
30001e196c8SJohn Marino #define DEFSHELL_INDEX_CUSTOM 0
30101e196c8SJohn Marino #define DEFSHELL_INDEX_SH     1
30201e196c8SJohn Marino #define DEFSHELL_INDEX_KSH    2
30301e196c8SJohn Marino #define DEFSHELL_INDEX_CSH    3
30401e196c8SJohn Marino #else /* !DEFSHELL_CUSTOM */
30501e196c8SJohn Marino #define DEFSHELL_INDEX_SH     0
30601e196c8SJohn Marino #define DEFSHELL_INDEX_KSH    1
30701e196c8SJohn Marino #define DEFSHELL_INDEX_CSH    2
30801e196c8SJohn Marino #endif /* !DEFSHELL_CUSTOM */
30901e196c8SJohn Marino 
31001e196c8SJohn Marino #ifndef DEFSHELL_INDEX
31101e196c8SJohn Marino #define DEFSHELL_INDEX 0	/* DEFSHELL_INDEX_CUSTOM or DEFSHELL_INDEX_SH */
31201e196c8SJohn Marino #endif /* !DEFSHELL_INDEX */
31301e196c8SJohn Marino 
31401e196c8SJohn Marino static Shell shells[] = {
31501e196c8SJohn Marino #ifdef DEFSHELL_CUSTOM
31601e196c8SJohn Marino     /*
31701e196c8SJohn Marino      * An sh-compatible shell with a non-standard name.
31801e196c8SJohn Marino      *
31901e196c8SJohn Marino      * Keep this in sync with the "sh" description below, but avoid
32001e196c8SJohn Marino      * non-portable features that might not be supplied by all
32101e196c8SJohn Marino      * sh-compatible shells.
32201e196c8SJohn Marino      */
32301e196c8SJohn Marino     {
324a34d5fb1SAntonio Huete Jimenez 	DEFSHELL_CUSTOM,	/* .name */
325*6eef5f0cSAntonio Huete Jimenez 	false,			/* .hasEchoCtl */
326a34d5fb1SAntonio Huete Jimenez 	"",			/* .echoOff */
327a34d5fb1SAntonio Huete Jimenez 	"",			/* .echoOn */
328a34d5fb1SAntonio Huete Jimenez 	"",			/* .noPrint */
329a34d5fb1SAntonio Huete Jimenez 	0,			/* .noPrintLen */
330*6eef5f0cSAntonio Huete Jimenez 	false,			/* .hasErrCtl */
331a34d5fb1SAntonio Huete Jimenez 	"",			/* .errOn */
332a34d5fb1SAntonio Huete Jimenez 	"",			/* .errOff */
333a34d5fb1SAntonio Huete Jimenez 	"echo \"%s\"\n",	/* .echoTmpl */
334a34d5fb1SAntonio Huete Jimenez 	"%s\n",			/* .runIgnTmpl */
335a34d5fb1SAntonio Huete Jimenez 	"{ %s \n} || exit $?\n", /* .runChkTmpl */
336a34d5fb1SAntonio Huete Jimenez 	"'\n'",			/* .newline */
337a34d5fb1SAntonio Huete Jimenez 	'#',			/* .commentChar */
338a34d5fb1SAntonio Huete Jimenez 	"",			/* .echoFlag */
339a34d5fb1SAntonio Huete Jimenez 	"",			/* .errFlag */
34001e196c8SJohn Marino     },
34101e196c8SJohn Marino #endif /* DEFSHELL_CUSTOM */
34201e196c8SJohn Marino     /*
34301e196c8SJohn Marino      * SH description. Echo control is also possible and, under
34401e196c8SJohn Marino      * sun UNIX anyway, one can even control error checking.
34501e196c8SJohn Marino      */
34601e196c8SJohn Marino     {
347a34d5fb1SAntonio Huete Jimenez 	"sh",			/* .name */
348*6eef5f0cSAntonio Huete Jimenez 	false,			/* .hasEchoCtl */
349a34d5fb1SAntonio Huete Jimenez 	"",			/* .echoOff */
350a34d5fb1SAntonio Huete Jimenez 	"",			/* .echoOn */
351a34d5fb1SAntonio Huete Jimenez 	"",			/* .noPrint */
352a34d5fb1SAntonio Huete Jimenez 	0,			/* .noPrintLen */
353*6eef5f0cSAntonio Huete Jimenez 	false,			/* .hasErrCtl */
354a34d5fb1SAntonio Huete Jimenez 	"",			/* .errOn */
355a34d5fb1SAntonio Huete Jimenez 	"",			/* .errOff */
356a34d5fb1SAntonio Huete Jimenez 	"echo \"%s\"\n",	/* .echoTmpl */
357a34d5fb1SAntonio Huete Jimenez 	"%s\n",			/* .runIgnTmpl */
358a34d5fb1SAntonio Huete Jimenez 	"{ %s \n} || exit $?\n", /* .runChkTmpl */
359a34d5fb1SAntonio Huete Jimenez 	"'\n'",			/* .newline */
360a34d5fb1SAntonio Huete Jimenez 	'#',			/* .commentChar*/
36101e196c8SJohn Marino #if defined(MAKE_NATIVE) && defined(__NetBSD__)
362a34d5fb1SAntonio Huete Jimenez 	/* XXX: -q is not really echoFlag, it's more like noEchoInSysFlag. */
363a34d5fb1SAntonio Huete Jimenez 	"q",			/* .echoFlag */
36401e196c8SJohn Marino #else
365a34d5fb1SAntonio Huete Jimenez 	"",			/* .echoFlag */
36601e196c8SJohn Marino #endif
367a34d5fb1SAntonio Huete Jimenez 	"",			/* .errFlag */
36801e196c8SJohn Marino     },
36901e196c8SJohn Marino     /*
37001e196c8SJohn Marino      * KSH description.
37101e196c8SJohn Marino      */
37201e196c8SJohn Marino     {
373a34d5fb1SAntonio Huete Jimenez 	"ksh",			/* .name */
374*6eef5f0cSAntonio Huete Jimenez 	true,			/* .hasEchoCtl */
375a34d5fb1SAntonio Huete Jimenez 	"set +v",		/* .echoOff */
376a34d5fb1SAntonio Huete Jimenez 	"set -v",		/* .echoOn */
377a34d5fb1SAntonio Huete Jimenez 	"set +v",		/* .noPrint */
378a34d5fb1SAntonio Huete Jimenez 	6,			/* .noPrintLen */
379*6eef5f0cSAntonio Huete Jimenez 	false,			/* .hasErrCtl */
380a34d5fb1SAntonio Huete Jimenez 	"",			/* .errOn */
381a34d5fb1SAntonio Huete Jimenez 	"",			/* .errOff */
382a34d5fb1SAntonio Huete Jimenez 	"echo \"%s\"\n",	/* .echoTmpl */
383a34d5fb1SAntonio Huete Jimenez 	"%s\n",			/* .runIgnTmpl */
384a34d5fb1SAntonio Huete Jimenez 	"{ %s \n} || exit $?\n", /* .runChkTmpl */
385a34d5fb1SAntonio Huete Jimenez 	"'\n'",			/* .newline */
386a34d5fb1SAntonio Huete Jimenez 	'#',			/* .commentChar */
387a34d5fb1SAntonio Huete Jimenez 	"v",			/* .echoFlag */
388a34d5fb1SAntonio Huete Jimenez 	"",			/* .errFlag */
38901e196c8SJohn Marino     },
39001e196c8SJohn Marino     /*
39101e196c8SJohn Marino      * CSH description. The csh can do echo control by playing
39201e196c8SJohn Marino      * with the setting of the 'echo' shell variable. Sadly,
39301e196c8SJohn Marino      * however, it is unable to do error control nicely.
39401e196c8SJohn Marino      */
39501e196c8SJohn Marino     {
396a34d5fb1SAntonio Huete Jimenez 	"csh",			/* .name */
397*6eef5f0cSAntonio Huete Jimenez 	true,			/* .hasEchoCtl */
398a34d5fb1SAntonio Huete Jimenez 	"unset verbose",	/* .echoOff */
399a34d5fb1SAntonio Huete Jimenez 	"set verbose",		/* .echoOn */
400a34d5fb1SAntonio Huete Jimenez 	"unset verbose",	/* .noPrint */
401a34d5fb1SAntonio Huete Jimenez 	13,			/* .noPrintLen */
402*6eef5f0cSAntonio Huete Jimenez 	false,			/* .hasErrCtl */
403a34d5fb1SAntonio Huete Jimenez 	"",			/* .errOn */
404a34d5fb1SAntonio Huete Jimenez 	"",			/* .errOff */
405a34d5fb1SAntonio Huete Jimenez 	"echo \"%s\"\n",	/* .echoTmpl */
406a34d5fb1SAntonio Huete Jimenez 	"csh -c \"%s || exit 0\"\n", /* .runIgnTmpl */
407a34d5fb1SAntonio Huete Jimenez 	"",			/* .runChkTmpl */
408a34d5fb1SAntonio Huete Jimenez 	"'\\\n'",		/* .newline */
409a34d5fb1SAntonio Huete Jimenez 	'#',			/* .commentChar */
410a34d5fb1SAntonio Huete Jimenez 	"v",			/* .echoFlag */
411a34d5fb1SAntonio Huete Jimenez 	"e",			/* .errFlag */
41201e196c8SJohn Marino     }
41301e196c8SJohn Marino };
414a34d5fb1SAntonio Huete Jimenez 
415a34d5fb1SAntonio Huete Jimenez /*
416a34d5fb1SAntonio Huete Jimenez  * This is the shell to which we pass all commands in the Makefile.
417a34d5fb1SAntonio Huete Jimenez  * It is set by the Job_ParseShell function.
418a34d5fb1SAntonio Huete Jimenez  */
419a34d5fb1SAntonio Huete Jimenez static Shell *shell = &shells[DEFSHELL_INDEX];
420a34d5fb1SAntonio Huete Jimenez const char *shellPath = NULL;	/* full pathname of executable image */
421a34d5fb1SAntonio Huete Jimenez const char *shellName = NULL;	/* last component of shellPath */
4225f1e34d9SAlexandre Perrin char *shellErrFlag = NULL;
423a34d5fb1SAntonio Huete Jimenez static char *shell_freeIt = NULL; /* Allocated memory for custom .SHELL */
42401e196c8SJohn Marino 
42501e196c8SJohn Marino 
426a34d5fb1SAntonio Huete Jimenez static Job *job_table;		/* The structures that describe them */
427a34d5fb1SAntonio Huete Jimenez static Job *job_table_end;	/* job_table + maxJobs */
428a34d5fb1SAntonio Huete Jimenez static unsigned int wantToken;	/* we want a token */
429*6eef5f0cSAntonio Huete Jimenez static bool lurking_children = false;
430*6eef5f0cSAntonio Huete Jimenez static bool make_suspended = false; /* Whether we've seen a SIGTSTP (etc) */
43101e196c8SJohn Marino 
43201e196c8SJohn Marino /*
43301e196c8SJohn Marino  * Set of descriptors of pipes connected to
43401e196c8SJohn Marino  * the output channels of children
43501e196c8SJohn Marino  */
43601e196c8SJohn Marino static struct pollfd *fds = NULL;
437a34d5fb1SAntonio Huete Jimenez static Job **jobByFdIndex = NULL;
438a34d5fb1SAntonio Huete Jimenez static nfds_t fdsLen = 0;
43901e196c8SJohn Marino static void watchfd(Job *);
44001e196c8SJohn Marino static void clearfd(Job *);
441*6eef5f0cSAntonio Huete Jimenez static bool readyfd(Job *);
44201e196c8SJohn Marino 
443a34d5fb1SAntonio Huete Jimenez static char *targPrefix = NULL;	/* To identify a job change in the output. */
44401e196c8SJohn Marino static Job tokenWaitJob;	/* token wait pseudo-job */
44501e196c8SJohn Marino 
44601e196c8SJohn Marino static Job childExitJob;	/* child exit pseudo-job */
44701e196c8SJohn Marino #define CHILD_EXIT "."
44801e196c8SJohn Marino #define DO_JOB_RESUME "R"
44901e196c8SJohn Marino 
450a34d5fb1SAntonio Huete Jimenez enum {
451a34d5fb1SAntonio Huete Jimenez 	npseudojobs = 2		/* number of pseudo-jobs */
452a34d5fb1SAntonio Huete Jimenez };
45301e196c8SJohn Marino 
45401e196c8SJohn Marino static sigset_t caught_signals;	/* Set of signals we handle */
455a34d5fb1SAntonio Huete Jimenez static volatile sig_atomic_t caught_sigchld;
45601e196c8SJohn Marino 
457*6eef5f0cSAntonio Huete Jimenez static void CollectOutput(Job *, bool);
458*6eef5f0cSAntonio Huete Jimenez static void JobInterrupt(bool, int) MAKE_ATTR_DEAD;
45901e196c8SJohn Marino static void JobRestartJobs(void);
46001e196c8SJohn Marino static void JobSigReset(void);
46101e196c8SJohn Marino 
462a34d5fb1SAntonio Huete Jimenez static void
SwitchOutputTo(GNode * gn)463a34d5fb1SAntonio Huete Jimenez SwitchOutputTo(GNode *gn)
464a34d5fb1SAntonio Huete Jimenez {
465a34d5fb1SAntonio Huete Jimenez 	/* The node for which output was most recently produced. */
466a34d5fb1SAntonio Huete Jimenez 	static GNode *lastNode = NULL;
467a34d5fb1SAntonio Huete Jimenez 
468a34d5fb1SAntonio Huete Jimenez 	if (gn == lastNode)
469a34d5fb1SAntonio Huete Jimenez 		return;
470a34d5fb1SAntonio Huete Jimenez 	lastNode = gn;
471a34d5fb1SAntonio Huete Jimenez 
472a34d5fb1SAntonio Huete Jimenez 	if (opts.maxJobs != 1 && targPrefix != NULL && targPrefix[0] != '\0')
473a34d5fb1SAntonio Huete Jimenez 		(void)fprintf(stdout, "%s %s ---\n", targPrefix, gn->name);
474a34d5fb1SAntonio Huete Jimenez }
475ca58f742SDaniel Fojt 
476ca58f742SDaniel Fojt static unsigned
nfds_per_job(void)477ca58f742SDaniel Fojt nfds_per_job(void)
478ca58f742SDaniel Fojt {
479ca58f742SDaniel Fojt #if defined(USE_FILEMON) && !defined(USE_FILEMON_DEV)
480ca58f742SDaniel Fojt 	if (useMeta)
481ca58f742SDaniel Fojt 		return 2;
482ca58f742SDaniel Fojt #endif
483ca58f742SDaniel Fojt 	return 1;
484ca58f742SDaniel Fojt }
48501e196c8SJohn Marino 
486a34d5fb1SAntonio Huete Jimenez void
Job_FlagsToString(const Job * job,char * buf,size_t bufsize)487a34d5fb1SAntonio Huete Jimenez Job_FlagsToString(const Job *job, char *buf, size_t bufsize)
488a34d5fb1SAntonio Huete Jimenez {
489a34d5fb1SAntonio Huete Jimenez 	snprintf(buf, bufsize, "%c%c%c",
490a34d5fb1SAntonio Huete Jimenez 	    job->ignerr ? 'i' : '-',
491a34d5fb1SAntonio Huete Jimenez 	    !job->echo ? 's' : '-',
492a34d5fb1SAntonio Huete Jimenez 	    job->special ? 'S' : '-');
493a34d5fb1SAntonio Huete Jimenez }
494a34d5fb1SAntonio Huete Jimenez 
49501e196c8SJohn Marino static void
DumpJobs(const char * where)496*6eef5f0cSAntonio Huete Jimenez DumpJobs(const char *where)
49701e196c8SJohn Marino {
49801e196c8SJohn Marino 	Job *job;
499a34d5fb1SAntonio Huete Jimenez 	char flags[4];
50001e196c8SJohn Marino 
501a34d5fb1SAntonio Huete Jimenez 	debug_printf("job table @ %s\n", where);
50201e196c8SJohn Marino 	for (job = job_table; job < job_table_end; job++) {
503a34d5fb1SAntonio Huete Jimenez 		Job_FlagsToString(job, flags, sizeof flags);
504a34d5fb1SAntonio Huete Jimenez 		debug_printf("job %d, status %d, flags %s, pid %d\n",
505a34d5fb1SAntonio Huete Jimenez 		    (int)(job - job_table), job->status, flags, job->pid);
50601e196c8SJohn Marino 	}
50701e196c8SJohn Marino }
50801e196c8SJohn Marino 
50901e196c8SJohn Marino /*
510ca58f742SDaniel Fojt  * Delete the target of a failed, interrupted, or otherwise
511ca58f742SDaniel Fojt  * unsuccessful job unless inhibited by .PRECIOUS.
512ca58f742SDaniel Fojt  */
513ca58f742SDaniel Fojt static void
JobDeleteTarget(GNode * gn)514ca58f742SDaniel Fojt JobDeleteTarget(GNode *gn)
515ca58f742SDaniel Fojt {
516a34d5fb1SAntonio Huete Jimenez 	const char *file;
517a34d5fb1SAntonio Huete Jimenez 
518a34d5fb1SAntonio Huete Jimenez 	if (gn->type & OP_JOIN)
519a34d5fb1SAntonio Huete Jimenez 		return;
520a34d5fb1SAntonio Huete Jimenez 	if (gn->type & OP_PHONY)
521a34d5fb1SAntonio Huete Jimenez 		return;
522*6eef5f0cSAntonio Huete Jimenez 	if (GNode_IsPrecious(gn))
523a34d5fb1SAntonio Huete Jimenez 		return;
524a34d5fb1SAntonio Huete Jimenez 	if (opts.noExecute)
525a34d5fb1SAntonio Huete Jimenez 		return;
526a34d5fb1SAntonio Huete Jimenez 
527a34d5fb1SAntonio Huete Jimenez 	file = GNode_Path(gn);
528*6eef5f0cSAntonio Huete Jimenez 	if (unlink_file(file))
529ca58f742SDaniel Fojt 		Error("*** %s removed", file);
530ca58f742SDaniel Fojt }
531ca58f742SDaniel Fojt 
532ca58f742SDaniel Fojt /*
53301e196c8SJohn Marino  * JobSigLock/JobSigUnlock
53401e196c8SJohn Marino  *
53501e196c8SJohn Marino  * Signal lock routines to get exclusive access. Currently used to
53601e196c8SJohn Marino  * protect `jobs' and `stoppedJobs' list manipulations.
53701e196c8SJohn Marino  */
538a34d5fb1SAntonio Huete Jimenez static void
JobSigLock(sigset_t * omaskp)539a34d5fb1SAntonio Huete Jimenez JobSigLock(sigset_t *omaskp)
54001e196c8SJohn Marino {
54101e196c8SJohn Marino 	if (sigprocmask(SIG_BLOCK, &caught_signals, omaskp) != 0) {
54201e196c8SJohn Marino 		Punt("JobSigLock: sigprocmask: %s", strerror(errno));
54301e196c8SJohn Marino 		sigemptyset(omaskp);
54401e196c8SJohn Marino 	}
54501e196c8SJohn Marino }
54601e196c8SJohn Marino 
547a34d5fb1SAntonio Huete Jimenez static void
JobSigUnlock(sigset_t * omaskp)548a34d5fb1SAntonio Huete Jimenez JobSigUnlock(sigset_t *omaskp)
54901e196c8SJohn Marino {
55001e196c8SJohn Marino 	(void)sigprocmask(SIG_SETMASK, omaskp, NULL);
55101e196c8SJohn Marino }
55201e196c8SJohn Marino 
55301e196c8SJohn Marino static void
JobCreatePipe(Job * job,int minfd)55401e196c8SJohn Marino JobCreatePipe(Job *job, int minfd)
55501e196c8SJohn Marino {
556ca58f742SDaniel Fojt 	int i, fd, flags;
557a34d5fb1SAntonio Huete Jimenez 	int pipe_fds[2];
55801e196c8SJohn Marino 
559a34d5fb1SAntonio Huete Jimenez 	if (pipe(pipe_fds) == -1)
56001e196c8SJohn Marino 		Punt("Cannot create pipe: %s", strerror(errno));
56101e196c8SJohn Marino 
5625f1e34d9SAlexandre Perrin 	for (i = 0; i < 2; i++) {
5635f1e34d9SAlexandre Perrin 		/* Avoid using low numbered fds */
564a34d5fb1SAntonio Huete Jimenez 		fd = fcntl(pipe_fds[i], F_DUPFD, minfd);
5655f1e34d9SAlexandre Perrin 		if (fd != -1) {
566a34d5fb1SAntonio Huete Jimenez 			close(pipe_fds[i]);
567a34d5fb1SAntonio Huete Jimenez 			pipe_fds[i] = fd;
5685f1e34d9SAlexandre Perrin 		}
5695f1e34d9SAlexandre Perrin 	}
5705f1e34d9SAlexandre Perrin 
571a34d5fb1SAntonio Huete Jimenez 	job->inPipe = pipe_fds[0];
572a34d5fb1SAntonio Huete Jimenez 	job->outPipe = pipe_fds[1];
573a34d5fb1SAntonio Huete Jimenez 
57401e196c8SJohn Marino 	/* Set close-on-exec flag for both */
575a34d5fb1SAntonio Huete Jimenez 	if (fcntl(job->inPipe, F_SETFD, FD_CLOEXEC) == -1)
576ca58f742SDaniel Fojt 		Punt("Cannot set close-on-exec: %s", strerror(errno));
577a34d5fb1SAntonio Huete Jimenez 	if (fcntl(job->outPipe, F_SETFD, FD_CLOEXEC) == -1)
578ca58f742SDaniel Fojt 		Punt("Cannot set close-on-exec: %s", strerror(errno));
57901e196c8SJohn Marino 
58001e196c8SJohn Marino 	/*
58101e196c8SJohn Marino 	 * We mark the input side of the pipe non-blocking; we poll(2) the
58201e196c8SJohn Marino 	 * pipe when we're waiting for a job token, but we might lose the
58301e196c8SJohn Marino 	 * race for the token when a new one becomes available, so the read
58401e196c8SJohn Marino 	 * from the pipe should not block.
58501e196c8SJohn Marino 	 */
586a34d5fb1SAntonio Huete Jimenez 	flags = fcntl(job->inPipe, F_GETFL, 0);
587ca58f742SDaniel Fojt 	if (flags == -1)
588ca58f742SDaniel Fojt 		Punt("Cannot get flags: %s", strerror(errno));
589ca58f742SDaniel Fojt 	flags |= O_NONBLOCK;
590a34d5fb1SAntonio Huete Jimenez 	if (fcntl(job->inPipe, F_SETFL, flags) == -1)
591ca58f742SDaniel Fojt 		Punt("Cannot set flags: %s", strerror(errno));
59201e196c8SJohn Marino }
59301e196c8SJohn Marino 
594a34d5fb1SAntonio Huete Jimenez /* Pass the signal to each running job. */
59501e196c8SJohn Marino static void
JobCondPassSig(int signo)59601e196c8SJohn Marino JobCondPassSig(int signo)
59701e196c8SJohn Marino {
59801e196c8SJohn Marino 	Job *job;
59901e196c8SJohn Marino 
600a34d5fb1SAntonio Huete Jimenez 	DEBUG1(JOB, "JobCondPassSig(%d) called.\n", signo);
60101e196c8SJohn Marino 
60201e196c8SJohn Marino 	for (job = job_table; job < job_table_end; job++) {
603a34d5fb1SAntonio Huete Jimenez 		if (job->status != JOB_ST_RUNNING)
60401e196c8SJohn Marino 			continue;
605a34d5fb1SAntonio Huete Jimenez 		DEBUG2(JOB, "JobCondPassSig passing signal %d to child %d.\n",
60601e196c8SJohn Marino 		    signo, job->pid);
60701e196c8SJohn Marino 		KILLPG(job->pid, signo);
60801e196c8SJohn Marino 	}
60901e196c8SJohn Marino }
61001e196c8SJohn Marino 
611a34d5fb1SAntonio Huete Jimenez /*
61201e196c8SJohn Marino  * SIGCHLD handler.
61301e196c8SJohn Marino  *
614a34d5fb1SAntonio Huete Jimenez  * Sends a token on the child exit pipe to wake us up from select()/poll().
61501e196c8SJohn Marino  */
616a34d5fb1SAntonio Huete Jimenez /*ARGSUSED*/
61701e196c8SJohn Marino static void
JobChildSig(int signo MAKE_ATTR_UNUSED)61801e196c8SJohn Marino JobChildSig(int signo MAKE_ATTR_UNUSED)
61901e196c8SJohn Marino {
620a34d5fb1SAntonio Huete Jimenez 	caught_sigchld = 1;
621a34d5fb1SAntonio Huete Jimenez 	while (write(childExitJob.outPipe, CHILD_EXIT, 1) == -1 &&
622a34d5fb1SAntonio Huete Jimenez 	       errno == EAGAIN)
6235f1e34d9SAlexandre Perrin 		continue;
62401e196c8SJohn Marino }
62501e196c8SJohn Marino 
62601e196c8SJohn Marino 
627a34d5fb1SAntonio Huete Jimenez /* Resume all stopped jobs. */
628a34d5fb1SAntonio Huete Jimenez /*ARGSUSED*/
62901e196c8SJohn Marino static void
JobContinueSig(int signo MAKE_ATTR_UNUSED)63001e196c8SJohn Marino JobContinueSig(int signo MAKE_ATTR_UNUSED)
63101e196c8SJohn Marino {
63201e196c8SJohn Marino 	/*
633a34d5fb1SAntonio Huete Jimenez 	 * Defer sending SIGCONT to our stopped children until we return
63401e196c8SJohn Marino 	 * from the signal handler.
63501e196c8SJohn Marino 	 */
6365f1e34d9SAlexandre Perrin 	while (write(childExitJob.outPipe, DO_JOB_RESUME, 1) == -1 &&
6375f1e34d9SAlexandre Perrin 	       errno == EAGAIN)
6385f1e34d9SAlexandre Perrin 		continue;
63901e196c8SJohn Marino }
64001e196c8SJohn Marino 
641a34d5fb1SAntonio Huete Jimenez /*
64201e196c8SJohn Marino  * Pass a signal on to all jobs, then resend to ourselves.
64301e196c8SJohn Marino  * We die by the same signal.
64401e196c8SJohn Marino  */
64501e196c8SJohn Marino MAKE_ATTR_DEAD static void
JobPassSig_int(int signo)64601e196c8SJohn Marino JobPassSig_int(int signo)
64701e196c8SJohn Marino {
64801e196c8SJohn Marino 	/* Run .INTERRUPT target then exit */
649*6eef5f0cSAntonio Huete Jimenez 	JobInterrupt(true, signo);
65001e196c8SJohn Marino }
65101e196c8SJohn Marino 
652a34d5fb1SAntonio Huete Jimenez /*
653a34d5fb1SAntonio Huete Jimenez  * Pass a signal on to all jobs, then resend to ourselves.
654a34d5fb1SAntonio Huete Jimenez  * We die by the same signal.
655a34d5fb1SAntonio Huete Jimenez  */
65601e196c8SJohn Marino MAKE_ATTR_DEAD static void
JobPassSig_term(int signo)65701e196c8SJohn Marino JobPassSig_term(int signo)
65801e196c8SJohn Marino {
65901e196c8SJohn Marino 	/* Dont run .INTERRUPT target then exit */
660*6eef5f0cSAntonio Huete Jimenez 	JobInterrupt(false, signo);
66101e196c8SJohn Marino }
66201e196c8SJohn Marino 
66301e196c8SJohn Marino static void
JobPassSig_suspend(int signo)66401e196c8SJohn Marino JobPassSig_suspend(int signo)
66501e196c8SJohn Marino {
66601e196c8SJohn Marino 	sigset_t nmask, omask;
66701e196c8SJohn Marino 	struct sigaction act;
66801e196c8SJohn Marino 
66901e196c8SJohn Marino 	/* Suppress job started/continued messages */
670*6eef5f0cSAntonio Huete Jimenez 	make_suspended = true;
67101e196c8SJohn Marino 
67201e196c8SJohn Marino 	/* Pass the signal onto every job */
67301e196c8SJohn Marino 	JobCondPassSig(signo);
67401e196c8SJohn Marino 
67501e196c8SJohn Marino 	/*
676a34d5fb1SAntonio Huete Jimenez 	 * Send ourselves the signal now we've given the message to everyone
677a34d5fb1SAntonio Huete Jimenez 	 * else. Note we block everything else possible while we're getting
678a34d5fb1SAntonio Huete Jimenez 	 * the signal. This ensures that all our jobs get continued when we
679a34d5fb1SAntonio Huete Jimenez 	 * wake up before we take any other signal.
68001e196c8SJohn Marino 	 */
68101e196c8SJohn Marino 	sigfillset(&nmask);
68201e196c8SJohn Marino 	sigdelset(&nmask, signo);
68301e196c8SJohn Marino 	(void)sigprocmask(SIG_SETMASK, &nmask, &omask);
68401e196c8SJohn Marino 
68501e196c8SJohn Marino 	act.sa_handler = SIG_DFL;
68601e196c8SJohn Marino 	sigemptyset(&act.sa_mask);
68701e196c8SJohn Marino 	act.sa_flags = 0;
68801e196c8SJohn Marino 	(void)sigaction(signo, &act, NULL);
68901e196c8SJohn Marino 
690a34d5fb1SAntonio Huete Jimenez 	DEBUG1(JOB, "JobPassSig passing signal %d to self.\n", signo);
69101e196c8SJohn Marino 
69201e196c8SJohn Marino 	(void)kill(getpid(), signo);
69301e196c8SJohn Marino 
69401e196c8SJohn Marino 	/*
69501e196c8SJohn Marino 	 * We've been continued.
69601e196c8SJohn Marino 	 *
69701e196c8SJohn Marino 	 * A whole host of signals continue to happen!
69801e196c8SJohn Marino 	 * SIGCHLD for any processes that actually suspended themselves.
69901e196c8SJohn Marino 	 * SIGCHLD for any processes that exited while we were alseep.
70001e196c8SJohn Marino 	 * The SIGCONT that actually caused us to wakeup.
70101e196c8SJohn Marino 	 *
70201e196c8SJohn Marino 	 * Since we defer passing the SIGCONT on to our children until
70301e196c8SJohn Marino 	 * the main processing loop, we can be sure that all the SIGCHLD
70401e196c8SJohn Marino 	 * events will have happened by then - and that the waitpid() will
70501e196c8SJohn Marino 	 * collect the child 'suspended' events.
70601e196c8SJohn Marino 	 * For correct sequencing we just need to ensure we process the
707a34d5fb1SAntonio Huete Jimenez 	 * waitpid() before passing on the SIGCONT.
70801e196c8SJohn Marino 	 *
70901e196c8SJohn Marino 	 * In any case nothing else is needed here.
71001e196c8SJohn Marino 	 */
71101e196c8SJohn Marino 
71201e196c8SJohn Marino 	/* Restore handler and signal mask */
71301e196c8SJohn Marino 	act.sa_handler = JobPassSig_suspend;
71401e196c8SJohn Marino 	(void)sigaction(signo, &act, NULL);
71501e196c8SJohn Marino 	(void)sigprocmask(SIG_SETMASK, &omask, NULL);
71601e196c8SJohn Marino }
71701e196c8SJohn Marino 
71801e196c8SJohn Marino static Job *
JobFindPid(int pid,JobStatus status,bool isJobs)719*6eef5f0cSAntonio Huete Jimenez JobFindPid(int pid, JobStatus status, bool isJobs)
72001e196c8SJohn Marino {
72101e196c8SJohn Marino 	Job *job;
72201e196c8SJohn Marino 
72301e196c8SJohn Marino 	for (job = job_table; job < job_table_end; job++) {
724a34d5fb1SAntonio Huete Jimenez 		if (job->status == status && job->pid == pid)
72501e196c8SJohn Marino 			return job;
72601e196c8SJohn Marino 	}
72701e196c8SJohn Marino 	if (DEBUG(JOB) && isJobs)
728*6eef5f0cSAntonio Huete Jimenez 		DumpJobs("no pid");
72901e196c8SJohn Marino 	return NULL;
73001e196c8SJohn Marino }
73101e196c8SJohn Marino 
732a34d5fb1SAntonio Huete Jimenez /* Parse leading '@', '-' and '+', which control the exact execution mode. */
733a34d5fb1SAntonio Huete Jimenez static void
ParseCommandFlags(char ** pp,CommandFlags * out_cmdFlags)734a34d5fb1SAntonio Huete Jimenez ParseCommandFlags(char **pp, CommandFlags *out_cmdFlags)
73501e196c8SJohn Marino {
736a34d5fb1SAntonio Huete Jimenez 	char *p = *pp;
737*6eef5f0cSAntonio Huete Jimenez 	out_cmdFlags->echo = true;
738*6eef5f0cSAntonio Huete Jimenez 	out_cmdFlags->ignerr = false;
739*6eef5f0cSAntonio Huete Jimenez 	out_cmdFlags->always = false;
74001e196c8SJohn Marino 
741a34d5fb1SAntonio Huete Jimenez 	for (;;) {
742a34d5fb1SAntonio Huete Jimenez 		if (*p == '@')
743a34d5fb1SAntonio Huete Jimenez 			out_cmdFlags->echo = DEBUG(LOUD);
744a34d5fb1SAntonio Huete Jimenez 		else if (*p == '-')
745*6eef5f0cSAntonio Huete Jimenez 			out_cmdFlags->ignerr = true;
746a34d5fb1SAntonio Huete Jimenez 		else if (*p == '+')
747*6eef5f0cSAntonio Huete Jimenez 			out_cmdFlags->always = true;
748a34d5fb1SAntonio Huete Jimenez 		else
749a34d5fb1SAntonio Huete Jimenez 			break;
750a34d5fb1SAntonio Huete Jimenez 		p++;
75101e196c8SJohn Marino 	}
75201e196c8SJohn Marino 
753a34d5fb1SAntonio Huete Jimenez 	pp_skip_whitespace(&p);
75401e196c8SJohn Marino 
755a34d5fb1SAntonio Huete Jimenez 	*pp = p;
756a34d5fb1SAntonio Huete Jimenez }
75701e196c8SJohn Marino 
758a34d5fb1SAntonio Huete Jimenez /* Escape a string for a double-quoted string literal in sh, csh and ksh. */
759a34d5fb1SAntonio Huete Jimenez static char *
EscapeShellDblQuot(const char * cmd)760a34d5fb1SAntonio Huete Jimenez EscapeShellDblQuot(const char *cmd)
761a34d5fb1SAntonio Huete Jimenez {
762a34d5fb1SAntonio Huete Jimenez 	size_t i, j;
763a34d5fb1SAntonio Huete Jimenez 
764a34d5fb1SAntonio Huete Jimenez 	/* Worst that could happen is every char needs escaping. */
765a34d5fb1SAntonio Huete Jimenez 	char *esc = bmake_malloc(strlen(cmd) * 2 + 1);
766a34d5fb1SAntonio Huete Jimenez 	for (i = 0, j = 0; cmd[i] != '\0'; i++, j++) {
767a34d5fb1SAntonio Huete Jimenez 		if (cmd[i] == '$' || cmd[i] == '`' || cmd[i] == '\\' ||
768a34d5fb1SAntonio Huete Jimenez 		    cmd[i] == '"')
769a34d5fb1SAntonio Huete Jimenez 			esc[j++] = '\\';
770a34d5fb1SAntonio Huete Jimenez 		esc[j] = cmd[i];
771a34d5fb1SAntonio Huete Jimenez 	}
772a34d5fb1SAntonio Huete Jimenez 	esc[j] = '\0';
773a34d5fb1SAntonio Huete Jimenez 
774a34d5fb1SAntonio Huete Jimenez 	return esc;
775a34d5fb1SAntonio Huete Jimenez }
776a34d5fb1SAntonio Huete Jimenez 
777a34d5fb1SAntonio Huete Jimenez static void
ShellWriter_WriteFmt(ShellWriter * wr,const char * fmt,const char * arg)778*6eef5f0cSAntonio Huete Jimenez ShellWriter_WriteFmt(ShellWriter *wr, const char *fmt, const char *arg)
779a34d5fb1SAntonio Huete Jimenez {
780a34d5fb1SAntonio Huete Jimenez 	DEBUG1(JOB, fmt, arg);
781a34d5fb1SAntonio Huete Jimenez 
782a34d5fb1SAntonio Huete Jimenez 	(void)fprintf(wr->f, fmt, arg);
783*6eef5f0cSAntonio Huete Jimenez 	if (wr->f == stdout)
784a34d5fb1SAntonio Huete Jimenez 		(void)fflush(wr->f);
785a34d5fb1SAntonio Huete Jimenez }
786a34d5fb1SAntonio Huete Jimenez 
787a34d5fb1SAntonio Huete Jimenez static void
ShellWriter_WriteLine(ShellWriter * wr,const char * line)788*6eef5f0cSAntonio Huete Jimenez ShellWriter_WriteLine(ShellWriter *wr, const char *line)
789a34d5fb1SAntonio Huete Jimenez {
790*6eef5f0cSAntonio Huete Jimenez 	ShellWriter_WriteFmt(wr, "%s\n", line);
791a34d5fb1SAntonio Huete Jimenez }
792a34d5fb1SAntonio Huete Jimenez 
793a34d5fb1SAntonio Huete Jimenez static void
ShellWriter_EchoOff(ShellWriter * wr)794a34d5fb1SAntonio Huete Jimenez ShellWriter_EchoOff(ShellWriter *wr)
795a34d5fb1SAntonio Huete Jimenez {
796a34d5fb1SAntonio Huete Jimenez 	if (shell->hasEchoCtl)
797*6eef5f0cSAntonio Huete Jimenez 		ShellWriter_WriteLine(wr, shell->echoOff);
798a34d5fb1SAntonio Huete Jimenez }
799a34d5fb1SAntonio Huete Jimenez 
800a34d5fb1SAntonio Huete Jimenez static void
ShellWriter_EchoCmd(ShellWriter * wr,const char * escCmd)801a34d5fb1SAntonio Huete Jimenez ShellWriter_EchoCmd(ShellWriter *wr, const char *escCmd)
802a34d5fb1SAntonio Huete Jimenez {
803*6eef5f0cSAntonio Huete Jimenez 	ShellWriter_WriteFmt(wr, shell->echoTmpl, escCmd);
804a34d5fb1SAntonio Huete Jimenez }
805a34d5fb1SAntonio Huete Jimenez 
806a34d5fb1SAntonio Huete Jimenez static void
ShellWriter_EchoOn(ShellWriter * wr)807a34d5fb1SAntonio Huete Jimenez ShellWriter_EchoOn(ShellWriter *wr)
808a34d5fb1SAntonio Huete Jimenez {
809a34d5fb1SAntonio Huete Jimenez 	if (shell->hasEchoCtl)
810*6eef5f0cSAntonio Huete Jimenez 		ShellWriter_WriteLine(wr, shell->echoOn);
811a34d5fb1SAntonio Huete Jimenez }
812a34d5fb1SAntonio Huete Jimenez 
813a34d5fb1SAntonio Huete Jimenez static void
ShellWriter_TraceOn(ShellWriter * wr)814a34d5fb1SAntonio Huete Jimenez ShellWriter_TraceOn(ShellWriter *wr)
815a34d5fb1SAntonio Huete Jimenez {
816a34d5fb1SAntonio Huete Jimenez 	if (!wr->xtraced) {
817*6eef5f0cSAntonio Huete Jimenez 		ShellWriter_WriteLine(wr, "set -x");
818*6eef5f0cSAntonio Huete Jimenez 		wr->xtraced = true;
819a34d5fb1SAntonio Huete Jimenez 	}
820a34d5fb1SAntonio Huete Jimenez }
821a34d5fb1SAntonio Huete Jimenez 
822a34d5fb1SAntonio Huete Jimenez static void
ShellWriter_ErrOff(ShellWriter * wr,bool echo)823*6eef5f0cSAntonio Huete Jimenez ShellWriter_ErrOff(ShellWriter *wr, bool echo)
824a34d5fb1SAntonio Huete Jimenez {
825a34d5fb1SAntonio Huete Jimenez 	if (echo)
826a34d5fb1SAntonio Huete Jimenez 		ShellWriter_EchoOff(wr);
827*6eef5f0cSAntonio Huete Jimenez 	ShellWriter_WriteLine(wr, shell->errOff);
828a34d5fb1SAntonio Huete Jimenez 	if (echo)
829a34d5fb1SAntonio Huete Jimenez 		ShellWriter_EchoOn(wr);
830a34d5fb1SAntonio Huete Jimenez }
831a34d5fb1SAntonio Huete Jimenez 
832a34d5fb1SAntonio Huete Jimenez static void
ShellWriter_ErrOn(ShellWriter * wr,bool echo)833*6eef5f0cSAntonio Huete Jimenez ShellWriter_ErrOn(ShellWriter *wr, bool echo)
834a34d5fb1SAntonio Huete Jimenez {
835a34d5fb1SAntonio Huete Jimenez 	if (echo)
836a34d5fb1SAntonio Huete Jimenez 		ShellWriter_EchoOff(wr);
837*6eef5f0cSAntonio Huete Jimenez 	ShellWriter_WriteLine(wr, shell->errOn);
838a34d5fb1SAntonio Huete Jimenez 	if (echo)
839a34d5fb1SAntonio Huete Jimenez 		ShellWriter_EchoOn(wr);
840a34d5fb1SAntonio Huete Jimenez }
841a34d5fb1SAntonio Huete Jimenez 
842a34d5fb1SAntonio Huete Jimenez /*
843a34d5fb1SAntonio Huete Jimenez  * The shell has no built-in error control, so emulate error control by
844a34d5fb1SAntonio Huete Jimenez  * enclosing each shell command in a template like "{ %s \n } || exit $?"
845a34d5fb1SAntonio Huete Jimenez  * (configurable per shell).
846a34d5fb1SAntonio Huete Jimenez  */
847a34d5fb1SAntonio Huete Jimenez static void
JobWriteSpecialsEchoCtl(Job * job,ShellWriter * wr,CommandFlags * inout_cmdFlags,const char * escCmd,const char ** inout_cmdTemplate)848*6eef5f0cSAntonio Huete Jimenez JobWriteSpecialsEchoCtl(Job *job, ShellWriter *wr, CommandFlags *inout_cmdFlags,
849a34d5fb1SAntonio Huete Jimenez 			const char *escCmd, const char **inout_cmdTemplate)
850a34d5fb1SAntonio Huete Jimenez {
851*6eef5f0cSAntonio Huete Jimenez 	/* XXX: Why is the whole job modified at this point? */
852*6eef5f0cSAntonio Huete Jimenez 	job->ignerr = true;
853a34d5fb1SAntonio Huete Jimenez 
854a34d5fb1SAntonio Huete Jimenez 	if (job->echo && inout_cmdFlags->echo) {
855a34d5fb1SAntonio Huete Jimenez 		ShellWriter_EchoOff(wr);
856a34d5fb1SAntonio Huete Jimenez 		ShellWriter_EchoCmd(wr, escCmd);
857a34d5fb1SAntonio Huete Jimenez 
858a34d5fb1SAntonio Huete Jimenez 		/*
859a34d5fb1SAntonio Huete Jimenez 		 * Leave echoing off so the user doesn't see the commands
860a34d5fb1SAntonio Huete Jimenez 		 * for toggling the error checking.
861a34d5fb1SAntonio Huete Jimenez 		 */
862*6eef5f0cSAntonio Huete Jimenez 		inout_cmdFlags->echo = false;
863a34d5fb1SAntonio Huete Jimenez 	}
864a34d5fb1SAntonio Huete Jimenez 	*inout_cmdTemplate = shell->runIgnTmpl;
865a34d5fb1SAntonio Huete Jimenez 
866a34d5fb1SAntonio Huete Jimenez 	/*
867a34d5fb1SAntonio Huete Jimenez 	 * The template runIgnTmpl already takes care of ignoring errors,
868a34d5fb1SAntonio Huete Jimenez 	 * so pretend error checking is still on.
869a34d5fb1SAntonio Huete Jimenez 	 * XXX: What effects does this have, and why is it necessary?
870a34d5fb1SAntonio Huete Jimenez 	 */
871*6eef5f0cSAntonio Huete Jimenez 	inout_cmdFlags->ignerr = false;
872a34d5fb1SAntonio Huete Jimenez }
873a34d5fb1SAntonio Huete Jimenez 
874a34d5fb1SAntonio Huete Jimenez static void
JobWriteSpecials(Job * job,ShellWriter * wr,const char * escCmd,bool run,CommandFlags * inout_cmdFlags,const char ** inout_cmdTemplate)875*6eef5f0cSAntonio Huete Jimenez JobWriteSpecials(Job *job, ShellWriter *wr, const char *escCmd, bool run,
876a34d5fb1SAntonio Huete Jimenez 		 CommandFlags *inout_cmdFlags, const char **inout_cmdTemplate)
877a34d5fb1SAntonio Huete Jimenez {
878a34d5fb1SAntonio Huete Jimenez 	if (!run) {
879a34d5fb1SAntonio Huete Jimenez 		/*
880a34d5fb1SAntonio Huete Jimenez 		 * If there is no command to run, there is no need to switch
881a34d5fb1SAntonio Huete Jimenez 		 * error checking off and on again for nothing.
882a34d5fb1SAntonio Huete Jimenez 		 */
883*6eef5f0cSAntonio Huete Jimenez 		inout_cmdFlags->ignerr = false;
884a34d5fb1SAntonio Huete Jimenez 	} else if (shell->hasErrCtl)
885a34d5fb1SAntonio Huete Jimenez 		ShellWriter_ErrOff(wr, job->echo && inout_cmdFlags->echo);
886a34d5fb1SAntonio Huete Jimenez 	else if (shell->runIgnTmpl != NULL && shell->runIgnTmpl[0] != '\0') {
887*6eef5f0cSAntonio Huete Jimenez 		JobWriteSpecialsEchoCtl(job, wr, inout_cmdFlags, escCmd,
888a34d5fb1SAntonio Huete Jimenez 		    inout_cmdTemplate);
889a34d5fb1SAntonio Huete Jimenez 	} else
890*6eef5f0cSAntonio Huete Jimenez 		inout_cmdFlags->ignerr = false;
891a34d5fb1SAntonio Huete Jimenez }
892a34d5fb1SAntonio Huete Jimenez 
893a34d5fb1SAntonio Huete Jimenez /*
894*6eef5f0cSAntonio Huete Jimenez  * Write a shell command to the job's commands file, to be run later.
895a34d5fb1SAntonio Huete Jimenez  *
896a34d5fb1SAntonio Huete Jimenez  * If the command starts with '@' and neither the -s nor the -n flag was
897*6eef5f0cSAntonio Huete Jimenez  * given to make, stick a shell-specific echoOff command in the script.
898a34d5fb1SAntonio Huete Jimenez  *
899a34d5fb1SAntonio Huete Jimenez  * If the command starts with '-' and the shell has no error control (none
900*6eef5f0cSAntonio Huete Jimenez  * of the predefined shells has that), ignore errors for the entire job.
901a34d5fb1SAntonio Huete Jimenez  *
902*6eef5f0cSAntonio Huete Jimenez  * XXX: Why ignore errors for the entire job?  This is even documented in the
903*6eef5f0cSAntonio Huete Jimenez  * manual page, but without any rationale since there is no known rationale.
904*6eef5f0cSAntonio Huete Jimenez  *
905*6eef5f0cSAntonio Huete Jimenez  * XXX: The manual page says the '-' "affects the entire job", but that's not
906*6eef5f0cSAntonio Huete Jimenez  * accurate.  The '-' does not affect the commands before the '-'.
907*6eef5f0cSAntonio Huete Jimenez  *
908*6eef5f0cSAntonio Huete Jimenez  * If the command is just "...", skip all further commands of this job.  These
909*6eef5f0cSAntonio Huete Jimenez  * commands are attached to the .END node instead and will be run by
910*6eef5f0cSAntonio Huete Jimenez  * Job_Finish after all other targets have been made.
911a34d5fb1SAntonio Huete Jimenez  */
912a34d5fb1SAntonio Huete Jimenez static void
JobWriteCommand(Job * job,ShellWriter * wr,StringListNode * ln,const char * ucmd)913*6eef5f0cSAntonio Huete Jimenez JobWriteCommand(Job *job, ShellWriter *wr, StringListNode *ln, const char *ucmd)
914a34d5fb1SAntonio Huete Jimenez {
915*6eef5f0cSAntonio Huete Jimenez 	bool run;
916a34d5fb1SAntonio Huete Jimenez 
917a34d5fb1SAntonio Huete Jimenez 	CommandFlags cmdFlags;
918*6eef5f0cSAntonio Huete Jimenez 	/* Template for writing a command to the shell file */
919a34d5fb1SAntonio Huete Jimenez 	const char *cmdTemplate;
920a34d5fb1SAntonio Huete Jimenez 	char *xcmd;		/* The expanded command */
921a34d5fb1SAntonio Huete Jimenez 	char *xcmdStart;
922a34d5fb1SAntonio Huete Jimenez 	char *escCmd;		/* xcmd escaped to be used in double quotes */
923a34d5fb1SAntonio Huete Jimenez 
924a34d5fb1SAntonio Huete Jimenez 	run = GNode_ShouldExecute(job->node);
925a34d5fb1SAntonio Huete Jimenez 
926*6eef5f0cSAntonio Huete Jimenez 	(void)Var_Subst(ucmd, job->node, VARE_WANTRES, &xcmd);
927a34d5fb1SAntonio Huete Jimenez 	/* TODO: handle errors */
928a34d5fb1SAntonio Huete Jimenez 	xcmdStart = xcmd;
92901e196c8SJohn Marino 
93001e196c8SJohn Marino 	cmdTemplate = "%s\n";
93101e196c8SJohn Marino 
932a34d5fb1SAntonio Huete Jimenez 	ParseCommandFlags(&xcmd, &cmdFlags);
933a34d5fb1SAntonio Huete Jimenez 
934a34d5fb1SAntonio Huete Jimenez 	/* The '+' command flag overrides the -n or -N options. */
935a34d5fb1SAntonio Huete Jimenez 	if (cmdFlags.always && !run) {
93601e196c8SJohn Marino 		/*
93701e196c8SJohn Marino 		 * We're not actually executing anything...
93801e196c8SJohn Marino 		 * but this one needs to be - use compat mode just for it.
93901e196c8SJohn Marino 		 */
940*6eef5f0cSAntonio Huete Jimenez 		(void)Compat_RunCommand(ucmd, job->node, ln);
941a34d5fb1SAntonio Huete Jimenez 		free(xcmdStart);
942a34d5fb1SAntonio Huete Jimenez 		return;
94301e196c8SJohn Marino 	}
944a34d5fb1SAntonio Huete Jimenez 
945a34d5fb1SAntonio Huete Jimenez 	/*
946a34d5fb1SAntonio Huete Jimenez 	 * If the shell doesn't have error control, the alternate echoing
947a34d5fb1SAntonio Huete Jimenez 	 * will be done (to avoid showing additional error checking code)
948a34d5fb1SAntonio Huete Jimenez 	 * and this needs some characters escaped.
949a34d5fb1SAntonio Huete Jimenez 	 */
950a34d5fb1SAntonio Huete Jimenez 	escCmd = shell->hasErrCtl ? NULL : EscapeShellDblQuot(xcmd);
951a34d5fb1SAntonio Huete Jimenez 
952a34d5fb1SAntonio Huete Jimenez 	if (!cmdFlags.echo) {
953a34d5fb1SAntonio Huete Jimenez 		if (job->echo && run && shell->hasEchoCtl) {
954a34d5fb1SAntonio Huete Jimenez 			ShellWriter_EchoOff(wr);
955a34d5fb1SAntonio Huete Jimenez 		} else {
956a34d5fb1SAntonio Huete Jimenez 			if (shell->hasErrCtl)
957*6eef5f0cSAntonio Huete Jimenez 				cmdFlags.echo = true;
958a34d5fb1SAntonio Huete Jimenez 		}
959a34d5fb1SAntonio Huete Jimenez 	}
960a34d5fb1SAntonio Huete Jimenez 
961a34d5fb1SAntonio Huete Jimenez 	if (cmdFlags.ignerr) {
962*6eef5f0cSAntonio Huete Jimenez 		JobWriteSpecials(job, wr, escCmd, run, &cmdFlags, &cmdTemplate);
963a34d5fb1SAntonio Huete Jimenez 	} else {
964a34d5fb1SAntonio Huete Jimenez 
965a34d5fb1SAntonio Huete Jimenez 		/*
966a34d5fb1SAntonio Huete Jimenez 		 * If errors are being checked and the shell doesn't have
967a34d5fb1SAntonio Huete Jimenez 		 * error control but does supply an runChkTmpl template, then
968a34d5fb1SAntonio Huete Jimenez 		 * set up commands to run through it.
969a34d5fb1SAntonio Huete Jimenez 		 */
970a34d5fb1SAntonio Huete Jimenez 
971a34d5fb1SAntonio Huete Jimenez 		if (!shell->hasErrCtl && shell->runChkTmpl != NULL &&
972a34d5fb1SAntonio Huete Jimenez 		    shell->runChkTmpl[0] != '\0') {
973a34d5fb1SAntonio Huete Jimenez 			if (job->echo && cmdFlags.echo) {
974a34d5fb1SAntonio Huete Jimenez 				ShellWriter_EchoOff(wr);
975a34d5fb1SAntonio Huete Jimenez 				ShellWriter_EchoCmd(wr, escCmd);
976*6eef5f0cSAntonio Huete Jimenez 				cmdFlags.echo = false;
977a34d5fb1SAntonio Huete Jimenez 			}
978a34d5fb1SAntonio Huete Jimenez 			/*
979a34d5fb1SAntonio Huete Jimenez 			 * If it's a comment line or blank, avoid the possible
980a34d5fb1SAntonio Huete Jimenez 			 * syntax error generated by "{\n} || exit $?".
981a34d5fb1SAntonio Huete Jimenez 			 */
982a34d5fb1SAntonio Huete Jimenez 			cmdTemplate = escCmd[0] == shell->commentChar ||
983a34d5fb1SAntonio Huete Jimenez 				      escCmd[0] == '\0'
984a34d5fb1SAntonio Huete Jimenez 			    ? shell->runIgnTmpl
985a34d5fb1SAntonio Huete Jimenez 			    : shell->runChkTmpl;
986*6eef5f0cSAntonio Huete Jimenez 			cmdFlags.ignerr = false;
987a34d5fb1SAntonio Huete Jimenez 		}
988a34d5fb1SAntonio Huete Jimenez 	}
989a34d5fb1SAntonio Huete Jimenez 
990a34d5fb1SAntonio Huete Jimenez 	if (DEBUG(SHELL) && strcmp(shellName, "sh") == 0)
991a34d5fb1SAntonio Huete Jimenez 		ShellWriter_TraceOn(wr);
992a34d5fb1SAntonio Huete Jimenez 
993*6eef5f0cSAntonio Huete Jimenez 	ShellWriter_WriteFmt(wr, cmdTemplate, xcmd);
994a34d5fb1SAntonio Huete Jimenez 	free(xcmdStart);
995a34d5fb1SAntonio Huete Jimenez 	free(escCmd);
996a34d5fb1SAntonio Huete Jimenez 
997a34d5fb1SAntonio Huete Jimenez 	if (cmdFlags.ignerr)
998a34d5fb1SAntonio Huete Jimenez 		ShellWriter_ErrOn(wr, cmdFlags.echo && job->echo);
999a34d5fb1SAntonio Huete Jimenez 
1000a34d5fb1SAntonio Huete Jimenez 	if (!cmdFlags.echo)
1001a34d5fb1SAntonio Huete Jimenez 		ShellWriter_EchoOn(wr);
1002a34d5fb1SAntonio Huete Jimenez }
1003a34d5fb1SAntonio Huete Jimenez 
1004a34d5fb1SAntonio Huete Jimenez /*
1005*6eef5f0cSAntonio Huete Jimenez  * Write all commands to the shell file that is later executed.
1006a34d5fb1SAntonio Huete Jimenez  *
1007*6eef5f0cSAntonio Huete Jimenez  * The special command "..." stops writing and saves the remaining commands
1008a34d5fb1SAntonio Huete Jimenez  * to be executed later, when the target '.END' is made.
1009a34d5fb1SAntonio Huete Jimenez  *
1010a34d5fb1SAntonio Huete Jimenez  * Return whether at least one command was written to the shell file.
1011a34d5fb1SAntonio Huete Jimenez  */
1012*6eef5f0cSAntonio Huete Jimenez static bool
JobWriteCommands(Job * job)1013*6eef5f0cSAntonio Huete Jimenez JobWriteCommands(Job *job)
1014a34d5fb1SAntonio Huete Jimenez {
1015a34d5fb1SAntonio Huete Jimenez 	StringListNode *ln;
1016*6eef5f0cSAntonio Huete Jimenez 	bool seen = false;
1017*6eef5f0cSAntonio Huete Jimenez 	ShellWriter wr;
1018*6eef5f0cSAntonio Huete Jimenez 
1019*6eef5f0cSAntonio Huete Jimenez 	wr.f = job->cmdFILE;
1020*6eef5f0cSAntonio Huete Jimenez 	wr.xtraced = false;
1021a34d5fb1SAntonio Huete Jimenez 
1022a34d5fb1SAntonio Huete Jimenez 	for (ln = job->node->commands.first; ln != NULL; ln = ln->next) {
1023a34d5fb1SAntonio Huete Jimenez 		const char *cmd = ln->datum;
1024a34d5fb1SAntonio Huete Jimenez 
1025a34d5fb1SAntonio Huete Jimenez 		if (strcmp(cmd, "...") == 0) {
1026a34d5fb1SAntonio Huete Jimenez 			job->node->type |= OP_SAVE_CMDS;
1027a34d5fb1SAntonio Huete Jimenez 			job->tailCmds = ln->next;
102801e196c8SJohn Marino 			break;
102901e196c8SJohn Marino 		}
1030a34d5fb1SAntonio Huete Jimenez 
1031*6eef5f0cSAntonio Huete Jimenez 		JobWriteCommand(job, &wr, ln, ln->datum);
1032*6eef5f0cSAntonio Huete Jimenez 		seen = true;
103301e196c8SJohn Marino 	}
103401e196c8SJohn Marino 
1035a34d5fb1SAntonio Huete Jimenez 	return seen;
1036a34d5fb1SAntonio Huete Jimenez }
103701e196c8SJohn Marino 
103801e196c8SJohn Marino /*
1039a34d5fb1SAntonio Huete Jimenez  * Save the delayed commands (those after '...'), to be executed later in
1040a34d5fb1SAntonio Huete Jimenez  * the '.END' node, when everything else is done.
104101e196c8SJohn Marino  */
104201e196c8SJohn Marino static void
JobSaveCommands(Job * job)1043a34d5fb1SAntonio Huete Jimenez JobSaveCommands(Job *job)
1044a34d5fb1SAntonio Huete Jimenez {
1045a34d5fb1SAntonio Huete Jimenez 	StringListNode *ln;
1046a34d5fb1SAntonio Huete Jimenez 
1047a34d5fb1SAntonio Huete Jimenez 	for (ln = job->tailCmds; ln != NULL; ln = ln->next) {
1048a34d5fb1SAntonio Huete Jimenez 		const char *cmd = ln->datum;
1049a34d5fb1SAntonio Huete Jimenez 		char *expanded_cmd;
1050a34d5fb1SAntonio Huete Jimenez 		/*
1051a34d5fb1SAntonio Huete Jimenez 		 * XXX: This Var_Subst is only intended to expand the dynamic
1052a34d5fb1SAntonio Huete Jimenez 		 * variables such as .TARGET, .IMPSRC.  It is not intended to
1053a34d5fb1SAntonio Huete Jimenez 		 * expand the other variables as well; see deptgt-end.mk.
1054a34d5fb1SAntonio Huete Jimenez 		 */
1055a34d5fb1SAntonio Huete Jimenez 		(void)Var_Subst(cmd, job->node, VARE_WANTRES, &expanded_cmd);
1056a34d5fb1SAntonio Huete Jimenez 		/* TODO: handle errors */
1057a34d5fb1SAntonio Huete Jimenez 		Lst_Append(&Targ_GetEndNode()->commands, expanded_cmd);
1058a34d5fb1SAntonio Huete Jimenez 	}
1059a34d5fb1SAntonio Huete Jimenez }
1060a34d5fb1SAntonio Huete Jimenez 
1061a34d5fb1SAntonio Huete Jimenez 
1062a34d5fb1SAntonio Huete Jimenez /* Called to close both input and output pipes when a job is finished. */
1063a34d5fb1SAntonio Huete Jimenez static void
JobClosePipes(Job * job)1064a34d5fb1SAntonio Huete Jimenez JobClosePipes(Job *job)
106501e196c8SJohn Marino {
106601e196c8SJohn Marino 	clearfd(job);
106701e196c8SJohn Marino 	(void)close(job->outPipe);
106801e196c8SJohn Marino 	job->outPipe = -1;
106901e196c8SJohn Marino 
1070*6eef5f0cSAntonio Huete Jimenez 	CollectOutput(job, true);
107101e196c8SJohn Marino 	(void)close(job->inPipe);
107201e196c8SJohn Marino 	job->inPipe = -1;
107301e196c8SJohn Marino }
107401e196c8SJohn Marino 
1075a34d5fb1SAntonio Huete Jimenez static void
DebugFailedJob(const Job * job)1076*6eef5f0cSAntonio Huete Jimenez DebugFailedJob(const Job *job)
1077*6eef5f0cSAntonio Huete Jimenez {
1078*6eef5f0cSAntonio Huete Jimenez 	const StringListNode *ln;
1079*6eef5f0cSAntonio Huete Jimenez 
1080*6eef5f0cSAntonio Huete Jimenez 	if (!DEBUG(ERROR))
1081*6eef5f0cSAntonio Huete Jimenez 		return;
1082*6eef5f0cSAntonio Huete Jimenez 
1083*6eef5f0cSAntonio Huete Jimenez 	debug_printf("\n");
1084*6eef5f0cSAntonio Huete Jimenez 	debug_printf("*** Failed target: %s\n", job->node->name);
1085*6eef5f0cSAntonio Huete Jimenez 	debug_printf("*** Failed commands:\n");
1086*6eef5f0cSAntonio Huete Jimenez 	for (ln = job->node->commands.first; ln != NULL; ln = ln->next) {
1087*6eef5f0cSAntonio Huete Jimenez 		const char *cmd = ln->datum;
1088*6eef5f0cSAntonio Huete Jimenez 		debug_printf("\t%s\n", cmd);
1089*6eef5f0cSAntonio Huete Jimenez 
1090*6eef5f0cSAntonio Huete Jimenez 		if (strchr(cmd, '$') != NULL) {
1091*6eef5f0cSAntonio Huete Jimenez 			char *xcmd;
1092*6eef5f0cSAntonio Huete Jimenez 			(void)Var_Subst(cmd, job->node, VARE_WANTRES, &xcmd);
1093*6eef5f0cSAntonio Huete Jimenez 			debug_printf("\t=> %s\n", xcmd);
1094*6eef5f0cSAntonio Huete Jimenez 			free(xcmd);
1095*6eef5f0cSAntonio Huete Jimenez 		}
1096*6eef5f0cSAntonio Huete Jimenez 	}
1097*6eef5f0cSAntonio Huete Jimenez }
1098*6eef5f0cSAntonio Huete Jimenez 
1099*6eef5f0cSAntonio Huete Jimenez static void
JobFinishDoneExitedError(Job * job,WAIT_T * inout_status)1100a34d5fb1SAntonio Huete Jimenez JobFinishDoneExitedError(Job *job, WAIT_T *inout_status)
1101a34d5fb1SAntonio Huete Jimenez {
1102a34d5fb1SAntonio Huete Jimenez 	SwitchOutputTo(job->node);
1103a34d5fb1SAntonio Huete Jimenez #ifdef USE_META
1104a34d5fb1SAntonio Huete Jimenez 	if (useMeta) {
1105a34d5fb1SAntonio Huete Jimenez 		meta_job_error(job, job->node,
1106a34d5fb1SAntonio Huete Jimenez 		    job->ignerr, WEXITSTATUS(*inout_status));
1107a34d5fb1SAntonio Huete Jimenez 	}
1108a34d5fb1SAntonio Huete Jimenez #endif
1109a34d5fb1SAntonio Huete Jimenez 	if (!shouldDieQuietly(job->node, -1)) {
1110*6eef5f0cSAntonio Huete Jimenez 		DebugFailedJob(job);
1111a34d5fb1SAntonio Huete Jimenez 		(void)printf("*** [%s] Error code %d%s\n",
1112a34d5fb1SAntonio Huete Jimenez 		    job->node->name, WEXITSTATUS(*inout_status),
1113a34d5fb1SAntonio Huete Jimenez 		    job->ignerr ? " (ignored)" : "");
1114a34d5fb1SAntonio Huete Jimenez 	}
1115a34d5fb1SAntonio Huete Jimenez 
1116a34d5fb1SAntonio Huete Jimenez 	if (job->ignerr)
1117a34d5fb1SAntonio Huete Jimenez 		WAIT_STATUS(*inout_status) = 0;
1118a34d5fb1SAntonio Huete Jimenez 	else {
1119a34d5fb1SAntonio Huete Jimenez 		if (deleteOnError)
1120a34d5fb1SAntonio Huete Jimenez 			JobDeleteTarget(job->node);
1121*6eef5f0cSAntonio Huete Jimenez 		PrintOnError(job->node, "\n");
1122a34d5fb1SAntonio Huete Jimenez 	}
1123a34d5fb1SAntonio Huete Jimenez }
1124a34d5fb1SAntonio Huete Jimenez 
1125a34d5fb1SAntonio Huete Jimenez static void
JobFinishDoneExited(Job * job,WAIT_T * inout_status)1126a34d5fb1SAntonio Huete Jimenez JobFinishDoneExited(Job *job, WAIT_T *inout_status)
1127a34d5fb1SAntonio Huete Jimenez {
1128a34d5fb1SAntonio Huete Jimenez 	DEBUG2(JOB, "Process %d [%s] exited.\n", job->pid, job->node->name);
1129a34d5fb1SAntonio Huete Jimenez 
1130a34d5fb1SAntonio Huete Jimenez 	if (WEXITSTATUS(*inout_status) != 0)
1131a34d5fb1SAntonio Huete Jimenez 		JobFinishDoneExitedError(job, inout_status);
1132a34d5fb1SAntonio Huete Jimenez 	else if (DEBUG(JOB)) {
1133a34d5fb1SAntonio Huete Jimenez 		SwitchOutputTo(job->node);
1134a34d5fb1SAntonio Huete Jimenez 		(void)printf("*** [%s] Completed successfully\n",
1135a34d5fb1SAntonio Huete Jimenez 		    job->node->name);
1136a34d5fb1SAntonio Huete Jimenez 	}
1137a34d5fb1SAntonio Huete Jimenez }
1138a34d5fb1SAntonio Huete Jimenez 
1139a34d5fb1SAntonio Huete Jimenez static void
JobFinishDoneSignaled(Job * job,WAIT_T status)1140a34d5fb1SAntonio Huete Jimenez JobFinishDoneSignaled(Job *job, WAIT_T status)
1141a34d5fb1SAntonio Huete Jimenez {
1142a34d5fb1SAntonio Huete Jimenez 	SwitchOutputTo(job->node);
1143*6eef5f0cSAntonio Huete Jimenez 	DebugFailedJob(job);
1144a34d5fb1SAntonio Huete Jimenez 	(void)printf("*** [%s] Signal %d\n", job->node->name, WTERMSIG(status));
1145a34d5fb1SAntonio Huete Jimenez 	if (deleteOnError)
1146a34d5fb1SAntonio Huete Jimenez 		JobDeleteTarget(job->node);
1147a34d5fb1SAntonio Huete Jimenez }
1148a34d5fb1SAntonio Huete Jimenez 
1149a34d5fb1SAntonio Huete Jimenez static void
JobFinishDone(Job * job,WAIT_T * inout_status)1150a34d5fb1SAntonio Huete Jimenez JobFinishDone(Job *job, WAIT_T *inout_status)
1151a34d5fb1SAntonio Huete Jimenez {
1152a34d5fb1SAntonio Huete Jimenez 	if (WIFEXITED(*inout_status))
1153a34d5fb1SAntonio Huete Jimenez 		JobFinishDoneExited(job, inout_status);
1154a34d5fb1SAntonio Huete Jimenez 	else
1155a34d5fb1SAntonio Huete Jimenez 		JobFinishDoneSignaled(job, *inout_status);
1156a34d5fb1SAntonio Huete Jimenez 
1157a34d5fb1SAntonio Huete Jimenez 	(void)fflush(stdout);
1158a34d5fb1SAntonio Huete Jimenez }
1159a34d5fb1SAntonio Huete Jimenez 
1160a34d5fb1SAntonio Huete Jimenez /*
1161a34d5fb1SAntonio Huete Jimenez  * Do final processing for the given job including updating parent nodes and
1162a34d5fb1SAntonio Huete Jimenez  * starting new jobs as available/necessary.
1163a34d5fb1SAntonio Huete Jimenez  *
1164a34d5fb1SAntonio Huete Jimenez  * Deferred commands for the job are placed on the .END node.
1165a34d5fb1SAntonio Huete Jimenez  *
1166a34d5fb1SAntonio Huete Jimenez  * If there was a serious error (job_errors != 0; not an ignored one), no more
1167a34d5fb1SAntonio Huete Jimenez  * jobs will be started.
116801e196c8SJohn Marino  *
116901e196c8SJohn Marino  * Input:
117001e196c8SJohn Marino  *	job		job to finish
117101e196c8SJohn Marino  *	status		sub-why job went away
117201e196c8SJohn Marino  */
117301e196c8SJohn Marino static void
JobFinish(Job * job,WAIT_T status)117401e196c8SJohn Marino JobFinish (Job *job, WAIT_T status)
117501e196c8SJohn Marino {
1176*6eef5f0cSAntonio Huete Jimenez 	bool done, return_job_token;
117701e196c8SJohn Marino 
1178a34d5fb1SAntonio Huete Jimenez 	DEBUG3(JOB, "JobFinish: %d [%s], status %d\n",
117901e196c8SJohn Marino 	    job->pid, job->node->name, status);
118001e196c8SJohn Marino 
118101e196c8SJohn Marino 	if ((WIFEXITED(status) &&
1182a34d5fb1SAntonio Huete Jimenez 	     ((WEXITSTATUS(status) != 0 && !job->ignerr))) ||
1183a34d5fb1SAntonio Huete Jimenez 	    WIFSIGNALED(status)) {
1184a34d5fb1SAntonio Huete Jimenez 		/* Finished because of an error. */
1185a34d5fb1SAntonio Huete Jimenez 
1186a34d5fb1SAntonio Huete Jimenez 		JobClosePipes(job);
118701e196c8SJohn Marino 		if (job->cmdFILE != NULL && job->cmdFILE != stdout) {
1188*6eef5f0cSAntonio Huete Jimenez 			if (fclose(job->cmdFILE) != 0)
1189*6eef5f0cSAntonio Huete Jimenez 				Punt("Cannot write shell script for '%s': %s",
1190*6eef5f0cSAntonio Huete Jimenez 				    job->node->name, strerror(errno));
119101e196c8SJohn Marino 			job->cmdFILE = NULL;
119201e196c8SJohn Marino 		}
1193*6eef5f0cSAntonio Huete Jimenez 		done = true;
1194a34d5fb1SAntonio Huete Jimenez 
119501e196c8SJohn Marino 	} else if (WIFEXITED(status)) {
119601e196c8SJohn Marino 		/*
1197a34d5fb1SAntonio Huete Jimenez 		 * Deal with ignored errors in -B mode. We need to print a
1198a34d5fb1SAntonio Huete Jimenez 		 * message telling of the ignored error as well as to run
1199a34d5fb1SAntonio Huete Jimenez 		 * the next command.
120001e196c8SJohn Marino 		 */
120101e196c8SJohn Marino 		done = WEXITSTATUS(status) != 0;
1202a34d5fb1SAntonio Huete Jimenez 
1203a34d5fb1SAntonio Huete Jimenez 		JobClosePipes(job);
1204a34d5fb1SAntonio Huete Jimenez 
120501e196c8SJohn Marino 	} else {
1206a34d5fb1SAntonio Huete Jimenez 		/* No need to close things down or anything. */
1207*6eef5f0cSAntonio Huete Jimenez 		done = false;
120801e196c8SJohn Marino 	}
120901e196c8SJohn Marino 
1210a34d5fb1SAntonio Huete Jimenez 	if (done)
1211a34d5fb1SAntonio Huete Jimenez 		JobFinishDone(job, &status);
121201e196c8SJohn Marino 
121301e196c8SJohn Marino #ifdef USE_META
121401e196c8SJohn Marino 	if (useMeta) {
1215a34d5fb1SAntonio Huete Jimenez 		int meta_status = meta_job_finish(job);
1216a34d5fb1SAntonio Huete Jimenez 		if (meta_status != 0 && status == 0)
1217a34d5fb1SAntonio Huete Jimenez 			status = meta_status;
121801e196c8SJohn Marino 	}
121901e196c8SJohn Marino #endif
122001e196c8SJohn Marino 
1221*6eef5f0cSAntonio Huete Jimenez 	return_job_token = false;
122201e196c8SJohn Marino 
122301e196c8SJohn Marino 	Trace_Log(JOBEND, job);
1224a34d5fb1SAntonio Huete Jimenez 	if (!job->special) {
1225a34d5fb1SAntonio Huete Jimenez 		if (WAIT_STATUS(status) != 0 ||
1226a34d5fb1SAntonio Huete Jimenez 		    (aborting == ABORT_ERROR) || aborting == ABORT_INTERRUPT)
1227*6eef5f0cSAntonio Huete Jimenez 			return_job_token = true;
122801e196c8SJohn Marino 	}
122901e196c8SJohn Marino 
1230a34d5fb1SAntonio Huete Jimenez 	if (aborting != ABORT_ERROR && aborting != ABORT_INTERRUPT &&
123101e196c8SJohn Marino 	    (WAIT_STATUS(status) == 0)) {
123201e196c8SJohn Marino 		/*
1233a34d5fb1SAntonio Huete Jimenez 		 * As long as we aren't aborting and the job didn't return a
1234a34d5fb1SAntonio Huete Jimenez 		 * non-zero status that we shouldn't ignore, we call
1235a34d5fb1SAntonio Huete Jimenez 		 * Make_Update to update the parents.
123601e196c8SJohn Marino 		 */
1237a34d5fb1SAntonio Huete Jimenez 		JobSaveCommands(job);
123801e196c8SJohn Marino 		job->node->made = MADE;
1239a34d5fb1SAntonio Huete Jimenez 		if (!job->special)
1240*6eef5f0cSAntonio Huete Jimenez 			return_job_token = true;
124101e196c8SJohn Marino 		Make_Update(job->node);
1242a34d5fb1SAntonio Huete Jimenez 		job->status = JOB_ST_FREE;
1243a34d5fb1SAntonio Huete Jimenez 	} else if (status != 0) {
1244a34d5fb1SAntonio Huete Jimenez 		job_errors++;
1245a34d5fb1SAntonio Huete Jimenez 		job->status = JOB_ST_FREE;
124601e196c8SJohn Marino 	}
124701e196c8SJohn Marino 
1248a34d5fb1SAntonio Huete Jimenez 	if (job_errors > 0 && !opts.keepgoing && aborting != ABORT_INTERRUPT) {
1249a34d5fb1SAntonio Huete Jimenez 		/* Prevent more jobs from getting started. */
125001e196c8SJohn Marino 		aborting = ABORT_ERROR;
125101e196c8SJohn Marino 	}
125201e196c8SJohn Marino 
125301e196c8SJohn Marino 	if (return_job_token)
125401e196c8SJohn Marino 		Job_TokenReturn();
125501e196c8SJohn Marino 
1256a34d5fb1SAntonio Huete Jimenez 	if (aborting == ABORT_ERROR && jobTokensRunning == 0)
1257a34d5fb1SAntonio Huete Jimenez 		Finish(job_errors);
125801e196c8SJohn Marino }
125901e196c8SJohn Marino 
1260a34d5fb1SAntonio Huete Jimenez static void
TouchRegular(GNode * gn)1261a34d5fb1SAntonio Huete Jimenez TouchRegular(GNode *gn)
1262a34d5fb1SAntonio Huete Jimenez {
1263a34d5fb1SAntonio Huete Jimenez 	const char *file = GNode_Path(gn);
1264*6eef5f0cSAntonio Huete Jimenez 	struct utimbuf times;
1265a34d5fb1SAntonio Huete Jimenez 	int fd;
1266a34d5fb1SAntonio Huete Jimenez 	char c;
1267a34d5fb1SAntonio Huete Jimenez 
1268*6eef5f0cSAntonio Huete Jimenez 	times.actime = now;
1269*6eef5f0cSAntonio Huete Jimenez 	times.modtime = now;
1270a34d5fb1SAntonio Huete Jimenez 	if (utime(file, &times) >= 0)
1271a34d5fb1SAntonio Huete Jimenez 		return;
1272a34d5fb1SAntonio Huete Jimenez 
1273a34d5fb1SAntonio Huete Jimenez 	fd = open(file, O_RDWR | O_CREAT, 0666);
1274a34d5fb1SAntonio Huete Jimenez 	if (fd < 0) {
1275a34d5fb1SAntonio Huete Jimenez 		(void)fprintf(stderr, "*** couldn't touch %s: %s\n",
1276a34d5fb1SAntonio Huete Jimenez 		    file, strerror(errno));
1277a34d5fb1SAntonio Huete Jimenez 		(void)fflush(stderr);
1278a34d5fb1SAntonio Huete Jimenez 		return;		/* XXX: What about propagating the error? */
1279a34d5fb1SAntonio Huete Jimenez 	}
1280a34d5fb1SAntonio Huete Jimenez 
1281*6eef5f0cSAntonio Huete Jimenez 	/*
1282*6eef5f0cSAntonio Huete Jimenez 	 * Last resort: update the file's time stamps in the traditional way.
1283a34d5fb1SAntonio Huete Jimenez 	 * XXX: This doesn't work for empty files, which are sometimes used
1284*6eef5f0cSAntonio Huete Jimenez 	 * as marker files.
1285*6eef5f0cSAntonio Huete Jimenez 	 */
1286a34d5fb1SAntonio Huete Jimenez 	if (read(fd, &c, 1) == 1) {
1287a34d5fb1SAntonio Huete Jimenez 		(void)lseek(fd, 0, SEEK_SET);
1288a34d5fb1SAntonio Huete Jimenez 		while (write(fd, &c, 1) == -1 && errno == EAGAIN)
1289a34d5fb1SAntonio Huete Jimenez 			continue;
1290a34d5fb1SAntonio Huete Jimenez 	}
1291a34d5fb1SAntonio Huete Jimenez 	(void)close(fd);	/* XXX: What about propagating the error? */
1292a34d5fb1SAntonio Huete Jimenez }
1293a34d5fb1SAntonio Huete Jimenez 
1294a34d5fb1SAntonio Huete Jimenez /*
1295a34d5fb1SAntonio Huete Jimenez  * Touch the given target. Called by JobStart when the -t flag was given.
129601e196c8SJohn Marino  *
1297a34d5fb1SAntonio Huete Jimenez  * The modification date of the file is changed.
1298a34d5fb1SAntonio Huete Jimenez  * If the file did not exist, it is created.
129901e196c8SJohn Marino  */
130001e196c8SJohn Marino void
Job_Touch(GNode * gn,bool echo)1301*6eef5f0cSAntonio Huete Jimenez Job_Touch(GNode *gn, bool echo)
130201e196c8SJohn Marino {
1303a34d5fb1SAntonio Huete Jimenez 	if (gn->type &
1304a34d5fb1SAntonio Huete Jimenez 	    (OP_JOIN | OP_USE | OP_USEBEFORE | OP_EXEC | OP_OPTIONAL |
130501e196c8SJohn Marino 	     OP_SPECIAL | OP_PHONY)) {
130601e196c8SJohn Marino 		/*
1307a34d5fb1SAntonio Huete Jimenez 		 * These are "virtual" targets and should not really be
1308a34d5fb1SAntonio Huete Jimenez 		 * created.
130901e196c8SJohn Marino 		 */
131001e196c8SJohn Marino 		return;
131101e196c8SJohn Marino 	}
131201e196c8SJohn Marino 
1313a34d5fb1SAntonio Huete Jimenez 	if (echo || !GNode_ShouldExecute(gn)) {
131401e196c8SJohn Marino 		(void)fprintf(stdout, "touch %s\n", gn->name);
131501e196c8SJohn Marino 		(void)fflush(stdout);
131601e196c8SJohn Marino 	}
131701e196c8SJohn Marino 
1318a34d5fb1SAntonio Huete Jimenez 	if (!GNode_ShouldExecute(gn))
131901e196c8SJohn Marino 		return;
132001e196c8SJohn Marino 
1321a34d5fb1SAntonio Huete Jimenez 	if (gn->type & OP_ARCHV)
132201e196c8SJohn Marino 		Arch_Touch(gn);
1323a34d5fb1SAntonio Huete Jimenez 	else if (gn->type & OP_LIB)
132401e196c8SJohn Marino 		Arch_TouchLib(gn);
1325a34d5fb1SAntonio Huete Jimenez 	else
1326a34d5fb1SAntonio Huete Jimenez 		TouchRegular(gn);
1327a34d5fb1SAntonio Huete Jimenez }
132801e196c8SJohn Marino 
132901e196c8SJohn Marino /*
133001e196c8SJohn Marino  * Make sure the given node has all the commands it needs.
133101e196c8SJohn Marino  *
1332a34d5fb1SAntonio Huete Jimenez  * The node will have commands from the .DEFAULT rule added to it if it
1333a34d5fb1SAntonio Huete Jimenez  * needs them.
1334a34d5fb1SAntonio Huete Jimenez  *
133501e196c8SJohn Marino  * Input:
133601e196c8SJohn Marino  *	gn		The target whose commands need verifying
133701e196c8SJohn Marino  *	abortProc	Function to abort with message
133801e196c8SJohn Marino  *
133901e196c8SJohn Marino  * Results:
1340*6eef5f0cSAntonio Huete Jimenez  *	true if the commands list is/was ok.
134101e196c8SJohn Marino  */
1342*6eef5f0cSAntonio Huete Jimenez bool
Job_CheckCommands(GNode * gn,void (* abortProc)(const char *,...))134301e196c8SJohn Marino Job_CheckCommands(GNode *gn, void (*abortProc)(const char *, ...))
134401e196c8SJohn Marino {
1345a34d5fb1SAntonio Huete Jimenez 	if (GNode_IsTarget(gn))
1346*6eef5f0cSAntonio Huete Jimenez 		return true;
1347a34d5fb1SAntonio Huete Jimenez 	if (!Lst_IsEmpty(&gn->commands))
1348*6eef5f0cSAntonio Huete Jimenez 		return true;
1349a34d5fb1SAntonio Huete Jimenez 	if ((gn->type & OP_LIB) && !Lst_IsEmpty(&gn->children))
1350*6eef5f0cSAntonio Huete Jimenez 		return true;
1351a34d5fb1SAntonio Huete Jimenez 
135201e196c8SJohn Marino 	/*
135301e196c8SJohn Marino 	 * No commands. Look for .DEFAULT rule from which we might infer
1354a34d5fb1SAntonio Huete Jimenez 	 * commands.
135501e196c8SJohn Marino 	 */
1356a34d5fb1SAntonio Huete Jimenez 	if (defaultNode != NULL && !Lst_IsEmpty(&defaultNode->commands) &&
1357a34d5fb1SAntonio Huete Jimenez 	    !(gn->type & OP_SPECIAL)) {
135801e196c8SJohn Marino 		/*
1359a34d5fb1SAntonio Huete Jimenez 		 * The traditional Make only looks for a .DEFAULT if the node
1360a34d5fb1SAntonio Huete Jimenez 		 * was never the target of an operator, so that's what we do
1361a34d5fb1SAntonio Huete Jimenez 		 * too.
1362a34d5fb1SAntonio Huete Jimenez 		 *
1363a34d5fb1SAntonio Huete Jimenez 		 * The .DEFAULT node acts like a transformation rule, in that
136401e196c8SJohn Marino 		 * gn also inherits any attributes or sources attached to
136501e196c8SJohn Marino 		 * .DEFAULT itself.
136601e196c8SJohn Marino 		 */
1367a34d5fb1SAntonio Huete Jimenez 		Make_HandleUse(defaultNode, gn);
1368a34d5fb1SAntonio Huete Jimenez 		Var_Set(gn, IMPSRC, GNode_VarTarget(gn));
1369*6eef5f0cSAntonio Huete Jimenez 		return true;
1370a34d5fb1SAntonio Huete Jimenez 	}
1371a34d5fb1SAntonio Huete Jimenez 
1372*6eef5f0cSAntonio Huete Jimenez 	Dir_UpdateMTime(gn, false);
1373a34d5fb1SAntonio Huete Jimenez 	if (gn->mtime != 0 || (gn->type & OP_SPECIAL))
1374*6eef5f0cSAntonio Huete Jimenez 		return true;
1375a34d5fb1SAntonio Huete Jimenez 
137601e196c8SJohn Marino 	/*
1377a34d5fb1SAntonio Huete Jimenez 	 * The node wasn't the target of an operator.  We have no .DEFAULT
137801e196c8SJohn Marino 	 * rule to go on and the target doesn't already exist. There's
137901e196c8SJohn Marino 	 * nothing more we can do for this branch. If the -k flag wasn't
138001e196c8SJohn Marino 	 * given, we stop in our tracks, otherwise we just don't update
138101e196c8SJohn Marino 	 * this node's parents so they never get examined.
138201e196c8SJohn Marino 	 */
138301e196c8SJohn Marino 
1384*6eef5f0cSAntonio Huete Jimenez 	if (gn->flags.fromDepend) {
13855f1e34d9SAlexandre Perrin 		if (!Job_RunTarget(".STALE", gn->fname))
1386a34d5fb1SAntonio Huete Jimenez 			fprintf(stdout,
1387*6eef5f0cSAntonio Huete Jimenez 			    "%s: %s, %u: ignoring stale %s for %s\n",
13885f1e34d9SAlexandre Perrin 			    progname, gn->fname, gn->lineno, makeDependfile,
13895f1e34d9SAlexandre Perrin 			    gn->name);
1390*6eef5f0cSAntonio Huete Jimenez 		return true;
139101e196c8SJohn Marino 	}
139201e196c8SJohn Marino 
139301e196c8SJohn Marino 	if (gn->type & OP_OPTIONAL) {
1394a34d5fb1SAntonio Huete Jimenez 		(void)fprintf(stdout, "%s: don't know how to make %s (%s)\n",
1395a34d5fb1SAntonio Huete Jimenez 		    progname, gn->name, "ignored");
139601e196c8SJohn Marino 		(void)fflush(stdout);
1397*6eef5f0cSAntonio Huete Jimenez 		return true;
139801e196c8SJohn Marino 	}
139901e196c8SJohn Marino 
1400a34d5fb1SAntonio Huete Jimenez 	if (opts.keepgoing) {
1401a34d5fb1SAntonio Huete Jimenez 		(void)fprintf(stdout, "%s: don't know how to make %s (%s)\n",
1402a34d5fb1SAntonio Huete Jimenez 		    progname, gn->name, "continuing");
1403a34d5fb1SAntonio Huete Jimenez 		(void)fflush(stdout);
1404*6eef5f0cSAntonio Huete Jimenez 		return false;
1405a34d5fb1SAntonio Huete Jimenez 	}
1406a34d5fb1SAntonio Huete Jimenez 
1407a34d5fb1SAntonio Huete Jimenez 	abortProc("%s: don't know how to make %s. Stop", progname, gn->name);
1408*6eef5f0cSAntonio Huete Jimenez 	return false;
1409a34d5fb1SAntonio Huete Jimenez }
1410a34d5fb1SAntonio Huete Jimenez 
1411a34d5fb1SAntonio Huete Jimenez /*
1412a34d5fb1SAntonio Huete Jimenez  * Execute the shell for the given job.
141301e196c8SJohn Marino  *
1414a34d5fb1SAntonio Huete Jimenez  * See Job_CatchOutput for handling the output of the shell.
141501e196c8SJohn Marino  */
141601e196c8SJohn Marino static void
JobExec(Job * job,char ** argv)141701e196c8SJohn Marino JobExec(Job *job, char **argv)
141801e196c8SJohn Marino {
141901e196c8SJohn Marino 	int cpid;		/* ID of new child */
142001e196c8SJohn Marino 	sigset_t mask;
142101e196c8SJohn Marino 
142201e196c8SJohn Marino 	if (DEBUG(JOB)) {
142301e196c8SJohn Marino 		int i;
142401e196c8SJohn Marino 
1425a34d5fb1SAntonio Huete Jimenez 		debug_printf("Running %s\n", job->node->name);
1426a34d5fb1SAntonio Huete Jimenez 		debug_printf("\tCommand: ");
142701e196c8SJohn Marino 		for (i = 0; argv[i] != NULL; i++) {
1428a34d5fb1SAntonio Huete Jimenez 			debug_printf("%s ", argv[i]);
142901e196c8SJohn Marino 		}
1430a34d5fb1SAntonio Huete Jimenez 		debug_printf("\n");
143101e196c8SJohn Marino 	}
143201e196c8SJohn Marino 
143301e196c8SJohn Marino 	/*
143401e196c8SJohn Marino 	 * Some jobs produce no output and it's disconcerting to have
143501e196c8SJohn Marino 	 * no feedback of their running (since they produce no output, the
143601e196c8SJohn Marino 	 * banner with their name in it never appears). This is an attempt to
143701e196c8SJohn Marino 	 * provide that feedback, even if nothing follows it.
143801e196c8SJohn Marino 	 */
1439a34d5fb1SAntonio Huete Jimenez 	if (job->echo)
1440a34d5fb1SAntonio Huete Jimenez 		SwitchOutputTo(job->node);
144101e196c8SJohn Marino 
144201e196c8SJohn Marino 	/* No interruptions until this job is on the `jobs' list */
144301e196c8SJohn Marino 	JobSigLock(&mask);
144401e196c8SJohn Marino 
144501e196c8SJohn Marino 	/* Pre-emptively mark job running, pid still zero though */
1446a34d5fb1SAntonio Huete Jimenez 	job->status = JOB_ST_RUNNING;
144701e196c8SJohn Marino 
1448a34d5fb1SAntonio Huete Jimenez 	Var_ReexportVars();
1449a34d5fb1SAntonio Huete Jimenez 
1450a34d5fb1SAntonio Huete Jimenez 	cpid = vfork();
145101e196c8SJohn Marino 	if (cpid == -1)
145201e196c8SJohn Marino 		Punt("Cannot vfork: %s", strerror(errno));
145301e196c8SJohn Marino 
145401e196c8SJohn Marino 	if (cpid == 0) {
145501e196c8SJohn Marino 		/* Child */
145601e196c8SJohn Marino 		sigset_t tmask;
145701e196c8SJohn Marino 
145801e196c8SJohn Marino #ifdef USE_META
1459*6eef5f0cSAntonio Huete Jimenez 		if (useMeta)
146001e196c8SJohn Marino 			meta_job_child(job);
146101e196c8SJohn Marino #endif
146201e196c8SJohn Marino 		/*
1463a34d5fb1SAntonio Huete Jimenez 		 * Reset all signal handlers; this is necessary because we
1464a34d5fb1SAntonio Huete Jimenez 		 * also need to unblock signals before we exec(2).
146501e196c8SJohn Marino 		 */
146601e196c8SJohn Marino 		JobSigReset();
146701e196c8SJohn Marino 
146801e196c8SJohn Marino 		/* Now unblock signals */
146901e196c8SJohn Marino 		sigemptyset(&tmask);
147001e196c8SJohn Marino 		JobSigUnlock(&tmask);
147101e196c8SJohn Marino 
147201e196c8SJohn Marino 		/*
1473a34d5fb1SAntonio Huete Jimenez 		 * Must duplicate the input stream down to the child's input
1474a34d5fb1SAntonio Huete Jimenez 		 * and reset it to the beginning (again). Since the stream
1475a34d5fb1SAntonio Huete Jimenez 		 * was marked close-on-exec, we must clear that bit in the
1476a34d5fb1SAntonio Huete Jimenez 		 * new input.
147701e196c8SJohn Marino 		 */
1478a34d5fb1SAntonio Huete Jimenez 		if (dup2(fileno(job->cmdFILE), 0) == -1)
1479a34d5fb1SAntonio Huete Jimenez 			execDie("dup2", "job->cmdFILE");
1480a34d5fb1SAntonio Huete Jimenez 		if (fcntl(0, F_SETFD, 0) == -1)
1481a34d5fb1SAntonio Huete Jimenez 			execDie("fcntl clear close-on-exec", "stdin");
1482a34d5fb1SAntonio Huete Jimenez 		if (lseek(0, 0, SEEK_SET) == -1)
1483a34d5fb1SAntonio Huete Jimenez 			execDie("lseek to 0", "stdin");
148401e196c8SJohn Marino 
14856a91b982SJohn Marino 		if (job->node->type & (OP_MAKE | OP_SUBMAKE)) {
148601e196c8SJohn Marino 			/*
148701e196c8SJohn Marino 			 * Pass job token pipe to submakes.
148801e196c8SJohn Marino 			 */
1489a34d5fb1SAntonio Huete Jimenez 			if (fcntl(tokenWaitJob.inPipe, F_SETFD, 0) == -1)
1490a34d5fb1SAntonio Huete Jimenez 				execDie("clear close-on-exec",
1491a34d5fb1SAntonio Huete Jimenez 				    "tokenWaitJob.inPipe");
1492a34d5fb1SAntonio Huete Jimenez 			if (fcntl(tokenWaitJob.outPipe, F_SETFD, 0) == -1)
1493a34d5fb1SAntonio Huete Jimenez 				execDie("clear close-on-exec",
1494a34d5fb1SAntonio Huete Jimenez 				    "tokenWaitJob.outPipe");
149501e196c8SJohn Marino 		}
149601e196c8SJohn Marino 
149701e196c8SJohn Marino 		/*
149801e196c8SJohn Marino 		 * Set up the child's output to be routed through the pipe
149901e196c8SJohn Marino 		 * we've created for it.
150001e196c8SJohn Marino 		 */
1501a34d5fb1SAntonio Huete Jimenez 		if (dup2(job->outPipe, 1) == -1)
1502a34d5fb1SAntonio Huete Jimenez 			execDie("dup2", "job->outPipe");
150301e196c8SJohn Marino 
150401e196c8SJohn Marino 		/*
1505a34d5fb1SAntonio Huete Jimenez 		 * The output channels are marked close on exec. This bit
1506a34d5fb1SAntonio Huete Jimenez 		 * was duplicated by the dup2(on some systems), so we have
1507a34d5fb1SAntonio Huete Jimenez 		 * to clear it before routing the shell's error output to
1508a34d5fb1SAntonio Huete Jimenez 		 * the same place as its standard output.
1509a34d5fb1SAntonio Huete Jimenez 		 */
1510a34d5fb1SAntonio Huete Jimenez 		if (fcntl(1, F_SETFD, 0) == -1)
1511a34d5fb1SAntonio Huete Jimenez 			execDie("clear close-on-exec", "stdout");
1512a34d5fb1SAntonio Huete Jimenez 		if (dup2(1, 2) == -1)
1513a34d5fb1SAntonio Huete Jimenez 			execDie("dup2", "1, 2");
1514a34d5fb1SAntonio Huete Jimenez 
1515a34d5fb1SAntonio Huete Jimenez 		/*
1516a34d5fb1SAntonio Huete Jimenez 		 * We want to switch the child into a different process
1517a34d5fb1SAntonio Huete Jimenez 		 * family so we can kill it and all its descendants in
1518a34d5fb1SAntonio Huete Jimenez 		 * one fell swoop, by killing its process family, but not
1519a34d5fb1SAntonio Huete Jimenez 		 * commit suicide.
152001e196c8SJohn Marino 		 */
152101e196c8SJohn Marino #if defined(HAVE_SETPGID)
152201e196c8SJohn Marino 		(void)setpgid(0, getpid());
152301e196c8SJohn Marino #else
152401e196c8SJohn Marino # if defined(HAVE_SETSID)
152501e196c8SJohn Marino 		/* XXX: dsl - I'm sure this should be setpgrp()... */
152601e196c8SJohn Marino 		(void)setsid();
152701e196c8SJohn Marino # else
152801e196c8SJohn Marino 		(void)setpgrp(0, getpid());
152901e196c8SJohn Marino # endif
153001e196c8SJohn Marino #endif
153101e196c8SJohn Marino 
153201e196c8SJohn Marino 		(void)execv(shellPath, argv);
1533a34d5fb1SAntonio Huete Jimenez 		execDie("exec", shellPath);
153401e196c8SJohn Marino 	}
153501e196c8SJohn Marino 
153601e196c8SJohn Marino 	/* Parent, continuing after the child exec */
153701e196c8SJohn Marino 	job->pid = cpid;
153801e196c8SJohn Marino 
153901e196c8SJohn Marino 	Trace_Log(JOBSTART, job);
154001e196c8SJohn Marino 
1541ca58f742SDaniel Fojt #ifdef USE_META
1542*6eef5f0cSAntonio Huete Jimenez 	if (useMeta)
1543ca58f742SDaniel Fojt 		meta_job_parent(job, cpid);
1544ca58f742SDaniel Fojt #endif
1545ca58f742SDaniel Fojt 
154601e196c8SJohn Marino 	/*
154701e196c8SJohn Marino 	 * Set the current position in the buffer to the beginning
154801e196c8SJohn Marino 	 * and mark another stream to watch in the outputs mask
154901e196c8SJohn Marino 	 */
155001e196c8SJohn Marino 	job->curPos = 0;
155101e196c8SJohn Marino 
155201e196c8SJohn Marino 	watchfd(job);
155301e196c8SJohn Marino 
155401e196c8SJohn Marino 	if (job->cmdFILE != NULL && job->cmdFILE != stdout) {
1555*6eef5f0cSAntonio Huete Jimenez 		if (fclose(job->cmdFILE) != 0)
1556*6eef5f0cSAntonio Huete Jimenez 			Punt("Cannot write shell script for '%s': %s",
1557*6eef5f0cSAntonio Huete Jimenez 			    job->node->name, strerror(errno));
155801e196c8SJohn Marino 		job->cmdFILE = NULL;
155901e196c8SJohn Marino 	}
156001e196c8SJohn Marino 
1561a34d5fb1SAntonio Huete Jimenez 	/* Now that the job is actually running, add it to the table. */
156201e196c8SJohn Marino 	if (DEBUG(JOB)) {
1563a34d5fb1SAntonio Huete Jimenez 		debug_printf("JobExec(%s): pid %d added to jobs table\n",
156401e196c8SJohn Marino 		    job->node->name, job->pid);
1565*6eef5f0cSAntonio Huete Jimenez 		DumpJobs("job started");
156601e196c8SJohn Marino 	}
156701e196c8SJohn Marino 	JobSigUnlock(&mask);
156801e196c8SJohn Marino }
156901e196c8SJohn Marino 
1570a34d5fb1SAntonio Huete Jimenez /* Create the argv needed to execute the shell for a given job. */
157101e196c8SJohn Marino static void
JobMakeArgv(Job * job,char ** argv)157201e196c8SJohn Marino JobMakeArgv(Job *job, char **argv)
157301e196c8SJohn Marino {
157401e196c8SJohn Marino 	int argc;
157501e196c8SJohn Marino 	static char args[10];	/* For merged arguments */
157601e196c8SJohn Marino 
157701e196c8SJohn Marino 	argv[0] = UNCONST(shellName);
157801e196c8SJohn Marino 	argc = 1;
157901e196c8SJohn Marino 
1580a34d5fb1SAntonio Huete Jimenez 	if ((shell->errFlag != NULL && shell->errFlag[0] != '-') ||
1581a34d5fb1SAntonio Huete Jimenez 	    (shell->echoFlag != NULL && shell->echoFlag[0] != '-')) {
158201e196c8SJohn Marino 		/*
1583a34d5fb1SAntonio Huete Jimenez 		 * At least one of the flags doesn't have a minus before it,
1584a34d5fb1SAntonio Huete Jimenez 		 * so merge them together. Have to do this because the Bourne
1585a34d5fb1SAntonio Huete Jimenez 		 * shell thinks its second argument is a file to source.
1586a34d5fb1SAntonio Huete Jimenez 		 * Grrrr. Note the ten-character limitation on the combined
1587a34d5fb1SAntonio Huete Jimenez 		 * arguments.
1588a34d5fb1SAntonio Huete Jimenez 		 *
1589a34d5fb1SAntonio Huete Jimenez 		 * TODO: Research until when the above comments were
1590a34d5fb1SAntonio Huete Jimenez 		 * practically relevant.
159101e196c8SJohn Marino 		 */
1592a34d5fb1SAntonio Huete Jimenez 		(void)snprintf(args, sizeof args, "-%s%s",
1593a34d5fb1SAntonio Huete Jimenez 		    (job->ignerr ? "" :
1594a34d5fb1SAntonio Huete Jimenez 			(shell->errFlag != NULL ? shell->errFlag : "")),
1595a34d5fb1SAntonio Huete Jimenez 		    (!job->echo ? "" :
1596a34d5fb1SAntonio Huete Jimenez 			(shell->echoFlag != NULL ? shell->echoFlag : "")));
159701e196c8SJohn Marino 
1598a34d5fb1SAntonio Huete Jimenez 		if (args[1] != '\0') {
159901e196c8SJohn Marino 			argv[argc] = args;
160001e196c8SJohn Marino 			argc++;
160101e196c8SJohn Marino 		}
160201e196c8SJohn Marino 	} else {
1603a34d5fb1SAntonio Huete Jimenez 		if (!job->ignerr && shell->errFlag != NULL) {
1604a34d5fb1SAntonio Huete Jimenez 			argv[argc] = UNCONST(shell->errFlag);
160501e196c8SJohn Marino 			argc++;
160601e196c8SJohn Marino 		}
1607a34d5fb1SAntonio Huete Jimenez 		if (job->echo && shell->echoFlag != NULL) {
1608a34d5fb1SAntonio Huete Jimenez 			argv[argc] = UNCONST(shell->echoFlag);
160901e196c8SJohn Marino 			argc++;
161001e196c8SJohn Marino 		}
161101e196c8SJohn Marino 	}
161201e196c8SJohn Marino 	argv[argc] = NULL;
161301e196c8SJohn Marino }
161401e196c8SJohn Marino 
1615a34d5fb1SAntonio Huete Jimenez static void
JobWriteShellCommands(Job * job,GNode * gn,bool * out_run)1616*6eef5f0cSAntonio Huete Jimenez JobWriteShellCommands(Job *job, GNode *gn, bool *out_run)
1617a34d5fb1SAntonio Huete Jimenez {
1618a34d5fb1SAntonio Huete Jimenez 	/*
1619a34d5fb1SAntonio Huete Jimenez 	 * tfile is the name of a file into which all shell commands
1620a34d5fb1SAntonio Huete Jimenez 	 * are put. It is removed before the child shell is executed,
1621a34d5fb1SAntonio Huete Jimenez 	 * unless DEBUG(SCRIPT) is set.
1622a34d5fb1SAntonio Huete Jimenez 	 */
1623a34d5fb1SAntonio Huete Jimenez 	char tfile[MAXPATHLEN];
1624a34d5fb1SAntonio Huete Jimenez 	int tfd;		/* File descriptor to the temp file */
1625a34d5fb1SAntonio Huete Jimenez 
1626a34d5fb1SAntonio Huete Jimenez 	tfd = Job_TempFile(TMPPAT, tfile, sizeof tfile);
1627a34d5fb1SAntonio Huete Jimenez 
1628a34d5fb1SAntonio Huete Jimenez 	job->cmdFILE = fdopen(tfd, "w+");
1629a34d5fb1SAntonio Huete Jimenez 	if (job->cmdFILE == NULL)
1630a34d5fb1SAntonio Huete Jimenez 		Punt("Could not fdopen %s", tfile);
1631a34d5fb1SAntonio Huete Jimenez 
1632a34d5fb1SAntonio Huete Jimenez 	(void)fcntl(fileno(job->cmdFILE), F_SETFD, FD_CLOEXEC);
1633a34d5fb1SAntonio Huete Jimenez 
1634a34d5fb1SAntonio Huete Jimenez #ifdef USE_META
1635a34d5fb1SAntonio Huete Jimenez 	if (useMeta) {
1636a34d5fb1SAntonio Huete Jimenez 		meta_job_start(job, gn);
1637a34d5fb1SAntonio Huete Jimenez 		if (gn->type & OP_SILENT)	/* might have changed */
1638*6eef5f0cSAntonio Huete Jimenez 			job->echo = false;
1639a34d5fb1SAntonio Huete Jimenez 	}
1640a34d5fb1SAntonio Huete Jimenez #endif
1641a34d5fb1SAntonio Huete Jimenez 
1642*6eef5f0cSAntonio Huete Jimenez 	*out_run = JobWriteCommands(job);
1643a34d5fb1SAntonio Huete Jimenez }
1644a34d5fb1SAntonio Huete Jimenez 
1645a34d5fb1SAntonio Huete Jimenez /*
1646*6eef5f0cSAntonio Huete Jimenez  * Start a target-creation process going for the target described by gn.
164701e196c8SJohn Marino  *
164801e196c8SJohn Marino  * Results:
164901e196c8SJohn Marino  *	JOB_ERROR if there was an error in the commands, JOB_FINISHED
165001e196c8SJohn Marino  *	if there isn't actually anything left to do for the job and
165101e196c8SJohn Marino  *	JOB_RUNNING if the job has been started.
165201e196c8SJohn Marino  *
1653*6eef5f0cSAntonio Huete Jimenez  * Details:
165401e196c8SJohn Marino  *	A new Job node is created and added to the list of running
165501e196c8SJohn Marino  *	jobs. PMake is forked and a child shell created.
165601e196c8SJohn Marino  *
1657a34d5fb1SAntonio Huete Jimenez  * NB: The return value is ignored by everyone.
165801e196c8SJohn Marino  */
1659a34d5fb1SAntonio Huete Jimenez static JobStartResult
JobStart(GNode * gn,bool special)1660*6eef5f0cSAntonio Huete Jimenez JobStart(GNode *gn, bool special)
166101e196c8SJohn Marino {
166201e196c8SJohn Marino 	Job *job;		/* new job descriptor */
166301e196c8SJohn Marino 	char *argv[10];		/* Argument vector to shell */
1664*6eef5f0cSAntonio Huete Jimenez 	bool cmdsOK;		/* true if the nodes commands were all right */
1665*6eef5f0cSAntonio Huete Jimenez 	bool run;
166601e196c8SJohn Marino 
166701e196c8SJohn Marino 	for (job = job_table; job < job_table_end; job++) {
1668a34d5fb1SAntonio Huete Jimenez 		if (job->status == JOB_ST_FREE)
166901e196c8SJohn Marino 			break;
167001e196c8SJohn Marino 	}
167101e196c8SJohn Marino 	if (job >= job_table_end)
167201e196c8SJohn Marino 		Punt("JobStart no job slots vacant");
167301e196c8SJohn Marino 
167401e196c8SJohn Marino 	memset(job, 0, sizeof *job);
167501e196c8SJohn Marino 	job->node = gn;
167601e196c8SJohn Marino 	job->tailCmds = NULL;
1677a34d5fb1SAntonio Huete Jimenez 	job->status = JOB_ST_SET_UP;
1678a34d5fb1SAntonio Huete Jimenez 
1679a34d5fb1SAntonio Huete Jimenez 	job->special = special || gn->type & OP_SPECIAL;
1680a34d5fb1SAntonio Huete Jimenez 	job->ignerr = opts.ignoreErrors || gn->type & OP_IGNORE;
1681*6eef5f0cSAntonio Huete Jimenez 	job->echo = !(opts.silent || gn->type & OP_SILENT);
168201e196c8SJohn Marino 
168301e196c8SJohn Marino 	/*
1684a34d5fb1SAntonio Huete Jimenez 	 * Check the commands now so any attributes from .DEFAULT have a
1685a34d5fb1SAntonio Huete Jimenez 	 * chance to migrate to the node.
168601e196c8SJohn Marino 	 */
168701e196c8SJohn Marino 	cmdsOK = Job_CheckCommands(gn, Error);
168801e196c8SJohn Marino 
168901e196c8SJohn Marino 	job->inPollfd = NULL;
169001e196c8SJohn Marino 
1691a34d5fb1SAntonio Huete Jimenez 	if (Lst_IsEmpty(&gn->commands)) {
169201e196c8SJohn Marino 		job->cmdFILE = stdout;
1693*6eef5f0cSAntonio Huete Jimenez 		run = false;
1694*6eef5f0cSAntonio Huete Jimenez 
1695*6eef5f0cSAntonio Huete Jimenez 		/*
1696*6eef5f0cSAntonio Huete Jimenez 		 * We're serious here, but if the commands were bogus, we're
1697*6eef5f0cSAntonio Huete Jimenez 		 * also dead...
1698*6eef5f0cSAntonio Huete Jimenez 		 */
1699*6eef5f0cSAntonio Huete Jimenez 		if (!cmdsOK) {
1700*6eef5f0cSAntonio Huete Jimenez 			PrintOnError(gn, "\n");	/* provide some clue */
1701*6eef5f0cSAntonio Huete Jimenez 			DieHorribly();
1702*6eef5f0cSAntonio Huete Jimenez 		}
1703a34d5fb1SAntonio Huete Jimenez 	} else if (((gn->type & OP_MAKE) && !opts.noRecursiveExecute) ||
1704*6eef5f0cSAntonio Huete Jimenez 	    (!opts.noExecute && !opts.touch)) {
170501e196c8SJohn Marino 		/*
1706a34d5fb1SAntonio Huete Jimenez 		 * The above condition looks very similar to
1707a34d5fb1SAntonio Huete Jimenez 		 * GNode_ShouldExecute but is subtly different.  It prevents
1708a34d5fb1SAntonio Huete Jimenez 		 * that .MAKE targets are touched since these are usually
1709a34d5fb1SAntonio Huete Jimenez 		 * virtual targets.
171001e196c8SJohn Marino 		 */
1711a34d5fb1SAntonio Huete Jimenez 
1712*6eef5f0cSAntonio Huete Jimenez 		/*
1713*6eef5f0cSAntonio Huete Jimenez 		 * We're serious here, but if the commands were bogus, we're
1714*6eef5f0cSAntonio Huete Jimenez 		 * also dead...
1715*6eef5f0cSAntonio Huete Jimenez 		 */
1716*6eef5f0cSAntonio Huete Jimenez 		if (!cmdsOK) {
1717*6eef5f0cSAntonio Huete Jimenez 			PrintOnError(gn, "\n");	/* provide some clue */
1718*6eef5f0cSAntonio Huete Jimenez 			DieHorribly();
1719*6eef5f0cSAntonio Huete Jimenez 		}
1720*6eef5f0cSAntonio Huete Jimenez 
1721*6eef5f0cSAntonio Huete Jimenez 		JobWriteShellCommands(job, gn, &run);
172201e196c8SJohn Marino 		(void)fflush(job->cmdFILE);
1723a34d5fb1SAntonio Huete Jimenez 	} else if (!GNode_ShouldExecute(gn)) {
1724a34d5fb1SAntonio Huete Jimenez 		/*
1725*6eef5f0cSAntonio Huete Jimenez 		 * Just write all the commands to stdout in one fell swoop.
1726a34d5fb1SAntonio Huete Jimenez 		 * This still sets up job->tailCmds correctly.
1727a34d5fb1SAntonio Huete Jimenez 		 */
1728a34d5fb1SAntonio Huete Jimenez 		SwitchOutputTo(gn);
1729a34d5fb1SAntonio Huete Jimenez 		job->cmdFILE = stdout;
1730a34d5fb1SAntonio Huete Jimenez 		if (cmdsOK)
1731*6eef5f0cSAntonio Huete Jimenez 			JobWriteCommands(job);
1732*6eef5f0cSAntonio Huete Jimenez 		run = false;
1733a34d5fb1SAntonio Huete Jimenez 		(void)fflush(job->cmdFILE);
1734a34d5fb1SAntonio Huete Jimenez 	} else {
1735a34d5fb1SAntonio Huete Jimenez 		Job_Touch(gn, job->echo);
1736*6eef5f0cSAntonio Huete Jimenez 		run = false;
1737a34d5fb1SAntonio Huete Jimenez 	}
173801e196c8SJohn Marino 
1739a34d5fb1SAntonio Huete Jimenez 	/* If we're not supposed to execute a shell, don't. */
1740a34d5fb1SAntonio Huete Jimenez 	if (!run) {
1741a34d5fb1SAntonio Huete Jimenez 		if (!job->special)
174201e196c8SJohn Marino 			Job_TokenReturn();
1743a34d5fb1SAntonio Huete Jimenez 		/* Unlink and close the command file if we opened one */
1744a34d5fb1SAntonio Huete Jimenez 		if (job->cmdFILE != NULL && job->cmdFILE != stdout) {
174501e196c8SJohn Marino 			(void)fclose(job->cmdFILE);
174601e196c8SJohn Marino 			job->cmdFILE = NULL;
174701e196c8SJohn Marino 		}
174801e196c8SJohn Marino 
174901e196c8SJohn Marino 		/*
1750a34d5fb1SAntonio Huete Jimenez 		 * We only want to work our way up the graph if we aren't
1751a34d5fb1SAntonio Huete Jimenez 		 * here because the commands for the job were no good.
175201e196c8SJohn Marino 		 */
1753a34d5fb1SAntonio Huete Jimenez 		if (cmdsOK && aborting == ABORT_NONE) {
1754a34d5fb1SAntonio Huete Jimenez 			JobSaveCommands(job);
175501e196c8SJohn Marino 			job->node->made = MADE;
175601e196c8SJohn Marino 			Make_Update(job->node);
175701e196c8SJohn Marino 		}
1758a34d5fb1SAntonio Huete Jimenez 		job->status = JOB_ST_FREE;
175901e196c8SJohn Marino 		return cmdsOK ? JOB_FINISHED : JOB_ERROR;
176001e196c8SJohn Marino 	}
176101e196c8SJohn Marino 
176201e196c8SJohn Marino 	/*
1763a34d5fb1SAntonio Huete Jimenez 	 * Set up the control arguments to the shell. This is based on the
1764a34d5fb1SAntonio Huete Jimenez 	 * flags set earlier for this job.
176501e196c8SJohn Marino 	 */
176601e196c8SJohn Marino 	JobMakeArgv(job, argv);
176701e196c8SJohn Marino 
176801e196c8SJohn Marino 	/* Create the pipe by which we'll get the shell's output. */
176901e196c8SJohn Marino 	JobCreatePipe(job, 3);
177001e196c8SJohn Marino 
177101e196c8SJohn Marino 	JobExec(job, argv);
1772ca58f742SDaniel Fojt 	return JOB_RUNNING;
177301e196c8SJohn Marino }
177401e196c8SJohn Marino 
1775a34d5fb1SAntonio Huete Jimenez /*
1776*6eef5f0cSAntonio Huete Jimenez  * If the shell has an output filter (which only csh and ksh have by default),
1777*6eef5f0cSAntonio Huete Jimenez  * print the output of the child process, skipping the noPrint text of the
1778*6eef5f0cSAntonio Huete Jimenez  * shell.
1779*6eef5f0cSAntonio Huete Jimenez  *
1780*6eef5f0cSAntonio Huete Jimenez  * Return the part of the output that the calling function needs to output by
1781*6eef5f0cSAntonio Huete Jimenez  * itself.
1782a34d5fb1SAntonio Huete Jimenez  */
178301e196c8SJohn Marino static char *
PrintFilteredOutput(char * cp,char * endp)1784*6eef5f0cSAntonio Huete Jimenez PrintFilteredOutput(char *cp, char *endp)	/* XXX: should all be const */
178501e196c8SJohn Marino {
1786a34d5fb1SAntonio Huete Jimenez 	char *ecp;		/* XXX: should be const */
178701e196c8SJohn Marino 
1788a34d5fb1SAntonio Huete Jimenez 	if (shell->noPrint == NULL || shell->noPrint[0] == '\0')
1789a34d5fb1SAntonio Huete Jimenez 		return cp;
1790a34d5fb1SAntonio Huete Jimenez 
1791a34d5fb1SAntonio Huete Jimenez 	/*
1792a34d5fb1SAntonio Huete Jimenez 	 * XXX: What happens if shell->noPrint occurs on the boundary of
1793a34d5fb1SAntonio Huete Jimenez 	 * the buffer?  To work correctly in all cases, this should rather
1794a34d5fb1SAntonio Huete Jimenez 	 * be a proper stream filter instead of doing string matching on
1795a34d5fb1SAntonio Huete Jimenez 	 * selected chunks of the output.
1796a34d5fb1SAntonio Huete Jimenez 	 */
1797a34d5fb1SAntonio Huete Jimenez 	while ((ecp = strstr(cp, shell->noPrint)) != NULL) {
1798a34d5fb1SAntonio Huete Jimenez 		if (ecp != cp) {
1799a34d5fb1SAntonio Huete Jimenez 			*ecp = '\0';	/* XXX: avoid writing to the buffer */
180001e196c8SJohn Marino 			/*
180101e196c8SJohn Marino 			 * The only way there wouldn't be a newline after
180201e196c8SJohn Marino 			 * this line is if it were the last in the buffer.
1803a34d5fb1SAntonio Huete Jimenez 			 * however, since the noPrint output comes after it,
180401e196c8SJohn Marino 			 * there must be a newline, so we don't print one.
180501e196c8SJohn Marino 			 */
1806a34d5fb1SAntonio Huete Jimenez 			/* XXX: What about null bytes in the output? */
180701e196c8SJohn Marino 			(void)fprintf(stdout, "%s", cp);
180801e196c8SJohn Marino 			(void)fflush(stdout);
180901e196c8SJohn Marino 		}
1810a34d5fb1SAntonio Huete Jimenez 		cp = ecp + shell->noPrintLen;
1811a34d5fb1SAntonio Huete Jimenez 		if (cp == endp)
1812a34d5fb1SAntonio Huete Jimenez 			break;
1813a34d5fb1SAntonio Huete Jimenez 		cp++;		/* skip over the (XXX: assumed) newline */
1814a34d5fb1SAntonio Huete Jimenez 		pp_skip_whitespace(&cp);
181501e196c8SJohn Marino 	}
181601e196c8SJohn Marino 	return cp;
181701e196c8SJohn Marino }
181801e196c8SJohn Marino 
1819a34d5fb1SAntonio Huete Jimenez /*
1820a34d5fb1SAntonio Huete Jimenez  * This function is called whenever there is something to read on the pipe.
1821a34d5fb1SAntonio Huete Jimenez  * We collect more output from the given job and store it in the job's
1822a34d5fb1SAntonio Huete Jimenez  * outBuf. If this makes up a line, we print it tagged by the job's
1823a34d5fb1SAntonio Huete Jimenez  * identifier, as necessary.
1824a34d5fb1SAntonio Huete Jimenez  *
1825a34d5fb1SAntonio Huete Jimenez  * In the output of the shell, the 'noPrint' lines are removed. If the
1826a34d5fb1SAntonio Huete Jimenez  * command is not alone on the line (the character after it is not \0 or
1827a34d5fb1SAntonio Huete Jimenez  * \n), we do print whatever follows it.
182801e196c8SJohn Marino  *
182901e196c8SJohn Marino  * Input:
183001e196c8SJohn Marino  *	job		the job whose output needs printing
1831*6eef5f0cSAntonio Huete Jimenez  *	finish		true if this is the last time we'll be called
183201e196c8SJohn Marino  *			for this job
183301e196c8SJohn Marino  */
1834a34d5fb1SAntonio Huete Jimenez static void
CollectOutput(Job * job,bool finish)1835*6eef5f0cSAntonio Huete Jimenez CollectOutput(Job *job, bool finish)
183601e196c8SJohn Marino {
1837*6eef5f0cSAntonio Huete Jimenez 	bool gotNL;		/* true if got a newline */
1838*6eef5f0cSAntonio Huete Jimenez 	bool fbuf;		/* true if our buffer filled up */
1839a34d5fb1SAntonio Huete Jimenez 	size_t nr;		/* number of bytes read */
1840a34d5fb1SAntonio Huete Jimenez 	size_t i;		/* auxiliary index into outBuf */
1841a34d5fb1SAntonio Huete Jimenez 	size_t max;		/* limit for i (end of current data) */
1842a34d5fb1SAntonio Huete Jimenez 	ssize_t nRead;		/* (Temporary) number of bytes read */
184301e196c8SJohn Marino 
1844a34d5fb1SAntonio Huete Jimenez 	/* Read as many bytes as will fit in the buffer. */
1845a34d5fb1SAntonio Huete Jimenez again:
1846*6eef5f0cSAntonio Huete Jimenez 	gotNL = false;
1847*6eef5f0cSAntonio Huete Jimenez 	fbuf = false;
184801e196c8SJohn Marino 
184901e196c8SJohn Marino 	nRead = read(job->inPipe, &job->outBuf[job->curPos],
185001e196c8SJohn Marino 	    JOB_BUFSIZE - job->curPos);
185101e196c8SJohn Marino 	if (nRead < 0) {
185201e196c8SJohn Marino 		if (errno == EAGAIN)
185301e196c8SJohn Marino 			return;
1854*6eef5f0cSAntonio Huete Jimenez 		if (DEBUG(JOB))
1855*6eef5f0cSAntonio Huete Jimenez 			perror("CollectOutput(piperead)");
185601e196c8SJohn Marino 		nr = 0;
1857*6eef5f0cSAntonio Huete Jimenez 	} else
1858a34d5fb1SAntonio Huete Jimenez 		nr = (size_t)nRead;
1859*6eef5f0cSAntonio Huete Jimenez 
1860*6eef5f0cSAntonio Huete Jimenez 	if (nr == 0)
1861*6eef5f0cSAntonio Huete Jimenez 		finish = false;	/* stop looping */
186201e196c8SJohn Marino 
186301e196c8SJohn Marino 	/*
186401e196c8SJohn Marino 	 * If we hit the end-of-file (the job is dead), we must flush its
186501e196c8SJohn Marino 	 * remaining output, so pretend we read a newline if there's any
186601e196c8SJohn Marino 	 * output remaining in the buffer.
186701e196c8SJohn Marino 	 */
1868a34d5fb1SAntonio Huete Jimenez 	if (nr == 0 && job->curPos != 0) {
186901e196c8SJohn Marino 		job->outBuf[job->curPos] = '\n';
187001e196c8SJohn Marino 		nr = 1;
187101e196c8SJohn Marino 	}
187201e196c8SJohn Marino 
187301e196c8SJohn Marino 	max = job->curPos + nr;
1874*6eef5f0cSAntonio Huete Jimenez 	for (i = job->curPos; i < max; i++)
1875*6eef5f0cSAntonio Huete Jimenez 		if (job->outBuf[i] == '\0')
1876*6eef5f0cSAntonio Huete Jimenez 			job->outBuf[i] = ' ';
1877*6eef5f0cSAntonio Huete Jimenez 
1878*6eef5f0cSAntonio Huete Jimenez 	/* Look for the last newline in the bytes we just got. */
1879a34d5fb1SAntonio Huete Jimenez 	for (i = job->curPos + nr - 1;
1880a34d5fb1SAntonio Huete Jimenez 	     i >= job->curPos && i != (size_t)-1; i--) {
188101e196c8SJohn Marino 		if (job->outBuf[i] == '\n') {
1882*6eef5f0cSAntonio Huete Jimenez 			gotNL = true;
188301e196c8SJohn Marino 			break;
188401e196c8SJohn Marino 		}
188501e196c8SJohn Marino 	}
188601e196c8SJohn Marino 
188701e196c8SJohn Marino 	if (!gotNL) {
188801e196c8SJohn Marino 		job->curPos += nr;
188901e196c8SJohn Marino 		if (job->curPos == JOB_BUFSIZE) {
189001e196c8SJohn Marino 			/*
189101e196c8SJohn Marino 			 * If we've run out of buffer space, we have no choice
189201e196c8SJohn Marino 			 * but to print the stuff. sigh.
189301e196c8SJohn Marino 			 */
1894*6eef5f0cSAntonio Huete Jimenez 			fbuf = true;
189501e196c8SJohn Marino 			i = job->curPos;
189601e196c8SJohn Marino 		}
189701e196c8SJohn Marino 	}
189801e196c8SJohn Marino 	if (gotNL || fbuf) {
189901e196c8SJohn Marino 		/*
190001e196c8SJohn Marino 		 * Need to send the output to the screen. Null terminate it
190101e196c8SJohn Marino 		 * first, overwriting the newline character if there was one.
190201e196c8SJohn Marino 		 * So long as the line isn't one we should filter (according
190301e196c8SJohn Marino 		 * to the shell description), we print the line, preceded
190401e196c8SJohn Marino 		 * by a target banner if this target isn't the same as the
190501e196c8SJohn Marino 		 * one for which we last printed something.
190601e196c8SJohn Marino 		 * The rest of the data in the buffer are then shifted down
190701e196c8SJohn Marino 		 * to the start of the buffer and curPos is set accordingly.
190801e196c8SJohn Marino 		 */
190901e196c8SJohn Marino 		job->outBuf[i] = '\0';
191001e196c8SJohn Marino 		if (i >= job->curPos) {
191101e196c8SJohn Marino 			char *cp;
191201e196c8SJohn Marino 
1913*6eef5f0cSAntonio Huete Jimenez 			/*
1914*6eef5f0cSAntonio Huete Jimenez 			 * FIXME: SwitchOutputTo should be here, according to
1915*6eef5f0cSAntonio Huete Jimenez 			 * the comment above.  But since PrintOutput does not
1916*6eef5f0cSAntonio Huete Jimenez 			 * do anything in the default shell, this bug has gone
1917*6eef5f0cSAntonio Huete Jimenez 			 * unnoticed until now.
1918*6eef5f0cSAntonio Huete Jimenez 			 */
1919*6eef5f0cSAntonio Huete Jimenez 			cp = PrintFilteredOutput(job->outBuf, &job->outBuf[i]);
192001e196c8SJohn Marino 
192101e196c8SJohn Marino 			/*
1922*6eef5f0cSAntonio Huete Jimenez 			 * There's still more in the output buffer. This time,
1923a34d5fb1SAntonio Huete Jimenez 			 * though, we know there's no newline at the end, so
1924a34d5fb1SAntonio Huete Jimenez 			 * we add one of our own free will.
192501e196c8SJohn Marino 			 */
192601e196c8SJohn Marino 			if (*cp != '\0') {
1927*6eef5f0cSAntonio Huete Jimenez 				if (!opts.silent)
1928a34d5fb1SAntonio Huete Jimenez 					SwitchOutputTo(job->node);
192901e196c8SJohn Marino #ifdef USE_META
193001e196c8SJohn Marino 				if (useMeta) {
1931a34d5fb1SAntonio Huete Jimenez 					meta_job_output(job, cp,
1932a34d5fb1SAntonio Huete Jimenez 					    gotNL ? "\n" : "");
193301e196c8SJohn Marino 				}
193401e196c8SJohn Marino #endif
1935a34d5fb1SAntonio Huete Jimenez 				(void)fprintf(stdout, "%s%s", cp,
1936a34d5fb1SAntonio Huete Jimenez 				    gotNL ? "\n" : "");
193701e196c8SJohn Marino 				(void)fflush(stdout);
193801e196c8SJohn Marino 			}
193901e196c8SJohn Marino 		}
194001e196c8SJohn Marino 		/*
1941a34d5fb1SAntonio Huete Jimenez 		 * max is the last offset still in the buffer. Move any
1942a34d5fb1SAntonio Huete Jimenez 		 * remaining characters to the start of the buffer and
1943a34d5fb1SAntonio Huete Jimenez 		 * update the end marker curPos.
194401e196c8SJohn Marino 		 */
1945f445c897SJohn Marino 		if (i < max) {
1946a34d5fb1SAntonio Huete Jimenez 			(void)memmove(job->outBuf, &job->outBuf[i + 1],
1947a34d5fb1SAntonio Huete Jimenez 			    max - (i + 1));
1948f445c897SJohn Marino 			job->curPos = max - (i + 1);
1949f445c897SJohn Marino 		} else {
1950f445c897SJohn Marino 			assert(i == max);
195101e196c8SJohn Marino 			job->curPos = 0;
195201e196c8SJohn Marino 		}
195301e196c8SJohn Marino 	}
195401e196c8SJohn Marino 	if (finish) {
195501e196c8SJohn Marino 		/*
195601e196c8SJohn Marino 		 * If the finish flag is true, we must loop until we hit
195701e196c8SJohn Marino 		 * end-of-file on the pipe. This is guaranteed to happen
195801e196c8SJohn Marino 		 * eventually since the other end of the pipe is now closed
195901e196c8SJohn Marino 		 * (we closed it explicitly and the child has exited). When
1960*6eef5f0cSAntonio Huete Jimenez 		 * we do get an EOF, finish will be set false and we'll fall
196101e196c8SJohn Marino 		 * through and out.
196201e196c8SJohn Marino 		 */
1963a34d5fb1SAntonio Huete Jimenez 		goto again;
196401e196c8SJohn Marino 	}
196501e196c8SJohn Marino }
196601e196c8SJohn Marino 
196701e196c8SJohn Marino static void
JobRun(GNode * targ)196801e196c8SJohn Marino JobRun(GNode *targ)
196901e196c8SJohn Marino {
1970a34d5fb1SAntonio Huete Jimenez #if 0
197101e196c8SJohn Marino 	/*
1972a34d5fb1SAntonio Huete Jimenez 	 * Unfortunately it is too complicated to run .BEGIN, .END, and
1973a34d5fb1SAntonio Huete Jimenez 	 * .INTERRUPT job in the parallel job module.  As of 2020-09-25,
1974a34d5fb1SAntonio Huete Jimenez 	 * unit-tests/deptgt-end-jobs.mk hangs in an endless loop.
1975a34d5fb1SAntonio Huete Jimenez 	 *
1976a34d5fb1SAntonio Huete Jimenez 	 * Running these jobs in compat mode also guarantees that these
1977a34d5fb1SAntonio Huete Jimenez 	 * jobs do not overlap with other unrelated jobs.
197801e196c8SJohn Marino 	 */
1979a34d5fb1SAntonio Huete Jimenez 	GNodeList lst = LST_INIT;
1980a34d5fb1SAntonio Huete Jimenez 	Lst_Append(&lst, targ);
1981a34d5fb1SAntonio Huete Jimenez 	(void)Make_Run(&lst);
1982a34d5fb1SAntonio Huete Jimenez 	Lst_Done(&lst);
1983*6eef5f0cSAntonio Huete Jimenez 	JobStart(targ, true);
1984a34d5fb1SAntonio Huete Jimenez 	while (jobTokensRunning != 0) {
198501e196c8SJohn Marino 		Job_CatchOutput();
198601e196c8SJohn Marino 	}
198701e196c8SJohn Marino #else
198801e196c8SJohn Marino 	Compat_Make(targ, targ);
1989a34d5fb1SAntonio Huete Jimenez 	/* XXX: Replace with GNode_IsError(gn) */
199001e196c8SJohn Marino 	if (targ->made == ERROR) {
1991*6eef5f0cSAntonio Huete Jimenez 		PrintOnError(targ, "\n\nStop.\n");
199201e196c8SJohn Marino 		exit(1);
199301e196c8SJohn Marino 	}
199401e196c8SJohn Marino #endif
199501e196c8SJohn Marino }
199601e196c8SJohn Marino 
1997a34d5fb1SAntonio Huete Jimenez /*
199801e196c8SJohn Marino  * Handle the exit of a child. Called from Make_Make.
199901e196c8SJohn Marino  *
200001e196c8SJohn Marino  * The job descriptor is removed from the list of children.
200101e196c8SJohn Marino  *
200201e196c8SJohn Marino  * Notes:
200301e196c8SJohn Marino  *	We do waits, blocking or not, according to the wisdom of our
200401e196c8SJohn Marino  *	caller, until there are no more children to report. For each
200501e196c8SJohn Marino  *	job, call JobFinish to finish things off.
200601e196c8SJohn Marino  */
200701e196c8SJohn Marino void
Job_CatchChildren(void)200801e196c8SJohn Marino Job_CatchChildren(void)
200901e196c8SJohn Marino {
201001e196c8SJohn Marino 	int pid;		/* pid of dead child */
201101e196c8SJohn Marino 	WAIT_T status;		/* Exit/termination status */
201201e196c8SJohn Marino 
2013a34d5fb1SAntonio Huete Jimenez 	/* Don't even bother if we know there's no one around. */
201401e196c8SJohn Marino 	if (jobTokensRunning == 0)
201501e196c8SJohn Marino 		return;
201601e196c8SJohn Marino 
2017a34d5fb1SAntonio Huete Jimenez 	/* Have we received SIGCHLD since last call? */
2018a34d5fb1SAntonio Huete Jimenez 	if (caught_sigchld == 0)
2019a34d5fb1SAntonio Huete Jimenez 		return;
2020a34d5fb1SAntonio Huete Jimenez 	caught_sigchld = 0;
2021a34d5fb1SAntonio Huete Jimenez 
202201e196c8SJohn Marino 	while ((pid = waitpid((pid_t)-1, &status, WNOHANG | WUNTRACED)) > 0) {
2023a34d5fb1SAntonio Huete Jimenez 		DEBUG2(JOB, "Process %d exited/stopped status %x.\n",
2024a34d5fb1SAntonio Huete Jimenez 		    pid, WAIT_STATUS(status));
2025*6eef5f0cSAntonio Huete Jimenez 		JobReapChild(pid, status, true);
202601e196c8SJohn Marino 	}
202701e196c8SJohn Marino }
202801e196c8SJohn Marino 
202901e196c8SJohn Marino /*
203001e196c8SJohn Marino  * It is possible that wait[pid]() was called from elsewhere,
203101e196c8SJohn Marino  * this lets us reap jobs regardless.
203201e196c8SJohn Marino  */
203301e196c8SJohn Marino void
JobReapChild(pid_t pid,WAIT_T status,bool isJobs)2034*6eef5f0cSAntonio Huete Jimenez JobReapChild(pid_t pid, WAIT_T status, bool isJobs)
203501e196c8SJohn Marino {
203601e196c8SJohn Marino 	Job *job;		/* job descriptor for dead child */
203701e196c8SJohn Marino 
2038a34d5fb1SAntonio Huete Jimenez 	/* Don't even bother if we know there's no one around. */
203901e196c8SJohn Marino 	if (jobTokensRunning == 0)
204001e196c8SJohn Marino 		return;
204101e196c8SJohn Marino 
204201e196c8SJohn Marino 	job = JobFindPid(pid, JOB_ST_RUNNING, isJobs);
204301e196c8SJohn Marino 	if (job == NULL) {
204401e196c8SJohn Marino 		if (isJobs) {
204501e196c8SJohn Marino 			if (!lurking_children)
2046a34d5fb1SAntonio Huete Jimenez 				Error("Child (%d) status %x not in table?",
2047a34d5fb1SAntonio Huete Jimenez 				    pid, status);
204801e196c8SJohn Marino 		}
204901e196c8SJohn Marino 		return;		/* not ours */
205001e196c8SJohn Marino 	}
205101e196c8SJohn Marino 	if (WIFSTOPPED(status)) {
2052a34d5fb1SAntonio Huete Jimenez 		DEBUG2(JOB, "Process %d (%s) stopped.\n",
205301e196c8SJohn Marino 		    job->pid, job->node->name);
205401e196c8SJohn Marino 		if (!make_suspended) {
205501e196c8SJohn Marino 			switch (WSTOPSIG(status)) {
205601e196c8SJohn Marino 			case SIGTSTP:
2057a34d5fb1SAntonio Huete Jimenez 				(void)printf("*** [%s] Suspended\n",
2058a34d5fb1SAntonio Huete Jimenez 				    job->node->name);
205901e196c8SJohn Marino 				break;
206001e196c8SJohn Marino 			case SIGSTOP:
2061a34d5fb1SAntonio Huete Jimenez 				(void)printf("*** [%s] Stopped\n",
2062a34d5fb1SAntonio Huete Jimenez 				    job->node->name);
206301e196c8SJohn Marino 				break;
206401e196c8SJohn Marino 			default:
206501e196c8SJohn Marino 				(void)printf("*** [%s] Stopped -- signal %d\n",
206601e196c8SJohn Marino 				    job->node->name, WSTOPSIG(status));
206701e196c8SJohn Marino 			}
2068*6eef5f0cSAntonio Huete Jimenez 			job->suspended = true;
206901e196c8SJohn Marino 		}
207001e196c8SJohn Marino 		(void)fflush(stdout);
207101e196c8SJohn Marino 		return;
207201e196c8SJohn Marino 	}
207301e196c8SJohn Marino 
2074a34d5fb1SAntonio Huete Jimenez 	job->status = JOB_ST_FINISHED;
207501e196c8SJohn Marino 	job->exit_status = WAIT_STATUS(status);
207601e196c8SJohn Marino 
207701e196c8SJohn Marino 	JobFinish(job, status);
207801e196c8SJohn Marino }
207901e196c8SJohn Marino 
2080a34d5fb1SAntonio Huete Jimenez /*
2081a34d5fb1SAntonio Huete Jimenez  * Catch the output from our children, if we're using pipes do so. Otherwise
2082a34d5fb1SAntonio Huete Jimenez  * just block time until we get a signal(most likely a SIGCHLD) since there's
2083a34d5fb1SAntonio Huete Jimenez  * no point in just spinning when there's nothing to do and the reaping of a
2084a34d5fb1SAntonio Huete Jimenez  * child can wait for a while.
208501e196c8SJohn Marino  */
208601e196c8SJohn Marino void
Job_CatchOutput(void)208701e196c8SJohn Marino Job_CatchOutput(void)
208801e196c8SJohn Marino {
208901e196c8SJohn Marino 	int nready;
209001e196c8SJohn Marino 	Job *job;
2091a34d5fb1SAntonio Huete Jimenez 	unsigned int i;
209201e196c8SJohn Marino 
209301e196c8SJohn Marino 	(void)fflush(stdout);
209401e196c8SJohn Marino 
209501e196c8SJohn Marino 	/* The first fd in the list is the job token pipe */
20965f1e34d9SAlexandre Perrin 	do {
2097a34d5fb1SAntonio Huete Jimenez 		nready = poll(fds + 1 - wantToken, fdsLen - 1 + wantToken,
2098a34d5fb1SAntonio Huete Jimenez 		    POLL_MSEC);
20995f1e34d9SAlexandre Perrin 	} while (nready < 0 && errno == EINTR);
210001e196c8SJohn Marino 
21015f1e34d9SAlexandre Perrin 	if (nready < 0)
21025f1e34d9SAlexandre Perrin 		Punt("poll: %s", strerror(errno));
21035f1e34d9SAlexandre Perrin 
21045f1e34d9SAlexandre Perrin 	if (nready > 0 && readyfd(&childExitJob)) {
210501e196c8SJohn Marino 		char token = 0;
21065f1e34d9SAlexandre Perrin 		ssize_t count;
21075f1e34d9SAlexandre Perrin 		count = read(childExitJob.inPipe, &token, 1);
2108a34d5fb1SAntonio Huete Jimenez 		if (count == 1) {
210901e196c8SJohn Marino 			if (token == DO_JOB_RESUME[0])
2110a34d5fb1SAntonio Huete Jimenez 				/*
2111a34d5fb1SAntonio Huete Jimenez 				 * Complete relay requested from our SIGCONT
2112a34d5fb1SAntonio Huete Jimenez 				 * handler
2113a34d5fb1SAntonio Huete Jimenez 				 */
211401e196c8SJohn Marino 				JobRestartJobs();
2115a34d5fb1SAntonio Huete Jimenez 		} else if (count == 0)
2116a34d5fb1SAntonio Huete Jimenez 			Punt("unexpected eof on token pipe");
2117*6eef5f0cSAntonio Huete Jimenez 		else if (errno != EAGAIN)
2118a34d5fb1SAntonio Huete Jimenez 			Punt("token pipe read: %s", strerror(errno));
2119a34d5fb1SAntonio Huete Jimenez 		nready--;
212001e196c8SJohn Marino 	}
212101e196c8SJohn Marino 
21225f1e34d9SAlexandre Perrin 	Job_CatchChildren();
21235f1e34d9SAlexandre Perrin 	if (nready == 0)
212401e196c8SJohn Marino 		return;
212501e196c8SJohn Marino 
2126a34d5fb1SAntonio Huete Jimenez 	for (i = npseudojobs * nfds_per_job(); i < fdsLen; i++) {
2127a34d5fb1SAntonio Huete Jimenez 		if (fds[i].revents == 0)
212801e196c8SJohn Marino 			continue;
2129a34d5fb1SAntonio Huete Jimenez 		job = jobByFdIndex[i];
2130a34d5fb1SAntonio Huete Jimenez 		if (job->status == JOB_ST_RUNNING)
2131*6eef5f0cSAntonio Huete Jimenez 			CollectOutput(job, false);
2132ca58f742SDaniel Fojt #if defined(USE_FILEMON) && !defined(USE_FILEMON_DEV)
2133ca58f742SDaniel Fojt 		/*
2134ca58f742SDaniel Fojt 		 * With meta mode, we may have activity on the job's filemon
2135a34d5fb1SAntonio Huete Jimenez 		 * descriptor too, which at the moment is any pollfd other
2136a34d5fb1SAntonio Huete Jimenez 		 * than job->inPollfd.
2137ca58f742SDaniel Fojt 		 */
2138ca58f742SDaniel Fojt 		if (useMeta && job->inPollfd != &fds[i]) {
2139*6eef5f0cSAntonio Huete Jimenez 			if (meta_job_event(job) <= 0)
2140ca58f742SDaniel Fojt 				fds[i].events = 0;	/* never mind */
2141ca58f742SDaniel Fojt 		}
2142ca58f742SDaniel Fojt #endif
21435f1e34d9SAlexandre Perrin 		if (--nready == 0)
21445f1e34d9SAlexandre Perrin 			return;
214501e196c8SJohn Marino 	}
214601e196c8SJohn Marino }
214701e196c8SJohn Marino 
2148a34d5fb1SAntonio Huete Jimenez /*
2149a34d5fb1SAntonio Huete Jimenez  * Start the creation of a target. Basically a front-end for JobStart used by
2150a34d5fb1SAntonio Huete Jimenez  * the Make module.
215101e196c8SJohn Marino  */
215201e196c8SJohn Marino void
Job_Make(GNode * gn)215301e196c8SJohn Marino Job_Make(GNode *gn)
215401e196c8SJohn Marino {
2155*6eef5f0cSAntonio Huete Jimenez 	(void)JobStart(gn, false);
2156a34d5fb1SAntonio Huete Jimenez }
2157a34d5fb1SAntonio Huete Jimenez 
2158a34d5fb1SAntonio Huete Jimenez static void
InitShellNameAndPath(void)2159a34d5fb1SAntonio Huete Jimenez InitShellNameAndPath(void)
2160a34d5fb1SAntonio Huete Jimenez {
2161a34d5fb1SAntonio Huete Jimenez 	shellName = shell->name;
2162a34d5fb1SAntonio Huete Jimenez 
2163a34d5fb1SAntonio Huete Jimenez #ifdef DEFSHELL_CUSTOM
2164a34d5fb1SAntonio Huete Jimenez 	if (shellName[0] == '/') {
2165a34d5fb1SAntonio Huete Jimenez 		shellPath = shellName;
2166a34d5fb1SAntonio Huete Jimenez 		shellName = str_basename(shellPath);
2167a34d5fb1SAntonio Huete Jimenez 		return;
2168a34d5fb1SAntonio Huete Jimenez 	}
2169a34d5fb1SAntonio Huete Jimenez #endif
2170*6eef5f0cSAntonio Huete Jimenez #ifdef DEFSHELL_PATH
2171*6eef5f0cSAntonio Huete Jimenez 	shellPath = DEFSHELL_PATH;
2172*6eef5f0cSAntonio Huete Jimenez #else
2173a34d5fb1SAntonio Huete Jimenez 	shellPath = str_concat3(_PATH_DEFSHELLDIR, "/", shellName);
2174*6eef5f0cSAntonio Huete Jimenez #endif
217501e196c8SJohn Marino }
217601e196c8SJohn Marino 
217701e196c8SJohn Marino void
Shell_Init(void)217801e196c8SJohn Marino Shell_Init(void)
217901e196c8SJohn Marino {
2180a34d5fb1SAntonio Huete Jimenez 	if (shellPath == NULL)
2181a34d5fb1SAntonio Huete Jimenez 		InitShellNameAndPath();
2182a34d5fb1SAntonio Huete Jimenez 
2183a34d5fb1SAntonio Huete Jimenez 	Var_SetWithFlags(SCOPE_CMDLINE, ".SHELL", shellPath, VAR_SET_READONLY);
2184a34d5fb1SAntonio Huete Jimenez 	if (shell->errFlag == NULL)
2185a34d5fb1SAntonio Huete Jimenez 		shell->errFlag = "";
2186a34d5fb1SAntonio Huete Jimenez 	if (shell->echoFlag == NULL)
2187a34d5fb1SAntonio Huete Jimenez 		shell->echoFlag = "";
2188a34d5fb1SAntonio Huete Jimenez 	if (shell->hasErrCtl && shell->errFlag[0] != '\0') {
2189a34d5fb1SAntonio Huete Jimenez 		if (shellErrFlag != NULL &&
2190a34d5fb1SAntonio Huete Jimenez 		    strcmp(shell->errFlag, &shellErrFlag[1]) != 0) {
21915f1e34d9SAlexandre Perrin 			free(shellErrFlag);
21925f1e34d9SAlexandre Perrin 			shellErrFlag = NULL;
21935f1e34d9SAlexandre Perrin 		}
2194*6eef5f0cSAntonio Huete Jimenez 		if (shellErrFlag == NULL)
2195*6eef5f0cSAntonio Huete Jimenez 			shellErrFlag = str_concat2("-", shell->errFlag);
2196a34d5fb1SAntonio Huete Jimenez 	} else if (shellErrFlag != NULL) {
21975f1e34d9SAlexandre Perrin 		free(shellErrFlag);
21985f1e34d9SAlexandre Perrin 		shellErrFlag = NULL;
21995f1e34d9SAlexandre Perrin 	}
220001e196c8SJohn Marino }
220101e196c8SJohn Marino 
2202a34d5fb1SAntonio Huete Jimenez /*
2203a34d5fb1SAntonio Huete Jimenez  * Return the string literal that is used in the current command shell
220401e196c8SJohn Marino  * to produce a newline character.
220501e196c8SJohn Marino  */
220601e196c8SJohn Marino const char *
Shell_GetNewline(void)220701e196c8SJohn Marino Shell_GetNewline(void)
220801e196c8SJohn Marino {
2209a34d5fb1SAntonio Huete Jimenez 	return shell->newline;
221001e196c8SJohn Marino }
221101e196c8SJohn Marino 
221201e196c8SJohn Marino void
Job_SetPrefix(void)221301e196c8SJohn Marino Job_SetPrefix(void)
221401e196c8SJohn Marino {
2215a34d5fb1SAntonio Huete Jimenez 	if (targPrefix != NULL) {
221601e196c8SJohn Marino 		free(targPrefix);
2217a34d5fb1SAntonio Huete Jimenez 	} else if (!Var_Exists(SCOPE_GLOBAL, MAKE_JOB_PREFIX)) {
2218a34d5fb1SAntonio Huete Jimenez 		Global_Set(MAKE_JOB_PREFIX, "---");
221901e196c8SJohn Marino 	}
222001e196c8SJohn Marino 
2221a34d5fb1SAntonio Huete Jimenez 	(void)Var_Subst("${" MAKE_JOB_PREFIX "}",
2222a34d5fb1SAntonio Huete Jimenez 	    SCOPE_GLOBAL, VARE_WANTRES, &targPrefix);
2223a34d5fb1SAntonio Huete Jimenez 	/* TODO: handle errors */
222401e196c8SJohn Marino }
222501e196c8SJohn Marino 
2226a34d5fb1SAntonio Huete Jimenez static void
AddSig(int sig,SignalProc handler)2227a34d5fb1SAntonio Huete Jimenez AddSig(int sig, SignalProc handler)
2228a34d5fb1SAntonio Huete Jimenez {
2229a34d5fb1SAntonio Huete Jimenez 	if (bmake_signal(sig, SIG_IGN) != SIG_IGN) {
2230a34d5fb1SAntonio Huete Jimenez 		sigaddset(&caught_signals, sig);
2231a34d5fb1SAntonio Huete Jimenez 		(void)bmake_signal(sig, handler);
2232a34d5fb1SAntonio Huete Jimenez 	}
2233a34d5fb1SAntonio Huete Jimenez }
2234a34d5fb1SAntonio Huete Jimenez 
2235a34d5fb1SAntonio Huete Jimenez /* Initialize the process module. */
223601e196c8SJohn Marino void
Job_Init(void)223701e196c8SJohn Marino Job_Init(void)
223801e196c8SJohn Marino {
22395f1e34d9SAlexandre Perrin 	Job_SetPrefix();
224001e196c8SJohn Marino 	/* Allocate space for all the job info */
2241a34d5fb1SAntonio Huete Jimenez 	job_table = bmake_malloc((size_t)opts.maxJobs * sizeof *job_table);
2242a34d5fb1SAntonio Huete Jimenez 	memset(job_table, 0, (size_t)opts.maxJobs * sizeof *job_table);
2243a34d5fb1SAntonio Huete Jimenez 	job_table_end = job_table + opts.maxJobs;
224401e196c8SJohn Marino 	wantToken = 0;
2245a34d5fb1SAntonio Huete Jimenez 	caught_sigchld = 0;
224601e196c8SJohn Marino 
2247a34d5fb1SAntonio Huete Jimenez 	aborting = ABORT_NONE;
2248a34d5fb1SAntonio Huete Jimenez 	job_errors = 0;
224901e196c8SJohn Marino 
225001e196c8SJohn Marino 	/*
225101e196c8SJohn Marino 	 * There is a non-zero chance that we already have children.
225201e196c8SJohn Marino 	 * eg after 'make -f- <<EOF'
2253a34d5fb1SAntonio Huete Jimenez 	 * Since their termination causes a 'Child (pid) not in table'
2254a34d5fb1SAntonio Huete Jimenez 	 * message, Collect the status of any that are already dead, and
2255a34d5fb1SAntonio Huete Jimenez 	 * suppress the error message if there are any undead ones.
225601e196c8SJohn Marino 	 */
225701e196c8SJohn Marino 	for (;;) {
2258a34d5fb1SAntonio Huete Jimenez 		int rval;
2259a34d5fb1SAntonio Huete Jimenez 		WAIT_T status;
2260a34d5fb1SAntonio Huete Jimenez 
226101e196c8SJohn Marino 		rval = waitpid((pid_t)-1, &status, WNOHANG);
226201e196c8SJohn Marino 		if (rval > 0)
226301e196c8SJohn Marino 			continue;
226401e196c8SJohn Marino 		if (rval == 0)
2265*6eef5f0cSAntonio Huete Jimenez 			lurking_children = true;
226601e196c8SJohn Marino 		break;
226701e196c8SJohn Marino 	}
226801e196c8SJohn Marino 
226901e196c8SJohn Marino 	Shell_Init();
227001e196c8SJohn Marino 
227101e196c8SJohn Marino 	JobCreatePipe(&childExitJob, 3);
227201e196c8SJohn Marino 
2273a34d5fb1SAntonio Huete Jimenez 	{
2274ca58f742SDaniel Fojt 		/* Preallocate enough for the maximum number of jobs. */
2275a34d5fb1SAntonio Huete Jimenez 		size_t nfds = (npseudojobs + (size_t)opts.maxJobs) *
2276a34d5fb1SAntonio Huete Jimenez 			      nfds_per_job();
2277a34d5fb1SAntonio Huete Jimenez 		fds = bmake_malloc(sizeof *fds * nfds);
2278a34d5fb1SAntonio Huete Jimenez 		jobByFdIndex = bmake_malloc(sizeof *jobByFdIndex * nfds);
2279a34d5fb1SAntonio Huete Jimenez 	}
228001e196c8SJohn Marino 
228101e196c8SJohn Marino 	/* These are permanent entries and take slots 0 and 1 */
228201e196c8SJohn Marino 	watchfd(&tokenWaitJob);
228301e196c8SJohn Marino 	watchfd(&childExitJob);
228401e196c8SJohn Marino 
228501e196c8SJohn Marino 	sigemptyset(&caught_signals);
228601e196c8SJohn Marino 	/*
228701e196c8SJohn Marino 	 * Install a SIGCHLD handler.
228801e196c8SJohn Marino 	 */
228901e196c8SJohn Marino 	(void)bmake_signal(SIGCHLD, JobChildSig);
229001e196c8SJohn Marino 	sigaddset(&caught_signals, SIGCHLD);
229101e196c8SJohn Marino 
229201e196c8SJohn Marino 	/*
229301e196c8SJohn Marino 	 * Catch the four signals that POSIX specifies if they aren't ignored.
229401e196c8SJohn Marino 	 * JobPassSig will take care of calling JobInterrupt if appropriate.
229501e196c8SJohn Marino 	 */
2296a34d5fb1SAntonio Huete Jimenez 	AddSig(SIGINT, JobPassSig_int);
2297a34d5fb1SAntonio Huete Jimenez 	AddSig(SIGHUP, JobPassSig_term);
2298a34d5fb1SAntonio Huete Jimenez 	AddSig(SIGTERM, JobPassSig_term);
2299a34d5fb1SAntonio Huete Jimenez 	AddSig(SIGQUIT, JobPassSig_term);
230001e196c8SJohn Marino 
230101e196c8SJohn Marino 	/*
230201e196c8SJohn Marino 	 * There are additional signals that need to be caught and passed if
230301e196c8SJohn Marino 	 * either the export system wants to be told directly of signals or if
230401e196c8SJohn Marino 	 * we're giving each job its own process group (since then it won't get
230501e196c8SJohn Marino 	 * signals from the terminal driver as we own the terminal)
230601e196c8SJohn Marino 	 */
2307a34d5fb1SAntonio Huete Jimenez 	AddSig(SIGTSTP, JobPassSig_suspend);
2308a34d5fb1SAntonio Huete Jimenez 	AddSig(SIGTTOU, JobPassSig_suspend);
2309a34d5fb1SAntonio Huete Jimenez 	AddSig(SIGTTIN, JobPassSig_suspend);
2310a34d5fb1SAntonio Huete Jimenez 	AddSig(SIGWINCH, JobCondPassSig);
2311a34d5fb1SAntonio Huete Jimenez 	AddSig(SIGCONT, JobContinueSig);
231201e196c8SJohn Marino 
23135f1e34d9SAlexandre Perrin 	(void)Job_RunTarget(".BEGIN", NULL);
2314*6eef5f0cSAntonio Huete Jimenez 	/*
2315*6eef5f0cSAntonio Huete Jimenez 	 * Create the .END node now, even though no code in the unit tests
2316*6eef5f0cSAntonio Huete Jimenez 	 * depends on it.  See also Targ_GetEndNode in Compat_MakeAll.
2317*6eef5f0cSAntonio Huete Jimenez 	 */
2318a34d5fb1SAntonio Huete Jimenez 	(void)Targ_GetEndNode();
231901e196c8SJohn Marino }
232001e196c8SJohn Marino 
2321a34d5fb1SAntonio Huete Jimenez static void
DelSig(int sig)2322a34d5fb1SAntonio Huete Jimenez DelSig(int sig)
232301e196c8SJohn Marino {
2324a34d5fb1SAntonio Huete Jimenez 	if (sigismember(&caught_signals, sig) != 0)
2325a34d5fb1SAntonio Huete Jimenez 		(void)bmake_signal(sig, SIG_DFL);
232601e196c8SJohn Marino }
232701e196c8SJohn Marino 
2328a34d5fb1SAntonio Huete Jimenez static void
JobSigReset(void)2329a34d5fb1SAntonio Huete Jimenez JobSigReset(void)
2330a34d5fb1SAntonio Huete Jimenez {
2331a34d5fb1SAntonio Huete Jimenez 	DelSig(SIGINT);
2332a34d5fb1SAntonio Huete Jimenez 	DelSig(SIGHUP);
2333a34d5fb1SAntonio Huete Jimenez 	DelSig(SIGQUIT);
2334a34d5fb1SAntonio Huete Jimenez 	DelSig(SIGTERM);
2335a34d5fb1SAntonio Huete Jimenez 	DelSig(SIGTSTP);
2336a34d5fb1SAntonio Huete Jimenez 	DelSig(SIGTTOU);
2337a34d5fb1SAntonio Huete Jimenez 	DelSig(SIGTTIN);
2338a34d5fb1SAntonio Huete Jimenez 	DelSig(SIGWINCH);
2339a34d5fb1SAntonio Huete Jimenez 	DelSig(SIGCONT);
234001e196c8SJohn Marino 	(void)bmake_signal(SIGCHLD, SIG_DFL);
234101e196c8SJohn Marino }
234201e196c8SJohn Marino 
2343a34d5fb1SAntonio Huete Jimenez /* Find a shell in 'shells' given its name, or return NULL. */
234401e196c8SJohn Marino static Shell *
FindShellByName(const char * name)2345a34d5fb1SAntonio Huete Jimenez FindShellByName(const char *name)
234601e196c8SJohn Marino {
2347a34d5fb1SAntonio Huete Jimenez 	Shell *sh = shells;
2348a34d5fb1SAntonio Huete Jimenez 	const Shell *shellsEnd = sh + sizeof shells / sizeof shells[0];
234901e196c8SJohn Marino 
2350a34d5fb1SAntonio Huete Jimenez 	for (sh = shells; sh < shellsEnd; sh++) {
235101e196c8SJohn Marino 		if (strcmp(name, sh->name) == 0)
2352ca58f742SDaniel Fojt 			return sh;
235301e196c8SJohn Marino 	}
235401e196c8SJohn Marino 	return NULL;
235501e196c8SJohn Marino }
235601e196c8SJohn Marino 
2357a34d5fb1SAntonio Huete Jimenez /*
2358a34d5fb1SAntonio Huete Jimenez  * Parse a shell specification and set up 'shell', shellPath and
2359a34d5fb1SAntonio Huete Jimenez  * shellName appropriately.
236001e196c8SJohn Marino  *
236101e196c8SJohn Marino  * Input:
236201e196c8SJohn Marino  *	line		The shell spec
236301e196c8SJohn Marino  *
236401e196c8SJohn Marino  * Results:
2365*6eef5f0cSAntonio Huete Jimenez  *	false if the specification was incorrect.
236601e196c8SJohn Marino  *
236701e196c8SJohn Marino  * Side Effects:
2368a34d5fb1SAntonio Huete Jimenez  *	'shell' points to a Shell structure (either predefined or
236901e196c8SJohn Marino  *	created from the shell spec), shellPath is the full path of the
2370a34d5fb1SAntonio Huete Jimenez  *	shell described by 'shell', while shellName is just the
237101e196c8SJohn Marino  *	final component of shellPath.
237201e196c8SJohn Marino  *
237301e196c8SJohn Marino  * Notes:
237401e196c8SJohn Marino  *	A shell specification consists of a .SHELL target, with dependency
237501e196c8SJohn Marino  *	operator, followed by a series of blank-separated words. Double
237601e196c8SJohn Marino  *	quotes can be used to use blanks in words. A backslash escapes
237701e196c8SJohn Marino  *	anything (most notably a double-quote and a space) and
237801e196c8SJohn Marino  *	provides the functionality it does in C. Each word consists of
237901e196c8SJohn Marino  *	keyword and value separated by an equal sign. There should be no
238001e196c8SJohn Marino  *	unnecessary spaces in the word. The keywords are as follows:
238101e196c8SJohn Marino  *	    name	Name of shell.
238201e196c8SJohn Marino  *	    path	Location of shell.
238301e196c8SJohn Marino  *	    quiet	Command to turn off echoing.
238401e196c8SJohn Marino  *	    echo	Command to turn echoing on
238501e196c8SJohn Marino  *	    filter	Result of turning off echoing that shouldn't be
238601e196c8SJohn Marino  *			printed.
238701e196c8SJohn Marino  *	    echoFlag	Flag to turn echoing on at the start
238801e196c8SJohn Marino  *	    errFlag	Flag to turn error checking on at the start
238901e196c8SJohn Marino  *	    hasErrCtl	True if shell has error checking control
239001e196c8SJohn Marino  *	    newline	String literal to represent a newline char
239101e196c8SJohn Marino  *	    check	Command to turn on error checking if hasErrCtl
2392*6eef5f0cSAntonio Huete Jimenez  *			is true or template of command to echo a command
239301e196c8SJohn Marino  *			for which error checking is off if hasErrCtl is
2394*6eef5f0cSAntonio Huete Jimenez  *			false.
239501e196c8SJohn Marino  *	    ignore	Command to turn off error checking if hasErrCtl
2396*6eef5f0cSAntonio Huete Jimenez  *			is true or template of command to execute a
239701e196c8SJohn Marino  *			command so as to ignore any errors it returns if
2398*6eef5f0cSAntonio Huete Jimenez  *			hasErrCtl is false.
239901e196c8SJohn Marino  */
2400*6eef5f0cSAntonio Huete Jimenez bool
Job_ParseShell(char * line)240101e196c8SJohn Marino Job_ParseShell(char *line)
240201e196c8SJohn Marino {
2403a34d5fb1SAntonio Huete Jimenez 	Words wordsList;
240401e196c8SJohn Marino 	char **words;
240501e196c8SJohn Marino 	char **argv;
2406a34d5fb1SAntonio Huete Jimenez 	size_t argc;
240701e196c8SJohn Marino 	char *path;
240801e196c8SJohn Marino 	Shell newShell;
2409*6eef5f0cSAntonio Huete Jimenez 	bool fullSpec = false;
241001e196c8SJohn Marino 	Shell *sh;
241101e196c8SJohn Marino 
2412a34d5fb1SAntonio Huete Jimenez 	/* XXX: don't use line as an iterator variable */
2413a34d5fb1SAntonio Huete Jimenez 	pp_skip_whitespace(&line);
241401e196c8SJohn Marino 
2415a34d5fb1SAntonio Huete Jimenez 	free(shell_freeIt);
241601e196c8SJohn Marino 
2417a34d5fb1SAntonio Huete Jimenez 	memset(&newShell, 0, sizeof newShell);
241801e196c8SJohn Marino 
241901e196c8SJohn Marino 	/*
242001e196c8SJohn Marino 	 * Parse the specification by keyword
242101e196c8SJohn Marino 	 */
2422*6eef5f0cSAntonio Huete Jimenez 	wordsList = Str_Words(line, true);
2423a34d5fb1SAntonio Huete Jimenez 	words = wordsList.words;
2424a34d5fb1SAntonio Huete Jimenez 	argc = wordsList.len;
2425a34d5fb1SAntonio Huete Jimenez 	path = wordsList.freeIt;
242601e196c8SJohn Marino 	if (words == NULL) {
242701e196c8SJohn Marino 		Error("Unterminated quoted string [%s]", line);
2428*6eef5f0cSAntonio Huete Jimenez 		return false;
242901e196c8SJohn Marino 	}
2430a34d5fb1SAntonio Huete Jimenez 	shell_freeIt = path;
243101e196c8SJohn Marino 
243201e196c8SJohn Marino 	for (path = NULL, argv = words; argc != 0; argc--, argv++) {
2433a34d5fb1SAntonio Huete Jimenez 		char *arg = *argv;
2434a34d5fb1SAntonio Huete Jimenez 		if (strncmp(arg, "path=", 5) == 0) {
2435a34d5fb1SAntonio Huete Jimenez 			path = arg + 5;
2436a34d5fb1SAntonio Huete Jimenez 		} else if (strncmp(arg, "name=", 5) == 0) {
2437a34d5fb1SAntonio Huete Jimenez 			newShell.name = arg + 5;
243801e196c8SJohn Marino 		} else {
2439a34d5fb1SAntonio Huete Jimenez 			if (strncmp(arg, "quiet=", 6) == 0) {
2440a34d5fb1SAntonio Huete Jimenez 				newShell.echoOff = arg + 6;
2441a34d5fb1SAntonio Huete Jimenez 			} else if (strncmp(arg, "echo=", 5) == 0) {
2442a34d5fb1SAntonio Huete Jimenez 				newShell.echoOn = arg + 5;
2443a34d5fb1SAntonio Huete Jimenez 			} else if (strncmp(arg, "filter=", 7) == 0) {
2444a34d5fb1SAntonio Huete Jimenez 				newShell.noPrint = arg + 7;
2445a34d5fb1SAntonio Huete Jimenez 				newShell.noPrintLen = strlen(newShell.noPrint);
2446a34d5fb1SAntonio Huete Jimenez 			} else if (strncmp(arg, "echoFlag=", 9) == 0) {
2447a34d5fb1SAntonio Huete Jimenez 				newShell.echoFlag = arg + 9;
2448a34d5fb1SAntonio Huete Jimenez 			} else if (strncmp(arg, "errFlag=", 8) == 0) {
2449a34d5fb1SAntonio Huete Jimenez 				newShell.errFlag = arg + 8;
2450a34d5fb1SAntonio Huete Jimenez 			} else if (strncmp(arg, "hasErrCtl=", 10) == 0) {
2451a34d5fb1SAntonio Huete Jimenez 				char c = arg[10];
2452a34d5fb1SAntonio Huete Jimenez 				newShell.hasErrCtl = c == 'Y' || c == 'y' ||
2453a34d5fb1SAntonio Huete Jimenez 						     c == 'T' || c == 't';
2454a34d5fb1SAntonio Huete Jimenez 			} else if (strncmp(arg, "newline=", 8) == 0) {
2455a34d5fb1SAntonio Huete Jimenez 				newShell.newline = arg + 8;
2456a34d5fb1SAntonio Huete Jimenez 			} else if (strncmp(arg, "check=", 6) == 0) {
2457*6eef5f0cSAntonio Huete Jimenez 				/*
2458*6eef5f0cSAntonio Huete Jimenez 				 * Before 2020-12-10, these two variables had
2459*6eef5f0cSAntonio Huete Jimenez 				 * been a single variable.
2460*6eef5f0cSAntonio Huete Jimenez 				 */
2461a34d5fb1SAntonio Huete Jimenez 				newShell.errOn = arg + 6;
2462a34d5fb1SAntonio Huete Jimenez 				newShell.echoTmpl = arg + 6;
2463a34d5fb1SAntonio Huete Jimenez 			} else if (strncmp(arg, "ignore=", 7) == 0) {
2464*6eef5f0cSAntonio Huete Jimenez 				/*
2465*6eef5f0cSAntonio Huete Jimenez 				 * Before 2020-12-10, these two variables had
2466*6eef5f0cSAntonio Huete Jimenez 				 * been a single variable.
2467*6eef5f0cSAntonio Huete Jimenez 				 */
2468a34d5fb1SAntonio Huete Jimenez 				newShell.errOff = arg + 7;
2469a34d5fb1SAntonio Huete Jimenez 				newShell.runIgnTmpl = arg + 7;
2470a34d5fb1SAntonio Huete Jimenez 			} else if (strncmp(arg, "errout=", 7) == 0) {
2471a34d5fb1SAntonio Huete Jimenez 				newShell.runChkTmpl = arg + 7;
2472a34d5fb1SAntonio Huete Jimenez 			} else if (strncmp(arg, "comment=", 8) == 0) {
2473a34d5fb1SAntonio Huete Jimenez 				newShell.commentChar = arg[8];
247401e196c8SJohn Marino 			} else {
2475a34d5fb1SAntonio Huete Jimenez 				Parse_Error(PARSE_FATAL,
2476a34d5fb1SAntonio Huete Jimenez 				    "Unknown keyword \"%s\"", arg);
247701e196c8SJohn Marino 				free(words);
2478*6eef5f0cSAntonio Huete Jimenez 				return false;
247901e196c8SJohn Marino 			}
2480*6eef5f0cSAntonio Huete Jimenez 			fullSpec = true;
248101e196c8SJohn Marino 		}
248201e196c8SJohn Marino 	}
248301e196c8SJohn Marino 
248401e196c8SJohn Marino 	if (path == NULL) {
248501e196c8SJohn Marino 		/*
2486a34d5fb1SAntonio Huete Jimenez 		 * If no path was given, the user wants one of the
2487a34d5fb1SAntonio Huete Jimenez 		 * pre-defined shells, yes? So we find the one s/he wants
2488a34d5fb1SAntonio Huete Jimenez 		 * with the help of FindShellByName and set things up the
2489a34d5fb1SAntonio Huete Jimenez 		 * right way. shellPath will be set up by Shell_Init.
249001e196c8SJohn Marino 		 */
249101e196c8SJohn Marino 		if (newShell.name == NULL) {
2492a34d5fb1SAntonio Huete Jimenez 			Parse_Error(PARSE_FATAL,
2493a34d5fb1SAntonio Huete Jimenez 			    "Neither path nor name specified");
249401e196c8SJohn Marino 			free(words);
2495*6eef5f0cSAntonio Huete Jimenez 			return false;
249601e196c8SJohn Marino 		} else {
2497a34d5fb1SAntonio Huete Jimenez 			if ((sh = FindShellByName(newShell.name)) == NULL) {
2498a34d5fb1SAntonio Huete Jimenez 				Parse_Error(PARSE_WARNING,
2499a34d5fb1SAntonio Huete Jimenez 				    "%s: No matching shell", newShell.name);
250001e196c8SJohn Marino 				free(words);
2501*6eef5f0cSAntonio Huete Jimenez 				return false;
250201e196c8SJohn Marino 			}
2503a34d5fb1SAntonio Huete Jimenez 			shell = sh;
250401e196c8SJohn Marino 			shellName = newShell.name;
2505a34d5fb1SAntonio Huete Jimenez 			if (shellPath != NULL) {
2506a34d5fb1SAntonio Huete Jimenez 				/*
2507a34d5fb1SAntonio Huete Jimenez 				 * Shell_Init has already been called!
2508a34d5fb1SAntonio Huete Jimenez 				 * Do it again.
2509a34d5fb1SAntonio Huete Jimenez 				 */
251001e196c8SJohn Marino 				free(UNCONST(shellPath));
251101e196c8SJohn Marino 				shellPath = NULL;
251201e196c8SJohn Marino 				Shell_Init();
251301e196c8SJohn Marino 			}
251401e196c8SJohn Marino 		}
251501e196c8SJohn Marino 	} else {
251601e196c8SJohn Marino 		/*
2517a34d5fb1SAntonio Huete Jimenez 		 * The user provided a path. If s/he gave nothing else
2518*6eef5f0cSAntonio Huete Jimenez 		 * (fullSpec is false), try and find a matching shell in the
2519a34d5fb1SAntonio Huete Jimenez 		 * ones we know of. Else we just take the specification at
2520a34d5fb1SAntonio Huete Jimenez 		 * its word and copy it to a new location. In either case,
2521a34d5fb1SAntonio Huete Jimenez 		 * we need to record the path the user gave for the shell.
252201e196c8SJohn Marino 		 */
252301e196c8SJohn Marino 		shellPath = path;
252401e196c8SJohn Marino 		path = strrchr(path, '/');
252501e196c8SJohn Marino 		if (path == NULL) {
252601e196c8SJohn Marino 			path = UNCONST(shellPath);
252701e196c8SJohn Marino 		} else {
2528a34d5fb1SAntonio Huete Jimenez 			path++;
252901e196c8SJohn Marino 		}
253001e196c8SJohn Marino 		if (newShell.name != NULL) {
253101e196c8SJohn Marino 			shellName = newShell.name;
253201e196c8SJohn Marino 		} else {
253301e196c8SJohn Marino 			shellName = path;
253401e196c8SJohn Marino 		}
253501e196c8SJohn Marino 		if (!fullSpec) {
2536a34d5fb1SAntonio Huete Jimenez 			if ((sh = FindShellByName(shellName)) == NULL) {
2537a34d5fb1SAntonio Huete Jimenez 				Parse_Error(PARSE_WARNING,
2538a34d5fb1SAntonio Huete Jimenez 				    "%s: No matching shell", shellName);
253901e196c8SJohn Marino 				free(words);
2540*6eef5f0cSAntonio Huete Jimenez 				return false;
254101e196c8SJohn Marino 			}
2542a34d5fb1SAntonio Huete Jimenez 			shell = sh;
254301e196c8SJohn Marino 		} else {
2544a34d5fb1SAntonio Huete Jimenez 			shell = bmake_malloc(sizeof *shell);
2545a34d5fb1SAntonio Huete Jimenez 			*shell = newShell;
254601e196c8SJohn Marino 		}
25475f1e34d9SAlexandre Perrin 		/* this will take care of shellErrFlag */
25485f1e34d9SAlexandre Perrin 		Shell_Init();
254901e196c8SJohn Marino 	}
255001e196c8SJohn Marino 
2551a34d5fb1SAntonio Huete Jimenez 	if (shell->echoOn != NULL && shell->echoOff != NULL)
2552*6eef5f0cSAntonio Huete Jimenez 		shell->hasEchoCtl = true;
255301e196c8SJohn Marino 
2554a34d5fb1SAntonio Huete Jimenez 	if (!shell->hasErrCtl) {
2555a34d5fb1SAntonio Huete Jimenez 		if (shell->echoTmpl == NULL)
2556a34d5fb1SAntonio Huete Jimenez 			shell->echoTmpl = "";
2557a34d5fb1SAntonio Huete Jimenez 		if (shell->runIgnTmpl == NULL)
2558a34d5fb1SAntonio Huete Jimenez 			shell->runIgnTmpl = "%s\n";
255901e196c8SJohn Marino 	}
256001e196c8SJohn Marino 
256101e196c8SJohn Marino 	/*
2562a34d5fb1SAntonio Huete Jimenez 	 * Do not free up the words themselves, since they might be in use
2563a34d5fb1SAntonio Huete Jimenez 	 * by the shell specification.
256401e196c8SJohn Marino 	 */
256501e196c8SJohn Marino 	free(words);
2566*6eef5f0cSAntonio Huete Jimenez 	return true;
256701e196c8SJohn Marino }
256801e196c8SJohn Marino 
2569a34d5fb1SAntonio Huete Jimenez /*
257001e196c8SJohn Marino  * Handle the receipt of an interrupt.
257101e196c8SJohn Marino  *
2572a34d5fb1SAntonio Huete Jimenez  * All children are killed. Another job will be started if the .INTERRUPT
2573a34d5fb1SAntonio Huete Jimenez  * target is defined.
2574a34d5fb1SAntonio Huete Jimenez  *
257501e196c8SJohn Marino  * Input:
257601e196c8SJohn Marino  *	runINTERRUPT	Non-zero if commands for the .INTERRUPT target
257701e196c8SJohn Marino  *			should be executed
257801e196c8SJohn Marino  *	signo		signal received
257901e196c8SJohn Marino  */
258001e196c8SJohn Marino static void
JobInterrupt(bool runINTERRUPT,int signo)2581*6eef5f0cSAntonio Huete Jimenez JobInterrupt(bool runINTERRUPT, int signo)
258201e196c8SJohn Marino {
258301e196c8SJohn Marino 	Job *job;		/* job descriptor in that element */
258401e196c8SJohn Marino 	GNode *interrupt;	/* the node describing the .INTERRUPT target */
258501e196c8SJohn Marino 	sigset_t mask;
258601e196c8SJohn Marino 	GNode *gn;
258701e196c8SJohn Marino 
258801e196c8SJohn Marino 	aborting = ABORT_INTERRUPT;
258901e196c8SJohn Marino 
259001e196c8SJohn Marino 	JobSigLock(&mask);
259101e196c8SJohn Marino 
259201e196c8SJohn Marino 	for (job = job_table; job < job_table_end; job++) {
2593a34d5fb1SAntonio Huete Jimenez 		if (job->status != JOB_ST_RUNNING)
259401e196c8SJohn Marino 			continue;
259501e196c8SJohn Marino 
259601e196c8SJohn Marino 		gn = job->node;
259701e196c8SJohn Marino 
2598ca58f742SDaniel Fojt 		JobDeleteTarget(gn);
2599a34d5fb1SAntonio Huete Jimenez 		if (job->pid != 0) {
2600a34d5fb1SAntonio Huete Jimenez 			DEBUG2(JOB,
260101e196c8SJohn Marino 			    "JobInterrupt passing signal %d to child %d.\n",
260201e196c8SJohn Marino 			    signo, job->pid);
260301e196c8SJohn Marino 			KILLPG(job->pid, signo);
260401e196c8SJohn Marino 		}
260501e196c8SJohn Marino 	}
260601e196c8SJohn Marino 
260701e196c8SJohn Marino 	JobSigUnlock(&mask);
260801e196c8SJohn Marino 
2609*6eef5f0cSAntonio Huete Jimenez 	if (runINTERRUPT && !opts.touch) {
2610a34d5fb1SAntonio Huete Jimenez 		interrupt = Targ_FindNode(".INTERRUPT");
261101e196c8SJohn Marino 		if (interrupt != NULL) {
2612*6eef5f0cSAntonio Huete Jimenez 			opts.ignoreErrors = false;
261301e196c8SJohn Marino 			JobRun(interrupt);
261401e196c8SJohn Marino 		}
261501e196c8SJohn Marino 	}
2616a34d5fb1SAntonio Huete Jimenez 	Trace_Log(MAKEINTR, NULL);
2617a34d5fb1SAntonio Huete Jimenez 	exit(signo);		/* XXX: why signo? */
261801e196c8SJohn Marino }
261901e196c8SJohn Marino 
262001e196c8SJohn Marino /*
2621a34d5fb1SAntonio Huete Jimenez  * Do the final processing, i.e. run the commands attached to the .END target.
262201e196c8SJohn Marino  *
2623a34d5fb1SAntonio Huete Jimenez  * Return the number of errors reported.
262401e196c8SJohn Marino  */
262501e196c8SJohn Marino int
Job_Finish(void)262601e196c8SJohn Marino Job_Finish(void)
262701e196c8SJohn Marino {
2628a34d5fb1SAntonio Huete Jimenez 	GNode *endNode = Targ_GetEndNode();
2629a34d5fb1SAntonio Huete Jimenez 	if (!Lst_IsEmpty(&endNode->commands) ||
2630a34d5fb1SAntonio Huete Jimenez 	    !Lst_IsEmpty(&endNode->children)) {
2631a34d5fb1SAntonio Huete Jimenez 		if (job_errors != 0) {
263201e196c8SJohn Marino 			Error("Errors reported so .END ignored");
263301e196c8SJohn Marino 		} else {
2634a34d5fb1SAntonio Huete Jimenez 			JobRun(endNode);
263501e196c8SJohn Marino 		}
263601e196c8SJohn Marino 	}
2637a34d5fb1SAntonio Huete Jimenez 	return job_errors;
263801e196c8SJohn Marino }
263901e196c8SJohn Marino 
2640a34d5fb1SAntonio Huete Jimenez /* Clean up any memory used by the jobs module. */
264101e196c8SJohn Marino void
Job_End(void)264201e196c8SJohn Marino Job_End(void)
264301e196c8SJohn Marino {
264401e196c8SJohn Marino #ifdef CLEANUP
2645a34d5fb1SAntonio Huete Jimenez 	free(shell_freeIt);
264601e196c8SJohn Marino #endif
264701e196c8SJohn Marino }
264801e196c8SJohn Marino 
2649a34d5fb1SAntonio Huete Jimenez /*
2650a34d5fb1SAntonio Huete Jimenez  * Waits for all running jobs to finish and returns.
2651a34d5fb1SAntonio Huete Jimenez  * Sets 'aborting' to ABORT_WAIT to prevent other jobs from starting.
265201e196c8SJohn Marino  */
265301e196c8SJohn Marino void
Job_Wait(void)265401e196c8SJohn Marino Job_Wait(void)
265501e196c8SJohn Marino {
265601e196c8SJohn Marino 	aborting = ABORT_WAIT;
265701e196c8SJohn Marino 	while (jobTokensRunning != 0) {
265801e196c8SJohn Marino 		Job_CatchOutput();
265901e196c8SJohn Marino 	}
2660a34d5fb1SAntonio Huete Jimenez 	aborting = ABORT_NONE;
266101e196c8SJohn Marino }
266201e196c8SJohn Marino 
2663a34d5fb1SAntonio Huete Jimenez /*
266401e196c8SJohn Marino  * Abort all currently running jobs without handling output or anything.
2665a34d5fb1SAntonio Huete Jimenez  * This function is to be called only in the event of a major error.
2666a34d5fb1SAntonio Huete Jimenez  * Most definitely NOT to be called from JobInterrupt.
266701e196c8SJohn Marino  *
2668a34d5fb1SAntonio Huete Jimenez  * All children are killed, not just the firstborn.
266901e196c8SJohn Marino  */
267001e196c8SJohn Marino void
Job_AbortAll(void)267101e196c8SJohn Marino Job_AbortAll(void)
267201e196c8SJohn Marino {
267301e196c8SJohn Marino 	Job *job;		/* the job descriptor in that element */
267401e196c8SJohn Marino 	WAIT_T foo;
267501e196c8SJohn Marino 
267601e196c8SJohn Marino 	aborting = ABORT_ERROR;
267701e196c8SJohn Marino 
2678a34d5fb1SAntonio Huete Jimenez 	if (jobTokensRunning != 0) {
267901e196c8SJohn Marino 		for (job = job_table; job < job_table_end; job++) {
2680a34d5fb1SAntonio Huete Jimenez 			if (job->status != JOB_ST_RUNNING)
268101e196c8SJohn Marino 				continue;
268201e196c8SJohn Marino 			/*
2683a34d5fb1SAntonio Huete Jimenez 			 * kill the child process with increasingly drastic
2684a34d5fb1SAntonio Huete Jimenez 			 * signals to make darn sure it's dead.
268501e196c8SJohn Marino 			 */
268601e196c8SJohn Marino 			KILLPG(job->pid, SIGINT);
268701e196c8SJohn Marino 			KILLPG(job->pid, SIGKILL);
268801e196c8SJohn Marino 		}
268901e196c8SJohn Marino 	}
269001e196c8SJohn Marino 
269101e196c8SJohn Marino 	/*
269201e196c8SJohn Marino 	 * Catch as many children as want to report in at first, then give up
269301e196c8SJohn Marino 	 */
269401e196c8SJohn Marino 	while (waitpid((pid_t)-1, &foo, WNOHANG) > 0)
269501e196c8SJohn Marino 		continue;
269601e196c8SJohn Marino }
269701e196c8SJohn Marino 
2698a34d5fb1SAntonio Huete Jimenez /*
269901e196c8SJohn Marino  * Tries to restart stopped jobs if there are slots available.
270001e196c8SJohn Marino  * Called in process context in response to a SIGCONT.
270101e196c8SJohn Marino  */
270201e196c8SJohn Marino static void
JobRestartJobs(void)270301e196c8SJohn Marino JobRestartJobs(void)
270401e196c8SJohn Marino {
270501e196c8SJohn Marino 	Job *job;
270601e196c8SJohn Marino 
270701e196c8SJohn Marino 	for (job = job_table; job < job_table_end; job++) {
2708a34d5fb1SAntonio Huete Jimenez 		if (job->status == JOB_ST_RUNNING &&
2709a34d5fb1SAntonio Huete Jimenez 		    (make_suspended || job->suspended)) {
2710a34d5fb1SAntonio Huete Jimenez 			DEBUG1(JOB, "Restarting stopped job pid %d.\n",
271101e196c8SJohn Marino 			    job->pid);
2712a34d5fb1SAntonio Huete Jimenez 			if (job->suspended) {
2713a34d5fb1SAntonio Huete Jimenez 				(void)printf("*** [%s] Continued\n",
2714a34d5fb1SAntonio Huete Jimenez 				    job->node->name);
271501e196c8SJohn Marino 				(void)fflush(stdout);
271601e196c8SJohn Marino 			}
2717*6eef5f0cSAntonio Huete Jimenez 			job->suspended = false;
271801e196c8SJohn Marino 			if (KILLPG(job->pid, SIGCONT) != 0 && DEBUG(JOB)) {
2719a34d5fb1SAntonio Huete Jimenez 				debug_printf("Failed to send SIGCONT to %d\n",
2720a34d5fb1SAntonio Huete Jimenez 				    job->pid);
272101e196c8SJohn Marino 			}
272201e196c8SJohn Marino 		}
2723a34d5fb1SAntonio Huete Jimenez 		if (job->status == JOB_ST_FINISHED) {
2724a34d5fb1SAntonio Huete Jimenez 			/*
2725a34d5fb1SAntonio Huete Jimenez 			 * Job exit deferred after calling waitpid() in a
2726a34d5fb1SAntonio Huete Jimenez 			 * signal handler
2727a34d5fb1SAntonio Huete Jimenez 			 */
272801e196c8SJohn Marino 			JobFinish(job, job->exit_status);
272901e196c8SJohn Marino 		}
2730a34d5fb1SAntonio Huete Jimenez 	}
2731*6eef5f0cSAntonio Huete Jimenez 	make_suspended = false;
273201e196c8SJohn Marino }
273301e196c8SJohn Marino 
273401e196c8SJohn Marino static void
watchfd(Job * job)273501e196c8SJohn Marino watchfd(Job *job)
273601e196c8SJohn Marino {
273701e196c8SJohn Marino 	if (job->inPollfd != NULL)
273801e196c8SJohn Marino 		Punt("Watching watched job");
273901e196c8SJohn Marino 
2740a34d5fb1SAntonio Huete Jimenez 	fds[fdsLen].fd = job->inPipe;
2741a34d5fb1SAntonio Huete Jimenez 	fds[fdsLen].events = POLLIN;
2742a34d5fb1SAntonio Huete Jimenez 	jobByFdIndex[fdsLen] = job;
2743a34d5fb1SAntonio Huete Jimenez 	job->inPollfd = &fds[fdsLen];
2744a34d5fb1SAntonio Huete Jimenez 	fdsLen++;
2745ca58f742SDaniel Fojt #if defined(USE_FILEMON) && !defined(USE_FILEMON_DEV)
2746ca58f742SDaniel Fojt 	if (useMeta) {
2747a34d5fb1SAntonio Huete Jimenez 		fds[fdsLen].fd = meta_job_fd(job);
2748a34d5fb1SAntonio Huete Jimenez 		fds[fdsLen].events = fds[fdsLen].fd == -1 ? 0 : POLLIN;
2749a34d5fb1SAntonio Huete Jimenez 		jobByFdIndex[fdsLen] = job;
2750a34d5fb1SAntonio Huete Jimenez 		fdsLen++;
2751ca58f742SDaniel Fojt 	}
2752ca58f742SDaniel Fojt #endif
275301e196c8SJohn Marino }
275401e196c8SJohn Marino 
275501e196c8SJohn Marino static void
clearfd(Job * job)275601e196c8SJohn Marino clearfd(Job *job)
275701e196c8SJohn Marino {
2758a34d5fb1SAntonio Huete Jimenez 	size_t i;
275901e196c8SJohn Marino 	if (job->inPollfd == NULL)
276001e196c8SJohn Marino 		Punt("Unwatching unwatched job");
2761a34d5fb1SAntonio Huete Jimenez 	i = (size_t)(job->inPollfd - fds);
2762a34d5fb1SAntonio Huete Jimenez 	fdsLen--;
2763ca58f742SDaniel Fojt #if defined(USE_FILEMON) && !defined(USE_FILEMON_DEV)
2764ca58f742SDaniel Fojt 	if (useMeta) {
2765ca58f742SDaniel Fojt 		/*
2766ca58f742SDaniel Fojt 		 * Sanity check: there should be two fds per job, so the job's
2767ca58f742SDaniel Fojt 		 * pollfd number should be even.
2768ca58f742SDaniel Fojt 		 */
2769ca58f742SDaniel Fojt 		assert(nfds_per_job() == 2);
2770a34d5fb1SAntonio Huete Jimenez 		if (i % 2 != 0)
2771ca58f742SDaniel Fojt 			Punt("odd-numbered fd with meta");
2772a34d5fb1SAntonio Huete Jimenez 		fdsLen--;
2773ca58f742SDaniel Fojt 	}
2774ca58f742SDaniel Fojt #endif
277501e196c8SJohn Marino 	/*
277601e196c8SJohn Marino 	 * Move last job in table into hole made by dead job.
277701e196c8SJohn Marino 	 */
2778a34d5fb1SAntonio Huete Jimenez 	if (fdsLen != i) {
2779a34d5fb1SAntonio Huete Jimenez 		fds[i] = fds[fdsLen];
2780a34d5fb1SAntonio Huete Jimenez 		jobByFdIndex[i] = jobByFdIndex[fdsLen];
2781a34d5fb1SAntonio Huete Jimenez 		jobByFdIndex[i]->inPollfd = &fds[i];
2782ca58f742SDaniel Fojt #if defined(USE_FILEMON) && !defined(USE_FILEMON_DEV)
2783ca58f742SDaniel Fojt 		if (useMeta) {
2784a34d5fb1SAntonio Huete Jimenez 			fds[i + 1] = fds[fdsLen + 1];
2785a34d5fb1SAntonio Huete Jimenez 			jobByFdIndex[i + 1] = jobByFdIndex[fdsLen + 1];
2786ca58f742SDaniel Fojt 		}
2787ca58f742SDaniel Fojt #endif
278801e196c8SJohn Marino 	}
278901e196c8SJohn Marino 	job->inPollfd = NULL;
279001e196c8SJohn Marino }
279101e196c8SJohn Marino 
2792*6eef5f0cSAntonio Huete Jimenez static bool
readyfd(Job * job)279301e196c8SJohn Marino readyfd(Job *job)
279401e196c8SJohn Marino {
279501e196c8SJohn Marino 	if (job->inPollfd == NULL)
279601e196c8SJohn Marino 		Punt("Polling unwatched job");
279701e196c8SJohn Marino 	return (job->inPollfd->revents & POLLIN) != 0;
279801e196c8SJohn Marino }
279901e196c8SJohn Marino 
2800a34d5fb1SAntonio Huete Jimenez /*
2801a34d5fb1SAntonio Huete Jimenez  * Put a token (back) into the job pipe.
2802a34d5fb1SAntonio Huete Jimenez  * This allows a make process to start a build job.
280301e196c8SJohn Marino  */
280401e196c8SJohn Marino static void
JobTokenAdd(void)280501e196c8SJohn Marino JobTokenAdd(void)
280601e196c8SJohn Marino {
280701e196c8SJohn Marino 	char tok = JOB_TOKENS[aborting], tok1;
280801e196c8SJohn Marino 
280901e196c8SJohn Marino 	/* If we are depositing an error token flush everything else */
281001e196c8SJohn Marino 	while (tok != '+' && read(tokenWaitJob.inPipe, &tok1, 1) == 1)
281101e196c8SJohn Marino 		continue;
281201e196c8SJohn Marino 
2813a34d5fb1SAntonio Huete Jimenez 	DEBUG3(JOB, "(%d) aborting %d, deposit token %c\n",
2814a34d5fb1SAntonio Huete Jimenez 	    getpid(), aborting, tok);
28155f1e34d9SAlexandre Perrin 	while (write(tokenWaitJob.outPipe, &tok, 1) == -1 && errno == EAGAIN)
28165f1e34d9SAlexandre Perrin 		continue;
281701e196c8SJohn Marino }
281801e196c8SJohn Marino 
2819a34d5fb1SAntonio Huete Jimenez /* Get a temp file */
2820a34d5fb1SAntonio Huete Jimenez int
Job_TempFile(const char * pattern,char * tfile,size_t tfile_sz)2821a34d5fb1SAntonio Huete Jimenez Job_TempFile(const char *pattern, char *tfile, size_t tfile_sz)
2822a34d5fb1SAntonio Huete Jimenez {
2823a34d5fb1SAntonio Huete Jimenez 	int fd;
2824a34d5fb1SAntonio Huete Jimenez 	sigset_t mask;
282501e196c8SJohn Marino 
2826a34d5fb1SAntonio Huete Jimenez 	JobSigLock(&mask);
2827a34d5fb1SAntonio Huete Jimenez 	fd = mkTempFile(pattern, tfile, tfile_sz);
2828a34d5fb1SAntonio Huete Jimenez 	if (tfile != NULL && !DEBUG(SCRIPT))
2829a34d5fb1SAntonio Huete Jimenez 		unlink(tfile);
2830a34d5fb1SAntonio Huete Jimenez 	JobSigUnlock(&mask);
2831a34d5fb1SAntonio Huete Jimenez 
2832a34d5fb1SAntonio Huete Jimenez 	return fd;
2833a34d5fb1SAntonio Huete Jimenez }
2834a34d5fb1SAntonio Huete Jimenez 
2835a34d5fb1SAntonio Huete Jimenez /* Prep the job token pipe in the root make process. */
283601e196c8SJohn Marino void
Job_ServerStart(int max_tokens,int jp_0,int jp_1)283701e196c8SJohn Marino Job_ServerStart(int max_tokens, int jp_0, int jp_1)
283801e196c8SJohn Marino {
283901e196c8SJohn Marino 	int i;
284001e196c8SJohn Marino 	char jobarg[64];
284101e196c8SJohn Marino 
284201e196c8SJohn Marino 	if (jp_0 >= 0 && jp_1 >= 0) {
284301e196c8SJohn Marino 		/* Pipe passed in from parent */
284401e196c8SJohn Marino 		tokenWaitJob.inPipe = jp_0;
284501e196c8SJohn Marino 		tokenWaitJob.outPipe = jp_1;
2846f445c897SJohn Marino 		(void)fcntl(jp_0, F_SETFD, FD_CLOEXEC);
2847f445c897SJohn Marino 		(void)fcntl(jp_1, F_SETFD, FD_CLOEXEC);
284801e196c8SJohn Marino 		return;
284901e196c8SJohn Marino 	}
285001e196c8SJohn Marino 
285101e196c8SJohn Marino 	JobCreatePipe(&tokenWaitJob, 15);
285201e196c8SJohn Marino 
2853a34d5fb1SAntonio Huete Jimenez 	snprintf(jobarg, sizeof jobarg, "%d,%d",
285401e196c8SJohn Marino 	    tokenWaitJob.inPipe, tokenWaitJob.outPipe);
285501e196c8SJohn Marino 
2856a34d5fb1SAntonio Huete Jimenez 	Global_Append(MAKEFLAGS, "-J");
2857a34d5fb1SAntonio Huete Jimenez 	Global_Append(MAKEFLAGS, jobarg);
285801e196c8SJohn Marino 
285901e196c8SJohn Marino 	/*
286001e196c8SJohn Marino 	 * Preload the job pipe with one token per job, save the one
286101e196c8SJohn Marino 	 * "extra" token for the primary job.
286201e196c8SJohn Marino 	 *
286301e196c8SJohn Marino 	 * XXX should clip maxJobs against PIPE_BUF -- if max_tokens is
286401e196c8SJohn Marino 	 * larger than the write buffer size of the pipe, we will
286501e196c8SJohn Marino 	 * deadlock here.
286601e196c8SJohn Marino 	 */
286701e196c8SJohn Marino 	for (i = 1; i < max_tokens; i++)
286801e196c8SJohn Marino 		JobTokenAdd();
286901e196c8SJohn Marino }
287001e196c8SJohn Marino 
2871a34d5fb1SAntonio Huete Jimenez /* Return a withdrawn token to the pool. */
287201e196c8SJohn Marino void
Job_TokenReturn(void)287301e196c8SJohn Marino Job_TokenReturn(void)
287401e196c8SJohn Marino {
287501e196c8SJohn Marino 	jobTokensRunning--;
287601e196c8SJohn Marino 	if (jobTokensRunning < 0)
287701e196c8SJohn Marino 		Punt("token botch");
2878a34d5fb1SAntonio Huete Jimenez 	if (jobTokensRunning != 0 || JOB_TOKENS[aborting] != '+')
287901e196c8SJohn Marino 		JobTokenAdd();
288001e196c8SJohn Marino }
288101e196c8SJohn Marino 
2882a34d5fb1SAntonio Huete Jimenez /*
288301e196c8SJohn Marino  * Attempt to withdraw a token from the pool.
288401e196c8SJohn Marino  *
2885a34d5fb1SAntonio Huete Jimenez  * If pool is empty, set wantToken so that we wake up when a token is
2886a34d5fb1SAntonio Huete Jimenez  * released.
288701e196c8SJohn Marino  *
2888*6eef5f0cSAntonio Huete Jimenez  * Returns true if a token was withdrawn, and false if the pool is currently
2889a34d5fb1SAntonio Huete Jimenez  * empty.
289001e196c8SJohn Marino  */
2891*6eef5f0cSAntonio Huete Jimenez bool
Job_TokenWithdraw(void)289201e196c8SJohn Marino Job_TokenWithdraw(void)
289301e196c8SJohn Marino {
289401e196c8SJohn Marino 	char tok, tok1;
2895a34d5fb1SAntonio Huete Jimenez 	ssize_t count;
289601e196c8SJohn Marino 
289701e196c8SJohn Marino 	wantToken = 0;
2898a34d5fb1SAntonio Huete Jimenez 	DEBUG3(JOB, "Job_TokenWithdraw(%d): aborting %d, running %d\n",
289901e196c8SJohn Marino 	    getpid(), aborting, jobTokensRunning);
290001e196c8SJohn Marino 
2901a34d5fb1SAntonio Huete Jimenez 	if (aborting != ABORT_NONE || (jobTokensRunning >= opts.maxJobs))
2902*6eef5f0cSAntonio Huete Jimenez 		return false;
290301e196c8SJohn Marino 
290401e196c8SJohn Marino 	count = read(tokenWaitJob.inPipe, &tok, 1);
290501e196c8SJohn Marino 	if (count == 0)
290601e196c8SJohn Marino 		Fatal("eof on job pipe!");
290701e196c8SJohn Marino 	if (count < 0 && jobTokensRunning != 0) {
290801e196c8SJohn Marino 		if (errno != EAGAIN) {
290901e196c8SJohn Marino 			Fatal("job pipe read: %s", strerror(errno));
291001e196c8SJohn Marino 		}
2911a34d5fb1SAntonio Huete Jimenez 		DEBUG1(JOB, "(%d) blocked for token\n", getpid());
2912a34d5fb1SAntonio Huete Jimenez 		wantToken = 1;
2913*6eef5f0cSAntonio Huete Jimenez 		return false;
291401e196c8SJohn Marino 	}
291501e196c8SJohn Marino 
291601e196c8SJohn Marino 	if (count == 1 && tok != '+') {
2917a34d5fb1SAntonio Huete Jimenez 		/* make being aborted - remove any other job tokens */
2918a34d5fb1SAntonio Huete Jimenez 		DEBUG2(JOB, "(%d) aborted by token %c\n", getpid(), tok);
291901e196c8SJohn Marino 		while (read(tokenWaitJob.inPipe, &tok1, 1) == 1)
292001e196c8SJohn Marino 			continue;
292101e196c8SJohn Marino 		/* And put the stopper back */
2922a34d5fb1SAntonio Huete Jimenez 		while (write(tokenWaitJob.outPipe, &tok, 1) == -1 &&
2923a34d5fb1SAntonio Huete Jimenez 		       errno == EAGAIN)
29245f1e34d9SAlexandre Perrin 			continue;
2925a34d5fb1SAntonio Huete Jimenez 		if (shouldDieQuietly(NULL, 1))
2926a34d5fb1SAntonio Huete Jimenez 			exit(6);	/* we aborted */
2927a34d5fb1SAntonio Huete Jimenez 		Fatal("A failure has been detected "
2928a34d5fb1SAntonio Huete Jimenez 		      "in another branch of the parallel make");
292901e196c8SJohn Marino 	}
293001e196c8SJohn Marino 
293101e196c8SJohn Marino 	if (count == 1 && jobTokensRunning == 0)
293201e196c8SJohn Marino 		/* We didn't want the token really */
2933a34d5fb1SAntonio Huete Jimenez 		while (write(tokenWaitJob.outPipe, &tok, 1) == -1 &&
2934a34d5fb1SAntonio Huete Jimenez 		       errno == EAGAIN)
29355f1e34d9SAlexandre Perrin 			continue;
293601e196c8SJohn Marino 
293701e196c8SJohn Marino 	jobTokensRunning++;
2938a34d5fb1SAntonio Huete Jimenez 	DEBUG1(JOB, "(%d) withdrew token\n", getpid());
2939*6eef5f0cSAntonio Huete Jimenez 	return true;
294001e196c8SJohn Marino }
294101e196c8SJohn Marino 
2942a34d5fb1SAntonio Huete Jimenez /*
2943a34d5fb1SAntonio Huete Jimenez  * Run the named target if found. If a filename is specified, then set that
2944a34d5fb1SAntonio Huete Jimenez  * to the sources.
29455f1e34d9SAlexandre Perrin  *
2946a34d5fb1SAntonio Huete Jimenez  * Exits if the target fails.
29475f1e34d9SAlexandre Perrin  */
2948*6eef5f0cSAntonio Huete Jimenez bool
Job_RunTarget(const char * target,const char * fname)2949a34d5fb1SAntonio Huete Jimenez Job_RunTarget(const char *target, const char *fname)
2950a34d5fb1SAntonio Huete Jimenez {
2951a34d5fb1SAntonio Huete Jimenez 	GNode *gn = Targ_FindNode(target);
29525f1e34d9SAlexandre Perrin 	if (gn == NULL)
2953*6eef5f0cSAntonio Huete Jimenez 		return false;
29545f1e34d9SAlexandre Perrin 
2955a34d5fb1SAntonio Huete Jimenez 	if (fname != NULL)
2956a34d5fb1SAntonio Huete Jimenez 		Var_Set(gn, ALLSRC, fname);
29575f1e34d9SAlexandre Perrin 
29585f1e34d9SAlexandre Perrin 	JobRun(gn);
2959a34d5fb1SAntonio Huete Jimenez 	/* XXX: Replace with GNode_IsError(gn) */
29605f1e34d9SAlexandre Perrin 	if (gn->made == ERROR) {
2961*6eef5f0cSAntonio Huete Jimenez 		PrintOnError(gn, "\n\nStop.\n");
29625f1e34d9SAlexandre Perrin 		exit(1);
29635f1e34d9SAlexandre Perrin 	}
2964*6eef5f0cSAntonio Huete Jimenez 	return true;
29655f1e34d9SAlexandre Perrin }
29665f1e34d9SAlexandre Perrin 
296701e196c8SJohn Marino #ifdef USE_SELECT
296801e196c8SJohn Marino int
emul_poll(struct pollfd * fd,int nfd,int timeout)296901e196c8SJohn Marino emul_poll(struct pollfd *fd, int nfd, int timeout)
297001e196c8SJohn Marino {
297101e196c8SJohn Marino 	fd_set rfds, wfds;
297201e196c8SJohn Marino 	int i, maxfd, nselect, npoll;
297301e196c8SJohn Marino 	struct timeval tv, *tvp;
297401e196c8SJohn Marino 	long usecs;
297501e196c8SJohn Marino 
297601e196c8SJohn Marino 	FD_ZERO(&rfds);
297701e196c8SJohn Marino 	FD_ZERO(&wfds);
297801e196c8SJohn Marino 
297901e196c8SJohn Marino 	maxfd = -1;
298001e196c8SJohn Marino 	for (i = 0; i < nfd; i++) {
298101e196c8SJohn Marino 		fd[i].revents = 0;
298201e196c8SJohn Marino 
298301e196c8SJohn Marino 		if (fd[i].events & POLLIN)
298401e196c8SJohn Marino 			FD_SET(fd[i].fd, &rfds);
298501e196c8SJohn Marino 
298601e196c8SJohn Marino 		if (fd[i].events & POLLOUT)
298701e196c8SJohn Marino 			FD_SET(fd[i].fd, &wfds);
298801e196c8SJohn Marino 
298901e196c8SJohn Marino 		if (fd[i].fd > maxfd)
299001e196c8SJohn Marino 			maxfd = fd[i].fd;
299101e196c8SJohn Marino 	}
299201e196c8SJohn Marino 
299301e196c8SJohn Marino 	if (maxfd >= FD_SETSIZE) {
299401e196c8SJohn Marino 		Punt("Ran out of fd_set slots; "
299501e196c8SJohn Marino 		     "recompile with a larger FD_SETSIZE.");
299601e196c8SJohn Marino 	}
299701e196c8SJohn Marino 
299801e196c8SJohn Marino 	if (timeout < 0) {
299901e196c8SJohn Marino 		tvp = NULL;
300001e196c8SJohn Marino 	} else {
300101e196c8SJohn Marino 		usecs = timeout * 1000;
300201e196c8SJohn Marino 		tv.tv_sec = usecs / 1000000;
300301e196c8SJohn Marino 		tv.tv_usec = usecs % 1000000;
300401e196c8SJohn Marino 		tvp = &tv;
300501e196c8SJohn Marino 	}
300601e196c8SJohn Marino 
3007a34d5fb1SAntonio Huete Jimenez 	nselect = select(maxfd + 1, &rfds, &wfds, NULL, tvp);
300801e196c8SJohn Marino 
300901e196c8SJohn Marino 	if (nselect <= 0)
301001e196c8SJohn Marino 		return nselect;
301101e196c8SJohn Marino 
301201e196c8SJohn Marino 	npoll = 0;
301301e196c8SJohn Marino 	for (i = 0; i < nfd; i++) {
301401e196c8SJohn Marino 		if (FD_ISSET(fd[i].fd, &rfds))
301501e196c8SJohn Marino 			fd[i].revents |= POLLIN;
301601e196c8SJohn Marino 
301701e196c8SJohn Marino 		if (FD_ISSET(fd[i].fd, &wfds))
301801e196c8SJohn Marino 			fd[i].revents |= POLLOUT;
301901e196c8SJohn Marino 
302001e196c8SJohn Marino 		if (fd[i].revents)
302101e196c8SJohn Marino 			npoll++;
302201e196c8SJohn Marino 	}
302301e196c8SJohn Marino 
302401e196c8SJohn Marino 	return npoll;
302501e196c8SJohn Marino }
302601e196c8SJohn Marino #endif				/* USE_SELECT */
3027