xref: /minix3/usr.bin/make/job.c (revision 0a6a1f1d05b60e214de2f05a7310ddd1f0e590e7)
1*0a6a1f1dSLionel Sambuc /*	$NetBSD: job.c,v 1.180 2015/04/16 13:31:03 joerg Exp $	*/
22e2caf59SThomas Veerman 
32e2caf59SThomas Veerman /*
42e2caf59SThomas Veerman  * Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
52e2caf59SThomas Veerman  * All rights reserved.
62e2caf59SThomas Veerman  *
72e2caf59SThomas Veerman  * This code is derived from software contributed to Berkeley by
82e2caf59SThomas Veerman  * Adam de Boor.
92e2caf59SThomas Veerman  *
102e2caf59SThomas Veerman  * Redistribution and use in source and binary forms, with or without
112e2caf59SThomas Veerman  * modification, are permitted provided that the following conditions
122e2caf59SThomas Veerman  * are met:
132e2caf59SThomas Veerman  * 1. Redistributions of source code must retain the above copyright
142e2caf59SThomas Veerman  *    notice, this list of conditions and the following disclaimer.
152e2caf59SThomas Veerman  * 2. Redistributions in binary form must reproduce the above copyright
162e2caf59SThomas Veerman  *    notice, this list of conditions and the following disclaimer in the
172e2caf59SThomas Veerman  *    documentation and/or other materials provided with the distribution.
182e2caf59SThomas Veerman  * 3. Neither the name of the University nor the names of its contributors
192e2caf59SThomas Veerman  *    may be used to endorse or promote products derived from this software
202e2caf59SThomas Veerman  *    without specific prior written permission.
212e2caf59SThomas Veerman  *
222e2caf59SThomas Veerman  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
232e2caf59SThomas Veerman  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
242e2caf59SThomas Veerman  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
252e2caf59SThomas Veerman  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
262e2caf59SThomas Veerman  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
272e2caf59SThomas Veerman  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
282e2caf59SThomas Veerman  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
292e2caf59SThomas Veerman  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
302e2caf59SThomas Veerman  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
312e2caf59SThomas Veerman  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
322e2caf59SThomas Veerman  * SUCH DAMAGE.
332e2caf59SThomas Veerman  */
342e2caf59SThomas Veerman 
352e2caf59SThomas Veerman /*
362e2caf59SThomas Veerman  * Copyright (c) 1988, 1989 by Adam de Boor
372e2caf59SThomas Veerman  * Copyright (c) 1989 by Berkeley Softworks
382e2caf59SThomas Veerman  * All rights reserved.
392e2caf59SThomas Veerman  *
402e2caf59SThomas Veerman  * This code is derived from software contributed to Berkeley by
412e2caf59SThomas Veerman  * Adam de Boor.
422e2caf59SThomas Veerman  *
432e2caf59SThomas Veerman  * Redistribution and use in source and binary forms, with or without
442e2caf59SThomas Veerman  * modification, are permitted provided that the following conditions
452e2caf59SThomas Veerman  * are met:
462e2caf59SThomas Veerman  * 1. Redistributions of source code must retain the above copyright
472e2caf59SThomas Veerman  *    notice, this list of conditions and the following disclaimer.
482e2caf59SThomas Veerman  * 2. Redistributions in binary form must reproduce the above copyright
492e2caf59SThomas Veerman  *    notice, this list of conditions and the following disclaimer in the
502e2caf59SThomas Veerman  *    documentation and/or other materials provided with the distribution.
512e2caf59SThomas Veerman  * 3. All advertising materials mentioning features or use of this software
522e2caf59SThomas Veerman  *    must display the following acknowledgement:
532e2caf59SThomas Veerman  *	This product includes software developed by the University of
542e2caf59SThomas Veerman  *	California, Berkeley and its contributors.
552e2caf59SThomas Veerman  * 4. Neither the name of the University nor the names of its contributors
562e2caf59SThomas Veerman  *    may be used to endorse or promote products derived from this software
572e2caf59SThomas Veerman  *    without specific prior written permission.
582e2caf59SThomas Veerman  *
592e2caf59SThomas Veerman  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
602e2caf59SThomas Veerman  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
612e2caf59SThomas Veerman  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
622e2caf59SThomas Veerman  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
632e2caf59SThomas Veerman  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
642e2caf59SThomas Veerman  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
652e2caf59SThomas Veerman  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
662e2caf59SThomas Veerman  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
672e2caf59SThomas Veerman  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
682e2caf59SThomas Veerman  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
692e2caf59SThomas Veerman  * SUCH DAMAGE.
702e2caf59SThomas Veerman  */
712e2caf59SThomas Veerman 
722e2caf59SThomas Veerman #ifndef MAKE_NATIVE
73*0a6a1f1dSLionel Sambuc static char rcsid[] = "$NetBSD: job.c,v 1.180 2015/04/16 13:31:03 joerg Exp $";
742e2caf59SThomas Veerman #else
752e2caf59SThomas Veerman #include <sys/cdefs.h>
762e2caf59SThomas Veerman #ifndef lint
772e2caf59SThomas Veerman #if 0
782e2caf59SThomas Veerman static char sccsid[] = "@(#)job.c	8.2 (Berkeley) 3/19/94";
792e2caf59SThomas Veerman #else
80*0a6a1f1dSLionel Sambuc __RCSID("$NetBSD: job.c,v 1.180 2015/04/16 13:31:03 joerg Exp $");
812e2caf59SThomas Veerman #endif
822e2caf59SThomas Veerman #endif /* not lint */
832e2caf59SThomas Veerman #endif
842e2caf59SThomas Veerman 
852e2caf59SThomas Veerman /*-
862e2caf59SThomas Veerman  * job.c --
872e2caf59SThomas Veerman  *	handle the creation etc. of our child processes.
882e2caf59SThomas Veerman  *
892e2caf59SThomas Veerman  * Interface:
902e2caf59SThomas Veerman  *	Job_Make  	    	Start the creation of the given target.
912e2caf59SThomas Veerman  *
922e2caf59SThomas Veerman  *	Job_CatchChildren   	Check for and handle the termination of any
932e2caf59SThomas Veerman  *	    	  	    	children. This must be called reasonably
942e2caf59SThomas Veerman  *	    	  	    	frequently to keep the whole make going at
952e2caf59SThomas Veerman  *	    	  	    	a decent clip, since job table entries aren't
962e2caf59SThomas Veerman  *	    	  	    	removed until their process is caught this way.
972e2caf59SThomas Veerman  *
982e2caf59SThomas Veerman  *	Job_CatchOutput	    	Print any output our children have produced.
992e2caf59SThomas Veerman  *	    	  	    	Should also be called fairly frequently to
1002e2caf59SThomas Veerman  *	    	  	    	keep the user informed of what's going on.
1012e2caf59SThomas Veerman  *	    	  	    	If no output is waiting, it will block for
1022e2caf59SThomas Veerman  *	    	  	    	a time given by the SEL_* constants, below,
1032e2caf59SThomas Veerman  *	    	  	    	or until output is ready.
1042e2caf59SThomas Veerman  *
1052e2caf59SThomas Veerman  *	Job_Init  	    	Called to intialize this module. in addition,
1062e2caf59SThomas Veerman  *	    	  	    	any commands attached to the .BEGIN target
1072e2caf59SThomas Veerman  *	    	  	    	are executed before this function returns.
1082e2caf59SThomas Veerman  *	    	  	    	Hence, the makefile must have been parsed
1092e2caf59SThomas Veerman  *	    	  	    	before this function is called.
1102e2caf59SThomas Veerman  *
1112e2caf59SThomas Veerman  *	Job_End  	    	Cleanup any memory used.
1122e2caf59SThomas Veerman  *
1132e2caf59SThomas Veerman  *	Job_ParseShell	    	Given the line following a .SHELL target, parse
1142e2caf59SThomas Veerman  *	    	  	    	the line as a shell specification. Returns
1152e2caf59SThomas Veerman  *	    	  	    	FAILURE if the spec was incorrect.
1162e2caf59SThomas Veerman  *
1172e2caf59SThomas Veerman  *	Job_Finish	    	Perform any final processing which needs doing.
1182e2caf59SThomas Veerman  *	    	  	    	This includes the execution of any commands
1192e2caf59SThomas Veerman  *	    	  	    	which have been/were attached to the .END
1202e2caf59SThomas Veerman  *	    	  	    	target. It should only be called when the
1212e2caf59SThomas Veerman  *	    	  	    	job table is empty.
1222e2caf59SThomas Veerman  *
1232e2caf59SThomas Veerman  *	Job_AbortAll	    	Abort all currently running jobs. It doesn't
1242e2caf59SThomas Veerman  *	    	  	    	handle output or do anything for the jobs,
1252e2caf59SThomas Veerman  *	    	  	    	just kills them. It should only be called in
1262e2caf59SThomas Veerman  *	    	  	    	an emergency, as it were.
1272e2caf59SThomas Veerman  *
1282e2caf59SThomas Veerman  *	Job_CheckCommands   	Verify that the commands for a target are
1292e2caf59SThomas Veerman  *	    	  	    	ok. Provide them if necessary and possible.
1302e2caf59SThomas Veerman  *
1312e2caf59SThomas Veerman  *	Job_Touch 	    	Update a target without really updating it.
1322e2caf59SThomas Veerman  *
1332e2caf59SThomas Veerman  *	Job_Wait  	    	Wait for all currently-running jobs to finish.
1342e2caf59SThomas Veerman  */
1352e2caf59SThomas Veerman 
1362e2caf59SThomas Veerman #include <sys/types.h>
1372e2caf59SThomas Veerman #include <sys/stat.h>
1382e2caf59SThomas Veerman #include <sys/file.h>
1392e2caf59SThomas Veerman #include <sys/time.h>
1402e2caf59SThomas Veerman #include <sys/wait.h>
1412e2caf59SThomas Veerman 
14284d9c625SLionel Sambuc #include <assert.h>
1432e2caf59SThomas Veerman #include <errno.h>
1442e2caf59SThomas Veerman #include <fcntl.h>
1452e2caf59SThomas Veerman #ifndef USE_SELECT
1462e2caf59SThomas Veerman #include <poll.h>
1472e2caf59SThomas Veerman #endif
1482e2caf59SThomas Veerman #include <signal.h>
1492e2caf59SThomas Veerman #include <stdio.h>
1502e2caf59SThomas Veerman #include <string.h>
1512e2caf59SThomas Veerman #include <utime.h>
1522e2caf59SThomas Veerman 
1532e2caf59SThomas Veerman #include "make.h"
1542e2caf59SThomas Veerman #include "hash.h"
1552e2caf59SThomas Veerman #include "dir.h"
1562e2caf59SThomas Veerman #include "job.h"
1572e2caf59SThomas Veerman #include "pathnames.h"
1582e2caf59SThomas Veerman #include "trace.h"
1592e2caf59SThomas Veerman # define STATIC static
1602e2caf59SThomas Veerman 
1612e2caf59SThomas Veerman /*
1622e2caf59SThomas Veerman  * error handling variables
1632e2caf59SThomas Veerman  */
1642e2caf59SThomas Veerman static int     	errors = 0;	    /* number of errors reported */
1652e2caf59SThomas Veerman static int    	aborting = 0;	    /* why is the make aborting? */
1662e2caf59SThomas Veerman #define ABORT_ERROR	1   	    /* Because of an error */
1672e2caf59SThomas Veerman #define ABORT_INTERRUPT	2   	    /* Because it was interrupted */
1682e2caf59SThomas Veerman #define ABORT_WAIT	3   	    /* Waiting for jobs to finish */
1692e2caf59SThomas Veerman #define JOB_TOKENS	"+EI+"	    /* Token to requeue for each abort state */
1702e2caf59SThomas Veerman 
1712e2caf59SThomas Veerman /*
1722e2caf59SThomas Veerman  * this tracks the number of tokens currently "out" to build jobs.
1732e2caf59SThomas Veerman  */
1742e2caf59SThomas Veerman int jobTokensRunning = 0;
1752e2caf59SThomas Veerman int not_parallel = 0;		    /* set if .NOT_PARALLEL */
1762e2caf59SThomas Veerman 
1772e2caf59SThomas Veerman /*
1782e2caf59SThomas Veerman  * XXX: Avoid SunOS bug... FILENO() is fp->_file, and file
1792e2caf59SThomas Veerman  * is a char! So when we go above 127 we turn negative!
1802e2caf59SThomas Veerman  */
1812e2caf59SThomas Veerman #define FILENO(a) ((unsigned) fileno(a))
1822e2caf59SThomas Veerman 
1832e2caf59SThomas Veerman /*
1842e2caf59SThomas Veerman  * post-make command processing. The node postCommands is really just the
1852e2caf59SThomas Veerman  * .END target but we keep it around to avoid having to search for it
1862e2caf59SThomas Veerman  * all the time.
1872e2caf59SThomas Veerman  */
1882e2caf59SThomas Veerman static GNode   	  *postCommands = NULL;
1892e2caf59SThomas Veerman 				    /* node containing commands to execute when
1902e2caf59SThomas Veerman 				     * everything else is done */
1912e2caf59SThomas Veerman static int     	  numCommands; 	    /* The number of commands actually printed
1922e2caf59SThomas Veerman 				     * for a target. Should this number be
1932e2caf59SThomas Veerman 				     * 0, no shell will be executed. */
1942e2caf59SThomas Veerman 
1952e2caf59SThomas Veerman /*
1962e2caf59SThomas Veerman  * Return values from JobStart.
1972e2caf59SThomas Veerman  */
1982e2caf59SThomas Veerman #define JOB_RUNNING	0   	/* Job is running */
1992e2caf59SThomas Veerman #define JOB_ERROR 	1   	/* Error in starting the job */
2002e2caf59SThomas Veerman #define JOB_FINISHED	2   	/* The job is already finished */
2012e2caf59SThomas Veerman 
2022e2caf59SThomas Veerman /*
2032e2caf59SThomas Veerman  * Descriptions for various shells.
2042e2caf59SThomas Veerman  *
2052e2caf59SThomas Veerman  * The build environment may set DEFSHELL_INDEX to one of
2062e2caf59SThomas Veerman  * DEFSHELL_INDEX_SH, DEFSHELL_INDEX_KSH, or DEFSHELL_INDEX_CSH, to
2072e2caf59SThomas Veerman  * select one of the prefedined shells as the default shell.
2082e2caf59SThomas Veerman  *
2092e2caf59SThomas Veerman  * Alternatively, the build environment may set DEFSHELL_CUSTOM to the
2102e2caf59SThomas Veerman  * name or the full path of a sh-compatible shell, which will be used as
2112e2caf59SThomas Veerman  * the default shell.
2122e2caf59SThomas Veerman  *
2132e2caf59SThomas Veerman  * ".SHELL" lines in Makefiles can choose the default shell from the
2142e2caf59SThomas Veerman  # set defined here, or add additional shells.
2152e2caf59SThomas Veerman  */
2162e2caf59SThomas Veerman 
2172e2caf59SThomas Veerman #ifdef DEFSHELL_CUSTOM
2182e2caf59SThomas Veerman #define DEFSHELL_INDEX_CUSTOM 0
2192e2caf59SThomas Veerman #define DEFSHELL_INDEX_SH     1
2202e2caf59SThomas Veerman #define DEFSHELL_INDEX_KSH    2
2212e2caf59SThomas Veerman #define DEFSHELL_INDEX_CSH    3
2222e2caf59SThomas Veerman #else /* !DEFSHELL_CUSTOM */
2232e2caf59SThomas Veerman #define DEFSHELL_INDEX_SH     0
2242e2caf59SThomas Veerman #define DEFSHELL_INDEX_KSH    1
2252e2caf59SThomas Veerman #define DEFSHELL_INDEX_CSH    2
2262e2caf59SThomas Veerman #endif /* !DEFSHELL_CUSTOM */
2272e2caf59SThomas Veerman 
2282e2caf59SThomas Veerman #ifndef DEFSHELL_INDEX
2292e2caf59SThomas Veerman #define DEFSHELL_INDEX 0	/* DEFSHELL_INDEX_CUSTOM or DEFSHELL_INDEX_SH */
2302e2caf59SThomas Veerman #endif /* !DEFSHELL_INDEX */
2312e2caf59SThomas Veerman 
2322e2caf59SThomas Veerman static Shell    shells[] = {
2332e2caf59SThomas Veerman #ifdef DEFSHELL_CUSTOM
2342e2caf59SThomas Veerman     /*
2352e2caf59SThomas Veerman      * An sh-compatible shell with a non-standard name.
2362e2caf59SThomas Veerman      *
2372e2caf59SThomas Veerman      * Keep this in sync with the "sh" description below, but avoid
2382e2caf59SThomas Veerman      * non-portable features that might not be supplied by all
2392e2caf59SThomas Veerman      * sh-compatible shells.
2402e2caf59SThomas Veerman      */
2412e2caf59SThomas Veerman {
2422e2caf59SThomas Veerman     DEFSHELL_CUSTOM,
2432e2caf59SThomas Veerman     FALSE, "", "", "", 0,
2442e2caf59SThomas Veerman     FALSE, "echo \"%s\"\n", "%s\n", "{ %s \n} || exit $?\n", "'\n'", '#',
2452e2caf59SThomas Veerman     "",
2462e2caf59SThomas Veerman     "",
2472e2caf59SThomas Veerman },
2482e2caf59SThomas Veerman #endif /* DEFSHELL_CUSTOM */
2492e2caf59SThomas Veerman     /*
2502e2caf59SThomas Veerman      * SH description. Echo control is also possible and, under
2512e2caf59SThomas Veerman      * sun UNIX anyway, one can even control error checking.
2522e2caf59SThomas Veerman      */
2532e2caf59SThomas Veerman {
2542e2caf59SThomas Veerman     "sh",
2552e2caf59SThomas Veerman     FALSE, "", "", "", 0,
2562e2caf59SThomas Veerman     FALSE, "echo \"%s\"\n", "%s\n", "{ %s \n} || exit $?\n", "'\n'", '#',
2572e2caf59SThomas Veerman #if defined(MAKE_NATIVE) && defined(__NetBSD__)
2582e2caf59SThomas Veerman     "q",
2592e2caf59SThomas Veerman #else
2602e2caf59SThomas Veerman     "",
2612e2caf59SThomas Veerman #endif
2622e2caf59SThomas Veerman     "",
2632e2caf59SThomas Veerman },
2642e2caf59SThomas Veerman     /*
2652e2caf59SThomas Veerman      * KSH description.
2662e2caf59SThomas Veerman      */
2672e2caf59SThomas Veerman {
2682e2caf59SThomas Veerman     "ksh",
2692e2caf59SThomas Veerman     TRUE, "set +v", "set -v", "set +v", 6,
2702e2caf59SThomas Veerman     FALSE, "echo \"%s\"\n", "%s\n", "{ %s \n} || exit $?\n", "'\n'", '#',
2712e2caf59SThomas Veerman     "v",
2722e2caf59SThomas Veerman     "",
2732e2caf59SThomas Veerman },
2742e2caf59SThomas Veerman     /*
2752e2caf59SThomas Veerman      * CSH description. The csh can do echo control by playing
2762e2caf59SThomas Veerman      * with the setting of the 'echo' shell variable. Sadly,
2772e2caf59SThomas Veerman      * however, it is unable to do error control nicely.
2782e2caf59SThomas Veerman      */
2792e2caf59SThomas Veerman {
2802e2caf59SThomas Veerman     "csh",
2812e2caf59SThomas Veerman     TRUE, "unset verbose", "set verbose", "unset verbose", 10,
2822e2caf59SThomas Veerman     FALSE, "echo \"%s\"\n", "csh -c \"%s || exit 0\"\n", "", "'\\\n'", '#',
2832e2caf59SThomas Veerman     "v", "e",
2842e2caf59SThomas Veerman },
2852e2caf59SThomas Veerman     /*
2862e2caf59SThomas Veerman      * UNKNOWN.
2872e2caf59SThomas Veerman      */
2882e2caf59SThomas Veerman {
2892e2caf59SThomas Veerman     NULL,
2902e2caf59SThomas Veerman     FALSE, NULL, NULL, NULL, 0,
2912e2caf59SThomas Veerman     FALSE, NULL, NULL, NULL, NULL, 0,
2922e2caf59SThomas Veerman     NULL, NULL,
2932e2caf59SThomas Veerman }
2942e2caf59SThomas Veerman };
2952e2caf59SThomas Veerman static Shell *commandShell = &shells[DEFSHELL_INDEX]; /* this is the shell to
2962e2caf59SThomas Veerman 						   * which we pass all
2972e2caf59SThomas Veerman 						   * commands in the Makefile.
2982e2caf59SThomas Veerman 						   * It is set by the
2992e2caf59SThomas Veerman 						   * Job_ParseShell function */
3002e2caf59SThomas Veerman const char *shellPath = NULL,		  	  /* full pathname of
3012e2caf59SThomas Veerman 						   * executable image */
3022e2caf59SThomas Veerman            *shellName = NULL;		      	  /* last component of shell */
30384d9c625SLionel Sambuc char *shellErrFlag = NULL;
3042e2caf59SThomas Veerman static const char *shellArgv = NULL;		  /* Custom shell args */
3052e2caf59SThomas Veerman 
3062e2caf59SThomas Veerman 
3072e2caf59SThomas Veerman STATIC Job	*job_table;	/* The structures that describe them */
3082e2caf59SThomas Veerman STATIC Job	*job_table_end;	/* job_table + maxJobs */
3092e2caf59SThomas Veerman static int	wantToken;	/* we want a token */
3102e2caf59SThomas Veerman static int lurking_children = 0;
3112e2caf59SThomas Veerman static int make_suspended = 0;	/* non-zero if we've seen a SIGTSTP (etc) */
3122e2caf59SThomas Veerman 
3132e2caf59SThomas Veerman /*
3142e2caf59SThomas Veerman  * Set of descriptors of pipes connected to
3152e2caf59SThomas Veerman  * the output channels of children
3162e2caf59SThomas Veerman  */
3172e2caf59SThomas Veerman static struct pollfd *fds = NULL;
3182e2caf59SThomas Veerman static Job **jobfds = NULL;
3192e2caf59SThomas Veerman static int nfds = 0;
3202e2caf59SThomas Veerman static void watchfd(Job *);
3212e2caf59SThomas Veerman static void clearfd(Job *);
3222e2caf59SThomas Veerman static int readyfd(Job *);
3232e2caf59SThomas Veerman 
3242e2caf59SThomas Veerman STATIC GNode   	*lastNode;	/* The node for which output was most recently
3252e2caf59SThomas Veerman 				 * produced. */
3262e2caf59SThomas Veerman static char *targPrefix = NULL; /* What we print at the start of TARG_FMT */
3272e2caf59SThomas Veerman static Job tokenWaitJob;	/* token wait pseudo-job */
3282e2caf59SThomas Veerman 
3292e2caf59SThomas Veerman static Job childExitJob;	/* child exit pseudo-job */
3302e2caf59SThomas Veerman #define	CHILD_EXIT	"."
3312e2caf59SThomas Veerman #define	DO_JOB_RESUME	"R"
3322e2caf59SThomas Veerman 
3332e2caf59SThomas Veerman #define TARG_FMT  "%s %s ---\n" /* Default format */
3342e2caf59SThomas Veerman #define MESSAGE(fp, gn) \
33584d9c625SLionel Sambuc 	if (maxJobs != 1 && targPrefix && *targPrefix) \
3362e2caf59SThomas Veerman 	    (void)fprintf(fp, TARG_FMT, targPrefix, gn->name)
3372e2caf59SThomas Veerman 
3382e2caf59SThomas Veerman static sigset_t caught_signals;	/* Set of signals we handle */
3392e2caf59SThomas Veerman #if defined(SYSV)
3402e2caf59SThomas Veerman #define KILLPG(pid, sig)	kill(-(pid), (sig))
3412e2caf59SThomas Veerman #else
3422e2caf59SThomas Veerman #define KILLPG(pid, sig)	killpg((pid), (sig))
3432e2caf59SThomas Veerman #endif
3442e2caf59SThomas Veerman 
3452e2caf59SThomas Veerman static void JobChildSig(int);
3462e2caf59SThomas Veerman static void JobContinueSig(int);
3472e2caf59SThomas Veerman static Job *JobFindPid(int, int, Boolean);
3482e2caf59SThomas Veerman static int JobPrintCommand(void *, void *);
3492e2caf59SThomas Veerman static int JobSaveCommand(void *, void *);
3502e2caf59SThomas Veerman static void JobClose(Job *);
3512e2caf59SThomas Veerman static void JobExec(Job *, char **);
3522e2caf59SThomas Veerman static void JobMakeArgv(Job *, char **);
3532e2caf59SThomas Veerman static int JobStart(GNode *, int);
3542e2caf59SThomas Veerman static char *JobOutput(Job *, char *, char *, int);
3552e2caf59SThomas Veerman static void JobDoOutput(Job *, Boolean);
3562e2caf59SThomas Veerman static Shell *JobMatchShell(const char *);
3572bc7c627SLionel Sambuc static void JobInterrupt(int, int) MAKE_ATTR_DEAD;
3582e2caf59SThomas Veerman static void JobRestartJobs(void);
3592e2caf59SThomas Veerman static void JobTokenAdd(void);
3602e2caf59SThomas Veerman static void JobSigLock(sigset_t *);
3612e2caf59SThomas Veerman static void JobSigUnlock(sigset_t *);
3622e2caf59SThomas Veerman static void JobSigReset(void);
3632e2caf59SThomas Veerman 
3642e2caf59SThomas Veerman const char *malloc_options="A";
3652e2caf59SThomas Veerman 
3662e2caf59SThomas Veerman static void
job_table_dump(const char * where)3672e2caf59SThomas Veerman job_table_dump(const char *where)
3682e2caf59SThomas Veerman {
3692e2caf59SThomas Veerman     Job *job;
3702e2caf59SThomas Veerman 
3712e2caf59SThomas Veerman     fprintf(debug_file, "job table @ %s\n", where);
3722e2caf59SThomas Veerman     for (job = job_table; job < job_table_end; job++) {
3732e2caf59SThomas Veerman 	fprintf(debug_file, "job %d, status %d, flags %d, pid %d\n",
3742e2caf59SThomas Veerman 	    (int)(job - job_table), job->job_state, job->flags, job->pid);
3752e2caf59SThomas Veerman     }
3762e2caf59SThomas Veerman }
3772e2caf59SThomas Veerman 
3782e2caf59SThomas Veerman /*
3792e2caf59SThomas Veerman  * JobSigLock/JobSigUnlock
3802e2caf59SThomas Veerman  *
3812e2caf59SThomas Veerman  * Signal lock routines to get exclusive access. Currently used to
3822e2caf59SThomas Veerman  * protect `jobs' and `stoppedJobs' list manipulations.
3832e2caf59SThomas Veerman  */
JobSigLock(sigset_t * omaskp)3842e2caf59SThomas Veerman static void JobSigLock(sigset_t *omaskp)
3852e2caf59SThomas Veerman {
3862e2caf59SThomas Veerman 	if (sigprocmask(SIG_BLOCK, &caught_signals, omaskp) != 0) {
3872e2caf59SThomas Veerman 		Punt("JobSigLock: sigprocmask: %s", strerror(errno));
3882e2caf59SThomas Veerman 		sigemptyset(omaskp);
3892e2caf59SThomas Veerman 	}
3902e2caf59SThomas Veerman }
3912e2caf59SThomas Veerman 
JobSigUnlock(sigset_t * omaskp)3922e2caf59SThomas Veerman static void JobSigUnlock(sigset_t *omaskp)
3932e2caf59SThomas Veerman {
3942e2caf59SThomas Veerman 	(void)sigprocmask(SIG_SETMASK, omaskp, NULL);
3952e2caf59SThomas Veerman }
3962e2caf59SThomas Veerman 
3972e2caf59SThomas Veerman static void
JobCreatePipe(Job * job,int minfd)3982e2caf59SThomas Veerman JobCreatePipe(Job *job, int minfd)
3992e2caf59SThomas Veerman {
4002e2caf59SThomas Veerman     int i, fd;
4012e2caf59SThomas Veerman 
4022e2caf59SThomas Veerman     if (pipe(job->jobPipe) == -1)
4032e2caf59SThomas Veerman 	Punt("Cannot create pipe: %s", strerror(errno));
4042e2caf59SThomas Veerman 
40584d9c625SLionel Sambuc     for (i = 0; i < 2; i++) {
40684d9c625SLionel Sambuc        /* Avoid using low numbered fds */
40784d9c625SLionel Sambuc        fd = fcntl(job->jobPipe[i], F_DUPFD, minfd);
40884d9c625SLionel Sambuc        if (fd != -1) {
40984d9c625SLionel Sambuc 	   close(job->jobPipe[i]);
41084d9c625SLionel Sambuc 	   job->jobPipe[i] = fd;
41184d9c625SLionel Sambuc        }
41284d9c625SLionel Sambuc     }
41384d9c625SLionel Sambuc 
4142e2caf59SThomas Veerman     /* Set close-on-exec flag for both */
4152e2caf59SThomas Veerman     (void)fcntl(job->jobPipe[0], F_SETFD, 1);
4162e2caf59SThomas Veerman     (void)fcntl(job->jobPipe[1], F_SETFD, 1);
4172e2caf59SThomas Veerman 
4182e2caf59SThomas Veerman     /*
4192e2caf59SThomas Veerman      * We mark the input side of the pipe non-blocking; we poll(2) the
4202e2caf59SThomas Veerman      * pipe when we're waiting for a job token, but we might lose the
4212e2caf59SThomas Veerman      * race for the token when a new one becomes available, so the read
4222e2caf59SThomas Veerman      * from the pipe should not block.
4232e2caf59SThomas Veerman      */
4242e2caf59SThomas Veerman     fcntl(job->jobPipe[0], F_SETFL,
4252e2caf59SThomas Veerman 	fcntl(job->jobPipe[0], F_GETFL, 0) | O_NONBLOCK);
4262e2caf59SThomas Veerman }
4272e2caf59SThomas Veerman 
4282e2caf59SThomas Veerman /*-
4292e2caf59SThomas Veerman  *-----------------------------------------------------------------------
4302e2caf59SThomas Veerman  * JobCondPassSig --
4312e2caf59SThomas Veerman  *	Pass a signal to a job
4322e2caf59SThomas Veerman  *
4332e2caf59SThomas Veerman  * Input:
4342e2caf59SThomas Veerman  *	signop		Signal to send it
4352e2caf59SThomas Veerman  *
4362e2caf59SThomas Veerman  * Side Effects:
4372e2caf59SThomas Veerman  *	None, except the job may bite it.
4382e2caf59SThomas Veerman  *
4392e2caf59SThomas Veerman  *-----------------------------------------------------------------------
4402e2caf59SThomas Veerman  */
4412e2caf59SThomas Veerman static void
JobCondPassSig(int signo)4422e2caf59SThomas Veerman JobCondPassSig(int signo)
4432e2caf59SThomas Veerman {
4442e2caf59SThomas Veerman     Job *job;
4452e2caf59SThomas Veerman 
4462e2caf59SThomas Veerman     if (DEBUG(JOB)) {
4472e2caf59SThomas Veerman 	(void)fprintf(debug_file, "JobCondPassSig(%d) called.\n", signo);
4482e2caf59SThomas Veerman     }
4492e2caf59SThomas Veerman 
4502e2caf59SThomas Veerman     for (job = job_table; job < job_table_end; job++) {
4512e2caf59SThomas Veerman 	if (job->job_state != JOB_ST_RUNNING)
4522e2caf59SThomas Veerman 	    continue;
4532e2caf59SThomas Veerman 	if (DEBUG(JOB)) {
4542e2caf59SThomas Veerman 	    (void)fprintf(debug_file,
4552e2caf59SThomas Veerman 			   "JobCondPassSig passing signal %d to child %d.\n",
4562e2caf59SThomas Veerman 			   signo, job->pid);
4572e2caf59SThomas Veerman 	}
4582e2caf59SThomas Veerman 	KILLPG(job->pid, signo);
4592e2caf59SThomas Veerman     }
4602e2caf59SThomas Veerman }
4612e2caf59SThomas Veerman 
4622e2caf59SThomas Veerman /*-
4632e2caf59SThomas Veerman  *-----------------------------------------------------------------------
4642e2caf59SThomas Veerman  * JobChldSig --
4652e2caf59SThomas Veerman  *	SIGCHLD handler.
4662e2caf59SThomas Veerman  *
4672e2caf59SThomas Veerman  * Input:
4682e2caf59SThomas Veerman  *	signo		The signal number we've received
4692e2caf59SThomas Veerman  *
4702e2caf59SThomas Veerman  * Results:
4712e2caf59SThomas Veerman  *	None.
4722e2caf59SThomas Veerman  *
4732e2caf59SThomas Veerman  * Side Effects:
4742e2caf59SThomas Veerman  *	Sends a token on the child exit pipe to wake us up from
4752e2caf59SThomas Veerman  *	select()/poll().
4762e2caf59SThomas Veerman  *
4772e2caf59SThomas Veerman  *-----------------------------------------------------------------------
4782e2caf59SThomas Veerman  */
4792e2caf59SThomas Veerman static void
JobChildSig(int signo MAKE_ATTR_UNUSED)4802bc7c627SLionel Sambuc JobChildSig(int signo MAKE_ATTR_UNUSED)
4812e2caf59SThomas Veerman {
48284d9c625SLionel Sambuc     while (write(childExitJob.outPipe, CHILD_EXIT, 1) == -1 && errno == EAGAIN)
48384d9c625SLionel Sambuc 	continue;
4842e2caf59SThomas Veerman }
4852e2caf59SThomas Veerman 
4862e2caf59SThomas Veerman 
4872e2caf59SThomas Veerman /*-
4882e2caf59SThomas Veerman  *-----------------------------------------------------------------------
4892e2caf59SThomas Veerman  * JobContinueSig --
4902e2caf59SThomas Veerman  *	Resume all stopped jobs.
4912e2caf59SThomas Veerman  *
4922e2caf59SThomas Veerman  * Input:
4932e2caf59SThomas Veerman  *	signo		The signal number we've received
4942e2caf59SThomas Veerman  *
4952e2caf59SThomas Veerman  * Results:
4962e2caf59SThomas Veerman  *	None.
4972e2caf59SThomas Veerman  *
4982e2caf59SThomas Veerman  * Side Effects:
4992e2caf59SThomas Veerman  *	Jobs start running again.
5002e2caf59SThomas Veerman  *
5012e2caf59SThomas Veerman  *-----------------------------------------------------------------------
5022e2caf59SThomas Veerman  */
5032e2caf59SThomas Veerman static void
JobContinueSig(int signo MAKE_ATTR_UNUSED)5042bc7c627SLionel Sambuc JobContinueSig(int signo MAKE_ATTR_UNUSED)
5052e2caf59SThomas Veerman {
5062e2caf59SThomas Veerman     /*
5072e2caf59SThomas Veerman      * Defer sending to SIGCONT to our stopped children until we return
5082e2caf59SThomas Veerman      * from the signal handler.
5092e2caf59SThomas Veerman      */
51084d9c625SLionel Sambuc     while (write(childExitJob.outPipe, DO_JOB_RESUME, 1) == -1 &&
51184d9c625SLionel Sambuc 	errno == EAGAIN)
51284d9c625SLionel Sambuc 	continue;
5132e2caf59SThomas Veerman }
5142e2caf59SThomas Veerman 
5152e2caf59SThomas Veerman /*-
5162e2caf59SThomas Veerman  *-----------------------------------------------------------------------
5172e2caf59SThomas Veerman  * JobPassSig --
5182e2caf59SThomas Veerman  *	Pass a signal on to all jobs, then resend to ourselves.
5192e2caf59SThomas Veerman  *
5202e2caf59SThomas Veerman  * Input:
5212e2caf59SThomas Veerman  *	signo		The signal number we've received
5222e2caf59SThomas Veerman  *
5232e2caf59SThomas Veerman  * Results:
5242e2caf59SThomas Veerman  *	None.
5252e2caf59SThomas Veerman  *
5262e2caf59SThomas Veerman  * Side Effects:
5272e2caf59SThomas Veerman  *	We die by the same signal.
5282e2caf59SThomas Veerman  *
5292e2caf59SThomas Veerman  *-----------------------------------------------------------------------
5302e2caf59SThomas Veerman  */
5312bc7c627SLionel Sambuc MAKE_ATTR_DEAD static void
JobPassSig_int(int signo)5322e2caf59SThomas Veerman JobPassSig_int(int signo)
5332e2caf59SThomas Veerman {
5342e2caf59SThomas Veerman     /* Run .INTERRUPT target then exit */
5352e2caf59SThomas Veerman     JobInterrupt(TRUE, signo);
5362e2caf59SThomas Veerman }
5372e2caf59SThomas Veerman 
5382bc7c627SLionel Sambuc MAKE_ATTR_DEAD static void
JobPassSig_term(int signo)5392e2caf59SThomas Veerman JobPassSig_term(int signo)
5402e2caf59SThomas Veerman {
5412e2caf59SThomas Veerman     /* Dont run .INTERRUPT target then exit */
5422e2caf59SThomas Veerman     JobInterrupt(FALSE, signo);
5432e2caf59SThomas Veerman }
5442e2caf59SThomas Veerman 
5452e2caf59SThomas Veerman static void
JobPassSig_suspend(int signo)5462e2caf59SThomas Veerman JobPassSig_suspend(int signo)
5472e2caf59SThomas Veerman {
5482e2caf59SThomas Veerman     sigset_t nmask, omask;
5492e2caf59SThomas Veerman     struct sigaction act;
5502e2caf59SThomas Veerman 
5512e2caf59SThomas Veerman     /* Suppress job started/continued messages */
5522e2caf59SThomas Veerman     make_suspended = 1;
5532e2caf59SThomas Veerman 
5542e2caf59SThomas Veerman     /* Pass the signal onto every job */
5552e2caf59SThomas Veerman     JobCondPassSig(signo);
5562e2caf59SThomas Veerman 
5572e2caf59SThomas Veerman     /*
5582e2caf59SThomas Veerman      * Send ourselves the signal now we've given the message to everyone else.
5592e2caf59SThomas Veerman      * Note we block everything else possible while we're getting the signal.
5602e2caf59SThomas Veerman      * This ensures that all our jobs get continued when we wake up before
5612e2caf59SThomas Veerman      * we take any other signal.
5622e2caf59SThomas Veerman      */
5632e2caf59SThomas Veerman     sigfillset(&nmask);
5642e2caf59SThomas Veerman     sigdelset(&nmask, signo);
5652e2caf59SThomas Veerman     (void)sigprocmask(SIG_SETMASK, &nmask, &omask);
5662e2caf59SThomas Veerman 
5672e2caf59SThomas Veerman     act.sa_handler = SIG_DFL;
5682e2caf59SThomas Veerman     sigemptyset(&act.sa_mask);
5692e2caf59SThomas Veerman     act.sa_flags = 0;
5702e2caf59SThomas Veerman     (void)sigaction(signo, &act, NULL);
5712e2caf59SThomas Veerman 
5722e2caf59SThomas Veerman     if (DEBUG(JOB)) {
5732e2caf59SThomas Veerman 	(void)fprintf(debug_file,
5742e2caf59SThomas Veerman 		       "JobPassSig passing signal %d to self.\n", signo);
5752e2caf59SThomas Veerman     }
5762e2caf59SThomas Veerman 
5772e2caf59SThomas Veerman     (void)kill(getpid(), signo);
5782e2caf59SThomas Veerman 
5792e2caf59SThomas Veerman     /*
5802e2caf59SThomas Veerman      * We've been continued.
5812e2caf59SThomas Veerman      *
5822e2caf59SThomas Veerman      * A whole host of signals continue to happen!
5832e2caf59SThomas Veerman      * SIGCHLD for any processes that actually suspended themselves.
5842e2caf59SThomas Veerman      * SIGCHLD for any processes that exited while we were alseep.
5852e2caf59SThomas Veerman      * The SIGCONT that actually caused us to wakeup.
5862e2caf59SThomas Veerman      *
5872e2caf59SThomas Veerman      * Since we defer passing the SIGCONT on to our children until
5882e2caf59SThomas Veerman      * the main processing loop, we can be sure that all the SIGCHLD
5892e2caf59SThomas Veerman      * events will have happened by then - and that the waitpid() will
5902e2caf59SThomas Veerman      * collect the child 'suspended' events.
5912e2caf59SThomas Veerman      * For correct sequencing we just need to ensure we process the
5922e2caf59SThomas Veerman      * waitpid() before passign on the SIGCONT.
5932e2caf59SThomas Veerman      *
5942e2caf59SThomas Veerman      * In any case nothing else is needed here.
5952e2caf59SThomas Veerman      */
5962e2caf59SThomas Veerman 
5972e2caf59SThomas Veerman     /* Restore handler and signal mask */
5982e2caf59SThomas Veerman     act.sa_handler = JobPassSig_suspend;
5992e2caf59SThomas Veerman     (void)sigaction(signo, &act, NULL);
6002e2caf59SThomas Veerman     (void)sigprocmask(SIG_SETMASK, &omask, NULL);
6012e2caf59SThomas Veerman }
6022e2caf59SThomas Veerman 
6032e2caf59SThomas Veerman /*-
6042e2caf59SThomas Veerman  *-----------------------------------------------------------------------
6052e2caf59SThomas Veerman  * JobFindPid  --
6062e2caf59SThomas Veerman  *	Compare the pid of the job with the given pid and return 0 if they
6072e2caf59SThomas Veerman  *	are equal. This function is called from Job_CatchChildren
6082e2caf59SThomas Veerman  *	to find the job descriptor of the finished job.
6092e2caf59SThomas Veerman  *
6102e2caf59SThomas Veerman  * Input:
6112e2caf59SThomas Veerman  *	job		job to examine
6122e2caf59SThomas Veerman  *	pid		process id desired
6132e2caf59SThomas Veerman  *
6142e2caf59SThomas Veerman  * Results:
6152e2caf59SThomas Veerman  *	Job with matching pid
6162e2caf59SThomas Veerman  *
6172e2caf59SThomas Veerman  * Side Effects:
6182e2caf59SThomas Veerman  *	None
6192e2caf59SThomas Veerman  *-----------------------------------------------------------------------
6202e2caf59SThomas Veerman  */
6212e2caf59SThomas Veerman static Job *
JobFindPid(int pid,int status,Boolean isJobs)6222e2caf59SThomas Veerman JobFindPid(int pid, int status, Boolean isJobs)
6232e2caf59SThomas Veerman {
6242e2caf59SThomas Veerman     Job *job;
6252e2caf59SThomas Veerman 
6262e2caf59SThomas Veerman     for (job = job_table; job < job_table_end; job++) {
6272e2caf59SThomas Veerman 	if ((job->job_state == status) && job->pid == pid)
6282e2caf59SThomas Veerman 	    return job;
6292e2caf59SThomas Veerman     }
6302e2caf59SThomas Veerman     if (DEBUG(JOB) && isJobs)
6312e2caf59SThomas Veerman 	job_table_dump("no pid");
6322e2caf59SThomas Veerman     return NULL;
6332e2caf59SThomas Veerman }
6342e2caf59SThomas Veerman 
6352e2caf59SThomas Veerman /*-
6362e2caf59SThomas Veerman  *-----------------------------------------------------------------------
6372e2caf59SThomas Veerman  * JobPrintCommand  --
6382e2caf59SThomas Veerman  *	Put out another command for the given job. If the command starts
6392e2caf59SThomas Veerman  *	with an @ or a - we process it specially. In the former case,
6402e2caf59SThomas Veerman  *	so long as the -s and -n flags weren't given to make, we stick
6412e2caf59SThomas Veerman  *	a shell-specific echoOff command in the script. In the latter,
6422e2caf59SThomas Veerman  *	we ignore errors for the entire job, unless the shell has error
6432e2caf59SThomas Veerman  *	control.
6442e2caf59SThomas Veerman  *	If the command is just "..." we take all future commands for this
6452e2caf59SThomas Veerman  *	job to be commands to be executed once the entire graph has been
6462e2caf59SThomas Veerman  *	made and return non-zero to signal that the end of the commands
6472e2caf59SThomas Veerman  *	was reached. These commands are later attached to the postCommands
6482e2caf59SThomas Veerman  *	node and executed by Job_End when all things are done.
6492e2caf59SThomas Veerman  *	This function is called from JobStart via Lst_ForEach.
6502e2caf59SThomas Veerman  *
6512e2caf59SThomas Veerman  * Input:
6522e2caf59SThomas Veerman  *	cmdp		command string to print
6532e2caf59SThomas Veerman  *	jobp		job for which to print it
6542e2caf59SThomas Veerman  *
6552e2caf59SThomas Veerman  * Results:
6562e2caf59SThomas Veerman  *	Always 0, unless the command was "..."
6572e2caf59SThomas Veerman  *
6582e2caf59SThomas Veerman  * Side Effects:
6592e2caf59SThomas Veerman  *	If the command begins with a '-' and the shell has no error control,
6602e2caf59SThomas Veerman  *	the JOB_IGNERR flag is set in the job descriptor.
6612e2caf59SThomas Veerman  *	If the command is "..." and we're not ignoring such things,
6622e2caf59SThomas Veerman  *	tailCmds is set to the successor node of the cmd.
6632e2caf59SThomas Veerman  *	numCommands is incremented if the command is actually printed.
6642e2caf59SThomas Veerman  *-----------------------------------------------------------------------
6652e2caf59SThomas Veerman  */
6662e2caf59SThomas Veerman static int
JobPrintCommand(void * cmdp,void * jobp)6672e2caf59SThomas Veerman JobPrintCommand(void *cmdp, void *jobp)
6682e2caf59SThomas Veerman {
6692e2caf59SThomas Veerman     Boolean	  noSpecials;	    /* true if we shouldn't worry about
6702e2caf59SThomas Veerman 				     * inserting special commands into
6712e2caf59SThomas Veerman 				     * the input stream. */
6722e2caf59SThomas Veerman     Boolean       shutUp = FALSE;   /* true if we put a no echo command
6732e2caf59SThomas Veerman 				     * into the command file */
6742e2caf59SThomas Veerman     Boolean	  errOff = FALSE;   /* true if we turned error checking
6752e2caf59SThomas Veerman 				     * off before printing the command
6762e2caf59SThomas Veerman 				     * and need to turn it back on */
6772e2caf59SThomas Veerman     const char    *cmdTemplate;	    /* Template to use when printing the
6782e2caf59SThomas Veerman 				     * command */
6792e2caf59SThomas Veerman     char    	  *cmdStart;	    /* Start of expanded command */
6802e2caf59SThomas Veerman     char	  *escCmd = NULL;    /* Command with quotes/backticks escaped */
6812e2caf59SThomas Veerman     char     	  *cmd = (char *)cmdp;
6822e2caf59SThomas Veerman     Job           *job = (Job *)jobp;
6832e2caf59SThomas Veerman     int           i, j;
6842e2caf59SThomas Veerman 
6852e2caf59SThomas Veerman     noSpecials = NoExecute(job->node);
6862e2caf59SThomas Veerman 
6872e2caf59SThomas Veerman     if (strcmp(cmd, "...") == 0) {
6882e2caf59SThomas Veerman 	job->node->type |= OP_SAVE_CMDS;
6892e2caf59SThomas Veerman 	if ((job->flags & JOB_IGNDOTS) == 0) {
6902e2caf59SThomas Veerman 	    job->tailCmds = Lst_Succ(Lst_Member(job->node->commands,
6912e2caf59SThomas Veerman 						cmd));
6922e2caf59SThomas Veerman 	    return 1;
6932e2caf59SThomas Veerman 	}
6942e2caf59SThomas Veerman 	return 0;
6952e2caf59SThomas Veerman     }
6962e2caf59SThomas Veerman 
6972e2caf59SThomas Veerman #define DBPRINTF(fmt, arg) if (DEBUG(JOB)) {	\
6982e2caf59SThomas Veerman 	(void)fprintf(debug_file, fmt, arg); 	\
6992e2caf59SThomas Veerman     }						\
7002e2caf59SThomas Veerman    (void)fprintf(job->cmdFILE, fmt, arg);	\
7012e2caf59SThomas Veerman    (void)fflush(job->cmdFILE);
7022e2caf59SThomas Veerman 
7032e2caf59SThomas Veerman     numCommands += 1;
7042e2caf59SThomas Veerman 
7052e2caf59SThomas Veerman     cmdStart = cmd = Var_Subst(NULL, cmd, job->node, FALSE);
7062e2caf59SThomas Veerman 
7072e2caf59SThomas Veerman     cmdTemplate = "%s\n";
7082e2caf59SThomas Veerman 
7092e2caf59SThomas Veerman     /*
7102e2caf59SThomas Veerman      * Check for leading @' and -'s to control echoing and error checking.
7112e2caf59SThomas Veerman      */
7122e2caf59SThomas Veerman     while (*cmd == '@' || *cmd == '-' || (*cmd == '+')) {
7132e2caf59SThomas Veerman 	switch (*cmd) {
7142e2caf59SThomas Veerman 	case '@':
7152e2caf59SThomas Veerman 	    shutUp = DEBUG(LOUD) ? FALSE : TRUE;
7162e2caf59SThomas Veerman 	    break;
7172e2caf59SThomas Veerman 	case '-':
7182e2caf59SThomas Veerman 	    errOff = TRUE;
7192e2caf59SThomas Veerman 	    break;
7202e2caf59SThomas Veerman 	case '+':
7212e2caf59SThomas Veerman 	    if (noSpecials) {
7222e2caf59SThomas Veerman 		/*
7232e2caf59SThomas Veerman 		 * We're not actually executing anything...
7242e2caf59SThomas Veerman 		 * but this one needs to be - use compat mode just for it.
7252e2caf59SThomas Veerman 		 */
7262e2caf59SThomas Veerman 		CompatRunCommand(cmdp, job->node);
7272e2caf59SThomas Veerman 		return 0;
7282e2caf59SThomas Veerman 	    }
7292e2caf59SThomas Veerman 	    break;
7302e2caf59SThomas Veerman 	}
7312e2caf59SThomas Veerman 	cmd++;
7322e2caf59SThomas Veerman     }
7332e2caf59SThomas Veerman 
7342e2caf59SThomas Veerman     while (isspace((unsigned char) *cmd))
7352e2caf59SThomas Veerman 	cmd++;
7362e2caf59SThomas Veerman 
7372e2caf59SThomas Veerman     /*
7382e2caf59SThomas Veerman      * If the shell doesn't have error control the alternate echo'ing will
7392e2caf59SThomas Veerman      * be done (to avoid showing additional error checking code)
7402e2caf59SThomas Veerman      * and this will need the characters '$ ` \ "' escaped
7412e2caf59SThomas Veerman      */
7422e2caf59SThomas Veerman 
7432e2caf59SThomas Veerman     if (!commandShell->hasErrCtl) {
7442e2caf59SThomas Veerman 	/* Worst that could happen is every char needs escaping. */
7452e2caf59SThomas Veerman 	escCmd = bmake_malloc((strlen(cmd) * 2) + 1);
7462e2caf59SThomas Veerman 	for (i = 0, j= 0; cmd[i] != '\0'; i++, j++) {
7472e2caf59SThomas Veerman 		if (cmd[i] == '$' || cmd[i] == '`' || cmd[i] == '\\' ||
7482e2caf59SThomas Veerman 			cmd[i] == '"')
7492e2caf59SThomas Veerman 			escCmd[j++] = '\\';
7502e2caf59SThomas Veerman 		escCmd[j] = cmd[i];
7512e2caf59SThomas Veerman 	}
7522e2caf59SThomas Veerman 	escCmd[j] = 0;
7532e2caf59SThomas Veerman     }
7542e2caf59SThomas Veerman 
7552e2caf59SThomas Veerman     if (shutUp) {
7562e2caf59SThomas Veerman 	if (!(job->flags & JOB_SILENT) && !noSpecials &&
7572e2caf59SThomas Veerman 	    commandShell->hasEchoCtl) {
7582e2caf59SThomas Veerman 		DBPRINTF("%s\n", commandShell->echoOff);
7592e2caf59SThomas Veerman 	} else {
7602e2caf59SThomas Veerman 	    if (commandShell->hasErrCtl)
7612e2caf59SThomas Veerman 		shutUp = FALSE;
7622e2caf59SThomas Veerman 	}
7632e2caf59SThomas Veerman     }
7642e2caf59SThomas Veerman 
7652e2caf59SThomas Veerman     if (errOff) {
7662e2caf59SThomas Veerman 	if (!noSpecials) {
7672e2caf59SThomas Veerman 	    if (commandShell->hasErrCtl) {
7682e2caf59SThomas Veerman 		/*
7692e2caf59SThomas Veerman 		 * we don't want the error-control commands showing
7702e2caf59SThomas Veerman 		 * up either, so we turn off echoing while executing
7712e2caf59SThomas Veerman 		 * them. We could put another field in the shell
7722e2caf59SThomas Veerman 		 * structure to tell JobDoOutput to look for this
7732e2caf59SThomas Veerman 		 * string too, but why make it any more complex than
7742e2caf59SThomas Veerman 		 * it already is?
7752e2caf59SThomas Veerman 		 */
7762e2caf59SThomas Veerman 		if (!(job->flags & JOB_SILENT) && !shutUp &&
7772e2caf59SThomas Veerman 		    commandShell->hasEchoCtl) {
7782e2caf59SThomas Veerman 			DBPRINTF("%s\n", commandShell->echoOff);
7792e2caf59SThomas Veerman 			DBPRINTF("%s\n", commandShell->ignErr);
7802e2caf59SThomas Veerman 			DBPRINTF("%s\n", commandShell->echoOn);
7812e2caf59SThomas Veerman 		} else {
7822e2caf59SThomas Veerman 			DBPRINTF("%s\n", commandShell->ignErr);
7832e2caf59SThomas Veerman 		}
7842e2caf59SThomas Veerman 	    } else if (commandShell->ignErr &&
7852e2caf59SThomas Veerman 		      (*commandShell->ignErr != '\0'))
7862e2caf59SThomas Veerman 	    {
7872e2caf59SThomas Veerman 		/*
7882e2caf59SThomas Veerman 		 * The shell has no error control, so we need to be
7892e2caf59SThomas Veerman 		 * weird to get it to ignore any errors from the command.
7902e2caf59SThomas Veerman 		 * If echoing is turned on, we turn it off and use the
7912e2caf59SThomas Veerman 		 * errCheck template to echo the command. Leave echoing
7922e2caf59SThomas Veerman 		 * off so the user doesn't see the weirdness we go through
7932e2caf59SThomas Veerman 		 * to ignore errors. Set cmdTemplate to use the weirdness
7942e2caf59SThomas Veerman 		 * instead of the simple "%s\n" template.
7952e2caf59SThomas Veerman 		 */
796*0a6a1f1dSLionel Sambuc 		job->flags |= JOB_IGNERR;
7972e2caf59SThomas Veerman 		if (!(job->flags & JOB_SILENT) && !shutUp) {
7982e2caf59SThomas Veerman 			if (commandShell->hasEchoCtl) {
7992e2caf59SThomas Veerman 				DBPRINTF("%s\n", commandShell->echoOff);
8002e2caf59SThomas Veerman 			}
8012e2caf59SThomas Veerman 			DBPRINTF(commandShell->errCheck, escCmd);
8022e2caf59SThomas Veerman 			shutUp = TRUE;
8032e2caf59SThomas Veerman 		} else {
8042e2caf59SThomas Veerman 			if (!shutUp) {
8052e2caf59SThomas Veerman 				DBPRINTF(commandShell->errCheck, escCmd);
8062e2caf59SThomas Veerman 			}
8072e2caf59SThomas Veerman 		}
8082e2caf59SThomas Veerman 		cmdTemplate = commandShell->ignErr;
8092e2caf59SThomas Veerman 		/*
8102e2caf59SThomas Veerman 		 * The error ignoration (hee hee) is already taken care
8112e2caf59SThomas Veerman 		 * of by the ignErr template, so pretend error checking
8122e2caf59SThomas Veerman 		 * is still on.
8132e2caf59SThomas Veerman 		 */
8142e2caf59SThomas Veerman 		errOff = FALSE;
8152e2caf59SThomas Veerman 	    } else {
8162e2caf59SThomas Veerman 		errOff = FALSE;
8172e2caf59SThomas Veerman 	    }
8182e2caf59SThomas Veerman 	} else {
8192e2caf59SThomas Veerman 	    errOff = FALSE;
8202e2caf59SThomas Veerman 	}
8212e2caf59SThomas Veerman     } else {
8222e2caf59SThomas Veerman 
8232e2caf59SThomas Veerman 	/*
8242e2caf59SThomas Veerman 	 * If errors are being checked and the shell doesn't have error control
8252e2caf59SThomas Veerman 	 * but does supply an errOut template, then setup commands to run
8262e2caf59SThomas Veerman 	 * through it.
8272e2caf59SThomas Veerman 	 */
8282e2caf59SThomas Veerman 
8292e2caf59SThomas Veerman 	if (!commandShell->hasErrCtl && commandShell->errOut &&
8302e2caf59SThomas Veerman 	    (*commandShell->errOut != '\0')) {
8312e2caf59SThomas Veerman 		if (!(job->flags & JOB_SILENT) && !shutUp) {
8322e2caf59SThomas Veerman 			if (commandShell->hasEchoCtl) {
8332e2caf59SThomas Veerman 				DBPRINTF("%s\n", commandShell->echoOff);
8342e2caf59SThomas Veerman 			}
8352e2caf59SThomas Veerman 			DBPRINTF(commandShell->errCheck, escCmd);
8362e2caf59SThomas Veerman 			shutUp = TRUE;
8372e2caf59SThomas Veerman 		}
8382e2caf59SThomas Veerman 		/* If it's a comment line or blank, treat as an ignored error */
8392e2caf59SThomas Veerman 		if ((escCmd[0] == commandShell->commentChar) ||
8402e2caf59SThomas Veerman 		    (escCmd[0] == 0))
8412e2caf59SThomas Veerman 			cmdTemplate = commandShell->ignErr;
8422e2caf59SThomas Veerman 		else
8432e2caf59SThomas Veerman 			cmdTemplate = commandShell->errOut;
8442e2caf59SThomas Veerman 		errOff = FALSE;
8452e2caf59SThomas Veerman 	}
8462e2caf59SThomas Veerman     }
8472e2caf59SThomas Veerman 
8482e2caf59SThomas Veerman     if (DEBUG(SHELL) && strcmp(shellName, "sh") == 0 &&
8492e2caf59SThomas Veerman 	(job->flags & JOB_TRACED) == 0) {
8502e2caf59SThomas Veerman 	    DBPRINTF("set -%s\n", "x");
8512e2caf59SThomas Veerman 	    job->flags |= JOB_TRACED;
8522e2caf59SThomas Veerman     }
8532e2caf59SThomas Veerman 
8542e2caf59SThomas Veerman     DBPRINTF(cmdTemplate, cmd);
8552e2caf59SThomas Veerman     free(cmdStart);
8562e2caf59SThomas Veerman     if (escCmd)
8572e2caf59SThomas Veerman         free(escCmd);
8582e2caf59SThomas Veerman     if (errOff) {
8592e2caf59SThomas Veerman 	/*
8602e2caf59SThomas Veerman 	 * If echoing is already off, there's no point in issuing the
8612e2caf59SThomas Veerman 	 * echoOff command. Otherwise we issue it and pretend it was on
8622e2caf59SThomas Veerman 	 * for the whole command...
8632e2caf59SThomas Veerman 	 */
8642e2caf59SThomas Veerman 	if (!shutUp && !(job->flags & JOB_SILENT) && commandShell->hasEchoCtl){
8652e2caf59SThomas Veerman 	    DBPRINTF("%s\n", commandShell->echoOff);
8662e2caf59SThomas Veerman 	    shutUp = TRUE;
8672e2caf59SThomas Veerman 	}
8682e2caf59SThomas Veerman 	DBPRINTF("%s\n", commandShell->errCheck);
8692e2caf59SThomas Veerman     }
8702e2caf59SThomas Veerman     if (shutUp && commandShell->hasEchoCtl) {
8712e2caf59SThomas Veerman 	DBPRINTF("%s\n", commandShell->echoOn);
8722e2caf59SThomas Veerman     }
8732e2caf59SThomas Veerman     return 0;
8742e2caf59SThomas Veerman }
8752e2caf59SThomas Veerman 
8762e2caf59SThomas Veerman /*-
8772e2caf59SThomas Veerman  *-----------------------------------------------------------------------
8782e2caf59SThomas Veerman  * JobSaveCommand --
8792e2caf59SThomas Veerman  *	Save a command to be executed when everything else is done.
8802e2caf59SThomas Veerman  *	Callback function for JobFinish...
8812e2caf59SThomas Veerman  *
8822e2caf59SThomas Veerman  * Results:
8832e2caf59SThomas Veerman  *	Always returns 0
8842e2caf59SThomas Veerman  *
8852e2caf59SThomas Veerman  * Side Effects:
8862e2caf59SThomas Veerman  *	The command is tacked onto the end of postCommands's commands list.
8872e2caf59SThomas Veerman  *
8882e2caf59SThomas Veerman  *-----------------------------------------------------------------------
8892e2caf59SThomas Veerman  */
8902e2caf59SThomas Veerman static int
JobSaveCommand(void * cmd,void * gn)8912e2caf59SThomas Veerman JobSaveCommand(void *cmd, void *gn)
8922e2caf59SThomas Veerman {
8932e2caf59SThomas Veerman     cmd = Var_Subst(NULL, (char *)cmd, (GNode *)gn, FALSE);
8942e2caf59SThomas Veerman     (void)Lst_AtEnd(postCommands->commands, cmd);
8952e2caf59SThomas Veerman     return(0);
8962e2caf59SThomas Veerman }
8972e2caf59SThomas Veerman 
8982e2caf59SThomas Veerman 
8992e2caf59SThomas Veerman /*-
9002e2caf59SThomas Veerman  *-----------------------------------------------------------------------
9012e2caf59SThomas Veerman  * JobClose --
9022e2caf59SThomas Veerman  *	Called to close both input and output pipes when a job is finished.
9032e2caf59SThomas Veerman  *
9042e2caf59SThomas Veerman  * Results:
9052e2caf59SThomas Veerman  *	Nada
9062e2caf59SThomas Veerman  *
9072e2caf59SThomas Veerman  * Side Effects:
9082e2caf59SThomas Veerman  *	The file descriptors associated with the job are closed.
9092e2caf59SThomas Veerman  *
9102e2caf59SThomas Veerman  *-----------------------------------------------------------------------
9112e2caf59SThomas Veerman  */
9122e2caf59SThomas Veerman static void
JobClose(Job * job)9132e2caf59SThomas Veerman JobClose(Job *job)
9142e2caf59SThomas Veerman {
9152e2caf59SThomas Veerman     clearfd(job);
9162e2caf59SThomas Veerman     (void)close(job->outPipe);
9172e2caf59SThomas Veerman     job->outPipe = -1;
9182e2caf59SThomas Veerman 
9192e2caf59SThomas Veerman     JobDoOutput(job, TRUE);
9202e2caf59SThomas Veerman     (void)close(job->inPipe);
9212e2caf59SThomas Veerman     job->inPipe = -1;
9222e2caf59SThomas Veerman }
9232e2caf59SThomas Veerman 
9242e2caf59SThomas Veerman /*-
9252e2caf59SThomas Veerman  *-----------------------------------------------------------------------
9262e2caf59SThomas Veerman  * JobFinish  --
9272e2caf59SThomas Veerman  *	Do final processing for the given job including updating
9282e2caf59SThomas Veerman  *	parents and starting new jobs as available/necessary. Note
9292e2caf59SThomas Veerman  *	that we pay no attention to the JOB_IGNERR flag here.
9302e2caf59SThomas Veerman  *	This is because when we're called because of a noexecute flag
9312e2caf59SThomas Veerman  *	or something, jstat.w_status is 0 and when called from
9322e2caf59SThomas Veerman  *	Job_CatchChildren, the status is zeroed if it s/b ignored.
9332e2caf59SThomas Veerman  *
9342e2caf59SThomas Veerman  * Input:
9352e2caf59SThomas Veerman  *	job		job to finish
9362e2caf59SThomas Veerman  *	status		sub-why job went away
9372e2caf59SThomas Veerman  *
9382e2caf59SThomas Veerman  * Results:
9392e2caf59SThomas Veerman  *	None
9402e2caf59SThomas Veerman  *
9412e2caf59SThomas Veerman  * Side Effects:
9422e2caf59SThomas Veerman  *	Final commands for the job are placed on postCommands.
9432e2caf59SThomas Veerman  *
9442e2caf59SThomas Veerman  *	If we got an error and are aborting (aborting == ABORT_ERROR) and
9452e2caf59SThomas Veerman  *	the job list is now empty, we are done for the day.
9462e2caf59SThomas Veerman  *	If we recognized an error (errors !=0), we set the aborting flag
9472e2caf59SThomas Veerman  *	to ABORT_ERROR so no more jobs will be started.
9482e2caf59SThomas Veerman  *-----------------------------------------------------------------------
9492e2caf59SThomas Veerman  */
9502e2caf59SThomas Veerman /*ARGSUSED*/
9512e2caf59SThomas Veerman static void
JobFinish(Job * job,int status)9522e2caf59SThomas Veerman JobFinish(Job *job, int status)
9532e2caf59SThomas Veerman {
9542e2caf59SThomas Veerman     Boolean 	 done, return_job_token;
9552e2caf59SThomas Veerman 
9562e2caf59SThomas Veerman     if (DEBUG(JOB)) {
9572e2caf59SThomas Veerman 	fprintf(debug_file, "Jobfinish: %d [%s], status %d\n",
9582e2caf59SThomas Veerman 				job->pid, job->node->name, status);
9592e2caf59SThomas Veerman     }
9602e2caf59SThomas Veerman 
9612e2caf59SThomas Veerman     if ((WIFEXITED(status) &&
9622e2caf59SThomas Veerman 	 (((WEXITSTATUS(status) != 0) && !(job->flags & JOB_IGNERR)))) ||
9632e2caf59SThomas Veerman 	WIFSIGNALED(status))
9642e2caf59SThomas Veerman     {
9652e2caf59SThomas Veerman 	/*
9662e2caf59SThomas Veerman 	 * If it exited non-zero and either we're doing things our
9672e2caf59SThomas Veerman 	 * way or we're not ignoring errors, the job is finished.
9682e2caf59SThomas Veerman 	 * Similarly, if the shell died because of a signal
9692e2caf59SThomas Veerman 	 * the job is also finished. In these
9702e2caf59SThomas Veerman 	 * cases, finish out the job's output before printing the exit
9712e2caf59SThomas Veerman 	 * status...
9722e2caf59SThomas Veerman 	 */
9732e2caf59SThomas Veerman 	JobClose(job);
9742e2caf59SThomas Veerman 	if (job->cmdFILE != NULL && job->cmdFILE != stdout) {
9752e2caf59SThomas Veerman 	   (void)fclose(job->cmdFILE);
9762e2caf59SThomas Veerman 	   job->cmdFILE = NULL;
9772e2caf59SThomas Veerman 	}
9782e2caf59SThomas Veerman 	done = TRUE;
9792e2caf59SThomas Veerman     } else if (WIFEXITED(status)) {
9802e2caf59SThomas Veerman 	/*
9812e2caf59SThomas Veerman 	 * Deal with ignored errors in -B mode. We need to print a message
9822e2caf59SThomas Veerman 	 * telling of the ignored error as well as setting status.w_status
9832e2caf59SThomas Veerman 	 * to 0 so the next command gets run. To do this, we set done to be
9842e2caf59SThomas Veerman 	 * TRUE if in -B mode and the job exited non-zero.
9852e2caf59SThomas Veerman 	 */
9862e2caf59SThomas Veerman 	done = WEXITSTATUS(status) != 0;
9872e2caf59SThomas Veerman 	/*
9882e2caf59SThomas Veerman 	 * Old comment said: "Note we don't
9892e2caf59SThomas Veerman 	 * want to close down any of the streams until we know we're at the
9902e2caf59SThomas Veerman 	 * end."
9912e2caf59SThomas Veerman 	 * But we do. Otherwise when are we going to print the rest of the
9922e2caf59SThomas Veerman 	 * stuff?
9932e2caf59SThomas Veerman 	 */
9942e2caf59SThomas Veerman 	JobClose(job);
9952e2caf59SThomas Veerman     } else {
9962e2caf59SThomas Veerman 	/*
9972e2caf59SThomas Veerman 	 * No need to close things down or anything.
9982e2caf59SThomas Veerman 	 */
9992e2caf59SThomas Veerman 	done = FALSE;
10002e2caf59SThomas Veerman     }
10012e2caf59SThomas Veerman 
10022e2caf59SThomas Veerman     if (done) {
10032e2caf59SThomas Veerman 	if (WIFEXITED(status)) {
10042e2caf59SThomas Veerman 	    if (DEBUG(JOB)) {
10052e2caf59SThomas Veerman 		(void)fprintf(debug_file, "Process %d [%s] exited.\n",
10062e2caf59SThomas Veerman 				job->pid, job->node->name);
10072e2caf59SThomas Veerman 	    }
10082e2caf59SThomas Veerman 	    if (WEXITSTATUS(status) != 0) {
10092e2caf59SThomas Veerman 		if (job->node != lastNode) {
10102e2caf59SThomas Veerman 		    MESSAGE(stdout, job->node);
10112e2caf59SThomas Veerman 		    lastNode = job->node;
10122e2caf59SThomas Veerman 		}
10132e2caf59SThomas Veerman #ifdef USE_META
10142e2caf59SThomas Veerman 		if (useMeta) {
10152e2caf59SThomas Veerman 		    meta_job_error(job, job->node, job->flags, WEXITSTATUS(status));
10162e2caf59SThomas Veerman 		}
10172e2caf59SThomas Veerman #endif
10182e2caf59SThomas Veerman 		(void)printf("*** [%s] Error code %d%s\n",
10192e2caf59SThomas Veerman 				job->node->name,
10202e2caf59SThomas Veerman 			       WEXITSTATUS(status),
10212e2caf59SThomas Veerman 			       (job->flags & JOB_IGNERR) ? " (ignored)" : "");
10222e2caf59SThomas Veerman 		if (job->flags & JOB_IGNERR) {
10232e2caf59SThomas Veerman 		    status = 0;
10242e2caf59SThomas Veerman 		} else {
10252e2caf59SThomas Veerman 		    PrintOnError(job->node, NULL);
10262e2caf59SThomas Veerman 		}
10272e2caf59SThomas Veerman 	    } else if (DEBUG(JOB)) {
10282e2caf59SThomas Veerman 		if (job->node != lastNode) {
10292e2caf59SThomas Veerman 		    MESSAGE(stdout, job->node);
10302e2caf59SThomas Veerman 		    lastNode = job->node;
10312e2caf59SThomas Veerman 		}
10322e2caf59SThomas Veerman 		(void)printf("*** [%s] Completed successfully\n",
10332e2caf59SThomas Veerman 				job->node->name);
10342e2caf59SThomas Veerman 	    }
10352e2caf59SThomas Veerman 	} else {
10362e2caf59SThomas Veerman 	    if (job->node != lastNode) {
10372e2caf59SThomas Veerman 		MESSAGE(stdout, job->node);
10382e2caf59SThomas Veerman 		lastNode = job->node;
10392e2caf59SThomas Veerman 	    }
10402e2caf59SThomas Veerman 	    (void)printf("*** [%s] Signal %d\n",
10412e2caf59SThomas Veerman 			job->node->name, WTERMSIG(status));
10422e2caf59SThomas Veerman 	}
10432e2caf59SThomas Veerman 	(void)fflush(stdout);
10442e2caf59SThomas Veerman     }
10452e2caf59SThomas Veerman 
10462e2caf59SThomas Veerman #ifdef USE_META
10472e2caf59SThomas Veerman     if (useMeta) {
10482e2caf59SThomas Veerman 	meta_job_finish(job);
10492e2caf59SThomas Veerman     }
10502e2caf59SThomas Veerman #endif
10512e2caf59SThomas Veerman 
10522e2caf59SThomas Veerman     return_job_token = FALSE;
10532e2caf59SThomas Veerman 
10542e2caf59SThomas Veerman     Trace_Log(JOBEND, job);
10552e2caf59SThomas Veerman     if (!(job->flags & JOB_SPECIAL)) {
10562e2caf59SThomas Veerman 	if ((status != 0) ||
10572e2caf59SThomas Veerman 		(aborting == ABORT_ERROR) ||
10582e2caf59SThomas Veerman 		(aborting == ABORT_INTERRUPT))
10592e2caf59SThomas Veerman 	    return_job_token = TRUE;
10602e2caf59SThomas Veerman     }
10612e2caf59SThomas Veerman 
10622e2caf59SThomas Veerman     if ((aborting != ABORT_ERROR) && (aborting != ABORT_INTERRUPT) && (status == 0)) {
10632e2caf59SThomas Veerman 	/*
10642e2caf59SThomas Veerman 	 * As long as we aren't aborting and the job didn't return a non-zero
10652e2caf59SThomas Veerman 	 * status that we shouldn't ignore, we call Make_Update to update
10662e2caf59SThomas Veerman 	 * the parents. In addition, any saved commands for the node are placed
10672e2caf59SThomas Veerman 	 * on the .END target.
10682e2caf59SThomas Veerman 	 */
10692e2caf59SThomas Veerman 	if (job->tailCmds != NULL) {
10702e2caf59SThomas Veerman 	    Lst_ForEachFrom(job->node->commands, job->tailCmds,
10712e2caf59SThomas Veerman 			     JobSaveCommand,
10722e2caf59SThomas Veerman 			    job->node);
10732e2caf59SThomas Veerman 	}
10742e2caf59SThomas Veerman 	job->node->made = MADE;
10752e2caf59SThomas Veerman 	if (!(job->flags & JOB_SPECIAL))
10762e2caf59SThomas Veerman 	    return_job_token = TRUE;
10772e2caf59SThomas Veerman 	Make_Update(job->node);
10782e2caf59SThomas Veerman 	job->job_state = JOB_ST_FREE;
10792e2caf59SThomas Veerman     } else if (status != 0) {
10802e2caf59SThomas Veerman 	errors += 1;
10812e2caf59SThomas Veerman 	job->job_state = JOB_ST_FREE;
10822e2caf59SThomas Veerman     }
10832e2caf59SThomas Veerman 
10842e2caf59SThomas Veerman     /*
10852e2caf59SThomas Veerman      * Set aborting if any error.
10862e2caf59SThomas Veerman      */
10872e2caf59SThomas Veerman     if (errors && !keepgoing && (aborting != ABORT_INTERRUPT)) {
10882e2caf59SThomas Veerman 	/*
10892e2caf59SThomas Veerman 	 * If we found any errors in this batch of children and the -k flag
10902e2caf59SThomas Veerman 	 * wasn't given, we set the aborting flag so no more jobs get
10912e2caf59SThomas Veerman 	 * started.
10922e2caf59SThomas Veerman 	 */
10932e2caf59SThomas Veerman 	aborting = ABORT_ERROR;
10942e2caf59SThomas Veerman     }
10952e2caf59SThomas Veerman 
10962e2caf59SThomas Veerman     if (return_job_token)
10972e2caf59SThomas Veerman 	Job_TokenReturn();
10982e2caf59SThomas Veerman 
10992e2caf59SThomas Veerman     if (aborting == ABORT_ERROR && jobTokensRunning == 0) {
11002e2caf59SThomas Veerman 	/*
11012e2caf59SThomas Veerman 	 * If we are aborting and the job table is now empty, we finish.
11022e2caf59SThomas Veerman 	 */
11032e2caf59SThomas Veerman 	Finish(errors);
11042e2caf59SThomas Veerman     }
11052e2caf59SThomas Veerman }
11062e2caf59SThomas Veerman 
11072e2caf59SThomas Veerman /*-
11082e2caf59SThomas Veerman  *-----------------------------------------------------------------------
11092e2caf59SThomas Veerman  * Job_Touch --
11102e2caf59SThomas Veerman  *	Touch the given target. Called by JobStart when the -t flag was
11112e2caf59SThomas Veerman  *	given
11122e2caf59SThomas Veerman  *
11132e2caf59SThomas Veerman  * Input:
11142e2caf59SThomas Veerman  *	gn		the node of the file to touch
11152e2caf59SThomas Veerman  *	silent		TRUE if should not print message
11162e2caf59SThomas Veerman  *
11172e2caf59SThomas Veerman  * Results:
11182e2caf59SThomas Veerman  *	None
11192e2caf59SThomas Veerman  *
11202e2caf59SThomas Veerman  * Side Effects:
11212e2caf59SThomas Veerman  *	The data modification of the file is changed. In addition, if the
11222e2caf59SThomas Veerman  *	file did not exist, it is created.
11232e2caf59SThomas Veerman  *-----------------------------------------------------------------------
11242e2caf59SThomas Veerman  */
11252e2caf59SThomas Veerman void
Job_Touch(GNode * gn,Boolean silent)11262e2caf59SThomas Veerman Job_Touch(GNode *gn, Boolean silent)
11272e2caf59SThomas Veerman {
11282e2caf59SThomas Veerman     int		  streamID;   	/* ID of stream opened to do the touch */
11292e2caf59SThomas Veerman     struct utimbuf times;	/* Times for utime() call */
11302e2caf59SThomas Veerman 
11312e2caf59SThomas Veerman     if (gn->type & (OP_JOIN|OP_USE|OP_USEBEFORE|OP_EXEC|OP_OPTIONAL|
11322e2caf59SThomas Veerman 	OP_SPECIAL|OP_PHONY)) {
11332e2caf59SThomas Veerman 	/*
11342e2caf59SThomas Veerman 	 * .JOIN, .USE, .ZEROTIME and .OPTIONAL targets are "virtual" targets
11352e2caf59SThomas Veerman 	 * and, as such, shouldn't really be created.
11362e2caf59SThomas Veerman 	 */
11372e2caf59SThomas Veerman 	return;
11382e2caf59SThomas Veerman     }
11392e2caf59SThomas Veerman 
11402e2caf59SThomas Veerman     if (!silent || NoExecute(gn)) {
11412e2caf59SThomas Veerman 	(void)fprintf(stdout, "touch %s\n", gn->name);
11422e2caf59SThomas Veerman 	(void)fflush(stdout);
11432e2caf59SThomas Veerman     }
11442e2caf59SThomas Veerman 
11452e2caf59SThomas Veerman     if (NoExecute(gn)) {
11462e2caf59SThomas Veerman 	return;
11472e2caf59SThomas Veerman     }
11482e2caf59SThomas Veerman 
11492e2caf59SThomas Veerman     if (gn->type & OP_ARCHV) {
11502e2caf59SThomas Veerman 	Arch_Touch(gn);
11512e2caf59SThomas Veerman     } else if (gn->type & OP_LIB) {
11522e2caf59SThomas Veerman 	Arch_TouchLib(gn);
11532e2caf59SThomas Veerman     } else {
11542e2caf59SThomas Veerman 	char	*file = gn->path ? gn->path : gn->name;
11552e2caf59SThomas Veerman 
11562e2caf59SThomas Veerman 	times.actime = times.modtime = now;
11572e2caf59SThomas Veerman 	if (utime(file, &times) < 0){
11582e2caf59SThomas Veerman 	    streamID = open(file, O_RDWR | O_CREAT, 0666);
11592e2caf59SThomas Veerman 
11602e2caf59SThomas Veerman 	    if (streamID >= 0) {
11612e2caf59SThomas Veerman 		char	c;
11622e2caf59SThomas Veerman 
11632e2caf59SThomas Veerman 		/*
11642e2caf59SThomas Veerman 		 * Read and write a byte to the file to change the
11652e2caf59SThomas Veerman 		 * modification time, then close the file.
11662e2caf59SThomas Veerman 		 */
11672e2caf59SThomas Veerman 		if (read(streamID, &c, 1) == 1) {
11682e2caf59SThomas Veerman 		    (void)lseek(streamID, (off_t)0, SEEK_SET);
116984d9c625SLionel Sambuc 		    while (write(streamID, &c, 1) == -1 && errno == EAGAIN)
117084d9c625SLionel Sambuc 			continue;
11712e2caf59SThomas Veerman 		}
11722e2caf59SThomas Veerman 
11732e2caf59SThomas Veerman 		(void)close(streamID);
11742e2caf59SThomas Veerman 	    } else {
11752e2caf59SThomas Veerman 		(void)fprintf(stdout, "*** couldn't touch %s: %s",
11762e2caf59SThomas Veerman 			       file, strerror(errno));
11772e2caf59SThomas Veerman 		(void)fflush(stdout);
11782e2caf59SThomas Veerman 	    }
11792e2caf59SThomas Veerman 	}
11802e2caf59SThomas Veerman     }
11812e2caf59SThomas Veerman }
11822e2caf59SThomas Veerman 
11832e2caf59SThomas Veerman /*-
11842e2caf59SThomas Veerman  *-----------------------------------------------------------------------
11852e2caf59SThomas Veerman  * Job_CheckCommands --
11862e2caf59SThomas Veerman  *	Make sure the given node has all the commands it needs.
11872e2caf59SThomas Veerman  *
11882e2caf59SThomas Veerman  * Input:
11892e2caf59SThomas Veerman  *	gn		The target whose commands need verifying
11902e2caf59SThomas Veerman  *	abortProc	Function to abort with message
11912e2caf59SThomas Veerman  *
11922e2caf59SThomas Veerman  * Results:
11932e2caf59SThomas Veerman  *	TRUE if the commands list is/was ok.
11942e2caf59SThomas Veerman  *
11952e2caf59SThomas Veerman  * Side Effects:
11962e2caf59SThomas Veerman  *	The node will have commands from the .DEFAULT rule added to it
11972e2caf59SThomas Veerman  *	if it needs them.
11982e2caf59SThomas Veerman  *-----------------------------------------------------------------------
11992e2caf59SThomas Veerman  */
12002e2caf59SThomas Veerman Boolean
Job_CheckCommands(GNode * gn,void (* abortProc)(const char *,...))12012e2caf59SThomas Veerman Job_CheckCommands(GNode *gn, void (*abortProc)(const char *, ...))
12022e2caf59SThomas Veerman {
12032e2caf59SThomas Veerman     if (OP_NOP(gn->type) && Lst_IsEmpty(gn->commands) &&
12042e2caf59SThomas Veerman 	((gn->type & OP_LIB) == 0 || Lst_IsEmpty(gn->children))) {
12052e2caf59SThomas Veerman 	/*
12062e2caf59SThomas Veerman 	 * No commands. Look for .DEFAULT rule from which we might infer
12072e2caf59SThomas Veerman 	 * commands
12082e2caf59SThomas Veerman 	 */
12092e2caf59SThomas Veerman 	if ((DEFAULT != NULL) && !Lst_IsEmpty(DEFAULT->commands) &&
12102e2caf59SThomas Veerman 		(gn->type & OP_SPECIAL) == 0) {
12112e2caf59SThomas Veerman 	    char *p1;
12122e2caf59SThomas Veerman 	    /*
12132e2caf59SThomas Veerman 	     * Make only looks for a .DEFAULT if the node was never the
12142e2caf59SThomas Veerman 	     * target of an operator, so that's what we do too. If
12152e2caf59SThomas Veerman 	     * a .DEFAULT was given, we substitute its commands for gn's
12162e2caf59SThomas Veerman 	     * commands and set the IMPSRC variable to be the target's name
12172e2caf59SThomas Veerman 	     * The DEFAULT node acts like a transformation rule, in that
12182e2caf59SThomas Veerman 	     * gn also inherits any attributes or sources attached to
12192e2caf59SThomas Veerman 	     * .DEFAULT itself.
12202e2caf59SThomas Veerman 	     */
12212e2caf59SThomas Veerman 	    Make_HandleUse(DEFAULT, gn);
12222e2caf59SThomas Veerman 	    Var_Set(IMPSRC, Var_Value(TARGET, gn, &p1), gn, 0);
12232e2caf59SThomas Veerman 	    if (p1)
12242e2caf59SThomas Veerman 		free(p1);
12252e2caf59SThomas Veerman 	} else if (Dir_MTime(gn, 0) == 0 && (gn->type & OP_SPECIAL) == 0) {
12262e2caf59SThomas Veerman 	    /*
12272e2caf59SThomas Veerman 	     * The node wasn't the target of an operator we have no .DEFAULT
12282e2caf59SThomas Veerman 	     * rule to go on and the target doesn't already exist. There's
12292e2caf59SThomas Veerman 	     * nothing more we can do for this branch. If the -k flag wasn't
12302e2caf59SThomas Veerman 	     * given, we stop in our tracks, otherwise we just don't update
12312e2caf59SThomas Veerman 	     * this node's parents so they never get examined.
12322e2caf59SThomas Veerman 	     */
12332e2caf59SThomas Veerman 	    static const char msg[] = ": don't know how to make";
12342e2caf59SThomas Veerman 
12352e2caf59SThomas Veerman 	    if (gn->flags & FROM_DEPEND) {
123684d9c625SLionel Sambuc 		if (!Job_RunTarget(".STALE", gn->fname))
123784d9c625SLionel Sambuc 		    fprintf(stdout, "%s: %s, %d: ignoring stale %s for %s\n",
123884d9c625SLionel Sambuc 			progname, gn->fname, gn->lineno, makeDependfile,
123984d9c625SLionel Sambuc 			gn->name);
12402e2caf59SThomas Veerman 		return TRUE;
12412e2caf59SThomas Veerman 	    }
12422e2caf59SThomas Veerman 
12432e2caf59SThomas Veerman 	    if (gn->type & OP_OPTIONAL) {
12442e2caf59SThomas Veerman 		(void)fprintf(stdout, "%s%s %s (ignored)\n", progname,
12452e2caf59SThomas Veerman 		    msg, gn->name);
12462e2caf59SThomas Veerman 		(void)fflush(stdout);
12472e2caf59SThomas Veerman 	    } else if (keepgoing) {
12482e2caf59SThomas Veerman 		(void)fprintf(stdout, "%s%s %s (continuing)\n", progname,
12492e2caf59SThomas Veerman 		    msg, gn->name);
12502e2caf59SThomas Veerman 		(void)fflush(stdout);
12512e2caf59SThomas Veerman   		return FALSE;
12522e2caf59SThomas Veerman 	    } else {
12532e2caf59SThomas Veerman 		(*abortProc)("%s%s %s. Stop", progname, msg, gn->name);
12542e2caf59SThomas Veerman 		return FALSE;
12552e2caf59SThomas Veerman 	    }
12562e2caf59SThomas Veerman 	}
12572e2caf59SThomas Veerman     }
12582e2caf59SThomas Veerman     return TRUE;
12592e2caf59SThomas Veerman }
12602e2caf59SThomas Veerman 
12612e2caf59SThomas Veerman /*-
12622e2caf59SThomas Veerman  *-----------------------------------------------------------------------
12632e2caf59SThomas Veerman  * JobExec --
12642e2caf59SThomas Veerman  *	Execute the shell for the given job. Called from JobStart
12652e2caf59SThomas Veerman  *
12662e2caf59SThomas Veerman  * Input:
12672e2caf59SThomas Veerman  *	job		Job to execute
12682e2caf59SThomas Veerman  *
12692e2caf59SThomas Veerman  * Results:
12702e2caf59SThomas Veerman  *	None.
12712e2caf59SThomas Veerman  *
12722e2caf59SThomas Veerman  * Side Effects:
12732e2caf59SThomas Veerman  *	A shell is executed, outputs is altered and the Job structure added
12742e2caf59SThomas Veerman  *	to the job table.
12752e2caf59SThomas Veerman  *
12762e2caf59SThomas Veerman  *-----------------------------------------------------------------------
12772e2caf59SThomas Veerman  */
12782e2caf59SThomas Veerman static void
JobExec(Job * job,char ** argv)12792e2caf59SThomas Veerman JobExec(Job *job, char **argv)
12802e2caf59SThomas Veerman {
12812e2caf59SThomas Veerman     int	    	  cpid;	    	/* ID of new child */
12822e2caf59SThomas Veerman     sigset_t	  mask;
12832e2caf59SThomas Veerman 
12842e2caf59SThomas Veerman     job->flags &= ~JOB_TRACED;
12852e2caf59SThomas Veerman 
12862e2caf59SThomas Veerman     if (DEBUG(JOB)) {
12872e2caf59SThomas Veerman 	int 	  i;
12882e2caf59SThomas Veerman 
12892e2caf59SThomas Veerman 	(void)fprintf(debug_file, "Running %s %sly\n", job->node->name, "local");
12902e2caf59SThomas Veerman 	(void)fprintf(debug_file, "\tCommand: ");
12912e2caf59SThomas Veerman 	for (i = 0; argv[i] != NULL; i++) {
12922e2caf59SThomas Veerman 	    (void)fprintf(debug_file, "%s ", argv[i]);
12932e2caf59SThomas Veerman 	}
12942e2caf59SThomas Veerman  	(void)fprintf(debug_file, "\n");
12952e2caf59SThomas Veerman     }
12962e2caf59SThomas Veerman 
12972e2caf59SThomas Veerman     /*
12982e2caf59SThomas Veerman      * Some jobs produce no output and it's disconcerting to have
12992e2caf59SThomas Veerman      * no feedback of their running (since they produce no output, the
13002e2caf59SThomas Veerman      * banner with their name in it never appears). This is an attempt to
13012e2caf59SThomas Veerman      * provide that feedback, even if nothing follows it.
13022e2caf59SThomas Veerman      */
13032e2caf59SThomas Veerman     if ((lastNode != job->node) && !(job->flags & JOB_SILENT)) {
13042e2caf59SThomas Veerman 	MESSAGE(stdout, job->node);
13052e2caf59SThomas Veerman 	lastNode = job->node;
13062e2caf59SThomas Veerman     }
13072e2caf59SThomas Veerman 
13082e2caf59SThomas Veerman     /* No interruptions until this job is on the `jobs' list */
13092e2caf59SThomas Veerman     JobSigLock(&mask);
13102e2caf59SThomas Veerman 
13112e2caf59SThomas Veerman     /* Pre-emptively mark job running, pid still zero though */
13122e2caf59SThomas Veerman     job->job_state = JOB_ST_RUNNING;
13132e2caf59SThomas Veerman 
13142e2caf59SThomas Veerman     cpid = vFork();
13152e2caf59SThomas Veerman     if (cpid == -1)
13162e2caf59SThomas Veerman 	Punt("Cannot vfork: %s", strerror(errno));
13172e2caf59SThomas Veerman 
13182e2caf59SThomas Veerman     if (cpid == 0) {
13192e2caf59SThomas Veerman 	/* Child */
13202e2caf59SThomas Veerman 	sigset_t tmask;
13212e2caf59SThomas Veerman 
13222e2caf59SThomas Veerman #ifdef USE_META
13232e2caf59SThomas Veerman 	if (useMeta) {
13242e2caf59SThomas Veerman 	    meta_job_child(job);
13252e2caf59SThomas Veerman 	}
13262e2caf59SThomas Veerman #endif
13272e2caf59SThomas Veerman 	/*
13282e2caf59SThomas Veerman 	 * Reset all signal handlers; this is necessary because we also
13292e2caf59SThomas Veerman 	 * need to unblock signals before we exec(2).
13302e2caf59SThomas Veerman 	 */
13312e2caf59SThomas Veerman 	JobSigReset();
13322e2caf59SThomas Veerman 
13332e2caf59SThomas Veerman 	/* Now unblock signals */
13342e2caf59SThomas Veerman 	sigemptyset(&tmask);
13352e2caf59SThomas Veerman 	JobSigUnlock(&tmask);
13362e2caf59SThomas Veerman 
13372e2caf59SThomas Veerman 	/*
13382e2caf59SThomas Veerman 	 * Must duplicate the input stream down to the child's input and
13392e2caf59SThomas Veerman 	 * reset it to the beginning (again). Since the stream was marked
13402e2caf59SThomas Veerman 	 * close-on-exec, we must clear that bit in the new input.
13412e2caf59SThomas Veerman 	 */
13422e2caf59SThomas Veerman 	if (dup2(FILENO(job->cmdFILE), 0) == -1) {
13432e2caf59SThomas Veerman 	    execError("dup2", "job->cmdFILE");
13442e2caf59SThomas Veerman 	    _exit(1);
13452e2caf59SThomas Veerman 	}
13462e2caf59SThomas Veerman 	(void)fcntl(0, F_SETFD, 0);
13472e2caf59SThomas Veerman 	(void)lseek(0, (off_t)0, SEEK_SET);
13482e2caf59SThomas Veerman 
1349*0a6a1f1dSLionel Sambuc 	if (job->node->type & (OP_MAKE | OP_SUBMAKE)) {
13502e2caf59SThomas Veerman 		/*
13512e2caf59SThomas Veerman 		 * Pass job token pipe to submakes.
13522e2caf59SThomas Veerman 		 */
13532e2caf59SThomas Veerman 		fcntl(tokenWaitJob.inPipe, F_SETFD, 0);
13542e2caf59SThomas Veerman 		fcntl(tokenWaitJob.outPipe, F_SETFD, 0);
13552e2caf59SThomas Veerman 	}
13562e2caf59SThomas Veerman 
13572e2caf59SThomas Veerman 	/*
13582e2caf59SThomas Veerman 	 * Set up the child's output to be routed through the pipe
13592e2caf59SThomas Veerman 	 * we've created for it.
13602e2caf59SThomas Veerman 	 */
13612e2caf59SThomas Veerman 	if (dup2(job->outPipe, 1) == -1) {
13622e2caf59SThomas Veerman 	    execError("dup2", "job->outPipe");
13632e2caf59SThomas Veerman 	    _exit(1);
13642e2caf59SThomas Veerman 	}
13652e2caf59SThomas Veerman 	/*
13662e2caf59SThomas Veerman 	 * The output channels are marked close on exec. This bit was
13672e2caf59SThomas Veerman 	 * duplicated by the dup2(on some systems), so we have to clear
13682e2caf59SThomas Veerman 	 * it before routing the shell's error output to the same place as
13692e2caf59SThomas Veerman 	 * its standard output.
13702e2caf59SThomas Veerman 	 */
13712e2caf59SThomas Veerman 	(void)fcntl(1, F_SETFD, 0);
13722e2caf59SThomas Veerman 	if (dup2(1, 2) == -1) {
13732e2caf59SThomas Veerman 	    execError("dup2", "1, 2");
13742e2caf59SThomas Veerman 	    _exit(1);
13752e2caf59SThomas Veerman 	}
13762e2caf59SThomas Veerman 
13772e2caf59SThomas Veerman 	/*
13782e2caf59SThomas Veerman 	 * We want to switch the child into a different process family so
13792e2caf59SThomas Veerman 	 * we can kill it and all its descendants in one fell swoop,
13802e2caf59SThomas Veerman 	 * by killing its process family, but not commit suicide.
13812e2caf59SThomas Veerman 	 */
138284d9c625SLionel Sambuc #if defined(MAKE_NATIVE) || defined(HAVE_SETPGID)
13837d98eee8SLionel Sambuc #if defined(SYSV)
13842e2caf59SThomas Veerman 	/* XXX: dsl - I'm sure this should be setpgrp()... */
13852e2caf59SThomas Veerman 	(void)setsid();
13862e2caf59SThomas Veerman #else
13872e2caf59SThomas Veerman 	(void)setpgid(0, getpid());
13882e2caf59SThomas Veerman #endif
138984d9c625SLionel Sambuc #endif
13902e2caf59SThomas Veerman 
13912e2caf59SThomas Veerman 	Var_ExportVars();
13922e2caf59SThomas Veerman 
13932e2caf59SThomas Veerman 	(void)execv(shellPath, argv);
13942e2caf59SThomas Veerman 	execError("exec", shellPath);
13952e2caf59SThomas Veerman 	_exit(1);
13962e2caf59SThomas Veerman     }
13972e2caf59SThomas Veerman 
13982e2caf59SThomas Veerman     /* Parent, continuing after the child exec */
13992e2caf59SThomas Veerman     job->pid = cpid;
14002e2caf59SThomas Veerman 
14012e2caf59SThomas Veerman     Trace_Log(JOBSTART, job);
14022e2caf59SThomas Veerman 
14032e2caf59SThomas Veerman     /*
14042e2caf59SThomas Veerman      * Set the current position in the buffer to the beginning
14052e2caf59SThomas Veerman      * and mark another stream to watch in the outputs mask
14062e2caf59SThomas Veerman      */
14072e2caf59SThomas Veerman     job->curPos = 0;
14082e2caf59SThomas Veerman 
14092e2caf59SThomas Veerman     watchfd(job);
14102e2caf59SThomas Veerman 
14112e2caf59SThomas Veerman     if (job->cmdFILE != NULL && job->cmdFILE != stdout) {
14122e2caf59SThomas Veerman 	(void)fclose(job->cmdFILE);
14132e2caf59SThomas Veerman 	job->cmdFILE = NULL;
14142e2caf59SThomas Veerman     }
14152e2caf59SThomas Veerman 
14162e2caf59SThomas Veerman     /*
14172e2caf59SThomas Veerman      * Now the job is actually running, add it to the table.
14182e2caf59SThomas Veerman      */
14192e2caf59SThomas Veerman     if (DEBUG(JOB)) {
14202e2caf59SThomas Veerman 	fprintf(debug_file, "JobExec(%s): pid %d added to jobs table\n",
14212e2caf59SThomas Veerman 		job->node->name, job->pid);
14222e2caf59SThomas Veerman 	job_table_dump("job started");
14232e2caf59SThomas Veerman     }
14242e2caf59SThomas Veerman     JobSigUnlock(&mask);
14252e2caf59SThomas Veerman }
14262e2caf59SThomas Veerman 
14272e2caf59SThomas Veerman /*-
14282e2caf59SThomas Veerman  *-----------------------------------------------------------------------
14292e2caf59SThomas Veerman  * JobMakeArgv --
14302e2caf59SThomas Veerman  *	Create the argv needed to execute the shell for a given job.
14312e2caf59SThomas Veerman  *
14322e2caf59SThomas Veerman  *
14332e2caf59SThomas Veerman  * Results:
14342e2caf59SThomas Veerman  *
14352e2caf59SThomas Veerman  * Side Effects:
14362e2caf59SThomas Veerman  *
14372e2caf59SThomas Veerman  *-----------------------------------------------------------------------
14382e2caf59SThomas Veerman  */
14392e2caf59SThomas Veerman static void
JobMakeArgv(Job * job,char ** argv)14402e2caf59SThomas Veerman JobMakeArgv(Job *job, char **argv)
14412e2caf59SThomas Veerman {
14422e2caf59SThomas Veerman     int	    	  argc;
14432e2caf59SThomas Veerman     static char args[10]; 	/* For merged arguments */
14442e2caf59SThomas Veerman 
14452e2caf59SThomas Veerman     argv[0] = UNCONST(shellName);
14462e2caf59SThomas Veerman     argc = 1;
14472e2caf59SThomas Veerman 
14482e2caf59SThomas Veerman     if ((commandShell->exit && (*commandShell->exit != '-')) ||
14492e2caf59SThomas Veerman 	(commandShell->echo && (*commandShell->echo != '-')))
14502e2caf59SThomas Veerman     {
14512e2caf59SThomas Veerman 	/*
14522e2caf59SThomas Veerman 	 * At least one of the flags doesn't have a minus before it, so
14532e2caf59SThomas Veerman 	 * merge them together. Have to do this because the *(&(@*#*&#$#
14542e2caf59SThomas Veerman 	 * Bourne shell thinks its second argument is a file to source.
14552e2caf59SThomas Veerman 	 * Grrrr. Note the ten-character limitation on the combined arguments.
14562e2caf59SThomas Veerman 	 */
14572e2caf59SThomas Veerman 	(void)snprintf(args, sizeof(args), "-%s%s",
14582e2caf59SThomas Veerman 		      ((job->flags & JOB_IGNERR) ? "" :
14592e2caf59SThomas Veerman 		       (commandShell->exit ? commandShell->exit : "")),
14602e2caf59SThomas Veerman 		      ((job->flags & JOB_SILENT) ? "" :
14612e2caf59SThomas Veerman 		       (commandShell->echo ? commandShell->echo : "")));
14622e2caf59SThomas Veerman 
14632e2caf59SThomas Veerman 	if (args[1]) {
14642e2caf59SThomas Veerman 	    argv[argc] = args;
14652e2caf59SThomas Veerman 	    argc++;
14662e2caf59SThomas Veerman 	}
14672e2caf59SThomas Veerman     } else {
14682e2caf59SThomas Veerman 	if (!(job->flags & JOB_IGNERR) && commandShell->exit) {
14692e2caf59SThomas Veerman 	    argv[argc] = UNCONST(commandShell->exit);
14702e2caf59SThomas Veerman 	    argc++;
14712e2caf59SThomas Veerman 	}
14722e2caf59SThomas Veerman 	if (!(job->flags & JOB_SILENT) && commandShell->echo) {
14732e2caf59SThomas Veerman 	    argv[argc] = UNCONST(commandShell->echo);
14742e2caf59SThomas Veerman 	    argc++;
14752e2caf59SThomas Veerman 	}
14762e2caf59SThomas Veerman     }
14772e2caf59SThomas Veerman     argv[argc] = NULL;
14782e2caf59SThomas Veerman }
14792e2caf59SThomas Veerman 
14802e2caf59SThomas Veerman /*-
14812e2caf59SThomas Veerman  *-----------------------------------------------------------------------
14822e2caf59SThomas Veerman  * JobStart  --
14832e2caf59SThomas Veerman  *	Start a target-creation process going for the target described
14842e2caf59SThomas Veerman  *	by the graph node gn.
14852e2caf59SThomas Veerman  *
14862e2caf59SThomas Veerman  * Input:
14872e2caf59SThomas Veerman  *	gn		target to create
14882e2caf59SThomas Veerman  *	flags		flags for the job to override normal ones.
14892e2caf59SThomas Veerman  *			e.g. JOB_SPECIAL or JOB_IGNDOTS
14902e2caf59SThomas Veerman  *	previous	The previous Job structure for this node, if any.
14912e2caf59SThomas Veerman  *
14922e2caf59SThomas Veerman  * Results:
14932e2caf59SThomas Veerman  *	JOB_ERROR if there was an error in the commands, JOB_FINISHED
14942e2caf59SThomas Veerman  *	if there isn't actually anything left to do for the job and
14952e2caf59SThomas Veerman  *	JOB_RUNNING if the job has been started.
14962e2caf59SThomas Veerman  *
14972e2caf59SThomas Veerman  * Side Effects:
14982e2caf59SThomas Veerman  *	A new Job node is created and added to the list of running
14992e2caf59SThomas Veerman  *	jobs. PMake is forked and a child shell created.
15002e2caf59SThomas Veerman  *
15012e2caf59SThomas Veerman  * NB: I'm fairly sure that this code is never called with JOB_SPECIAL set
15022e2caf59SThomas Veerman  *     JOB_IGNDOTS is never set (dsl)
15032e2caf59SThomas Veerman  *     Also the return value is ignored by everyone.
15042e2caf59SThomas Veerman  *-----------------------------------------------------------------------
15052e2caf59SThomas Veerman  */
15062e2caf59SThomas Veerman static int
JobStart(GNode * gn,int flags)15072e2caf59SThomas Veerman JobStart(GNode *gn, int flags)
15082e2caf59SThomas Veerman {
15092e2caf59SThomas Veerman     Job		  *job;       /* new job descriptor */
15102e2caf59SThomas Veerman     char	  *argv[10];  /* Argument vector to shell */
15112e2caf59SThomas Veerman     Boolean	  cmdsOK;     /* true if the nodes commands were all right */
15122e2caf59SThomas Veerman     Boolean 	  noExec;     /* Set true if we decide not to run the job */
15132e2caf59SThomas Veerman     int		  tfd;	      /* File descriptor to the temp file */
15142e2caf59SThomas Veerman 
15152e2caf59SThomas Veerman     for (job = job_table; job < job_table_end; job++) {
15162e2caf59SThomas Veerman 	if (job->job_state == JOB_ST_FREE)
15172e2caf59SThomas Veerman 	    break;
15182e2caf59SThomas Veerman     }
15192e2caf59SThomas Veerman     if (job >= job_table_end)
15202e2caf59SThomas Veerman 	Punt("JobStart no job slots vacant");
15212e2caf59SThomas Veerman 
15222e2caf59SThomas Veerman     memset(job, 0, sizeof *job);
15232e2caf59SThomas Veerman     job->job_state = JOB_ST_SETUP;
15242e2caf59SThomas Veerman     if (gn->type & OP_SPECIAL)
15252e2caf59SThomas Veerman 	flags |= JOB_SPECIAL;
15262e2caf59SThomas Veerman 
15272e2caf59SThomas Veerman     job->node = gn;
15282e2caf59SThomas Veerman     job->tailCmds = NULL;
15292e2caf59SThomas Veerman 
15302e2caf59SThomas Veerman     /*
15312e2caf59SThomas Veerman      * Set the initial value of the flags for this job based on the global
15322e2caf59SThomas Veerman      * ones and the node's attributes... Any flags supplied by the caller
15332e2caf59SThomas Veerman      * are also added to the field.
15342e2caf59SThomas Veerman      */
15352e2caf59SThomas Veerman     job->flags = 0;
15362e2caf59SThomas Veerman     if (Targ_Ignore(gn)) {
15372e2caf59SThomas Veerman 	job->flags |= JOB_IGNERR;
15382e2caf59SThomas Veerman     }
15392e2caf59SThomas Veerman     if (Targ_Silent(gn)) {
15402e2caf59SThomas Veerman 	job->flags |= JOB_SILENT;
15412e2caf59SThomas Veerman     }
15422e2caf59SThomas Veerman     job->flags |= flags;
15432e2caf59SThomas Veerman 
15442e2caf59SThomas Veerman     /*
15452e2caf59SThomas Veerman      * Check the commands now so any attributes from .DEFAULT have a chance
15462e2caf59SThomas Veerman      * to migrate to the node
15472e2caf59SThomas Veerman      */
15482e2caf59SThomas Veerman     cmdsOK = Job_CheckCommands(gn, Error);
15492e2caf59SThomas Veerman 
15502e2caf59SThomas Veerman     job->inPollfd = NULL;
15512e2caf59SThomas Veerman     /*
15522e2caf59SThomas Veerman      * If the -n flag wasn't given, we open up OUR (not the child's)
15532e2caf59SThomas Veerman      * temporary file to stuff commands in it. The thing is rd/wr so we don't
15542e2caf59SThomas Veerman      * need to reopen it to feed it to the shell. If the -n flag *was* given,
15552e2caf59SThomas Veerman      * we just set the file to be stdout. Cute, huh?
15562e2caf59SThomas Veerman      */
15572e2caf59SThomas Veerman     if (((gn->type & OP_MAKE) && !(noRecursiveExecute)) ||
15582e2caf59SThomas Veerman 	    (!noExecute && !touchFlag)) {
15592e2caf59SThomas Veerman 	/*
15602e2caf59SThomas Veerman 	 * tfile is the name of a file into which all shell commands are
15612e2caf59SThomas Veerman 	 * put. It is removed before the child shell is executed, unless
15622e2caf59SThomas Veerman 	 * DEBUG(SCRIPT) is set.
15632e2caf59SThomas Veerman 	 */
15642e2caf59SThomas Veerman 	char *tfile;
15652e2caf59SThomas Veerman 	sigset_t mask;
15662e2caf59SThomas Veerman 	/*
15672e2caf59SThomas Veerman 	 * We're serious here, but if the commands were bogus, we're
15682e2caf59SThomas Veerman 	 * also dead...
15692e2caf59SThomas Veerman 	 */
15702e2caf59SThomas Veerman 	if (!cmdsOK) {
15712e2caf59SThomas Veerman 	    PrintOnError(gn, NULL);	/* provide some clue */
15722e2caf59SThomas Veerman 	    DieHorribly();
15732e2caf59SThomas Veerman 	}
15742e2caf59SThomas Veerman 
15752e2caf59SThomas Veerman 	JobSigLock(&mask);
15762e2caf59SThomas Veerman 	tfd = mkTempFile(TMPPAT, &tfile);
15772e2caf59SThomas Veerman 	if (!DEBUG(SCRIPT))
15782e2caf59SThomas Veerman 		(void)eunlink(tfile);
15792e2caf59SThomas Veerman 	JobSigUnlock(&mask);
15802e2caf59SThomas Veerman 
15812e2caf59SThomas Veerman 	job->cmdFILE = fdopen(tfd, "w+");
15822e2caf59SThomas Veerman 	if (job->cmdFILE == NULL) {
15832e2caf59SThomas Veerman 	    Punt("Could not fdopen %s", tfile);
15842e2caf59SThomas Veerman 	}
15852e2caf59SThomas Veerman 	(void)fcntl(FILENO(job->cmdFILE), F_SETFD, 1);
15862e2caf59SThomas Veerman 	/*
15872e2caf59SThomas Veerman 	 * Send the commands to the command file, flush all its buffers then
15882e2caf59SThomas Veerman 	 * rewind and remove the thing.
15892e2caf59SThomas Veerman 	 */
15902e2caf59SThomas Veerman 	noExec = FALSE;
15912e2caf59SThomas Veerman 
15922e2caf59SThomas Veerman #ifdef USE_META
15932e2caf59SThomas Veerman 	if (useMeta) {
15942e2caf59SThomas Veerman 	    meta_job_start(job, gn);
15952e2caf59SThomas Veerman 	    if (Targ_Silent(gn)) {	/* might have changed */
15962e2caf59SThomas Veerman 		job->flags |= JOB_SILENT;
15972e2caf59SThomas Veerman 	    }
15982e2caf59SThomas Veerman 	}
15992e2caf59SThomas Veerman #endif
16002e2caf59SThomas Veerman 	/*
16012e2caf59SThomas Veerman 	 * We can do all the commands at once. hooray for sanity
16022e2caf59SThomas Veerman 	 */
16032e2caf59SThomas Veerman 	numCommands = 0;
16042e2caf59SThomas Veerman 	Lst_ForEach(gn->commands, JobPrintCommand, job);
16052e2caf59SThomas Veerman 
16062e2caf59SThomas Veerman 	/*
16072e2caf59SThomas Veerman 	 * If we didn't print out any commands to the shell script,
16082e2caf59SThomas Veerman 	 * there's not much point in executing the shell, is there?
16092e2caf59SThomas Veerman 	 */
16102e2caf59SThomas Veerman 	if (numCommands == 0) {
16112e2caf59SThomas Veerman 	    noExec = TRUE;
16122e2caf59SThomas Veerman 	}
16132e2caf59SThomas Veerman 
16142e2caf59SThomas Veerman 	free(tfile);
16152e2caf59SThomas Veerman     } else if (NoExecute(gn)) {
16162e2caf59SThomas Veerman 	/*
16172e2caf59SThomas Veerman 	 * Not executing anything -- just print all the commands to stdout
16182e2caf59SThomas Veerman 	 * in one fell swoop. This will still set up job->tailCmds correctly.
16192e2caf59SThomas Veerman 	 */
16202e2caf59SThomas Veerman 	if (lastNode != gn) {
16212e2caf59SThomas Veerman 	    MESSAGE(stdout, gn);
16222e2caf59SThomas Veerman 	    lastNode = gn;
16232e2caf59SThomas Veerman 	}
16242e2caf59SThomas Veerman 	job->cmdFILE = stdout;
16252e2caf59SThomas Veerman 	/*
16262e2caf59SThomas Veerman 	 * Only print the commands if they're ok, but don't die if they're
16272e2caf59SThomas Veerman 	 * not -- just let the user know they're bad and keep going. It
16282e2caf59SThomas Veerman 	 * doesn't do any harm in this case and may do some good.
16292e2caf59SThomas Veerman 	 */
16302e2caf59SThomas Veerman 	if (cmdsOK) {
16312e2caf59SThomas Veerman 	    Lst_ForEach(gn->commands, JobPrintCommand, job);
16322e2caf59SThomas Veerman 	}
16332e2caf59SThomas Veerman 	/*
16342e2caf59SThomas Veerman 	 * Don't execute the shell, thank you.
16352e2caf59SThomas Veerman 	 */
16362e2caf59SThomas Veerman 	noExec = TRUE;
16372e2caf59SThomas Veerman     } else {
16382e2caf59SThomas Veerman 	/*
16392e2caf59SThomas Veerman 	 * Just touch the target and note that no shell should be executed.
16402e2caf59SThomas Veerman 	 * Set cmdFILE to stdout to make life easier. Check the commands, too,
16412e2caf59SThomas Veerman 	 * but don't die if they're no good -- it does no harm to keep working
16422e2caf59SThomas Veerman 	 * up the graph.
16432e2caf59SThomas Veerman 	 */
16442e2caf59SThomas Veerman 	job->cmdFILE = stdout;
16452e2caf59SThomas Veerman     	Job_Touch(gn, job->flags&JOB_SILENT);
16462e2caf59SThomas Veerman 	noExec = TRUE;
16472e2caf59SThomas Veerman     }
16482e2caf59SThomas Veerman     /* Just in case it isn't already... */
16492e2caf59SThomas Veerman     (void)fflush(job->cmdFILE);
16502e2caf59SThomas Veerman 
16512e2caf59SThomas Veerman     /*
16522e2caf59SThomas Veerman      * If we're not supposed to execute a shell, don't.
16532e2caf59SThomas Veerman      */
16542e2caf59SThomas Veerman     if (noExec) {
16552e2caf59SThomas Veerman 	if (!(job->flags & JOB_SPECIAL))
16562e2caf59SThomas Veerman 	    Job_TokenReturn();
16572e2caf59SThomas Veerman 	/*
16582e2caf59SThomas Veerman 	 * Unlink and close the command file if we opened one
16592e2caf59SThomas Veerman 	 */
16602e2caf59SThomas Veerman 	if (job->cmdFILE != stdout) {
16612e2caf59SThomas Veerman 	    if (job->cmdFILE != NULL) {
16622e2caf59SThomas Veerman 		(void)fclose(job->cmdFILE);
16632e2caf59SThomas Veerman 		job->cmdFILE = NULL;
16642e2caf59SThomas Veerman 	    }
16652e2caf59SThomas Veerman 	}
16662e2caf59SThomas Veerman 
16672e2caf59SThomas Veerman 	/*
16682e2caf59SThomas Veerman 	 * We only want to work our way up the graph if we aren't here because
16692e2caf59SThomas Veerman 	 * the commands for the job were no good.
16702e2caf59SThomas Veerman 	 */
16712e2caf59SThomas Veerman 	if (cmdsOK && aborting == 0) {
16722e2caf59SThomas Veerman 	    if (job->tailCmds != NULL) {
16732e2caf59SThomas Veerman 		Lst_ForEachFrom(job->node->commands, job->tailCmds,
16742e2caf59SThomas Veerman 				JobSaveCommand,
16752e2caf59SThomas Veerman 			       job->node);
16762e2caf59SThomas Veerman 	    }
16772e2caf59SThomas Veerman 	    job->node->made = MADE;
16782e2caf59SThomas Veerman 	    Make_Update(job->node);
16792e2caf59SThomas Veerman 	}
16802e2caf59SThomas Veerman 	job->job_state = JOB_ST_FREE;
16812e2caf59SThomas Veerman 	return cmdsOK ? JOB_FINISHED : JOB_ERROR;
16822e2caf59SThomas Veerman     }
16832e2caf59SThomas Veerman 
16842e2caf59SThomas Veerman     /*
16852e2caf59SThomas Veerman      * Set up the control arguments to the shell. This is based on the flags
16862e2caf59SThomas Veerman      * set earlier for this job.
16872e2caf59SThomas Veerman      */
16882e2caf59SThomas Veerman     JobMakeArgv(job, argv);
16892e2caf59SThomas Veerman 
16902e2caf59SThomas Veerman     /* Create the pipe by which we'll get the shell's output.  */
16912e2caf59SThomas Veerman     JobCreatePipe(job, 3);
16922e2caf59SThomas Veerman 
16932e2caf59SThomas Veerman     JobExec(job, argv);
16942e2caf59SThomas Veerman     return(JOB_RUNNING);
16952e2caf59SThomas Veerman }
16962e2caf59SThomas Veerman 
16972e2caf59SThomas Veerman static char *
JobOutput(Job * job,char * cp,char * endp,int msg)16982e2caf59SThomas Veerman JobOutput(Job *job, char *cp, char *endp, int msg)
16992e2caf59SThomas Veerman {
17002e2caf59SThomas Veerman     char *ecp;
17012e2caf59SThomas Veerman 
17022e2caf59SThomas Veerman     if (commandShell->noPrint) {
17032e2caf59SThomas Veerman 	ecp = Str_FindSubstring(cp, commandShell->noPrint);
17042e2caf59SThomas Veerman 	while (ecp != NULL) {
17052e2caf59SThomas Veerman 	    if (cp != ecp) {
17062e2caf59SThomas Veerman 		*ecp = '\0';
17072e2caf59SThomas Veerman 		if (!beSilent && msg && job->node != lastNode) {
17082e2caf59SThomas Veerman 		    MESSAGE(stdout, job->node);
17092e2caf59SThomas Veerman 		    lastNode = job->node;
17102e2caf59SThomas Veerman 		}
17112e2caf59SThomas Veerman 		/*
17122e2caf59SThomas Veerman 		 * The only way there wouldn't be a newline after
17132e2caf59SThomas Veerman 		 * this line is if it were the last in the buffer.
17142e2caf59SThomas Veerman 		 * however, since the non-printable comes after it,
17152e2caf59SThomas Veerman 		 * there must be a newline, so we don't print one.
17162e2caf59SThomas Veerman 		 */
17172e2caf59SThomas Veerman 		(void)fprintf(stdout, "%s", cp);
17182e2caf59SThomas Veerman 		(void)fflush(stdout);
17192e2caf59SThomas Veerman 	    }
17202e2caf59SThomas Veerman 	    cp = ecp + commandShell->noPLen;
17212e2caf59SThomas Veerman 	    if (cp != endp) {
17222e2caf59SThomas Veerman 		/*
17232e2caf59SThomas Veerman 		 * Still more to print, look again after skipping
17242e2caf59SThomas Veerman 		 * the whitespace following the non-printable
17252e2caf59SThomas Veerman 		 * command....
17262e2caf59SThomas Veerman 		 */
17272e2caf59SThomas Veerman 		cp++;
17282e2caf59SThomas Veerman 		while (*cp == ' ' || *cp == '\t' || *cp == '\n') {
17292e2caf59SThomas Veerman 		    cp++;
17302e2caf59SThomas Veerman 		}
17312e2caf59SThomas Veerman 		ecp = Str_FindSubstring(cp, commandShell->noPrint);
17322e2caf59SThomas Veerman 	    } else {
17332e2caf59SThomas Veerman 		return cp;
17342e2caf59SThomas Veerman 	    }
17352e2caf59SThomas Veerman 	}
17362e2caf59SThomas Veerman     }
17372e2caf59SThomas Veerman     return cp;
17382e2caf59SThomas Veerman }
17392e2caf59SThomas Veerman 
17402e2caf59SThomas Veerman /*-
17412e2caf59SThomas Veerman  *-----------------------------------------------------------------------
17422e2caf59SThomas Veerman  * JobDoOutput  --
17432e2caf59SThomas Veerman  *	This function is called at different times depending on
17442e2caf59SThomas Veerman  *	whether the user has specified that output is to be collected
17452e2caf59SThomas Veerman  *	via pipes or temporary files. In the former case, we are called
17462e2caf59SThomas Veerman  *	whenever there is something to read on the pipe. We collect more
17472e2caf59SThomas Veerman  *	output from the given job and store it in the job's outBuf. If
17482e2caf59SThomas Veerman  *	this makes up a line, we print it tagged by the job's identifier,
17492e2caf59SThomas Veerman  *	as necessary.
17502e2caf59SThomas Veerman  *	If output has been collected in a temporary file, we open the
17512e2caf59SThomas Veerman  *	file and read it line by line, transfering it to our own
17522e2caf59SThomas Veerman  *	output channel until the file is empty. At which point we
17532e2caf59SThomas Veerman  *	remove the temporary file.
17542e2caf59SThomas Veerman  *	In both cases, however, we keep our figurative eye out for the
17552e2caf59SThomas Veerman  *	'noPrint' line for the shell from which the output came. If
17562e2caf59SThomas Veerman  *	we recognize a line, we don't print it. If the command is not
17572e2caf59SThomas Veerman  *	alone on the line (the character after it is not \0 or \n), we
17582e2caf59SThomas Veerman  *	do print whatever follows it.
17592e2caf59SThomas Veerman  *
17602e2caf59SThomas Veerman  * Input:
17612e2caf59SThomas Veerman  *	job		the job whose output needs printing
17622e2caf59SThomas Veerman  *	finish		TRUE if this is the last time we'll be called
17632e2caf59SThomas Veerman  *			for this job
17642e2caf59SThomas Veerman  *
17652e2caf59SThomas Veerman  * Results:
17662e2caf59SThomas Veerman  *	None
17672e2caf59SThomas Veerman  *
17682e2caf59SThomas Veerman  * Side Effects:
17692e2caf59SThomas Veerman  *	curPos may be shifted as may the contents of outBuf.
17702e2caf59SThomas Veerman  *-----------------------------------------------------------------------
17712e2caf59SThomas Veerman  */
17722e2caf59SThomas Veerman STATIC void
JobDoOutput(Job * job,Boolean finish)17732e2caf59SThomas Veerman JobDoOutput(Job *job, Boolean finish)
17742e2caf59SThomas Veerman {
17752e2caf59SThomas Veerman     Boolean       gotNL = FALSE;  /* true if got a newline */
17762e2caf59SThomas Veerman     Boolean       fbuf;  	  /* true if our buffer filled up */
17772e2caf59SThomas Veerman     int		  nr;	      	  /* number of bytes read */
17782e2caf59SThomas Veerman     int		  i;	      	  /* auxiliary index into outBuf */
17792e2caf59SThomas Veerman     int		  max;	      	  /* limit for i (end of current data) */
17802e2caf59SThomas Veerman     int		  nRead;      	  /* (Temporary) number of bytes read */
17812e2caf59SThomas Veerman 
17822e2caf59SThomas Veerman     /*
17832e2caf59SThomas Veerman      * Read as many bytes as will fit in the buffer.
17842e2caf59SThomas Veerman      */
17852e2caf59SThomas Veerman end_loop:
17862e2caf59SThomas Veerman     gotNL = FALSE;
17872e2caf59SThomas Veerman     fbuf = FALSE;
17882e2caf59SThomas Veerman 
17892e2caf59SThomas Veerman     nRead = read(job->inPipe, &job->outBuf[job->curPos],
17902e2caf59SThomas Veerman 		     JOB_BUFSIZE - job->curPos);
17912e2caf59SThomas Veerman     if (nRead < 0) {
17922e2caf59SThomas Veerman 	if (errno == EAGAIN)
17932e2caf59SThomas Veerman 	    return;
17942e2caf59SThomas Veerman 	if (DEBUG(JOB)) {
17952e2caf59SThomas Veerman 	    perror("JobDoOutput(piperead)");
17962e2caf59SThomas Veerman 	}
17972e2caf59SThomas Veerman 	nr = 0;
17982e2caf59SThomas Veerman     } else {
17992e2caf59SThomas Veerman 	nr = nRead;
18002e2caf59SThomas Veerman     }
18012e2caf59SThomas Veerman 
18022e2caf59SThomas Veerman     /*
18032e2caf59SThomas Veerman      * If we hit the end-of-file (the job is dead), we must flush its
18042e2caf59SThomas Veerman      * remaining output, so pretend we read a newline if there's any
18052e2caf59SThomas Veerman      * output remaining in the buffer.
18062e2caf59SThomas Veerman      * Also clear the 'finish' flag so we stop looping.
18072e2caf59SThomas Veerman      */
18082e2caf59SThomas Veerman     if ((nr == 0) && (job->curPos != 0)) {
18092e2caf59SThomas Veerman 	job->outBuf[job->curPos] = '\n';
18102e2caf59SThomas Veerman 	nr = 1;
18112e2caf59SThomas Veerman 	finish = FALSE;
18122e2caf59SThomas Veerman     } else if (nr == 0) {
18132e2caf59SThomas Veerman 	finish = FALSE;
18142e2caf59SThomas Veerman     }
18152e2caf59SThomas Veerman 
18162e2caf59SThomas Veerman     /*
18172e2caf59SThomas Veerman      * Look for the last newline in the bytes we just got. If there is
18182e2caf59SThomas Veerman      * one, break out of the loop with 'i' as its index and gotNL set
18192e2caf59SThomas Veerman      * TRUE.
18202e2caf59SThomas Veerman      */
18212e2caf59SThomas Veerman     max = job->curPos + nr;
18222e2caf59SThomas Veerman     for (i = job->curPos + nr - 1; i >= job->curPos; i--) {
18232e2caf59SThomas Veerman 	if (job->outBuf[i] == '\n') {
18242e2caf59SThomas Veerman 	    gotNL = TRUE;
18252e2caf59SThomas Veerman 	    break;
18262e2caf59SThomas Veerman 	} else if (job->outBuf[i] == '\0') {
18272e2caf59SThomas Veerman 	    /*
18282e2caf59SThomas Veerman 	     * Why?
18292e2caf59SThomas Veerman 	     */
18302e2caf59SThomas Veerman 	    job->outBuf[i] = ' ';
18312e2caf59SThomas Veerman 	}
18322e2caf59SThomas Veerman     }
18332e2caf59SThomas Veerman 
18342e2caf59SThomas Veerman     if (!gotNL) {
18352e2caf59SThomas Veerman 	job->curPos += nr;
18362e2caf59SThomas Veerman 	if (job->curPos == JOB_BUFSIZE) {
18372e2caf59SThomas Veerman 	    /*
18382e2caf59SThomas Veerman 	     * If we've run out of buffer space, we have no choice
18392e2caf59SThomas Veerman 	     * but to print the stuff. sigh.
18402e2caf59SThomas Veerman 	     */
18412e2caf59SThomas Veerman 	    fbuf = TRUE;
18422e2caf59SThomas Veerman 	    i = job->curPos;
18432e2caf59SThomas Veerman 	}
18442e2caf59SThomas Veerman     }
18452e2caf59SThomas Veerman     if (gotNL || fbuf) {
18462e2caf59SThomas Veerman 	/*
18472e2caf59SThomas Veerman 	 * Need to send the output to the screen. Null terminate it
18482e2caf59SThomas Veerman 	 * first, overwriting the newline character if there was one.
18492e2caf59SThomas Veerman 	 * So long as the line isn't one we should filter (according
18502e2caf59SThomas Veerman 	 * to the shell description), we print the line, preceded
18512e2caf59SThomas Veerman 	 * by a target banner if this target isn't the same as the
18522e2caf59SThomas Veerman 	 * one for which we last printed something.
18532e2caf59SThomas Veerman 	 * The rest of the data in the buffer are then shifted down
18542e2caf59SThomas Veerman 	 * to the start of the buffer and curPos is set accordingly.
18552e2caf59SThomas Veerman 	 */
18562e2caf59SThomas Veerman 	job->outBuf[i] = '\0';
18572e2caf59SThomas Veerman 	if (i >= job->curPos) {
18582e2caf59SThomas Veerman 	    char *cp;
18592e2caf59SThomas Veerman 
18602e2caf59SThomas Veerman 	    cp = JobOutput(job, job->outBuf, &job->outBuf[i], FALSE);
18612e2caf59SThomas Veerman 
18622e2caf59SThomas Veerman 	    /*
18632e2caf59SThomas Veerman 	     * There's still more in that thar buffer. This time, though,
18642e2caf59SThomas Veerman 	     * we know there's no newline at the end, so we add one of
18652e2caf59SThomas Veerman 	     * our own free will.
18662e2caf59SThomas Veerman 	     */
18672e2caf59SThomas Veerman 	    if (*cp != '\0') {
18682e2caf59SThomas Veerman 		if (!beSilent && job->node != lastNode) {
18692e2caf59SThomas Veerman 		    MESSAGE(stdout, job->node);
18702e2caf59SThomas Veerman 		    lastNode = job->node;
18712e2caf59SThomas Veerman 		}
18722e2caf59SThomas Veerman #ifdef USE_META
18732e2caf59SThomas Veerman 		if (useMeta) {
18742e2caf59SThomas Veerman 		    meta_job_output(job, cp, gotNL ? "\n" : "");
18752e2caf59SThomas Veerman 		}
18762e2caf59SThomas Veerman #endif
18772e2caf59SThomas Veerman 		(void)fprintf(stdout, "%s%s", cp, gotNL ? "\n" : "");
18782e2caf59SThomas Veerman 		(void)fflush(stdout);
18792e2caf59SThomas Veerman 	    }
18802e2caf59SThomas Veerman 	}
18812e2caf59SThomas Veerman 	/*
1882*0a6a1f1dSLionel Sambuc 	 * max is the last offset still in the buffer. Move any remaining
1883*0a6a1f1dSLionel Sambuc 	 * characters to the start of the buffer and update the end marker
1884*0a6a1f1dSLionel Sambuc 	 * curPos.
18852e2caf59SThomas Veerman 	 */
1886*0a6a1f1dSLionel Sambuc 	if (i < max) {
1887*0a6a1f1dSLionel Sambuc 	    (void)memmove(job->outBuf, &job->outBuf[i + 1], max - (i + 1));
1888*0a6a1f1dSLionel Sambuc 	    job->curPos = max - (i + 1);
1889*0a6a1f1dSLionel Sambuc 	} else {
1890*0a6a1f1dSLionel Sambuc 	    assert(i == max);
18912e2caf59SThomas Veerman 	    job->curPos = 0;
18922e2caf59SThomas Veerman 	}
18932e2caf59SThomas Veerman     }
18942e2caf59SThomas Veerman     if (finish) {
18952e2caf59SThomas Veerman 	/*
18962e2caf59SThomas Veerman 	 * If the finish flag is true, we must loop until we hit
18972e2caf59SThomas Veerman 	 * end-of-file on the pipe. This is guaranteed to happen
18982e2caf59SThomas Veerman 	 * eventually since the other end of the pipe is now closed
18992e2caf59SThomas Veerman 	 * (we closed it explicitly and the child has exited). When
19002e2caf59SThomas Veerman 	 * we do get an EOF, finish will be set FALSE and we'll fall
19012e2caf59SThomas Veerman 	 * through and out.
19022e2caf59SThomas Veerman 	 */
19032e2caf59SThomas Veerman 	goto end_loop;
19042e2caf59SThomas Veerman     }
19052e2caf59SThomas Veerman }
19062e2caf59SThomas Veerman 
19072e2caf59SThomas Veerman static void
JobRun(GNode * targ)19082e2caf59SThomas Veerman JobRun(GNode *targ)
19092e2caf59SThomas Veerman {
19102e2caf59SThomas Veerman #ifdef notyet
19112e2caf59SThomas Veerman     /*
19122e2caf59SThomas Veerman      * Unfortunately it is too complicated to run .BEGIN, .END,
19132e2caf59SThomas Veerman      * and .INTERRUPT job in the parallel job module. This has
19142e2caf59SThomas Veerman      * the nice side effect that it avoids a lot of other problems.
19152e2caf59SThomas Veerman      */
19162e2caf59SThomas Veerman     Lst lst = Lst_Init(FALSE);
19172e2caf59SThomas Veerman     Lst_AtEnd(lst, targ);
19182e2caf59SThomas Veerman     (void)Make_Run(lst);
19192e2caf59SThomas Veerman     Lst_Destroy(lst, NULL);
19202e2caf59SThomas Veerman     JobStart(targ, JOB_SPECIAL);
19212e2caf59SThomas Veerman     while (jobTokensRunning) {
19222e2caf59SThomas Veerman 	Job_CatchOutput();
19232e2caf59SThomas Veerman     }
19242e2caf59SThomas Veerman #else
19252e2caf59SThomas Veerman     Compat_Make(targ, targ);
19262e2caf59SThomas Veerman     if (targ->made == ERROR) {
19272e2caf59SThomas Veerman 	PrintOnError(targ, "\n\nStop.");
19282e2caf59SThomas Veerman 	exit(1);
19292e2caf59SThomas Veerman     }
19302e2caf59SThomas Veerman #endif
19312e2caf59SThomas Veerman }
19322e2caf59SThomas Veerman 
19332e2caf59SThomas Veerman /*-
19342e2caf59SThomas Veerman  *-----------------------------------------------------------------------
19352e2caf59SThomas Veerman  * Job_CatchChildren --
19362e2caf59SThomas Veerman  *	Handle the exit of a child. Called from Make_Make.
19372e2caf59SThomas Veerman  *
19382e2caf59SThomas Veerman  * Input:
19392e2caf59SThomas Veerman  *	block		TRUE if should block on the wait
19402e2caf59SThomas Veerman  *
19412e2caf59SThomas Veerman  * Results:
19422e2caf59SThomas Veerman  *	none.
19432e2caf59SThomas Veerman  *
19442e2caf59SThomas Veerman  * Side Effects:
19452e2caf59SThomas Veerman  *	The job descriptor is removed from the list of children.
19462e2caf59SThomas Veerman  *
19472e2caf59SThomas Veerman  * Notes:
19482e2caf59SThomas Veerman  *	We do waits, blocking or not, according to the wisdom of our
19492e2caf59SThomas Veerman  *	caller, until there are no more children to report. For each
19502e2caf59SThomas Veerman  *	job, call JobFinish to finish things off.
19512e2caf59SThomas Veerman  *
19522e2caf59SThomas Veerman  *-----------------------------------------------------------------------
19532e2caf59SThomas Veerman  */
19542e2caf59SThomas Veerman 
19552e2caf59SThomas Veerman void
Job_CatchChildren(void)19562e2caf59SThomas Veerman Job_CatchChildren(void)
19572e2caf59SThomas Veerman {
19582e2caf59SThomas Veerman     int    	  pid;	    	/* pid of dead child */
19592e2caf59SThomas Veerman     int	  	  status;   	/* Exit/termination status */
19602e2caf59SThomas Veerman 
19612e2caf59SThomas Veerman     /*
19622e2caf59SThomas Veerman      * Don't even bother if we know there's no one around.
19632e2caf59SThomas Veerman      */
19642e2caf59SThomas Veerman     if (jobTokensRunning == 0)
19652e2caf59SThomas Veerman 	return;
19662e2caf59SThomas Veerman 
19672e2caf59SThomas Veerman     while ((pid = waitpid((pid_t) -1, &status, WNOHANG | WUNTRACED)) > 0) {
19682e2caf59SThomas Veerman 	if (DEBUG(JOB)) {
19692e2caf59SThomas Veerman 	    (void)fprintf(debug_file, "Process %d exited/stopped status %x.\n", pid,
19702e2caf59SThomas Veerman 	      status);
19712e2caf59SThomas Veerman 	}
19722e2caf59SThomas Veerman 	JobReapChild(pid, status, TRUE);
19732e2caf59SThomas Veerman     }
19742e2caf59SThomas Veerman }
19752e2caf59SThomas Veerman 
19762e2caf59SThomas Veerman /*
19772e2caf59SThomas Veerman  * It is possible that wait[pid]() was called from elsewhere,
19782e2caf59SThomas Veerman  * this lets us reap jobs regardless.
19792e2caf59SThomas Veerman  */
19802e2caf59SThomas Veerman void
JobReapChild(pid_t pid,int status,Boolean isJobs)19812e2caf59SThomas Veerman JobReapChild(pid_t pid, int status, Boolean isJobs)
19822e2caf59SThomas Veerman {
19832e2caf59SThomas Veerman     Job		  *job;	    	/* job descriptor for dead child */
19842e2caf59SThomas Veerman 
19852e2caf59SThomas Veerman     /*
19862e2caf59SThomas Veerman      * Don't even bother if we know there's no one around.
19872e2caf59SThomas Veerman      */
19882e2caf59SThomas Veerman     if (jobTokensRunning == 0)
19892e2caf59SThomas Veerman 	return;
19902e2caf59SThomas Veerman 
19912e2caf59SThomas Veerman     job = JobFindPid(pid, JOB_ST_RUNNING, isJobs);
19922e2caf59SThomas Veerman     if (job == NULL) {
19932e2caf59SThomas Veerman 	if (isJobs) {
19942e2caf59SThomas Veerman 	    if (!lurking_children)
19952e2caf59SThomas Veerman 		Error("Child (%d) status %x not in table?", pid, status);
19962e2caf59SThomas Veerman 	}
19972e2caf59SThomas Veerman 	return;				/* not ours */
19982e2caf59SThomas Veerman     }
19992e2caf59SThomas Veerman     if (WIFSTOPPED(status)) {
20002e2caf59SThomas Veerman 	if (DEBUG(JOB)) {
20012e2caf59SThomas Veerman 	    (void)fprintf(debug_file, "Process %d (%s) stopped.\n",
20022e2caf59SThomas Veerman 			  job->pid, job->node->name);
20032e2caf59SThomas Veerman 	}
20042e2caf59SThomas Veerman 	if (!make_suspended) {
20052e2caf59SThomas Veerman 	    switch (WSTOPSIG(status)) {
20062e2caf59SThomas Veerman 	    case SIGTSTP:
20072e2caf59SThomas Veerman 		(void)printf("*** [%s] Suspended\n", job->node->name);
20082e2caf59SThomas Veerman 		break;
20092e2caf59SThomas Veerman 	    case SIGSTOP:
20102e2caf59SThomas Veerman 		(void)printf("*** [%s] Stopped\n", job->node->name);
20112e2caf59SThomas Veerman 		break;
20122e2caf59SThomas Veerman 	    default:
20132e2caf59SThomas Veerman 		(void)printf("*** [%s] Stopped -- signal %d\n",
20142e2caf59SThomas Veerman 			     job->node->name, WSTOPSIG(status));
20152e2caf59SThomas Veerman 	    }
20162e2caf59SThomas Veerman 	    job->job_suspended = 1;
20172e2caf59SThomas Veerman 	}
20182e2caf59SThomas Veerman 	(void)fflush(stdout);
20192e2caf59SThomas Veerman 	return;
20202e2caf59SThomas Veerman     }
20212e2caf59SThomas Veerman 
20222e2caf59SThomas Veerman     job->job_state = JOB_ST_FINISHED;
20232e2caf59SThomas Veerman     job->exit_status = status;
20242e2caf59SThomas Veerman 
20252e2caf59SThomas Veerman     JobFinish(job, status);
20262e2caf59SThomas Veerman }
20272e2caf59SThomas Veerman 
20282e2caf59SThomas Veerman /*-
20292e2caf59SThomas Veerman  *-----------------------------------------------------------------------
20302e2caf59SThomas Veerman  * Job_CatchOutput --
20312e2caf59SThomas Veerman  *	Catch the output from our children, if we're using
20322e2caf59SThomas Veerman  *	pipes do so. Otherwise just block time until we get a
20332e2caf59SThomas Veerman  *	signal(most likely a SIGCHLD) since there's no point in
20342e2caf59SThomas Veerman  *	just spinning when there's nothing to do and the reaping
20352e2caf59SThomas Veerman  *	of a child can wait for a while.
20362e2caf59SThomas Veerman  *
20372e2caf59SThomas Veerman  * Results:
20382e2caf59SThomas Veerman  *	None
20392e2caf59SThomas Veerman  *
20402e2caf59SThomas Veerman  * Side Effects:
20412e2caf59SThomas Veerman  *	Output is read from pipes if we're piping.
20422e2caf59SThomas Veerman  * -----------------------------------------------------------------------
20432e2caf59SThomas Veerman  */
20442e2caf59SThomas Veerman void
Job_CatchOutput(void)20452e2caf59SThomas Veerman Job_CatchOutput(void)
20462e2caf59SThomas Veerman {
20472e2caf59SThomas Veerman     int nready;
20482e2caf59SThomas Veerman     Job *job;
20492e2caf59SThomas Veerman     int i;
20502e2caf59SThomas Veerman 
20512e2caf59SThomas Veerman     (void)fflush(stdout);
20522e2caf59SThomas Veerman 
20532e2caf59SThomas Veerman     /* The first fd in the list is the job token pipe */
205484d9c625SLionel Sambuc     do {
20552e2caf59SThomas Veerman 	nready = poll(fds + 1 - wantToken, nfds - 1 + wantToken, POLL_MSEC);
205684d9c625SLionel Sambuc     } while (nready < 0 && errno == EINTR);
20572e2caf59SThomas Veerman 
205884d9c625SLionel Sambuc     if (nready < 0)
205984d9c625SLionel Sambuc 	Punt("poll: %s", strerror(errno));
206084d9c625SLionel Sambuc 
206184d9c625SLionel Sambuc     if (nready > 0 && readyfd(&childExitJob)) {
20622e2caf59SThomas Veerman 	char token = 0;
206384d9c625SLionel Sambuc 	ssize_t count;
20647d98eee8SLionel Sambuc #if defined(__minix)
20657d98eee8SLionel Sambuc 	/* Workaround: While the pipe is deemed ready to be read, it can still
20667d98eee8SLionel Sambuc 	 * return EAGAIN in the read below. */
20677d98eee8SLionel Sambuc 	do {
20687d98eee8SLionel Sambuc #endif /* defined(__minix) */
206984d9c625SLionel Sambuc 	count = read(childExitJob.inPipe, &token, 1);
20707d98eee8SLionel Sambuc #if defined(__minix)
20717d98eee8SLionel Sambuc 	} while(-1 == count && EAGAIN == errno);
20727d98eee8SLionel Sambuc #endif /* defined(__minix) */
207384d9c625SLionel Sambuc 	switch (count) {
207484d9c625SLionel Sambuc 	case 0:
207584d9c625SLionel Sambuc 	    Punt("unexpected eof on token pipe");
207684d9c625SLionel Sambuc 	case -1:
207784d9c625SLionel Sambuc 	    Punt("token pipe read: %s", strerror(errno));
207884d9c625SLionel Sambuc 	case 1:
20792e2caf59SThomas Veerman 	    if (token == DO_JOB_RESUME[0])
20802e2caf59SThomas Veerman 		/* Complete relay requested from our SIGCONT handler */
20812e2caf59SThomas Veerman 		JobRestartJobs();
208284d9c625SLionel Sambuc 	    break;
208384d9c625SLionel Sambuc 	default:
208484d9c625SLionel Sambuc 	    abort();
208584d9c625SLionel Sambuc 	}
208684d9c625SLionel Sambuc 	--nready;
20872e2caf59SThomas Veerman     }
20882e2caf59SThomas Veerman 
208984d9c625SLionel Sambuc     Job_CatchChildren();
209084d9c625SLionel Sambuc     if (nready == 0)
20912e2caf59SThomas Veerman 	    return;
20922e2caf59SThomas Veerman 
20932e2caf59SThomas Veerman     for (i = 2; i < nfds; i++) {
20942e2caf59SThomas Veerman 	if (!fds[i].revents)
20952e2caf59SThomas Veerman 	    continue;
20962e2caf59SThomas Veerman 	job = jobfds[i];
209784d9c625SLionel Sambuc 	if (job->job_state == JOB_ST_RUNNING)
20982e2caf59SThomas Veerman 	    JobDoOutput(job, FALSE);
209984d9c625SLionel Sambuc 	if (--nready == 0)
210084d9c625SLionel Sambuc 		return;
21012e2caf59SThomas Veerman     }
21022e2caf59SThomas Veerman }
21032e2caf59SThomas Veerman 
21042e2caf59SThomas Veerman /*-
21052e2caf59SThomas Veerman  *-----------------------------------------------------------------------
21062e2caf59SThomas Veerman  * Job_Make --
21072e2caf59SThomas Veerman  *	Start the creation of a target. Basically a front-end for
21082e2caf59SThomas Veerman  *	JobStart used by the Make module.
21092e2caf59SThomas Veerman  *
21102e2caf59SThomas Veerman  * Results:
21112e2caf59SThomas Veerman  *	None.
21122e2caf59SThomas Veerman  *
21132e2caf59SThomas Veerman  * Side Effects:
21142e2caf59SThomas Veerman  *	Another job is started.
21152e2caf59SThomas Veerman  *
21162e2caf59SThomas Veerman  *-----------------------------------------------------------------------
21172e2caf59SThomas Veerman  */
21182e2caf59SThomas Veerman void
Job_Make(GNode * gn)21192e2caf59SThomas Veerman Job_Make(GNode *gn)
21202e2caf59SThomas Veerman {
21212e2caf59SThomas Veerman     (void)JobStart(gn, 0);
21222e2caf59SThomas Veerman }
21232e2caf59SThomas Veerman 
21242e2caf59SThomas Veerman void
Shell_Init(void)21252e2caf59SThomas Veerman Shell_Init(void)
21262e2caf59SThomas Veerman {
21272e2caf59SThomas Veerman     if (shellPath == NULL) {
21282e2caf59SThomas Veerman 	/*
21292e2caf59SThomas Veerman 	 * We are using the default shell, which may be an absolute
21302e2caf59SThomas Veerman 	 * path if DEFSHELL_CUSTOM is defined.
21312e2caf59SThomas Veerman 	 */
21322e2caf59SThomas Veerman 	shellName = commandShell->name;
21332e2caf59SThomas Veerman #ifdef DEFSHELL_CUSTOM
21342e2caf59SThomas Veerman 	if (*shellName == '/') {
21352e2caf59SThomas Veerman 	    shellPath = shellName;
21362e2caf59SThomas Veerman 	    shellName = strrchr(shellPath, '/');
21372e2caf59SThomas Veerman 	    shellName++;
21382e2caf59SThomas Veerman 	} else
21392e2caf59SThomas Veerman #endif
21402e2caf59SThomas Veerman 	shellPath = str_concat(_PATH_DEFSHELLDIR, shellName, STR_ADDSLASH);
21412e2caf59SThomas Veerman     }
21422e2caf59SThomas Veerman     if (commandShell->exit == NULL) {
21432e2caf59SThomas Veerman 	commandShell->exit = "";
21442e2caf59SThomas Veerman     }
21452e2caf59SThomas Veerman     if (commandShell->echo == NULL) {
21462e2caf59SThomas Veerman 	commandShell->echo = "";
21472e2caf59SThomas Veerman     }
214884d9c625SLionel Sambuc     if (commandShell->hasErrCtl && *commandShell->exit) {
214984d9c625SLionel Sambuc 	if (shellErrFlag &&
215084d9c625SLionel Sambuc 	    strcmp(commandShell->exit, &shellErrFlag[1]) != 0) {
215184d9c625SLionel Sambuc 	    free(shellErrFlag);
215284d9c625SLionel Sambuc 	    shellErrFlag = NULL;
215384d9c625SLionel Sambuc 	}
215484d9c625SLionel Sambuc 	if (!shellErrFlag) {
215584d9c625SLionel Sambuc 	    int n = strlen(commandShell->exit) + 2;
215684d9c625SLionel Sambuc 
215784d9c625SLionel Sambuc 	    shellErrFlag = bmake_malloc(n);
215884d9c625SLionel Sambuc 	    if (shellErrFlag) {
215984d9c625SLionel Sambuc 		snprintf(shellErrFlag, n, "-%s", commandShell->exit);
216084d9c625SLionel Sambuc 	    }
216184d9c625SLionel Sambuc 	}
216284d9c625SLionel Sambuc     } else if (shellErrFlag) {
216384d9c625SLionel Sambuc 	free(shellErrFlag);
216484d9c625SLionel Sambuc 	shellErrFlag = NULL;
216584d9c625SLionel Sambuc     }
21662e2caf59SThomas Veerman }
21672e2caf59SThomas Veerman 
21682e2caf59SThomas Veerman /*-
21692e2caf59SThomas Veerman  * Returns the string literal that is used in the current command shell
21702e2caf59SThomas Veerman  * to produce a newline character.
21712e2caf59SThomas Veerman  */
21722e2caf59SThomas Veerman const char *
Shell_GetNewline(void)21732e2caf59SThomas Veerman Shell_GetNewline(void)
21742e2caf59SThomas Veerman {
21752e2caf59SThomas Veerman 
21762e2caf59SThomas Veerman     return commandShell->newline;
21772e2caf59SThomas Veerman }
21782e2caf59SThomas Veerman 
21792e2caf59SThomas Veerman void
Job_SetPrefix(void)21802e2caf59SThomas Veerman Job_SetPrefix(void)
21812e2caf59SThomas Veerman {
21822e2caf59SThomas Veerman 
21832e2caf59SThomas Veerman     if (targPrefix) {
21842e2caf59SThomas Veerman 	free(targPrefix);
21852e2caf59SThomas Veerman     } else if (!Var_Exists(MAKE_JOB_PREFIX, VAR_GLOBAL)) {
21862e2caf59SThomas Veerman 	Var_Set(MAKE_JOB_PREFIX, "---", VAR_GLOBAL, 0);
21872e2caf59SThomas Veerman     }
21882e2caf59SThomas Veerman 
21892e2caf59SThomas Veerman     targPrefix = Var_Subst(NULL, "${" MAKE_JOB_PREFIX "}", VAR_GLOBAL, 0);
21902e2caf59SThomas Veerman }
21912e2caf59SThomas Veerman 
21922e2caf59SThomas Veerman /*-
21932e2caf59SThomas Veerman  *-----------------------------------------------------------------------
21942e2caf59SThomas Veerman  * Job_Init --
21952e2caf59SThomas Veerman  *	Initialize the process module
21962e2caf59SThomas Veerman  *
21972e2caf59SThomas Veerman  * Input:
21982e2caf59SThomas Veerman  *
21992e2caf59SThomas Veerman  * Results:
22002e2caf59SThomas Veerman  *	none
22012e2caf59SThomas Veerman  *
22022e2caf59SThomas Veerman  * Side Effects:
22032e2caf59SThomas Veerman  *	lists and counters are initialized
22042e2caf59SThomas Veerman  *-----------------------------------------------------------------------
22052e2caf59SThomas Veerman  */
22062e2caf59SThomas Veerman void
Job_Init(void)22072e2caf59SThomas Veerman Job_Init(void)
22082e2caf59SThomas Veerman {
220984d9c625SLionel Sambuc     Job_SetPrefix();
22102e2caf59SThomas Veerman     /* Allocate space for all the job info */
22112e2caf59SThomas Veerman     job_table = bmake_malloc(maxJobs * sizeof *job_table);
22122e2caf59SThomas Veerman     memset(job_table, 0, maxJobs * sizeof *job_table);
22132e2caf59SThomas Veerman     job_table_end = job_table + maxJobs;
22142e2caf59SThomas Veerman     wantToken =	0;
22152e2caf59SThomas Veerman 
22162e2caf59SThomas Veerman     aborting = 	  0;
22172e2caf59SThomas Veerman     errors = 	  0;
22182e2caf59SThomas Veerman 
22192e2caf59SThomas Veerman     lastNode =	  NULL;
22202e2caf59SThomas Veerman 
22212e2caf59SThomas Veerman     /*
22222e2caf59SThomas Veerman      * There is a non-zero chance that we already have children.
22232e2caf59SThomas Veerman      * eg after 'make -f- <<EOF'
22242e2caf59SThomas Veerman      * Since their termination causes a 'Child (pid) not in table' message,
22252e2caf59SThomas Veerman      * Collect the status of any that are already dead, and suppress the
22262e2caf59SThomas Veerman      * error message if there are any undead ones.
22272e2caf59SThomas Veerman      */
22282e2caf59SThomas Veerman     for (;;) {
22292e2caf59SThomas Veerman 	int rval, status;
22302e2caf59SThomas Veerman 	rval = waitpid((pid_t) -1, &status, WNOHANG);
22312e2caf59SThomas Veerman 	if (rval > 0)
22322e2caf59SThomas Veerman 	    continue;
22332e2caf59SThomas Veerman 	if (rval == 0)
22342e2caf59SThomas Veerman 	    lurking_children = 1;
22352e2caf59SThomas Veerman 	break;
22362e2caf59SThomas Veerman     }
22372e2caf59SThomas Veerman 
22382e2caf59SThomas Veerman     Shell_Init();
22392e2caf59SThomas Veerman 
22402e2caf59SThomas Veerman     JobCreatePipe(&childExitJob, 3);
22412e2caf59SThomas Veerman 
22422e2caf59SThomas Veerman     /* We can only need to wait for tokens, children and output from each job */
22432e2caf59SThomas Veerman     fds = bmake_malloc(sizeof (*fds) * (2 + maxJobs));
22442e2caf59SThomas Veerman     jobfds = bmake_malloc(sizeof (*jobfds) * (2 + maxJobs));
22452e2caf59SThomas Veerman 
22462e2caf59SThomas Veerman     /* These are permanent entries and take slots 0 and 1 */
22472e2caf59SThomas Veerman     watchfd(&tokenWaitJob);
22482e2caf59SThomas Veerman     watchfd(&childExitJob);
22492e2caf59SThomas Veerman 
22502e2caf59SThomas Veerman     sigemptyset(&caught_signals);
22512e2caf59SThomas Veerman     /*
22522e2caf59SThomas Veerman      * Install a SIGCHLD handler.
22532e2caf59SThomas Veerman      */
22542e2caf59SThomas Veerman     (void)bmake_signal(SIGCHLD, JobChildSig);
22552e2caf59SThomas Veerman     sigaddset(&caught_signals, SIGCHLD);
22562e2caf59SThomas Veerman 
22572e2caf59SThomas Veerman #define ADDSIG(s,h)				\
22582e2caf59SThomas Veerman     if (bmake_signal(s, SIG_IGN) != SIG_IGN) {	\
22592e2caf59SThomas Veerman 	sigaddset(&caught_signals, s);		\
22602e2caf59SThomas Veerman 	(void)bmake_signal(s, h);			\
22612e2caf59SThomas Veerman     }
22622e2caf59SThomas Veerman 
22632e2caf59SThomas Veerman     /*
22642e2caf59SThomas Veerman      * Catch the four signals that POSIX specifies if they aren't ignored.
22652e2caf59SThomas Veerman      * JobPassSig will take care of calling JobInterrupt if appropriate.
22662e2caf59SThomas Veerman      */
22672e2caf59SThomas Veerman     ADDSIG(SIGINT, JobPassSig_int)
22682e2caf59SThomas Veerman     ADDSIG(SIGHUP, JobPassSig_term)
22692e2caf59SThomas Veerman     ADDSIG(SIGTERM, JobPassSig_term)
22702e2caf59SThomas Veerman     ADDSIG(SIGQUIT, JobPassSig_term)
22712e2caf59SThomas Veerman 
22722e2caf59SThomas Veerman     /*
22732e2caf59SThomas Veerman      * There are additional signals that need to be caught and passed if
22742e2caf59SThomas Veerman      * either the export system wants to be told directly of signals or if
22752e2caf59SThomas Veerman      * we're giving each job its own process group (since then it won't get
22762e2caf59SThomas Veerman      * signals from the terminal driver as we own the terminal)
22772e2caf59SThomas Veerman      */
22782e2caf59SThomas Veerman     ADDSIG(SIGTSTP, JobPassSig_suspend)
22792e2caf59SThomas Veerman     ADDSIG(SIGTTOU, JobPassSig_suspend)
22802e2caf59SThomas Veerman     ADDSIG(SIGTTIN, JobPassSig_suspend)
22812e2caf59SThomas Veerman     ADDSIG(SIGWINCH, JobCondPassSig)
22822e2caf59SThomas Veerman     ADDSIG(SIGCONT, JobContinueSig)
22832e2caf59SThomas Veerman #undef ADDSIG
22842e2caf59SThomas Veerman 
228584d9c625SLionel Sambuc     (void)Job_RunTarget(".BEGIN", NULL);
22862e2caf59SThomas Veerman     postCommands = Targ_FindNode(".END", TARG_CREATE);
22872e2caf59SThomas Veerman }
22882e2caf59SThomas Veerman 
JobSigReset(void)22892e2caf59SThomas Veerman static void JobSigReset(void)
22902e2caf59SThomas Veerman {
22912e2caf59SThomas Veerman #define DELSIG(s)					\
22922e2caf59SThomas Veerman     if (sigismember(&caught_signals, s)) {		\
22932e2caf59SThomas Veerman 	(void)bmake_signal(s, SIG_DFL);			\
22942e2caf59SThomas Veerman     }
22952e2caf59SThomas Veerman 
22962e2caf59SThomas Veerman     DELSIG(SIGINT)
22972e2caf59SThomas Veerman     DELSIG(SIGHUP)
22982e2caf59SThomas Veerman     DELSIG(SIGQUIT)
22992e2caf59SThomas Veerman     DELSIG(SIGTERM)
23002e2caf59SThomas Veerman     DELSIG(SIGTSTP)
23012e2caf59SThomas Veerman     DELSIG(SIGTTOU)
23022e2caf59SThomas Veerman     DELSIG(SIGTTIN)
23032e2caf59SThomas Veerman     DELSIG(SIGWINCH)
23042e2caf59SThomas Veerman     DELSIG(SIGCONT)
23052e2caf59SThomas Veerman #undef DELSIG
23062e2caf59SThomas Veerman     (void)bmake_signal(SIGCHLD, SIG_DFL);
23072e2caf59SThomas Veerman }
23082e2caf59SThomas Veerman 
23092e2caf59SThomas Veerman /*-
23102e2caf59SThomas Veerman  *-----------------------------------------------------------------------
23112e2caf59SThomas Veerman  * JobMatchShell --
23122e2caf59SThomas Veerman  *	Find a shell in 'shells' given its name.
23132e2caf59SThomas Veerman  *
23142e2caf59SThomas Veerman  * Results:
23152e2caf59SThomas Veerman  *	A pointer to the Shell structure.
23162e2caf59SThomas Veerman  *
23172e2caf59SThomas Veerman  * Side Effects:
23182e2caf59SThomas Veerman  *	None.
23192e2caf59SThomas Veerman  *
23202e2caf59SThomas Veerman  *-----------------------------------------------------------------------
23212e2caf59SThomas Veerman  */
23222e2caf59SThomas Veerman static Shell *
JobMatchShell(const char * name)23232e2caf59SThomas Veerman JobMatchShell(const char *name)
23242e2caf59SThomas Veerman {
23252e2caf59SThomas Veerman     Shell	*sh;
23262e2caf59SThomas Veerman 
23272e2caf59SThomas Veerman     for (sh = shells; sh->name != NULL; sh++) {
23282e2caf59SThomas Veerman 	if (strcmp(name, sh->name) == 0)
23292e2caf59SThomas Veerman 		return (sh);
23302e2caf59SThomas Veerman     }
23312e2caf59SThomas Veerman     return NULL;
23322e2caf59SThomas Veerman }
23332e2caf59SThomas Veerman 
23342e2caf59SThomas Veerman /*-
23352e2caf59SThomas Veerman  *-----------------------------------------------------------------------
23362e2caf59SThomas Veerman  * Job_ParseShell --
23372e2caf59SThomas Veerman  *	Parse a shell specification and set up commandShell, shellPath
23382e2caf59SThomas Veerman  *	and shellName appropriately.
23392e2caf59SThomas Veerman  *
23402e2caf59SThomas Veerman  * Input:
23412e2caf59SThomas Veerman  *	line		The shell spec
23422e2caf59SThomas Veerman  *
23432e2caf59SThomas Veerman  * Results:
23442e2caf59SThomas Veerman  *	FAILURE if the specification was incorrect.
23452e2caf59SThomas Veerman  *
23462e2caf59SThomas Veerman  * Side Effects:
23472e2caf59SThomas Veerman  *	commandShell points to a Shell structure (either predefined or
23482e2caf59SThomas Veerman  *	created from the shell spec), shellPath is the full path of the
23492e2caf59SThomas Veerman  *	shell described by commandShell, while shellName is just the
23502e2caf59SThomas Veerman  *	final component of shellPath.
23512e2caf59SThomas Veerman  *
23522e2caf59SThomas Veerman  * Notes:
23532e2caf59SThomas Veerman  *	A shell specification consists of a .SHELL target, with dependency
23542e2caf59SThomas Veerman  *	operator, followed by a series of blank-separated words. Double
23552e2caf59SThomas Veerman  *	quotes can be used to use blanks in words. A backslash escapes
23562e2caf59SThomas Veerman  *	anything (most notably a double-quote and a space) and
23572e2caf59SThomas Veerman  *	provides the functionality it does in C. Each word consists of
23582e2caf59SThomas Veerman  *	keyword and value separated by an equal sign. There should be no
23592e2caf59SThomas Veerman  *	unnecessary spaces in the word. The keywords are as follows:
23602e2caf59SThomas Veerman  *	    name  	    Name of shell.
23612e2caf59SThomas Veerman  *	    path  	    Location of shell.
23622e2caf59SThomas Veerman  *	    quiet 	    Command to turn off echoing.
23632e2caf59SThomas Veerman  *	    echo  	    Command to turn echoing on
23642e2caf59SThomas Veerman  *	    filter	    Result of turning off echoing that shouldn't be
23652e2caf59SThomas Veerman  *	    	  	    printed.
23662e2caf59SThomas Veerman  *	    echoFlag	    Flag to turn echoing on at the start
23672e2caf59SThomas Veerman  *	    errFlag	    Flag to turn error checking on at the start
23682e2caf59SThomas Veerman  *	    hasErrCtl	    True if shell has error checking control
23692e2caf59SThomas Veerman  *	    newline	    String literal to represent a newline char
23702e2caf59SThomas Veerman  *	    check 	    Command to turn on error checking if hasErrCtl
23712e2caf59SThomas Veerman  *	    	  	    is TRUE or template of command to echo a command
23722e2caf59SThomas Veerman  *	    	  	    for which error checking is off if hasErrCtl is
23732e2caf59SThomas Veerman  *	    	  	    FALSE.
23742e2caf59SThomas Veerman  *	    ignore	    Command to turn off error checking if hasErrCtl
23752e2caf59SThomas Veerman  *	    	  	    is TRUE or template of command to execute a
23762e2caf59SThomas Veerman  *	    	  	    command so as to ignore any errors it returns if
23772e2caf59SThomas Veerman  *	    	  	    hasErrCtl is FALSE.
23782e2caf59SThomas Veerman  *
23792e2caf59SThomas Veerman  *-----------------------------------------------------------------------
23802e2caf59SThomas Veerman  */
23812e2caf59SThomas Veerman ReturnStatus
Job_ParseShell(char * line)23822e2caf59SThomas Veerman Job_ParseShell(char *line)
23832e2caf59SThomas Veerman {
23842e2caf59SThomas Veerman     char	**words;
23852e2caf59SThomas Veerman     char	**argv;
23862e2caf59SThomas Veerman     int		argc;
23872e2caf59SThomas Veerman     char	*path;
23882e2caf59SThomas Veerman     Shell	newShell;
23892e2caf59SThomas Veerman     Boolean	fullSpec = FALSE;
23902e2caf59SThomas Veerman     Shell	*sh;
23912e2caf59SThomas Veerman 
23922e2caf59SThomas Veerman     while (isspace((unsigned char)*line)) {
23932e2caf59SThomas Veerman 	line++;
23942e2caf59SThomas Veerman     }
23952e2caf59SThomas Veerman 
23962e2caf59SThomas Veerman     if (shellArgv)
23972e2caf59SThomas Veerman 	free(UNCONST(shellArgv));
23982e2caf59SThomas Veerman 
23992e2caf59SThomas Veerman     memset(&newShell, 0, sizeof(newShell));
24002e2caf59SThomas Veerman 
24012e2caf59SThomas Veerman     /*
24022e2caf59SThomas Veerman      * Parse the specification by keyword
24032e2caf59SThomas Veerman      */
24042e2caf59SThomas Veerman     words = brk_string(line, &argc, TRUE, &path);
24052e2caf59SThomas Veerman     if (words == NULL) {
24062e2caf59SThomas Veerman 	Error("Unterminated quoted string [%s]", line);
24072e2caf59SThomas Veerman 	return FAILURE;
24082e2caf59SThomas Veerman     }
24092e2caf59SThomas Veerman     shellArgv = path;
24102e2caf59SThomas Veerman 
24112e2caf59SThomas Veerman     for (path = NULL, argv = words; argc != 0; argc--, argv++) {
24122e2caf59SThomas Veerman 	    if (strncmp(*argv, "path=", 5) == 0) {
24132e2caf59SThomas Veerman 		path = &argv[0][5];
24142e2caf59SThomas Veerman 	    } else if (strncmp(*argv, "name=", 5) == 0) {
24152e2caf59SThomas Veerman 		newShell.name = &argv[0][5];
24162e2caf59SThomas Veerman 	    } else {
24172e2caf59SThomas Veerman 		if (strncmp(*argv, "quiet=", 6) == 0) {
24182e2caf59SThomas Veerman 		    newShell.echoOff = &argv[0][6];
24192e2caf59SThomas Veerman 		} else if (strncmp(*argv, "echo=", 5) == 0) {
24202e2caf59SThomas Veerman 		    newShell.echoOn = &argv[0][5];
24212e2caf59SThomas Veerman 		} else if (strncmp(*argv, "filter=", 7) == 0) {
24222e2caf59SThomas Veerman 		    newShell.noPrint = &argv[0][7];
24232e2caf59SThomas Veerman 		    newShell.noPLen = strlen(newShell.noPrint);
24242e2caf59SThomas Veerman 		} else if (strncmp(*argv, "echoFlag=", 9) == 0) {
24252e2caf59SThomas Veerman 		    newShell.echo = &argv[0][9];
24262e2caf59SThomas Veerman 		} else if (strncmp(*argv, "errFlag=", 8) == 0) {
24272e2caf59SThomas Veerman 		    newShell.exit = &argv[0][8];
24282e2caf59SThomas Veerman 		} else if (strncmp(*argv, "hasErrCtl=", 10) == 0) {
24292e2caf59SThomas Veerman 		    char c = argv[0][10];
24302e2caf59SThomas Veerman 		    newShell.hasErrCtl = !((c != 'Y') && (c != 'y') &&
24312e2caf59SThomas Veerman 					   (c != 'T') && (c != 't'));
24322e2caf59SThomas Veerman 		} else if (strncmp(*argv, "newline=", 8) == 0) {
24332e2caf59SThomas Veerman 		    newShell.newline = &argv[0][8];
24342e2caf59SThomas Veerman 		} else if (strncmp(*argv, "check=", 6) == 0) {
24352e2caf59SThomas Veerman 		    newShell.errCheck = &argv[0][6];
24362e2caf59SThomas Veerman 		} else if (strncmp(*argv, "ignore=", 7) == 0) {
24372e2caf59SThomas Veerman 		    newShell.ignErr = &argv[0][7];
24382e2caf59SThomas Veerman 		} else if (strncmp(*argv, "errout=", 7) == 0) {
24392e2caf59SThomas Veerman 		    newShell.errOut = &argv[0][7];
24402e2caf59SThomas Veerman 		} else if (strncmp(*argv, "comment=", 8) == 0) {
24412e2caf59SThomas Veerman 		    newShell.commentChar = argv[0][8];
24422e2caf59SThomas Veerman 		} else {
24432e2caf59SThomas Veerman 		    Parse_Error(PARSE_FATAL, "Unknown keyword \"%s\"",
24442e2caf59SThomas Veerman 				*argv);
24452e2caf59SThomas Veerman 		    free(words);
24462e2caf59SThomas Veerman 		    return(FAILURE);
24472e2caf59SThomas Veerman 		}
24482e2caf59SThomas Veerman 		fullSpec = TRUE;
24492e2caf59SThomas Veerman 	    }
24502e2caf59SThomas Veerman     }
24512e2caf59SThomas Veerman 
24522e2caf59SThomas Veerman     if (path == NULL) {
24532e2caf59SThomas Veerman 	/*
24542e2caf59SThomas Veerman 	 * If no path was given, the user wants one of the pre-defined shells,
24552e2caf59SThomas Veerman 	 * yes? So we find the one s/he wants with the help of JobMatchShell
24562e2caf59SThomas Veerman 	 * and set things up the right way. shellPath will be set up by
24572bc7c627SLionel Sambuc 	 * Shell_Init.
24582e2caf59SThomas Veerman 	 */
24592e2caf59SThomas Veerman 	if (newShell.name == NULL) {
24602e2caf59SThomas Veerman 	    Parse_Error(PARSE_FATAL, "Neither path nor name specified");
24612e2caf59SThomas Veerman 	    free(words);
24622e2caf59SThomas Veerman 	    return(FAILURE);
24632e2caf59SThomas Veerman 	} else {
24642e2caf59SThomas Veerman 	    if ((sh = JobMatchShell(newShell.name)) == NULL) {
24652e2caf59SThomas Veerman 		    Parse_Error(PARSE_WARNING, "%s: No matching shell",
24662e2caf59SThomas Veerman 				newShell.name);
24672e2caf59SThomas Veerman 		    free(words);
24682e2caf59SThomas Veerman 		    return(FAILURE);
24692e2caf59SThomas Veerman 	    }
24702e2caf59SThomas Veerman 	    commandShell = sh;
24712e2caf59SThomas Veerman 	    shellName = newShell.name;
24722bc7c627SLionel Sambuc 	    if (shellPath) {
24732bc7c627SLionel Sambuc 		/* Shell_Init has already been called!  Do it again. */
24742bc7c627SLionel Sambuc 		free(UNCONST(shellPath));
24752bc7c627SLionel Sambuc 		shellPath = NULL;
24762bc7c627SLionel Sambuc 		Shell_Init();
24772bc7c627SLionel Sambuc 	    }
24782e2caf59SThomas Veerman 	}
24792e2caf59SThomas Veerman     } else {
24802e2caf59SThomas Veerman 	/*
24812e2caf59SThomas Veerman 	 * The user provided a path. If s/he gave nothing else (fullSpec is
24822e2caf59SThomas Veerman 	 * FALSE), try and find a matching shell in the ones we know of.
24832e2caf59SThomas Veerman 	 * Else we just take the specification at its word and copy it
24842e2caf59SThomas Veerman 	 * to a new location. In either case, we need to record the
24852e2caf59SThomas Veerman 	 * path the user gave for the shell.
24862e2caf59SThomas Veerman 	 */
24872e2caf59SThomas Veerman 	shellPath = path;
24882e2caf59SThomas Veerman 	path = strrchr(path, '/');
24892e2caf59SThomas Veerman 	if (path == NULL) {
24902e2caf59SThomas Veerman 	    path = UNCONST(shellPath);
24912e2caf59SThomas Veerman 	} else {
24922e2caf59SThomas Veerman 	    path += 1;
24932e2caf59SThomas Veerman 	}
24942e2caf59SThomas Veerman 	if (newShell.name != NULL) {
24952e2caf59SThomas Veerman 	    shellName = newShell.name;
24962e2caf59SThomas Veerman 	} else {
24972e2caf59SThomas Veerman 	    shellName = path;
24982e2caf59SThomas Veerman 	}
24992e2caf59SThomas Veerman 	if (!fullSpec) {
25002e2caf59SThomas Veerman 	    if ((sh = JobMatchShell(shellName)) == NULL) {
25012e2caf59SThomas Veerman 		    Parse_Error(PARSE_WARNING, "%s: No matching shell",
25022e2caf59SThomas Veerman 				shellName);
25032e2caf59SThomas Veerman 		    free(words);
25042e2caf59SThomas Veerman 		    return(FAILURE);
25052e2caf59SThomas Veerman 	    }
25062e2caf59SThomas Veerman 	    commandShell = sh;
25072e2caf59SThomas Veerman 	} else {
25082e2caf59SThomas Veerman 	    commandShell = bmake_malloc(sizeof(Shell));
25092e2caf59SThomas Veerman 	    *commandShell = newShell;
25102e2caf59SThomas Veerman 	}
251184d9c625SLionel Sambuc 	/* this will take care of shellErrFlag */
251284d9c625SLionel Sambuc 	Shell_Init();
25132e2caf59SThomas Veerman     }
25142e2caf59SThomas Veerman 
25152e2caf59SThomas Veerman     if (commandShell->echoOn && commandShell->echoOff) {
25162e2caf59SThomas Veerman 	commandShell->hasEchoCtl = TRUE;
25172e2caf59SThomas Veerman     }
25182e2caf59SThomas Veerman 
25192e2caf59SThomas Veerman     if (!commandShell->hasErrCtl) {
25202e2caf59SThomas Veerman 	if (commandShell->errCheck == NULL) {
25212e2caf59SThomas Veerman 	    commandShell->errCheck = "";
25222e2caf59SThomas Veerman 	}
25232e2caf59SThomas Veerman 	if (commandShell->ignErr == NULL) {
25242e2caf59SThomas Veerman 	    commandShell->ignErr = "%s\n";
25252e2caf59SThomas Veerman 	}
25262e2caf59SThomas Veerman     }
25272e2caf59SThomas Veerman 
25282e2caf59SThomas Veerman     /*
25292e2caf59SThomas Veerman      * Do not free up the words themselves, since they might be in use by the
25302e2caf59SThomas Veerman      * shell specification.
25312e2caf59SThomas Veerman      */
25322e2caf59SThomas Veerman     free(words);
25332e2caf59SThomas Veerman     return SUCCESS;
25342e2caf59SThomas Veerman }
25352e2caf59SThomas Veerman 
25362e2caf59SThomas Veerman /*-
25372e2caf59SThomas Veerman  *-----------------------------------------------------------------------
25382e2caf59SThomas Veerman  * JobInterrupt --
25392e2caf59SThomas Veerman  *	Handle the receipt of an interrupt.
25402e2caf59SThomas Veerman  *
25412e2caf59SThomas Veerman  * Input:
25422e2caf59SThomas Veerman  *	runINTERRUPT	Non-zero if commands for the .INTERRUPT target
25432e2caf59SThomas Veerman  *			should be executed
25442e2caf59SThomas Veerman  *	signo		signal received
25452e2caf59SThomas Veerman  *
25462e2caf59SThomas Veerman  * Results:
25472e2caf59SThomas Veerman  *	None
25482e2caf59SThomas Veerman  *
25492e2caf59SThomas Veerman  * Side Effects:
25502e2caf59SThomas Veerman  *	All children are killed. Another job will be started if the
25512e2caf59SThomas Veerman  *	.INTERRUPT target was given.
25522e2caf59SThomas Veerman  *-----------------------------------------------------------------------
25532e2caf59SThomas Veerman  */
25542e2caf59SThomas Veerman static void
JobInterrupt(int runINTERRUPT,int signo)25552e2caf59SThomas Veerman JobInterrupt(int runINTERRUPT, int signo)
25562e2caf59SThomas Veerman {
25572e2caf59SThomas Veerman     Job		*job;		/* job descriptor in that element */
25582e2caf59SThomas Veerman     GNode	*interrupt;	/* the node describing the .INTERRUPT target */
25592e2caf59SThomas Veerman     sigset_t	mask;
25602e2caf59SThomas Veerman     GNode	*gn;
25612e2caf59SThomas Veerman 
25622e2caf59SThomas Veerman     aborting = ABORT_INTERRUPT;
25632e2caf59SThomas Veerman 
25642e2caf59SThomas Veerman     JobSigLock(&mask);
25652e2caf59SThomas Veerman 
25662e2caf59SThomas Veerman     for (job = job_table; job < job_table_end; job++) {
25672e2caf59SThomas Veerman 	if (job->job_state != JOB_ST_RUNNING)
25682e2caf59SThomas Veerman 	    continue;
25692e2caf59SThomas Veerman 
25702e2caf59SThomas Veerman 	gn = job->node;
25712e2caf59SThomas Veerman 
25722e2caf59SThomas Veerman 	if ((gn->type & (OP_JOIN|OP_PHONY)) == 0 && !Targ_Precious(gn)) {
25732e2caf59SThomas Veerman 	    char *file = (gn->path == NULL ? gn->name : gn->path);
25742e2caf59SThomas Veerman 	    if (!noExecute && eunlink(file) != -1) {
25752e2caf59SThomas Veerman 		Error("*** %s removed", file);
25762e2caf59SThomas Veerman 	    }
25772e2caf59SThomas Veerman 	}
25782e2caf59SThomas Veerman 	if (job->pid) {
25792e2caf59SThomas Veerman 	    if (DEBUG(JOB)) {
25802e2caf59SThomas Veerman 		(void)fprintf(debug_file,
25812e2caf59SThomas Veerman 			   "JobInterrupt passing signal %d to child %d.\n",
25822e2caf59SThomas Veerman 			   signo, job->pid);
25832e2caf59SThomas Veerman 	    }
25842e2caf59SThomas Veerman 	    KILLPG(job->pid, signo);
25852e2caf59SThomas Veerman 	}
25862e2caf59SThomas Veerman     }
25872e2caf59SThomas Veerman 
25882e2caf59SThomas Veerman     JobSigUnlock(&mask);
25892e2caf59SThomas Veerman 
25902e2caf59SThomas Veerman     if (runINTERRUPT && !touchFlag) {
25912e2caf59SThomas Veerman 	interrupt = Targ_FindNode(".INTERRUPT", TARG_NOCREATE);
25922e2caf59SThomas Veerman 	if (interrupt != NULL) {
25932e2caf59SThomas Veerman 	    ignoreErrors = FALSE;
25942e2caf59SThomas Veerman 	    JobRun(interrupt);
25952e2caf59SThomas Veerman 	}
25962e2caf59SThomas Veerman     }
25972e2caf59SThomas Veerman     Trace_Log(MAKEINTR, 0);
25982e2caf59SThomas Veerman     exit(signo);
25992e2caf59SThomas Veerman }
26002e2caf59SThomas Veerman 
26012e2caf59SThomas Veerman /*
26022e2caf59SThomas Veerman  *-----------------------------------------------------------------------
26032e2caf59SThomas Veerman  * Job_Finish --
26042e2caf59SThomas Veerman  *	Do final processing such as the running of the commands
26052e2caf59SThomas Veerman  *	attached to the .END target.
26062e2caf59SThomas Veerman  *
26072e2caf59SThomas Veerman  * Results:
26082e2caf59SThomas Veerman  *	Number of errors reported.
26092e2caf59SThomas Veerman  *
26102e2caf59SThomas Veerman  * Side Effects:
26112e2caf59SThomas Veerman  *	None.
26122e2caf59SThomas Veerman  *-----------------------------------------------------------------------
26132e2caf59SThomas Veerman  */
26142e2caf59SThomas Veerman int
Job_Finish(void)26152e2caf59SThomas Veerman Job_Finish(void)
26162e2caf59SThomas Veerman {
26172e2caf59SThomas Veerman     if (postCommands != NULL &&
26182e2caf59SThomas Veerman 	(!Lst_IsEmpty(postCommands->commands) ||
26192e2caf59SThomas Veerman 	 !Lst_IsEmpty(postCommands->children))) {
26202e2caf59SThomas Veerman 	if (errors) {
26212e2caf59SThomas Veerman 	    Error("Errors reported so .END ignored");
26222e2caf59SThomas Veerman 	} else {
26232e2caf59SThomas Veerman 	    JobRun(postCommands);
26242e2caf59SThomas Veerman 	}
26252e2caf59SThomas Veerman     }
26262e2caf59SThomas Veerman     return(errors);
26272e2caf59SThomas Veerman }
26282e2caf59SThomas Veerman 
26292e2caf59SThomas Veerman /*-
26302e2caf59SThomas Veerman  *-----------------------------------------------------------------------
26312e2caf59SThomas Veerman  * Job_End --
26322e2caf59SThomas Veerman  *	Cleanup any memory used by the jobs module
26332e2caf59SThomas Veerman  *
26342e2caf59SThomas Veerman  * Results:
26352e2caf59SThomas Veerman  *	None.
26362e2caf59SThomas Veerman  *
26372e2caf59SThomas Veerman  * Side Effects:
26382e2caf59SThomas Veerman  *	Memory is freed
26392e2caf59SThomas Veerman  *-----------------------------------------------------------------------
26402e2caf59SThomas Veerman  */
26412e2caf59SThomas Veerman void
Job_End(void)26422e2caf59SThomas Veerman Job_End(void)
26432e2caf59SThomas Veerman {
26442e2caf59SThomas Veerman #ifdef CLEANUP
26452e2caf59SThomas Veerman     if (shellArgv)
26462e2caf59SThomas Veerman 	free(shellArgv);
26472e2caf59SThomas Veerman #endif
26482e2caf59SThomas Veerman }
26492e2caf59SThomas Veerman 
26502e2caf59SThomas Veerman /*-
26512e2caf59SThomas Veerman  *-----------------------------------------------------------------------
26522e2caf59SThomas Veerman  * Job_Wait --
26532e2caf59SThomas Veerman  *	Waits for all running jobs to finish and returns. Sets 'aborting'
26542e2caf59SThomas Veerman  *	to ABORT_WAIT to prevent other jobs from starting.
26552e2caf59SThomas Veerman  *
26562e2caf59SThomas Veerman  * Results:
26572e2caf59SThomas Veerman  *	None.
26582e2caf59SThomas Veerman  *
26592e2caf59SThomas Veerman  * Side Effects:
26602e2caf59SThomas Veerman  *	Currently running jobs finish.
26612e2caf59SThomas Veerman  *
26622e2caf59SThomas Veerman  *-----------------------------------------------------------------------
26632e2caf59SThomas Veerman  */
26642e2caf59SThomas Veerman void
Job_Wait(void)26652e2caf59SThomas Veerman Job_Wait(void)
26662e2caf59SThomas Veerman {
26672e2caf59SThomas Veerman     aborting = ABORT_WAIT;
26682e2caf59SThomas Veerman     while (jobTokensRunning != 0) {
26692e2caf59SThomas Veerman 	Job_CatchOutput();
26702e2caf59SThomas Veerman     }
26712e2caf59SThomas Veerman     aborting = 0;
26722e2caf59SThomas Veerman }
26732e2caf59SThomas Veerman 
26742e2caf59SThomas Veerman /*-
26752e2caf59SThomas Veerman  *-----------------------------------------------------------------------
26762e2caf59SThomas Veerman  * Job_AbortAll --
26772e2caf59SThomas Veerman  *	Abort all currently running jobs without handling output or anything.
26782e2caf59SThomas Veerman  *	This function is to be called only in the event of a major
26792e2caf59SThomas Veerman  *	error. Most definitely NOT to be called from JobInterrupt.
26802e2caf59SThomas Veerman  *
26812e2caf59SThomas Veerman  * Results:
26822e2caf59SThomas Veerman  *	None
26832e2caf59SThomas Veerman  *
26842e2caf59SThomas Veerman  * Side Effects:
26852e2caf59SThomas Veerman  *	All children are killed, not just the firstborn
26862e2caf59SThomas Veerman  *-----------------------------------------------------------------------
26872e2caf59SThomas Veerman  */
26882e2caf59SThomas Veerman void
Job_AbortAll(void)26892e2caf59SThomas Veerman Job_AbortAll(void)
26902e2caf59SThomas Veerman {
26912e2caf59SThomas Veerman     Job		*job;	/* the job descriptor in that element */
26922e2caf59SThomas Veerman     int		foo;
26932e2caf59SThomas Veerman 
26942e2caf59SThomas Veerman     aborting = ABORT_ERROR;
26952e2caf59SThomas Veerman 
26962e2caf59SThomas Veerman     if (jobTokensRunning) {
26972e2caf59SThomas Veerman 	for (job = job_table; job < job_table_end; job++) {
26982e2caf59SThomas Veerman 	    if (job->job_state != JOB_ST_RUNNING)
26992e2caf59SThomas Veerman 		continue;
27002e2caf59SThomas Veerman 	    /*
27012e2caf59SThomas Veerman 	     * kill the child process with increasingly drastic signals to make
27022e2caf59SThomas Veerman 	     * darn sure it's dead.
27032e2caf59SThomas Veerman 	     */
27042e2caf59SThomas Veerman 	    KILLPG(job->pid, SIGINT);
27052e2caf59SThomas Veerman 	    KILLPG(job->pid, SIGKILL);
27062e2caf59SThomas Veerman 	}
27072e2caf59SThomas Veerman     }
27082e2caf59SThomas Veerman 
27092e2caf59SThomas Veerman     /*
27102e2caf59SThomas Veerman      * Catch as many children as want to report in at first, then give up
27112e2caf59SThomas Veerman      */
27122e2caf59SThomas Veerman     while (waitpid((pid_t) -1, &foo, WNOHANG) > 0)
27132e2caf59SThomas Veerman 	continue;
27142e2caf59SThomas Veerman }
27152e2caf59SThomas Veerman 
27162e2caf59SThomas Veerman 
27172e2caf59SThomas Veerman /*-
27182e2caf59SThomas Veerman  *-----------------------------------------------------------------------
27192e2caf59SThomas Veerman  * JobRestartJobs --
27202e2caf59SThomas Veerman  *	Tries to restart stopped jobs if there are slots available.
27212e2caf59SThomas Veerman  *	Called in process context in response to a SIGCONT.
27222e2caf59SThomas Veerman  *
27232e2caf59SThomas Veerman  * Results:
27242e2caf59SThomas Veerman  *	None.
27252e2caf59SThomas Veerman  *
27262e2caf59SThomas Veerman  * Side Effects:
27272e2caf59SThomas Veerman  *	Resumes jobs.
27282e2caf59SThomas Veerman  *
27292e2caf59SThomas Veerman  *-----------------------------------------------------------------------
27302e2caf59SThomas Veerman  */
27312e2caf59SThomas Veerman static void
JobRestartJobs(void)27322e2caf59SThomas Veerman JobRestartJobs(void)
27332e2caf59SThomas Veerman {
27342e2caf59SThomas Veerman     Job *job;
27352e2caf59SThomas Veerman 
27362e2caf59SThomas Veerman     for (job = job_table; job < job_table_end; job++) {
27372e2caf59SThomas Veerman 	if (job->job_state == JOB_ST_RUNNING &&
27382e2caf59SThomas Veerman 		(make_suspended || job->job_suspended)) {
27392e2caf59SThomas Veerman 	    if (DEBUG(JOB)) {
27402e2caf59SThomas Veerman 		(void)fprintf(debug_file, "Restarting stopped job pid %d.\n",
27412e2caf59SThomas Veerman 			job->pid);
27422e2caf59SThomas Veerman 	    }
27432e2caf59SThomas Veerman 	    if (job->job_suspended) {
27442e2caf59SThomas Veerman 		    (void)printf("*** [%s] Continued\n", job->node->name);
27452e2caf59SThomas Veerman 		    (void)fflush(stdout);
27462e2caf59SThomas Veerman 	    }
27472e2caf59SThomas Veerman 	    job->job_suspended = 0;
27482e2caf59SThomas Veerman 	    if (KILLPG(job->pid, SIGCONT) != 0 && DEBUG(JOB)) {
27492e2caf59SThomas Veerman 		fprintf(debug_file, "Failed to send SIGCONT to %d\n", job->pid);
27502e2caf59SThomas Veerman 	    }
27512e2caf59SThomas Veerman 	}
27522e2caf59SThomas Veerman 	if (job->job_state == JOB_ST_FINISHED)
27532e2caf59SThomas Veerman 	    /* Job exit deferred after calling waitpid() in a signal handler */
27542e2caf59SThomas Veerman 	    JobFinish(job, job->exit_status);
27552e2caf59SThomas Veerman     }
27562e2caf59SThomas Veerman     make_suspended = 0;
27572e2caf59SThomas Veerman }
27582e2caf59SThomas Veerman 
27592e2caf59SThomas Veerman static void
watchfd(Job * job)27602e2caf59SThomas Veerman watchfd(Job *job)
27612e2caf59SThomas Veerman {
27622e2caf59SThomas Veerman     if (job->inPollfd != NULL)
27632e2caf59SThomas Veerman 	Punt("Watching watched job");
27642e2caf59SThomas Veerman 
27652e2caf59SThomas Veerman     fds[nfds].fd = job->inPipe;
27662e2caf59SThomas Veerman     fds[nfds].events = POLLIN;
27672e2caf59SThomas Veerman     jobfds[nfds] = job;
27682e2caf59SThomas Veerman     job->inPollfd = &fds[nfds];
27692e2caf59SThomas Veerman     nfds++;
27702e2caf59SThomas Veerman }
27712e2caf59SThomas Veerman 
27722e2caf59SThomas Veerman static void
clearfd(Job * job)27732e2caf59SThomas Veerman clearfd(Job *job)
27742e2caf59SThomas Veerman {
27752e2caf59SThomas Veerman     int i;
27762e2caf59SThomas Veerman     if (job->inPollfd == NULL)
27772e2caf59SThomas Veerman 	Punt("Unwatching unwatched job");
27782e2caf59SThomas Veerman     i = job->inPollfd - fds;
27792e2caf59SThomas Veerman     nfds--;
27802e2caf59SThomas Veerman     /*
27812e2caf59SThomas Veerman      * Move last job in table into hole made by dead job.
27822e2caf59SThomas Veerman      */
27832e2caf59SThomas Veerman     if (nfds != i) {
27842e2caf59SThomas Veerman 	fds[i] = fds[nfds];
27852e2caf59SThomas Veerman 	jobfds[i] = jobfds[nfds];
27862e2caf59SThomas Veerman 	jobfds[i]->inPollfd = &fds[i];
27872e2caf59SThomas Veerman     }
27882e2caf59SThomas Veerman     job->inPollfd = NULL;
27892e2caf59SThomas Veerman }
27902e2caf59SThomas Veerman 
27912e2caf59SThomas Veerman static int
readyfd(Job * job)27922e2caf59SThomas Veerman readyfd(Job *job)
27932e2caf59SThomas Veerman {
27942e2caf59SThomas Veerman     if (job->inPollfd == NULL)
27952e2caf59SThomas Veerman 	Punt("Polling unwatched job");
27962e2caf59SThomas Veerman     return (job->inPollfd->revents & POLLIN) != 0;
27972e2caf59SThomas Veerman }
27982e2caf59SThomas Veerman 
27992e2caf59SThomas Veerman /*-
28002e2caf59SThomas Veerman  *-----------------------------------------------------------------------
28012e2caf59SThomas Veerman  * JobTokenAdd --
28022e2caf59SThomas Veerman  *	Put a token into the job pipe so that some make process can start
28032e2caf59SThomas Veerman  *	another job.
28042e2caf59SThomas Veerman  *
28052e2caf59SThomas Veerman  * Side Effects:
28062e2caf59SThomas Veerman  *	Allows more build jobs to be spawned somewhere.
28072e2caf59SThomas Veerman  *
28082e2caf59SThomas Veerman  *-----------------------------------------------------------------------
28092e2caf59SThomas Veerman  */
28102e2caf59SThomas Veerman 
28112e2caf59SThomas Veerman static void
JobTokenAdd(void)28122e2caf59SThomas Veerman JobTokenAdd(void)
28132e2caf59SThomas Veerman {
28142e2caf59SThomas Veerman     char tok = JOB_TOKENS[aborting], tok1;
28152e2caf59SThomas Veerman 
28162e2caf59SThomas Veerman     /* If we are depositing an error token flush everything else */
28172e2caf59SThomas Veerman     while (tok != '+' && read(tokenWaitJob.inPipe, &tok1, 1) == 1)
28182e2caf59SThomas Veerman 	continue;
28192e2caf59SThomas Veerman 
28202e2caf59SThomas Veerman     if (DEBUG(JOB))
28212e2caf59SThomas Veerman 	fprintf(debug_file, "(%d) aborting %d, deposit token %c\n",
28222e2caf59SThomas Veerman 	    getpid(), aborting, JOB_TOKENS[aborting]);
282384d9c625SLionel Sambuc     while (write(tokenWaitJob.outPipe, &tok, 1) == -1 && errno == EAGAIN)
282484d9c625SLionel Sambuc 	continue;
28252e2caf59SThomas Veerman }
28262e2caf59SThomas Veerman 
28272e2caf59SThomas Veerman /*-
28282e2caf59SThomas Veerman  *-----------------------------------------------------------------------
28292e2caf59SThomas Veerman  * Job_ServerStartTokenAdd --
28302e2caf59SThomas Veerman  *	Prep the job token pipe in the root make process.
28312e2caf59SThomas Veerman  *
28322e2caf59SThomas Veerman  *-----------------------------------------------------------------------
28332e2caf59SThomas Veerman  */
28342e2caf59SThomas Veerman 
28352e2caf59SThomas Veerman void
Job_ServerStart(int max_tokens,int jp_0,int jp_1)28362e2caf59SThomas Veerman Job_ServerStart(int max_tokens, int jp_0, int jp_1)
28372e2caf59SThomas Veerman {
28382e2caf59SThomas Veerman     int i;
28392e2caf59SThomas Veerman     char jobarg[64];
28402e2caf59SThomas Veerman 
28412e2caf59SThomas Veerman     if (jp_0 >= 0 && jp_1 >= 0) {
28422e2caf59SThomas Veerman 	/* Pipe passed in from parent */
28432e2caf59SThomas Veerman 	tokenWaitJob.inPipe = jp_0;
28442e2caf59SThomas Veerman 	tokenWaitJob.outPipe = jp_1;
284584d9c625SLionel Sambuc 	(void)fcntl(jp_0, F_SETFD, 1);
284684d9c625SLionel Sambuc 	(void)fcntl(jp_1, F_SETFD, 1);
28472e2caf59SThomas Veerman 	return;
28482e2caf59SThomas Veerman     }
28492e2caf59SThomas Veerman 
28502e2caf59SThomas Veerman     JobCreatePipe(&tokenWaitJob, 15);
28512e2caf59SThomas Veerman 
28522e2caf59SThomas Veerman     snprintf(jobarg, sizeof(jobarg), "%d,%d",
28532e2caf59SThomas Veerman 	    tokenWaitJob.inPipe, tokenWaitJob.outPipe);
28542e2caf59SThomas Veerman 
28552e2caf59SThomas Veerman     Var_Append(MAKEFLAGS, "-J", VAR_GLOBAL);
28562e2caf59SThomas Veerman     Var_Append(MAKEFLAGS, jobarg, VAR_GLOBAL);
28572e2caf59SThomas Veerman 
28582e2caf59SThomas Veerman     /*
28592e2caf59SThomas Veerman      * Preload the job pipe with one token per job, save the one
28602e2caf59SThomas Veerman      * "extra" token for the primary job.
28612e2caf59SThomas Veerman      *
28622e2caf59SThomas Veerman      * XXX should clip maxJobs against PIPE_BUF -- if max_tokens is
28632e2caf59SThomas Veerman      * larger than the write buffer size of the pipe, we will
28642e2caf59SThomas Veerman      * deadlock here.
28652e2caf59SThomas Veerman      */
28662e2caf59SThomas Veerman     for (i = 1; i < max_tokens; i++)
28672e2caf59SThomas Veerman 	JobTokenAdd();
28682e2caf59SThomas Veerman }
28692e2caf59SThomas Veerman 
28702e2caf59SThomas Veerman /*-
28712e2caf59SThomas Veerman  *-----------------------------------------------------------------------
28722e2caf59SThomas Veerman  * Job_TokenReturn --
28732e2caf59SThomas Veerman  *	Return a withdrawn token to the pool.
28742e2caf59SThomas Veerman  *
28752e2caf59SThomas Veerman  *-----------------------------------------------------------------------
28762e2caf59SThomas Veerman  */
28772e2caf59SThomas Veerman 
28782e2caf59SThomas Veerman void
Job_TokenReturn(void)28792e2caf59SThomas Veerman Job_TokenReturn(void)
28802e2caf59SThomas Veerman {
28812e2caf59SThomas Veerman     jobTokensRunning--;
28822e2caf59SThomas Veerman     if (jobTokensRunning < 0)
28832e2caf59SThomas Veerman 	Punt("token botch");
28842e2caf59SThomas Veerman     if (jobTokensRunning || JOB_TOKENS[aborting] != '+')
28852e2caf59SThomas Veerman 	JobTokenAdd();
28862e2caf59SThomas Veerman }
28872e2caf59SThomas Veerman 
28882e2caf59SThomas Veerman /*-
28892e2caf59SThomas Veerman  *-----------------------------------------------------------------------
28902e2caf59SThomas Veerman  * Job_TokenWithdraw --
28912e2caf59SThomas Veerman  *	Attempt to withdraw a token from the pool.
28922e2caf59SThomas Veerman  *
28932e2caf59SThomas Veerman  * Results:
28942e2caf59SThomas Veerman  *	Returns TRUE if a token was withdrawn, and FALSE if the pool
28952e2caf59SThomas Veerman  *	is currently empty.
28962e2caf59SThomas Veerman  *
28972e2caf59SThomas Veerman  * Side Effects:
28982e2caf59SThomas Veerman  * 	If pool is empty, set wantToken so that we wake up
28992e2caf59SThomas Veerman  *	when a token is released.
29002e2caf59SThomas Veerman  *
29012e2caf59SThomas Veerman  *-----------------------------------------------------------------------
29022e2caf59SThomas Veerman  */
29032e2caf59SThomas Veerman 
29042e2caf59SThomas Veerman 
29052e2caf59SThomas Veerman Boolean
Job_TokenWithdraw(void)29062e2caf59SThomas Veerman Job_TokenWithdraw(void)
29072e2caf59SThomas Veerman {
29082e2caf59SThomas Veerman     char tok, tok1;
29092e2caf59SThomas Veerman     int count;
29102e2caf59SThomas Veerman 
29112e2caf59SThomas Veerman     wantToken = 0;
29122e2caf59SThomas Veerman     if (DEBUG(JOB))
29132e2caf59SThomas Veerman 	fprintf(debug_file, "Job_TokenWithdraw(%d): aborting %d, running %d\n",
29142e2caf59SThomas Veerman 		getpid(), aborting, jobTokensRunning);
29152e2caf59SThomas Veerman 
29162e2caf59SThomas Veerman     if (aborting || (jobTokensRunning >= maxJobs))
29172e2caf59SThomas Veerman 	return FALSE;
29182e2caf59SThomas Veerman 
29192e2caf59SThomas Veerman     count = read(tokenWaitJob.inPipe, &tok, 1);
29202e2caf59SThomas Veerman     if (count == 0)
29212e2caf59SThomas Veerman 	Fatal("eof on job pipe!");
29222e2caf59SThomas Veerman     if (count < 0 && jobTokensRunning != 0) {
29232e2caf59SThomas Veerman 	if (errno != EAGAIN) {
29242e2caf59SThomas Veerman 	    Fatal("job pipe read: %s", strerror(errno));
29252e2caf59SThomas Veerman 	}
29262e2caf59SThomas Veerman 	if (DEBUG(JOB))
29272e2caf59SThomas Veerman 	    fprintf(debug_file, "(%d) blocked for token\n", getpid());
29282e2caf59SThomas Veerman 	wantToken = 1;
29292e2caf59SThomas Veerman 	return FALSE;
29302e2caf59SThomas Veerman     }
29312e2caf59SThomas Veerman 
29322e2caf59SThomas Veerman     if (count == 1 && tok != '+') {
29332e2caf59SThomas Veerman 	/* make being abvorted - remove any other job tokens */
29342e2caf59SThomas Veerman 	if (DEBUG(JOB))
29352e2caf59SThomas Veerman 	    fprintf(debug_file, "(%d) aborted by token %c\n", getpid(), tok);
29362e2caf59SThomas Veerman 	while (read(tokenWaitJob.inPipe, &tok1, 1) == 1)
29372e2caf59SThomas Veerman 	    continue;
29382e2caf59SThomas Veerman 	/* And put the stopper back */
293984d9c625SLionel Sambuc 	while (write(tokenWaitJob.outPipe, &tok, 1) == -1 && errno == EAGAIN)
294084d9c625SLionel Sambuc 	    continue;
29412e2caf59SThomas Veerman 	Fatal("A failure has been detected in another branch of the parallel make");
29422e2caf59SThomas Veerman     }
29432e2caf59SThomas Veerman 
29442e2caf59SThomas Veerman     if (count == 1 && jobTokensRunning == 0)
29452e2caf59SThomas Veerman 	/* We didn't want the token really */
294684d9c625SLionel Sambuc 	while (write(tokenWaitJob.outPipe, &tok, 1) == -1 && errno == EAGAIN)
294784d9c625SLionel Sambuc 	    continue;
29482e2caf59SThomas Veerman 
29492e2caf59SThomas Veerman     jobTokensRunning++;
29502e2caf59SThomas Veerman     if (DEBUG(JOB))
29512e2caf59SThomas Veerman 	fprintf(debug_file, "(%d) withdrew token\n", getpid());
29522e2caf59SThomas Veerman     return TRUE;
29532e2caf59SThomas Veerman }
29542e2caf59SThomas Veerman 
295584d9c625SLionel Sambuc /*-
295684d9c625SLionel Sambuc  *-----------------------------------------------------------------------
295784d9c625SLionel Sambuc  * Job_RunTarget --
295884d9c625SLionel Sambuc  *	Run the named target if found. If a filename is specified, then
295984d9c625SLionel Sambuc  *	set that to the sources.
296084d9c625SLionel Sambuc  *
296184d9c625SLionel Sambuc  * Results:
296284d9c625SLionel Sambuc  *	None
296384d9c625SLionel Sambuc  *
296484d9c625SLionel Sambuc  * Side Effects:
296584d9c625SLionel Sambuc  * 	exits if the target fails.
296684d9c625SLionel Sambuc  *
296784d9c625SLionel Sambuc  *-----------------------------------------------------------------------
296884d9c625SLionel Sambuc  */
296984d9c625SLionel Sambuc Boolean
Job_RunTarget(const char * target,const char * fname)297084d9c625SLionel Sambuc Job_RunTarget(const char *target, const char *fname) {
297184d9c625SLionel Sambuc     GNode *gn = Targ_FindNode(target, TARG_NOCREATE);
297284d9c625SLionel Sambuc 
297384d9c625SLionel Sambuc     if (gn == NULL)
297484d9c625SLionel Sambuc 	return FALSE;
297584d9c625SLionel Sambuc 
297684d9c625SLionel Sambuc     if (fname)
297784d9c625SLionel Sambuc 	Var_Set(ALLSRC, fname, gn, 0);
297884d9c625SLionel Sambuc 
297984d9c625SLionel Sambuc     JobRun(gn);
298084d9c625SLionel Sambuc     if (gn->made == ERROR) {
298184d9c625SLionel Sambuc 	PrintOnError(gn, "\n\nStop.");
298284d9c625SLionel Sambuc 	exit(1);
298384d9c625SLionel Sambuc     }
298484d9c625SLionel Sambuc     return TRUE;
298584d9c625SLionel Sambuc }
298684d9c625SLionel Sambuc 
29872e2caf59SThomas Veerman #ifdef USE_SELECT
29882e2caf59SThomas Veerman int
emul_poll(struct pollfd * fd,int nfd,int timeout)29892e2caf59SThomas Veerman emul_poll(struct pollfd *fd, int nfd, int timeout)
29902e2caf59SThomas Veerman {
29912e2caf59SThomas Veerman     fd_set rfds, wfds;
29922e2caf59SThomas Veerman     int i, maxfd, nselect, npoll;
29932e2caf59SThomas Veerman     struct timeval tv, *tvp;
29942e2caf59SThomas Veerman     long usecs;
29952e2caf59SThomas Veerman 
29962e2caf59SThomas Veerman     FD_ZERO(&rfds);
29972e2caf59SThomas Veerman     FD_ZERO(&wfds);
29982e2caf59SThomas Veerman 
29992e2caf59SThomas Veerman     maxfd = -1;
30002e2caf59SThomas Veerman     for (i = 0; i < nfd; i++) {
30012e2caf59SThomas Veerman 	fd[i].revents = 0;
30022e2caf59SThomas Veerman 
30032e2caf59SThomas Veerman 	if (fd[i].events & POLLIN)
30042e2caf59SThomas Veerman 	    FD_SET(fd[i].fd, &rfds);
30052e2caf59SThomas Veerman 
30062e2caf59SThomas Veerman 	if (fd[i].events & POLLOUT)
30072e2caf59SThomas Veerman 	    FD_SET(fd[i].fd, &wfds);
30082e2caf59SThomas Veerman 
30092e2caf59SThomas Veerman 	if (fd[i].fd > maxfd)
30102e2caf59SThomas Veerman 	    maxfd = fd[i].fd;
30112e2caf59SThomas Veerman     }
30122e2caf59SThomas Veerman 
30132e2caf59SThomas Veerman     if (maxfd >= FD_SETSIZE) {
30142e2caf59SThomas Veerman 	Punt("Ran out of fd_set slots; "
30152e2caf59SThomas Veerman 	     "recompile with a larger FD_SETSIZE.");
30162e2caf59SThomas Veerman     }
30172e2caf59SThomas Veerman 
30182e2caf59SThomas Veerman     if (timeout < 0) {
30192e2caf59SThomas Veerman 	tvp = NULL;
30202e2caf59SThomas Veerman     } else {
30212e2caf59SThomas Veerman 	usecs = timeout * 1000;
30222e2caf59SThomas Veerman 	tv.tv_sec = usecs / 1000000;
30232e2caf59SThomas Veerman 	tv.tv_usec = usecs % 1000000;
30242e2caf59SThomas Veerman         tvp = &tv;
30252e2caf59SThomas Veerman     }
30262e2caf59SThomas Veerman 
30272e2caf59SThomas Veerman     nselect = select(maxfd + 1, &rfds, &wfds, 0, tvp);
30282e2caf59SThomas Veerman 
30292e2caf59SThomas Veerman     if (nselect <= 0)
30302e2caf59SThomas Veerman 	return nselect;
30312e2caf59SThomas Veerman 
30322e2caf59SThomas Veerman     npoll = 0;
30332e2caf59SThomas Veerman     for (i = 0; i < nfd; i++) {
30342e2caf59SThomas Veerman 	if (FD_ISSET(fd[i].fd, &rfds))
30352e2caf59SThomas Veerman 	    fd[i].revents |= POLLIN;
30362e2caf59SThomas Veerman 
30372e2caf59SThomas Veerman 	if (FD_ISSET(fd[i].fd, &wfds))
30382e2caf59SThomas Veerman 	    fd[i].revents |= POLLOUT;
30392e2caf59SThomas Veerman 
30402e2caf59SThomas Veerman 	if (fd[i].revents)
30412e2caf59SThomas Veerman 	    npoll++;
30422e2caf59SThomas Veerman     }
30432e2caf59SThomas Veerman 
30442e2caf59SThomas Veerman     return npoll;
30452e2caf59SThomas Veerman }
30462e2caf59SThomas Veerman #endif /* USE_SELECT */
3047