xref: /freebsd/contrib/bmake/job.c (revision 49caa483b3fafffd9cf5197eb30e8bb235aa7410)
1*49caa483SSimon J. Gerraty /*	$NetBSD: job.c,v 1.197 2020/02/06 01:13:19 sjg Exp $	*/
23955d011SMarcel Moolenaar 
33955d011SMarcel Moolenaar /*
43955d011SMarcel Moolenaar  * Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
53955d011SMarcel Moolenaar  * All rights reserved.
63955d011SMarcel Moolenaar  *
73955d011SMarcel Moolenaar  * This code is derived from software contributed to Berkeley by
83955d011SMarcel Moolenaar  * Adam de Boor.
93955d011SMarcel Moolenaar  *
103955d011SMarcel Moolenaar  * Redistribution and use in source and binary forms, with or without
113955d011SMarcel Moolenaar  * modification, are permitted provided that the following conditions
123955d011SMarcel Moolenaar  * are met:
133955d011SMarcel Moolenaar  * 1. Redistributions of source code must retain the above copyright
143955d011SMarcel Moolenaar  *    notice, this list of conditions and the following disclaimer.
153955d011SMarcel Moolenaar  * 2. Redistributions in binary form must reproduce the above copyright
163955d011SMarcel Moolenaar  *    notice, this list of conditions and the following disclaimer in the
173955d011SMarcel Moolenaar  *    documentation and/or other materials provided with the distribution.
183955d011SMarcel Moolenaar  * 3. Neither the name of the University nor the names of its contributors
193955d011SMarcel Moolenaar  *    may be used to endorse or promote products derived from this software
203955d011SMarcel Moolenaar  *    without specific prior written permission.
213955d011SMarcel Moolenaar  *
223955d011SMarcel Moolenaar  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
233955d011SMarcel Moolenaar  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
243955d011SMarcel Moolenaar  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
253955d011SMarcel Moolenaar  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
263955d011SMarcel Moolenaar  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
273955d011SMarcel Moolenaar  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
283955d011SMarcel Moolenaar  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
293955d011SMarcel Moolenaar  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
303955d011SMarcel Moolenaar  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
313955d011SMarcel Moolenaar  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
323955d011SMarcel Moolenaar  * SUCH DAMAGE.
333955d011SMarcel Moolenaar  */
343955d011SMarcel Moolenaar 
353955d011SMarcel Moolenaar /*
363955d011SMarcel Moolenaar  * Copyright (c) 1988, 1989 by Adam de Boor
373955d011SMarcel Moolenaar  * Copyright (c) 1989 by Berkeley Softworks
383955d011SMarcel Moolenaar  * All rights reserved.
393955d011SMarcel Moolenaar  *
403955d011SMarcel Moolenaar  * This code is derived from software contributed to Berkeley by
413955d011SMarcel Moolenaar  * Adam de Boor.
423955d011SMarcel Moolenaar  *
433955d011SMarcel Moolenaar  * Redistribution and use in source and binary forms, with or without
443955d011SMarcel Moolenaar  * modification, are permitted provided that the following conditions
453955d011SMarcel Moolenaar  * are met:
463955d011SMarcel Moolenaar  * 1. Redistributions of source code must retain the above copyright
473955d011SMarcel Moolenaar  *    notice, this list of conditions and the following disclaimer.
483955d011SMarcel Moolenaar  * 2. Redistributions in binary form must reproduce the above copyright
493955d011SMarcel Moolenaar  *    notice, this list of conditions and the following disclaimer in the
503955d011SMarcel Moolenaar  *    documentation and/or other materials provided with the distribution.
513955d011SMarcel Moolenaar  * 3. All advertising materials mentioning features or use of this software
523955d011SMarcel Moolenaar  *    must display the following acknowledgement:
533955d011SMarcel Moolenaar  *	This product includes software developed by the University of
543955d011SMarcel Moolenaar  *	California, Berkeley and its contributors.
553955d011SMarcel Moolenaar  * 4. Neither the name of the University nor the names of its contributors
563955d011SMarcel Moolenaar  *    may be used to endorse or promote products derived from this software
573955d011SMarcel Moolenaar  *    without specific prior written permission.
583955d011SMarcel Moolenaar  *
593955d011SMarcel Moolenaar  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
603955d011SMarcel Moolenaar  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
613955d011SMarcel Moolenaar  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
623955d011SMarcel Moolenaar  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
633955d011SMarcel Moolenaar  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
643955d011SMarcel Moolenaar  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
653955d011SMarcel Moolenaar  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
663955d011SMarcel Moolenaar  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
673955d011SMarcel Moolenaar  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
683955d011SMarcel Moolenaar  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
693955d011SMarcel Moolenaar  * SUCH DAMAGE.
703955d011SMarcel Moolenaar  */
713955d011SMarcel Moolenaar 
723955d011SMarcel Moolenaar #ifndef MAKE_NATIVE
73*49caa483SSimon J. Gerraty static char rcsid[] = "$NetBSD: job.c,v 1.197 2020/02/06 01:13:19 sjg Exp $";
743955d011SMarcel Moolenaar #else
753955d011SMarcel Moolenaar #include <sys/cdefs.h>
763955d011SMarcel Moolenaar #ifndef lint
773955d011SMarcel Moolenaar #if 0
783955d011SMarcel Moolenaar static char sccsid[] = "@(#)job.c	8.2 (Berkeley) 3/19/94";
793955d011SMarcel Moolenaar #else
80*49caa483SSimon J. Gerraty __RCSID("$NetBSD: job.c,v 1.197 2020/02/06 01:13:19 sjg Exp $");
813955d011SMarcel Moolenaar #endif
823955d011SMarcel Moolenaar #endif /* not lint */
833955d011SMarcel Moolenaar #endif
843955d011SMarcel Moolenaar 
853955d011SMarcel Moolenaar /*-
863955d011SMarcel Moolenaar  * job.c --
873955d011SMarcel Moolenaar  *	handle the creation etc. of our child processes.
883955d011SMarcel Moolenaar  *
893955d011SMarcel Moolenaar  * Interface:
903955d011SMarcel Moolenaar  *	Job_Make  	    	Start the creation of the given target.
913955d011SMarcel Moolenaar  *
923955d011SMarcel Moolenaar  *	Job_CatchChildren   	Check for and handle the termination of any
933955d011SMarcel Moolenaar  *	    	  	    	children. This must be called reasonably
943955d011SMarcel Moolenaar  *	    	  	    	frequently to keep the whole make going at
953955d011SMarcel Moolenaar  *	    	  	    	a decent clip, since job table entries aren't
963955d011SMarcel Moolenaar  *	    	  	    	removed until their process is caught this way.
973955d011SMarcel Moolenaar  *
983955d011SMarcel Moolenaar  *	Job_CatchOutput	    	Print any output our children have produced.
993955d011SMarcel Moolenaar  *	    	  	    	Should also be called fairly frequently to
1003955d011SMarcel Moolenaar  *	    	  	    	keep the user informed of what's going on.
1013955d011SMarcel Moolenaar  *	    	  	    	If no output is waiting, it will block for
1023955d011SMarcel Moolenaar  *	    	  	    	a time given by the SEL_* constants, below,
1033955d011SMarcel Moolenaar  *	    	  	    	or until output is ready.
1043955d011SMarcel Moolenaar  *
10550d2e745SSimon J. Gerraty  *	Job_Init  	    	Called to initialize this module. in addition,
1063955d011SMarcel Moolenaar  *	    	  	    	any commands attached to the .BEGIN target
1073955d011SMarcel Moolenaar  *	    	  	    	are executed before this function returns.
1083955d011SMarcel Moolenaar  *	    	  	    	Hence, the makefile must have been parsed
1093955d011SMarcel Moolenaar  *	    	  	    	before this function is called.
1103955d011SMarcel Moolenaar  *
1113955d011SMarcel Moolenaar  *	Job_End  	    	Cleanup any memory used.
1123955d011SMarcel Moolenaar  *
1133955d011SMarcel Moolenaar  *	Job_ParseShell	    	Given the line following a .SHELL target, parse
1143955d011SMarcel Moolenaar  *	    	  	    	the line as a shell specification. Returns
1153955d011SMarcel Moolenaar  *	    	  	    	FAILURE if the spec was incorrect.
1163955d011SMarcel Moolenaar  *
1173955d011SMarcel Moolenaar  *	Job_Finish	    	Perform any final processing which needs doing.
1183955d011SMarcel Moolenaar  *	    	  	    	This includes the execution of any commands
1193955d011SMarcel Moolenaar  *	    	  	    	which have been/were attached to the .END
1203955d011SMarcel Moolenaar  *	    	  	    	target. It should only be called when the
1213955d011SMarcel Moolenaar  *	    	  	    	job table is empty.
1223955d011SMarcel Moolenaar  *
1233955d011SMarcel Moolenaar  *	Job_AbortAll	    	Abort all currently running jobs. It doesn't
1243955d011SMarcel Moolenaar  *	    	  	    	handle output or do anything for the jobs,
1253955d011SMarcel Moolenaar  *	    	  	    	just kills them. It should only be called in
1263955d011SMarcel Moolenaar  *	    	  	    	an emergency, as it were.
1273955d011SMarcel Moolenaar  *
1283955d011SMarcel Moolenaar  *	Job_CheckCommands   	Verify that the commands for a target are
1293955d011SMarcel Moolenaar  *	    	  	    	ok. Provide them if necessary and possible.
1303955d011SMarcel Moolenaar  *
1313955d011SMarcel Moolenaar  *	Job_Touch 	    	Update a target without really updating it.
1323955d011SMarcel Moolenaar  *
1333955d011SMarcel Moolenaar  *	Job_Wait  	    	Wait for all currently-running jobs to finish.
1343955d011SMarcel Moolenaar  */
1353955d011SMarcel Moolenaar 
1363955d011SMarcel Moolenaar #ifdef HAVE_CONFIG_H
1373955d011SMarcel Moolenaar # include "config.h"
1383955d011SMarcel Moolenaar #endif
1393955d011SMarcel Moolenaar #include <sys/types.h>
1403955d011SMarcel Moolenaar #include <sys/stat.h>
1413955d011SMarcel Moolenaar #include <sys/file.h>
1423955d011SMarcel Moolenaar #include <sys/time.h>
1433955d011SMarcel Moolenaar #include "wait.h"
1443955d011SMarcel Moolenaar 
1451748de26SSimon J. Gerraty #include <assert.h>
1463955d011SMarcel Moolenaar #include <errno.h>
1473955d011SMarcel Moolenaar #if !defined(USE_SELECT) && defined(HAVE_POLL_H)
1483955d011SMarcel Moolenaar #include <poll.h>
1493955d011SMarcel Moolenaar #else
1503955d011SMarcel Moolenaar #ifndef USE_SELECT			/* no poll.h */
1513955d011SMarcel Moolenaar # define USE_SELECT
1523955d011SMarcel Moolenaar #endif
1533955d011SMarcel Moolenaar #if defined(HAVE_SYS_SELECT_H)
1543955d011SMarcel Moolenaar # include <sys/select.h>
1553955d011SMarcel Moolenaar #endif
1563955d011SMarcel Moolenaar #endif
1573955d011SMarcel Moolenaar #include <signal.h>
1583955d011SMarcel Moolenaar #include <stdio.h>
1593955d011SMarcel Moolenaar #include <string.h>
1603955d011SMarcel Moolenaar #include <utime.h>
1613955d011SMarcel Moolenaar #if defined(HAVE_SYS_SOCKET_H)
1623955d011SMarcel Moolenaar # include <sys/socket.h>
1633955d011SMarcel Moolenaar #endif
1643955d011SMarcel Moolenaar 
1653955d011SMarcel Moolenaar #include "make.h"
1663955d011SMarcel Moolenaar #include "hash.h"
1673955d011SMarcel Moolenaar #include "dir.h"
1683955d011SMarcel Moolenaar #include "job.h"
1693955d011SMarcel Moolenaar #include "pathnames.h"
1703955d011SMarcel Moolenaar #include "trace.h"
1713955d011SMarcel Moolenaar # define STATIC static
1723955d011SMarcel Moolenaar 
1733955d011SMarcel Moolenaar /*
1749a4bc556SSimon J. Gerraty  * FreeBSD: traditionally .MAKE is not required to
1759a4bc556SSimon J. Gerraty  * pass jobs queue to sub-makes.
1769a4bc556SSimon J. Gerraty  * Use .MAKE.ALWAYS_PASS_JOB_QUEUE=no to disable.
1779a4bc556SSimon J. Gerraty  */
1789a4bc556SSimon J. Gerraty #define MAKE_ALWAYS_PASS_JOB_QUEUE ".MAKE.ALWAYS_PASS_JOB_QUEUE"
1799a4bc556SSimon J. Gerraty static int Always_pass_job_queue = TRUE;
1802d395cb5SSimon J. Gerraty /*
1812d395cb5SSimon J. Gerraty  * FreeBSD: aborting entire parallel make isn't always
1822d395cb5SSimon J. Gerraty  * desired. When doing tinderbox for example, failure of
1832d395cb5SSimon J. Gerraty  * one architecture should not stop all.
1842d395cb5SSimon J. Gerraty  * We still want to bail on interrupt though.
1852d395cb5SSimon J. Gerraty  */
1862d395cb5SSimon J. Gerraty #define MAKE_JOB_ERROR_TOKEN "MAKE_JOB_ERROR_TOKEN"
1872d395cb5SSimon J. Gerraty static int Job_error_token = TRUE;
1889a4bc556SSimon J. Gerraty 
1899a4bc556SSimon J. Gerraty /*
1903955d011SMarcel Moolenaar  * error handling variables
1913955d011SMarcel Moolenaar  */
1923955d011SMarcel Moolenaar static int     	errors = 0;	    /* number of errors reported */
1933955d011SMarcel Moolenaar static int    	aborting = 0;	    /* why is the make aborting? */
1943955d011SMarcel Moolenaar #define ABORT_ERROR	1   	    /* Because of an error */
1953955d011SMarcel Moolenaar #define ABORT_INTERRUPT	2   	    /* Because it was interrupted */
1963955d011SMarcel Moolenaar #define ABORT_WAIT	3   	    /* Waiting for jobs to finish */
1973955d011SMarcel Moolenaar #define JOB_TOKENS	"+EI+"	    /* Token to requeue for each abort state */
1983955d011SMarcel Moolenaar 
1993955d011SMarcel Moolenaar /*
2003955d011SMarcel Moolenaar  * this tracks the number of tokens currently "out" to build jobs.
2013955d011SMarcel Moolenaar  */
2023955d011SMarcel Moolenaar int jobTokensRunning = 0;
2033955d011SMarcel Moolenaar int not_parallel = 0;		    /* set if .NOT_PARALLEL */
2043955d011SMarcel Moolenaar 
2053955d011SMarcel Moolenaar /*
2063955d011SMarcel Moolenaar  * XXX: Avoid SunOS bug... FILENO() is fp->_file, and file
2073955d011SMarcel Moolenaar  * is a char! So when we go above 127 we turn negative!
2083955d011SMarcel Moolenaar  */
2093955d011SMarcel Moolenaar #define FILENO(a) ((unsigned) fileno(a))
2103955d011SMarcel Moolenaar 
2113955d011SMarcel Moolenaar /*
2123955d011SMarcel Moolenaar  * post-make command processing. The node postCommands is really just the
2133955d011SMarcel Moolenaar  * .END target but we keep it around to avoid having to search for it
2143955d011SMarcel Moolenaar  * all the time.
2153955d011SMarcel Moolenaar  */
2163955d011SMarcel Moolenaar static GNode   	  *postCommands = NULL;
2173955d011SMarcel Moolenaar 				    /* node containing commands to execute when
2183955d011SMarcel Moolenaar 				     * everything else is done */
2193955d011SMarcel Moolenaar static int     	  numCommands; 	    /* The number of commands actually printed
2203955d011SMarcel Moolenaar 				     * for a target. Should this number be
2213955d011SMarcel Moolenaar 				     * 0, no shell will be executed. */
2223955d011SMarcel Moolenaar 
2233955d011SMarcel Moolenaar /*
2243955d011SMarcel Moolenaar  * Return values from JobStart.
2253955d011SMarcel Moolenaar  */
2263955d011SMarcel Moolenaar #define JOB_RUNNING	0   	/* Job is running */
2273955d011SMarcel Moolenaar #define JOB_ERROR 	1   	/* Error in starting the job */
2283955d011SMarcel Moolenaar #define JOB_FINISHED	2   	/* The job is already finished */
2293955d011SMarcel Moolenaar 
2303955d011SMarcel Moolenaar /*
2313955d011SMarcel Moolenaar  * Descriptions for various shells.
2323955d011SMarcel Moolenaar  *
2333955d011SMarcel Moolenaar  * The build environment may set DEFSHELL_INDEX to one of
2343955d011SMarcel Moolenaar  * DEFSHELL_INDEX_SH, DEFSHELL_INDEX_KSH, or DEFSHELL_INDEX_CSH, to
2353955d011SMarcel Moolenaar  * select one of the prefedined shells as the default shell.
2363955d011SMarcel Moolenaar  *
2373955d011SMarcel Moolenaar  * Alternatively, the build environment may set DEFSHELL_CUSTOM to the
2383955d011SMarcel Moolenaar  * name or the full path of a sh-compatible shell, which will be used as
2393955d011SMarcel Moolenaar  * the default shell.
2403955d011SMarcel Moolenaar  *
2413955d011SMarcel Moolenaar  * ".SHELL" lines in Makefiles can choose the default shell from the
2423955d011SMarcel Moolenaar  # set defined here, or add additional shells.
2433955d011SMarcel Moolenaar  */
2443955d011SMarcel Moolenaar 
2453955d011SMarcel Moolenaar #ifdef DEFSHELL_CUSTOM
2463955d011SMarcel Moolenaar #define DEFSHELL_INDEX_CUSTOM 0
2473955d011SMarcel Moolenaar #define DEFSHELL_INDEX_SH     1
2483955d011SMarcel Moolenaar #define DEFSHELL_INDEX_KSH    2
2493955d011SMarcel Moolenaar #define DEFSHELL_INDEX_CSH    3
2503955d011SMarcel Moolenaar #else /* !DEFSHELL_CUSTOM */
2513955d011SMarcel Moolenaar #define DEFSHELL_INDEX_SH     0
2523955d011SMarcel Moolenaar #define DEFSHELL_INDEX_KSH    1
2533955d011SMarcel Moolenaar #define DEFSHELL_INDEX_CSH    2
2543955d011SMarcel Moolenaar #endif /* !DEFSHELL_CUSTOM */
2553955d011SMarcel Moolenaar 
2563955d011SMarcel Moolenaar #ifndef DEFSHELL_INDEX
2573955d011SMarcel Moolenaar #define DEFSHELL_INDEX 0	/* DEFSHELL_INDEX_CUSTOM or DEFSHELL_INDEX_SH */
2583955d011SMarcel Moolenaar #endif /* !DEFSHELL_INDEX */
2593955d011SMarcel Moolenaar 
2603955d011SMarcel Moolenaar static Shell    shells[] = {
2613955d011SMarcel Moolenaar #ifdef DEFSHELL_CUSTOM
2623955d011SMarcel Moolenaar     /*
2633955d011SMarcel Moolenaar      * An sh-compatible shell with a non-standard name.
2643955d011SMarcel Moolenaar      *
2653955d011SMarcel Moolenaar      * Keep this in sync with the "sh" description below, but avoid
2663955d011SMarcel Moolenaar      * non-portable features that might not be supplied by all
2673955d011SMarcel Moolenaar      * sh-compatible shells.
2683955d011SMarcel Moolenaar      */
2693955d011SMarcel Moolenaar {
2703955d011SMarcel Moolenaar     DEFSHELL_CUSTOM,
2713955d011SMarcel Moolenaar     FALSE, "", "", "", 0,
2723955d011SMarcel Moolenaar     FALSE, "echo \"%s\"\n", "%s\n", "{ %s \n} || exit $?\n", "'\n'", '#',
2733955d011SMarcel Moolenaar     "",
2743955d011SMarcel Moolenaar     "",
2753955d011SMarcel Moolenaar },
2763955d011SMarcel Moolenaar #endif /* DEFSHELL_CUSTOM */
2773955d011SMarcel Moolenaar     /*
2783955d011SMarcel Moolenaar      * SH description. Echo control is also possible and, under
2793955d011SMarcel Moolenaar      * sun UNIX anyway, one can even control error checking.
2803955d011SMarcel Moolenaar      */
2813955d011SMarcel Moolenaar {
2823955d011SMarcel Moolenaar     "sh",
2833955d011SMarcel Moolenaar     FALSE, "", "", "", 0,
2843955d011SMarcel Moolenaar     FALSE, "echo \"%s\"\n", "%s\n", "{ %s \n} || exit $?\n", "'\n'", '#',
2853955d011SMarcel Moolenaar #if defined(MAKE_NATIVE) && defined(__NetBSD__)
2863955d011SMarcel Moolenaar     "q",
2873955d011SMarcel Moolenaar #else
2883955d011SMarcel Moolenaar     "",
2893955d011SMarcel Moolenaar #endif
2903955d011SMarcel Moolenaar     "",
2913955d011SMarcel Moolenaar },
2923955d011SMarcel Moolenaar     /*
2933955d011SMarcel Moolenaar      * KSH description.
2943955d011SMarcel Moolenaar      */
2953955d011SMarcel Moolenaar {
2963955d011SMarcel Moolenaar     "ksh",
2973955d011SMarcel Moolenaar     TRUE, "set +v", "set -v", "set +v", 6,
2983955d011SMarcel Moolenaar     FALSE, "echo \"%s\"\n", "%s\n", "{ %s \n} || exit $?\n", "'\n'", '#',
2993955d011SMarcel Moolenaar     "v",
3003955d011SMarcel Moolenaar     "",
3013955d011SMarcel Moolenaar },
3023955d011SMarcel Moolenaar     /*
3033955d011SMarcel Moolenaar      * CSH description. The csh can do echo control by playing
3043955d011SMarcel Moolenaar      * with the setting of the 'echo' shell variable. Sadly,
3053955d011SMarcel Moolenaar      * however, it is unable to do error control nicely.
3063955d011SMarcel Moolenaar      */
3073955d011SMarcel Moolenaar {
3083955d011SMarcel Moolenaar     "csh",
3093955d011SMarcel Moolenaar     TRUE, "unset verbose", "set verbose", "unset verbose", 10,
3103955d011SMarcel Moolenaar     FALSE, "echo \"%s\"\n", "csh -c \"%s || exit 0\"\n", "", "'\\\n'", '#',
3113955d011SMarcel Moolenaar     "v", "e",
3123955d011SMarcel Moolenaar },
3133955d011SMarcel Moolenaar     /*
3143955d011SMarcel Moolenaar      * UNKNOWN.
3153955d011SMarcel Moolenaar      */
3163955d011SMarcel Moolenaar {
3173955d011SMarcel Moolenaar     NULL,
3183955d011SMarcel Moolenaar     FALSE, NULL, NULL, NULL, 0,
3193955d011SMarcel Moolenaar     FALSE, NULL, NULL, NULL, NULL, 0,
3203955d011SMarcel Moolenaar     NULL, NULL,
3213955d011SMarcel Moolenaar }
3223955d011SMarcel Moolenaar };
3233955d011SMarcel Moolenaar static Shell *commandShell = &shells[DEFSHELL_INDEX]; /* this is the shell to
3243955d011SMarcel Moolenaar 						   * which we pass all
3253955d011SMarcel Moolenaar 						   * commands in the Makefile.
3263955d011SMarcel Moolenaar 						   * It is set by the
3273955d011SMarcel Moolenaar 						   * Job_ParseShell function */
3283955d011SMarcel Moolenaar const char *shellPath = NULL,		  	  /* full pathname of
3293955d011SMarcel Moolenaar 						   * executable image */
3303955d011SMarcel Moolenaar            *shellName = NULL;		      	  /* last component of shell */
33151ee2c1cSSimon J. Gerraty char *shellErrFlag = NULL;
3323955d011SMarcel Moolenaar static const char *shellArgv = NULL;		  /* Custom shell args */
3333955d011SMarcel Moolenaar 
3343955d011SMarcel Moolenaar 
3353955d011SMarcel Moolenaar STATIC Job	*job_table;	/* The structures that describe them */
3363955d011SMarcel Moolenaar STATIC Job	*job_table_end;	/* job_table + maxJobs */
3373955d011SMarcel Moolenaar static int	wantToken;	/* we want a token */
3383955d011SMarcel Moolenaar static int lurking_children = 0;
3393955d011SMarcel Moolenaar static int make_suspended = 0;	/* non-zero if we've seen a SIGTSTP (etc) */
3403955d011SMarcel Moolenaar 
3413955d011SMarcel Moolenaar /*
3423955d011SMarcel Moolenaar  * Set of descriptors of pipes connected to
3433955d011SMarcel Moolenaar  * the output channels of children
3443955d011SMarcel Moolenaar  */
3453955d011SMarcel Moolenaar static struct pollfd *fds = NULL;
3463955d011SMarcel Moolenaar static Job **jobfds = NULL;
3473955d011SMarcel Moolenaar static int nfds = 0;
3483955d011SMarcel Moolenaar static void watchfd(Job *);
3493955d011SMarcel Moolenaar static void clearfd(Job *);
3503955d011SMarcel Moolenaar static int readyfd(Job *);
3513955d011SMarcel Moolenaar 
3523955d011SMarcel Moolenaar STATIC GNode   	*lastNode;	/* The node for which output was most recently
3533955d011SMarcel Moolenaar 				 * produced. */
3543955d011SMarcel Moolenaar static char *targPrefix = NULL; /* What we print at the start of TARG_FMT */
3553955d011SMarcel Moolenaar static Job tokenWaitJob;	/* token wait pseudo-job */
3563955d011SMarcel Moolenaar 
3573955d011SMarcel Moolenaar static Job childExitJob;	/* child exit pseudo-job */
3583955d011SMarcel Moolenaar #define	CHILD_EXIT	"."
3593955d011SMarcel Moolenaar #define	DO_JOB_RESUME	"R"
3603955d011SMarcel Moolenaar 
361*49caa483SSimon J. Gerraty static const int npseudojobs = 2; /* number of pseudo-jobs */
362*49caa483SSimon J. Gerraty 
3633955d011SMarcel Moolenaar #define TARG_FMT  "%s %s ---\n" /* Default format */
3643955d011SMarcel Moolenaar #define MESSAGE(fp, gn) \
36551ee2c1cSSimon J. Gerraty 	if (maxJobs != 1 && targPrefix && *targPrefix) \
3663955d011SMarcel Moolenaar 	    (void)fprintf(fp, TARG_FMT, targPrefix, gn->name)
3673955d011SMarcel Moolenaar 
3683955d011SMarcel Moolenaar static sigset_t caught_signals;	/* Set of signals we handle */
3693955d011SMarcel Moolenaar 
3703955d011SMarcel Moolenaar static void JobChildSig(int);
3713955d011SMarcel Moolenaar static void JobContinueSig(int);
3723955d011SMarcel Moolenaar static Job *JobFindPid(int, int, Boolean);
3733955d011SMarcel Moolenaar static int JobPrintCommand(void *, void *);
3743955d011SMarcel Moolenaar static int JobSaveCommand(void *, void *);
3753955d011SMarcel Moolenaar static void JobClose(Job *);
3763955d011SMarcel Moolenaar static void JobExec(Job *, char **);
3773955d011SMarcel Moolenaar static void JobMakeArgv(Job *, char **);
3783955d011SMarcel Moolenaar static int JobStart(GNode *, int);
3793955d011SMarcel Moolenaar static char *JobOutput(Job *, char *, char *, int);
3803955d011SMarcel Moolenaar static void JobDoOutput(Job *, Boolean);
3813955d011SMarcel Moolenaar static Shell *JobMatchShell(const char *);
3823955d011SMarcel Moolenaar static void JobInterrupt(int, int) MAKE_ATTR_DEAD;
3833955d011SMarcel Moolenaar static void JobRestartJobs(void);
3843955d011SMarcel Moolenaar static void JobTokenAdd(void);
3853955d011SMarcel Moolenaar static void JobSigLock(sigset_t *);
3863955d011SMarcel Moolenaar static void JobSigUnlock(sigset_t *);
3873955d011SMarcel Moolenaar static void JobSigReset(void);
3883955d011SMarcel Moolenaar 
389b46b9039SSimon J. Gerraty #if !defined(MALLOC_OPTIONS)
390b46b9039SSimon J. Gerraty # define MALLOC_OPTIONS "A"
391b46b9039SSimon J. Gerraty #endif
392b46b9039SSimon J. Gerraty const char *malloc_options= MALLOC_OPTIONS;
3933955d011SMarcel Moolenaar 
394*49caa483SSimon J. Gerraty static unsigned
395*49caa483SSimon J. Gerraty nfds_per_job(void)
396*49caa483SSimon J. Gerraty {
397*49caa483SSimon J. Gerraty #if defined(USE_FILEMON) && !defined(USE_FILEMON_DEV)
398*49caa483SSimon J. Gerraty     if (useMeta)
399*49caa483SSimon J. Gerraty 	return 2;
400*49caa483SSimon J. Gerraty #endif
401*49caa483SSimon J. Gerraty     return 1;
402*49caa483SSimon J. Gerraty }
403*49caa483SSimon J. Gerraty 
4043955d011SMarcel Moolenaar static void
4053955d011SMarcel Moolenaar job_table_dump(const char *where)
4063955d011SMarcel Moolenaar {
4073955d011SMarcel Moolenaar     Job *job;
4083955d011SMarcel Moolenaar 
4093955d011SMarcel Moolenaar     fprintf(debug_file, "job table @ %s\n", where);
4103955d011SMarcel Moolenaar     for (job = job_table; job < job_table_end; job++) {
4113955d011SMarcel Moolenaar 	fprintf(debug_file, "job %d, status %d, flags %d, pid %d\n",
4123955d011SMarcel Moolenaar 	    (int)(job - job_table), job->job_state, job->flags, job->pid);
4133955d011SMarcel Moolenaar     }
4143955d011SMarcel Moolenaar }
4153955d011SMarcel Moolenaar 
4163955d011SMarcel Moolenaar /*
41745447996SSimon J. Gerraty  * Delete the target of a failed, interrupted, or otherwise
41845447996SSimon J. Gerraty  * unsuccessful job unless inhibited by .PRECIOUS.
41945447996SSimon J. Gerraty  */
42045447996SSimon J. Gerraty static void
42145447996SSimon J. Gerraty JobDeleteTarget(GNode *gn)
42245447996SSimon J. Gerraty {
42345447996SSimon J. Gerraty 	if ((gn->type & (OP_JOIN|OP_PHONY)) == 0 && !Targ_Precious(gn)) {
42445447996SSimon J. Gerraty 	    char *file = (gn->path == NULL ? gn->name : gn->path);
42545447996SSimon J. Gerraty 	    if (!noExecute && eunlink(file) != -1) {
42645447996SSimon J. Gerraty 		Error("*** %s removed", file);
42745447996SSimon J. Gerraty 	    }
42845447996SSimon J. Gerraty 	}
42945447996SSimon J. Gerraty }
43045447996SSimon J. Gerraty 
43145447996SSimon J. Gerraty /*
4323955d011SMarcel Moolenaar  * JobSigLock/JobSigUnlock
4333955d011SMarcel Moolenaar  *
4343955d011SMarcel Moolenaar  * Signal lock routines to get exclusive access. Currently used to
4353955d011SMarcel Moolenaar  * protect `jobs' and `stoppedJobs' list manipulations.
4363955d011SMarcel Moolenaar  */
4373955d011SMarcel Moolenaar static void JobSigLock(sigset_t *omaskp)
4383955d011SMarcel Moolenaar {
4393955d011SMarcel Moolenaar 	if (sigprocmask(SIG_BLOCK, &caught_signals, omaskp) != 0) {
4403955d011SMarcel Moolenaar 		Punt("JobSigLock: sigprocmask: %s", strerror(errno));
4413955d011SMarcel Moolenaar 		sigemptyset(omaskp);
4423955d011SMarcel Moolenaar 	}
4433955d011SMarcel Moolenaar }
4443955d011SMarcel Moolenaar 
4453955d011SMarcel Moolenaar static void JobSigUnlock(sigset_t *omaskp)
4463955d011SMarcel Moolenaar {
4473955d011SMarcel Moolenaar 	(void)sigprocmask(SIG_SETMASK, omaskp, NULL);
4483955d011SMarcel Moolenaar }
4493955d011SMarcel Moolenaar 
4503955d011SMarcel Moolenaar static void
4513955d011SMarcel Moolenaar JobCreatePipe(Job *job, int minfd)
4523955d011SMarcel Moolenaar {
453e1cee40dSSimon J. Gerraty     int i, fd, flags;
4543955d011SMarcel Moolenaar 
4553955d011SMarcel Moolenaar     if (pipe(job->jobPipe) == -1)
4563955d011SMarcel Moolenaar 	Punt("Cannot create pipe: %s", strerror(errno));
4573955d011SMarcel Moolenaar 
45874d2e02bSSimon J. Gerraty     for (i = 0; i < 2; i++) {
45974d2e02bSSimon J. Gerraty        /* Avoid using low numbered fds */
46074d2e02bSSimon J. Gerraty        fd = fcntl(job->jobPipe[i], F_DUPFD, minfd);
46174d2e02bSSimon J. Gerraty        if (fd != -1) {
46274d2e02bSSimon J. Gerraty 	   close(job->jobPipe[i]);
46374d2e02bSSimon J. Gerraty 	   job->jobPipe[i] = fd;
46474d2e02bSSimon J. Gerraty        }
46574d2e02bSSimon J. Gerraty     }
46674d2e02bSSimon J. Gerraty 
4673955d011SMarcel Moolenaar     /* Set close-on-exec flag for both */
468e1cee40dSSimon J. Gerraty     if (fcntl(job->jobPipe[0], F_SETFD, FD_CLOEXEC) == -1)
469e1cee40dSSimon J. Gerraty 	Punt("Cannot set close-on-exec: %s", strerror(errno));
470e1cee40dSSimon J. Gerraty     if (fcntl(job->jobPipe[1], F_SETFD, FD_CLOEXEC) == -1)
471e1cee40dSSimon J. Gerraty 	Punt("Cannot set close-on-exec: %s", strerror(errno));
4723955d011SMarcel Moolenaar 
4733955d011SMarcel Moolenaar     /*
4743955d011SMarcel Moolenaar      * We mark the input side of the pipe non-blocking; we poll(2) the
4753955d011SMarcel Moolenaar      * pipe when we're waiting for a job token, but we might lose the
4763955d011SMarcel Moolenaar      * race for the token when a new one becomes available, so the read
4773955d011SMarcel Moolenaar      * from the pipe should not block.
4783955d011SMarcel Moolenaar      */
479e1cee40dSSimon J. Gerraty     flags = fcntl(job->jobPipe[0], F_GETFL, 0);
480e1cee40dSSimon J. Gerraty     if (flags == -1)
481e1cee40dSSimon J. Gerraty 	Punt("Cannot get flags: %s", strerror(errno));
482e1cee40dSSimon J. Gerraty     flags |= O_NONBLOCK;
483e1cee40dSSimon J. Gerraty     if (fcntl(job->jobPipe[0], F_SETFL, flags) == -1)
484e1cee40dSSimon J. Gerraty 	Punt("Cannot set flags: %s", strerror(errno));
4853955d011SMarcel Moolenaar }
4863955d011SMarcel Moolenaar 
4873955d011SMarcel Moolenaar /*-
4883955d011SMarcel Moolenaar  *-----------------------------------------------------------------------
4893955d011SMarcel Moolenaar  * JobCondPassSig --
4903955d011SMarcel Moolenaar  *	Pass a signal to a job
4913955d011SMarcel Moolenaar  *
4923955d011SMarcel Moolenaar  * Input:
4933955d011SMarcel Moolenaar  *	signop		Signal to send it
4943955d011SMarcel Moolenaar  *
4953955d011SMarcel Moolenaar  * Side Effects:
4963955d011SMarcel Moolenaar  *	None, except the job may bite it.
4973955d011SMarcel Moolenaar  *
4983955d011SMarcel Moolenaar  *-----------------------------------------------------------------------
4993955d011SMarcel Moolenaar  */
5003955d011SMarcel Moolenaar static void
5013955d011SMarcel Moolenaar JobCondPassSig(int signo)
5023955d011SMarcel Moolenaar {
5033955d011SMarcel Moolenaar     Job *job;
5043955d011SMarcel Moolenaar 
5053955d011SMarcel Moolenaar     if (DEBUG(JOB)) {
5063955d011SMarcel Moolenaar 	(void)fprintf(debug_file, "JobCondPassSig(%d) called.\n", signo);
5073955d011SMarcel Moolenaar     }
5083955d011SMarcel Moolenaar 
5093955d011SMarcel Moolenaar     for (job = job_table; job < job_table_end; job++) {
5103955d011SMarcel Moolenaar 	if (job->job_state != JOB_ST_RUNNING)
5113955d011SMarcel Moolenaar 	    continue;
5123955d011SMarcel Moolenaar 	if (DEBUG(JOB)) {
5133955d011SMarcel Moolenaar 	    (void)fprintf(debug_file,
5143955d011SMarcel Moolenaar 			   "JobCondPassSig passing signal %d to child %d.\n",
5153955d011SMarcel Moolenaar 			   signo, job->pid);
5163955d011SMarcel Moolenaar 	}
5173955d011SMarcel Moolenaar 	KILLPG(job->pid, signo);
5183955d011SMarcel Moolenaar     }
5193955d011SMarcel Moolenaar }
5203955d011SMarcel Moolenaar 
5213955d011SMarcel Moolenaar /*-
5223955d011SMarcel Moolenaar  *-----------------------------------------------------------------------
5233955d011SMarcel Moolenaar  * JobChldSig --
5243955d011SMarcel Moolenaar  *	SIGCHLD handler.
5253955d011SMarcel Moolenaar  *
5263955d011SMarcel Moolenaar  * Input:
5273955d011SMarcel Moolenaar  *	signo		The signal number we've received
5283955d011SMarcel Moolenaar  *
5293955d011SMarcel Moolenaar  * Results:
5303955d011SMarcel Moolenaar  *	None.
5313955d011SMarcel Moolenaar  *
5323955d011SMarcel Moolenaar  * Side Effects:
5333955d011SMarcel Moolenaar  *	Sends a token on the child exit pipe to wake us up from
5343955d011SMarcel Moolenaar  *	select()/poll().
5353955d011SMarcel Moolenaar  *
5363955d011SMarcel Moolenaar  *-----------------------------------------------------------------------
5373955d011SMarcel Moolenaar  */
5383955d011SMarcel Moolenaar static void
5393955d011SMarcel Moolenaar JobChildSig(int signo MAKE_ATTR_UNUSED)
5403955d011SMarcel Moolenaar {
5413cbdda60SSimon J. Gerraty     while (write(childExitJob.outPipe, CHILD_EXIT, 1) == -1 && errno == EAGAIN)
5423cbdda60SSimon J. Gerraty 	continue;
5433955d011SMarcel Moolenaar }
5443955d011SMarcel Moolenaar 
5453955d011SMarcel Moolenaar 
5463955d011SMarcel Moolenaar /*-
5473955d011SMarcel Moolenaar  *-----------------------------------------------------------------------
5483955d011SMarcel Moolenaar  * JobContinueSig --
5493955d011SMarcel Moolenaar  *	Resume all stopped jobs.
5503955d011SMarcel Moolenaar  *
5513955d011SMarcel Moolenaar  * Input:
5523955d011SMarcel Moolenaar  *	signo		The signal number we've received
5533955d011SMarcel Moolenaar  *
5543955d011SMarcel Moolenaar  * Results:
5553955d011SMarcel Moolenaar  *	None.
5563955d011SMarcel Moolenaar  *
5573955d011SMarcel Moolenaar  * Side Effects:
5583955d011SMarcel Moolenaar  *	Jobs start running again.
5593955d011SMarcel Moolenaar  *
5603955d011SMarcel Moolenaar  *-----------------------------------------------------------------------
5613955d011SMarcel Moolenaar  */
5623955d011SMarcel Moolenaar static void
5633955d011SMarcel Moolenaar JobContinueSig(int signo MAKE_ATTR_UNUSED)
5643955d011SMarcel Moolenaar {
5653955d011SMarcel Moolenaar     /*
5663955d011SMarcel Moolenaar      * Defer sending to SIGCONT to our stopped children until we return
5673955d011SMarcel Moolenaar      * from the signal handler.
5683955d011SMarcel Moolenaar      */
5693cbdda60SSimon J. Gerraty     while (write(childExitJob.outPipe, DO_JOB_RESUME, 1) == -1 &&
5703cbdda60SSimon J. Gerraty 	errno == EAGAIN)
5713cbdda60SSimon J. Gerraty 	continue;
5723955d011SMarcel Moolenaar }
5733955d011SMarcel Moolenaar 
5743955d011SMarcel Moolenaar /*-
5753955d011SMarcel Moolenaar  *-----------------------------------------------------------------------
5763955d011SMarcel Moolenaar  * JobPassSig --
5773955d011SMarcel Moolenaar  *	Pass a signal on to all jobs, then resend to ourselves.
5783955d011SMarcel Moolenaar  *
5793955d011SMarcel Moolenaar  * Input:
5803955d011SMarcel Moolenaar  *	signo		The signal number we've received
5813955d011SMarcel Moolenaar  *
5823955d011SMarcel Moolenaar  * Results:
5833955d011SMarcel Moolenaar  *	None.
5843955d011SMarcel Moolenaar  *
5853955d011SMarcel Moolenaar  * Side Effects:
5863955d011SMarcel Moolenaar  *	We die by the same signal.
5873955d011SMarcel Moolenaar  *
5883955d011SMarcel Moolenaar  *-----------------------------------------------------------------------
5893955d011SMarcel Moolenaar  */
5903955d011SMarcel Moolenaar MAKE_ATTR_DEAD static void
5913955d011SMarcel Moolenaar JobPassSig_int(int signo)
5923955d011SMarcel Moolenaar {
5933955d011SMarcel Moolenaar     /* Run .INTERRUPT target then exit */
5943955d011SMarcel Moolenaar     JobInterrupt(TRUE, signo);
5953955d011SMarcel Moolenaar }
5963955d011SMarcel Moolenaar 
5973955d011SMarcel Moolenaar MAKE_ATTR_DEAD static void
5983955d011SMarcel Moolenaar JobPassSig_term(int signo)
5993955d011SMarcel Moolenaar {
6003955d011SMarcel Moolenaar     /* Dont run .INTERRUPT target then exit */
6013955d011SMarcel Moolenaar     JobInterrupt(FALSE, signo);
6023955d011SMarcel Moolenaar }
6033955d011SMarcel Moolenaar 
6043955d011SMarcel Moolenaar static void
6053955d011SMarcel Moolenaar JobPassSig_suspend(int signo)
6063955d011SMarcel Moolenaar {
6073955d011SMarcel Moolenaar     sigset_t nmask, omask;
6083955d011SMarcel Moolenaar     struct sigaction act;
6093955d011SMarcel Moolenaar 
6103955d011SMarcel Moolenaar     /* Suppress job started/continued messages */
6113955d011SMarcel Moolenaar     make_suspended = 1;
6123955d011SMarcel Moolenaar 
6133955d011SMarcel Moolenaar     /* Pass the signal onto every job */
6143955d011SMarcel Moolenaar     JobCondPassSig(signo);
6153955d011SMarcel Moolenaar 
6163955d011SMarcel Moolenaar     /*
6173955d011SMarcel Moolenaar      * Send ourselves the signal now we've given the message to everyone else.
6183955d011SMarcel Moolenaar      * Note we block everything else possible while we're getting the signal.
6193955d011SMarcel Moolenaar      * This ensures that all our jobs get continued when we wake up before
6203955d011SMarcel Moolenaar      * we take any other signal.
6213955d011SMarcel Moolenaar      */
6223955d011SMarcel Moolenaar     sigfillset(&nmask);
6233955d011SMarcel Moolenaar     sigdelset(&nmask, signo);
6243955d011SMarcel Moolenaar     (void)sigprocmask(SIG_SETMASK, &nmask, &omask);
6253955d011SMarcel Moolenaar 
6263955d011SMarcel Moolenaar     act.sa_handler = SIG_DFL;
6273955d011SMarcel Moolenaar     sigemptyset(&act.sa_mask);
6283955d011SMarcel Moolenaar     act.sa_flags = 0;
6293955d011SMarcel Moolenaar     (void)sigaction(signo, &act, NULL);
6303955d011SMarcel Moolenaar 
6313955d011SMarcel Moolenaar     if (DEBUG(JOB)) {
6323955d011SMarcel Moolenaar 	(void)fprintf(debug_file,
6333955d011SMarcel Moolenaar 		       "JobPassSig passing signal %d to self.\n", signo);
6343955d011SMarcel Moolenaar     }
6353955d011SMarcel Moolenaar 
6363955d011SMarcel Moolenaar     (void)kill(getpid(), signo);
6373955d011SMarcel Moolenaar 
6383955d011SMarcel Moolenaar     /*
6393955d011SMarcel Moolenaar      * We've been continued.
6403955d011SMarcel Moolenaar      *
6413955d011SMarcel Moolenaar      * A whole host of signals continue to happen!
6423955d011SMarcel Moolenaar      * SIGCHLD for any processes that actually suspended themselves.
6433955d011SMarcel Moolenaar      * SIGCHLD for any processes that exited while we were alseep.
6443955d011SMarcel Moolenaar      * The SIGCONT that actually caused us to wakeup.
6453955d011SMarcel Moolenaar      *
6463955d011SMarcel Moolenaar      * Since we defer passing the SIGCONT on to our children until
6473955d011SMarcel Moolenaar      * the main processing loop, we can be sure that all the SIGCHLD
6483955d011SMarcel Moolenaar      * events will have happened by then - and that the waitpid() will
6493955d011SMarcel Moolenaar      * collect the child 'suspended' events.
6503955d011SMarcel Moolenaar      * For correct sequencing we just need to ensure we process the
6513955d011SMarcel Moolenaar      * waitpid() before passign on the SIGCONT.
6523955d011SMarcel Moolenaar      *
6533955d011SMarcel Moolenaar      * In any case nothing else is needed here.
6543955d011SMarcel Moolenaar      */
6553955d011SMarcel Moolenaar 
6563955d011SMarcel Moolenaar     /* Restore handler and signal mask */
6573955d011SMarcel Moolenaar     act.sa_handler = JobPassSig_suspend;
6583955d011SMarcel Moolenaar     (void)sigaction(signo, &act, NULL);
6593955d011SMarcel Moolenaar     (void)sigprocmask(SIG_SETMASK, &omask, NULL);
6603955d011SMarcel Moolenaar }
6613955d011SMarcel Moolenaar 
6623955d011SMarcel Moolenaar /*-
6633955d011SMarcel Moolenaar  *-----------------------------------------------------------------------
6643955d011SMarcel Moolenaar  * JobFindPid  --
6653955d011SMarcel Moolenaar  *	Compare the pid of the job with the given pid and return 0 if they
6663955d011SMarcel Moolenaar  *	are equal. This function is called from Job_CatchChildren
6673955d011SMarcel Moolenaar  *	to find the job descriptor of the finished job.
6683955d011SMarcel Moolenaar  *
6693955d011SMarcel Moolenaar  * Input:
6703955d011SMarcel Moolenaar  *	job		job to examine
6713955d011SMarcel Moolenaar  *	pid		process id desired
6723955d011SMarcel Moolenaar  *
6733955d011SMarcel Moolenaar  * Results:
6743955d011SMarcel Moolenaar  *	Job with matching pid
6753955d011SMarcel Moolenaar  *
6763955d011SMarcel Moolenaar  * Side Effects:
6773955d011SMarcel Moolenaar  *	None
6783955d011SMarcel Moolenaar  *-----------------------------------------------------------------------
6793955d011SMarcel Moolenaar  */
6803955d011SMarcel Moolenaar static Job *
6813955d011SMarcel Moolenaar JobFindPid(int pid, int status, Boolean isJobs)
6823955d011SMarcel Moolenaar {
6833955d011SMarcel Moolenaar     Job *job;
6843955d011SMarcel Moolenaar 
6853955d011SMarcel Moolenaar     for (job = job_table; job < job_table_end; job++) {
6863955d011SMarcel Moolenaar 	if ((job->job_state == status) && job->pid == pid)
6873955d011SMarcel Moolenaar 	    return job;
6883955d011SMarcel Moolenaar     }
6893955d011SMarcel Moolenaar     if (DEBUG(JOB) && isJobs)
6903955d011SMarcel Moolenaar 	job_table_dump("no pid");
6913955d011SMarcel Moolenaar     return NULL;
6923955d011SMarcel Moolenaar }
6933955d011SMarcel Moolenaar 
6943955d011SMarcel Moolenaar /*-
6953955d011SMarcel Moolenaar  *-----------------------------------------------------------------------
6963955d011SMarcel Moolenaar  * JobPrintCommand  --
6973955d011SMarcel Moolenaar  *	Put out another command for the given job. If the command starts
6983955d011SMarcel Moolenaar  *	with an @ or a - we process it specially. In the former case,
6993955d011SMarcel Moolenaar  *	so long as the -s and -n flags weren't given to make, we stick
7003955d011SMarcel Moolenaar  *	a shell-specific echoOff command in the script. In the latter,
7013955d011SMarcel Moolenaar  *	we ignore errors for the entire job, unless the shell has error
7023955d011SMarcel Moolenaar  *	control.
7033955d011SMarcel Moolenaar  *	If the command is just "..." we take all future commands for this
7043955d011SMarcel Moolenaar  *	job to be commands to be executed once the entire graph has been
7053955d011SMarcel Moolenaar  *	made and return non-zero to signal that the end of the commands
7063955d011SMarcel Moolenaar  *	was reached. These commands are later attached to the postCommands
7073955d011SMarcel Moolenaar  *	node and executed by Job_End when all things are done.
7083955d011SMarcel Moolenaar  *	This function is called from JobStart via Lst_ForEach.
7093955d011SMarcel Moolenaar  *
7103955d011SMarcel Moolenaar  * Input:
7113955d011SMarcel Moolenaar  *	cmdp		command string to print
7123955d011SMarcel Moolenaar  *	jobp		job for which to print it
7133955d011SMarcel Moolenaar  *
7143955d011SMarcel Moolenaar  * Results:
7153955d011SMarcel Moolenaar  *	Always 0, unless the command was "..."
7163955d011SMarcel Moolenaar  *
7173955d011SMarcel Moolenaar  * Side Effects:
7183955d011SMarcel Moolenaar  *	If the command begins with a '-' and the shell has no error control,
7193955d011SMarcel Moolenaar  *	the JOB_IGNERR flag is set in the job descriptor.
7203955d011SMarcel Moolenaar  *	If the command is "..." and we're not ignoring such things,
7213955d011SMarcel Moolenaar  *	tailCmds is set to the successor node of the cmd.
7223955d011SMarcel Moolenaar  *	numCommands is incremented if the command is actually printed.
7233955d011SMarcel Moolenaar  *-----------------------------------------------------------------------
7243955d011SMarcel Moolenaar  */
7253955d011SMarcel Moolenaar static int
7263955d011SMarcel Moolenaar JobPrintCommand(void *cmdp, void *jobp)
7273955d011SMarcel Moolenaar {
7283955d011SMarcel Moolenaar     Boolean	  noSpecials;	    /* true if we shouldn't worry about
7293955d011SMarcel Moolenaar 				     * inserting special commands into
7303955d011SMarcel Moolenaar 				     * the input stream. */
7313955d011SMarcel Moolenaar     Boolean       shutUp = FALSE;   /* true if we put a no echo command
7323955d011SMarcel Moolenaar 				     * into the command file */
7333955d011SMarcel Moolenaar     Boolean	  errOff = FALSE;   /* true if we turned error checking
7343955d011SMarcel Moolenaar 				     * off before printing the command
7353955d011SMarcel Moolenaar 				     * and need to turn it back on */
7363955d011SMarcel Moolenaar     const char    *cmdTemplate;	    /* Template to use when printing the
7373955d011SMarcel Moolenaar 				     * command */
7383955d011SMarcel Moolenaar     char    	  *cmdStart;	    /* Start of expanded command */
7393955d011SMarcel Moolenaar     char	  *escCmd = NULL;    /* Command with quotes/backticks escaped */
7403955d011SMarcel Moolenaar     char     	  *cmd = (char *)cmdp;
7413955d011SMarcel Moolenaar     Job           *job = (Job *)jobp;
7423955d011SMarcel Moolenaar     int           i, j;
7433955d011SMarcel Moolenaar 
7443955d011SMarcel Moolenaar     noSpecials = NoExecute(job->node);
7453955d011SMarcel Moolenaar 
7463955d011SMarcel Moolenaar     if (strcmp(cmd, "...") == 0) {
7473955d011SMarcel Moolenaar 	job->node->type |= OP_SAVE_CMDS;
7483955d011SMarcel Moolenaar 	if ((job->flags & JOB_IGNDOTS) == 0) {
7493955d011SMarcel Moolenaar 	    job->tailCmds = Lst_Succ(Lst_Member(job->node->commands,
7503955d011SMarcel Moolenaar 						cmd));
7513955d011SMarcel Moolenaar 	    return 1;
7523955d011SMarcel Moolenaar 	}
7533955d011SMarcel Moolenaar 	return 0;
7543955d011SMarcel Moolenaar     }
7553955d011SMarcel Moolenaar 
7563955d011SMarcel Moolenaar #define DBPRINTF(fmt, arg) if (DEBUG(JOB)) {	\
7573955d011SMarcel Moolenaar 	(void)fprintf(debug_file, fmt, arg); 	\
7583955d011SMarcel Moolenaar     }						\
7593955d011SMarcel Moolenaar    (void)fprintf(job->cmdFILE, fmt, arg);	\
7603955d011SMarcel Moolenaar    (void)fflush(job->cmdFILE);
7613955d011SMarcel Moolenaar 
7623955d011SMarcel Moolenaar     numCommands += 1;
7633955d011SMarcel Moolenaar 
764be19d90bSSimon J. Gerraty     cmdStart = cmd = Var_Subst(NULL, cmd, job->node, VARF_WANTRES);
7653955d011SMarcel Moolenaar 
7663955d011SMarcel Moolenaar     cmdTemplate = "%s\n";
7673955d011SMarcel Moolenaar 
7683955d011SMarcel Moolenaar     /*
7693955d011SMarcel Moolenaar      * Check for leading @' and -'s to control echoing and error checking.
7703955d011SMarcel Moolenaar      */
7713955d011SMarcel Moolenaar     while (*cmd == '@' || *cmd == '-' || (*cmd == '+')) {
7723955d011SMarcel Moolenaar 	switch (*cmd) {
7733955d011SMarcel Moolenaar 	case '@':
7743955d011SMarcel Moolenaar 	    shutUp = DEBUG(LOUD) ? FALSE : TRUE;
7753955d011SMarcel Moolenaar 	    break;
7763955d011SMarcel Moolenaar 	case '-':
7773955d011SMarcel Moolenaar 	    errOff = TRUE;
7783955d011SMarcel Moolenaar 	    break;
7793955d011SMarcel Moolenaar 	case '+':
7803955d011SMarcel Moolenaar 	    if (noSpecials) {
7813955d011SMarcel Moolenaar 		/*
7823955d011SMarcel Moolenaar 		 * We're not actually executing anything...
7833955d011SMarcel Moolenaar 		 * but this one needs to be - use compat mode just for it.
7843955d011SMarcel Moolenaar 		 */
7853955d011SMarcel Moolenaar 		CompatRunCommand(cmdp, job->node);
786e1cee40dSSimon J. Gerraty 		free(cmdStart);
7873955d011SMarcel Moolenaar 		return 0;
7883955d011SMarcel Moolenaar 	    }
7893955d011SMarcel Moolenaar 	    break;
7903955d011SMarcel Moolenaar 	}
7913955d011SMarcel Moolenaar 	cmd++;
7923955d011SMarcel Moolenaar     }
7933955d011SMarcel Moolenaar 
7943955d011SMarcel Moolenaar     while (isspace((unsigned char) *cmd))
7953955d011SMarcel Moolenaar 	cmd++;
7963955d011SMarcel Moolenaar 
7973955d011SMarcel Moolenaar     /*
7983955d011SMarcel Moolenaar      * If the shell doesn't have error control the alternate echo'ing will
7993955d011SMarcel Moolenaar      * be done (to avoid showing additional error checking code)
8003955d011SMarcel Moolenaar      * and this will need the characters '$ ` \ "' escaped
8013955d011SMarcel Moolenaar      */
8023955d011SMarcel Moolenaar 
8033955d011SMarcel Moolenaar     if (!commandShell->hasErrCtl) {
8043955d011SMarcel Moolenaar 	/* Worst that could happen is every char needs escaping. */
8053955d011SMarcel Moolenaar 	escCmd = bmake_malloc((strlen(cmd) * 2) + 1);
8063955d011SMarcel Moolenaar 	for (i = 0, j= 0; cmd[i] != '\0'; i++, j++) {
8073955d011SMarcel Moolenaar 		if (cmd[i] == '$' || cmd[i] == '`' || cmd[i] == '\\' ||
8083955d011SMarcel Moolenaar 			cmd[i] == '"')
8093955d011SMarcel Moolenaar 			escCmd[j++] = '\\';
8103955d011SMarcel Moolenaar 		escCmd[j] = cmd[i];
8113955d011SMarcel Moolenaar 	}
8123955d011SMarcel Moolenaar 	escCmd[j] = 0;
8133955d011SMarcel Moolenaar     }
8143955d011SMarcel Moolenaar 
8153955d011SMarcel Moolenaar     if (shutUp) {
8163955d011SMarcel Moolenaar 	if (!(job->flags & JOB_SILENT) && !noSpecials &&
8173955d011SMarcel Moolenaar 	    commandShell->hasEchoCtl) {
8183955d011SMarcel Moolenaar 		DBPRINTF("%s\n", commandShell->echoOff);
8193955d011SMarcel Moolenaar 	} else {
8203955d011SMarcel Moolenaar 	    if (commandShell->hasErrCtl)
8213955d011SMarcel Moolenaar 		shutUp = FALSE;
8223955d011SMarcel Moolenaar 	}
8233955d011SMarcel Moolenaar     }
8243955d011SMarcel Moolenaar 
8253955d011SMarcel Moolenaar     if (errOff) {
8263955d011SMarcel Moolenaar 	if (!noSpecials) {
8273955d011SMarcel Moolenaar 	    if (commandShell->hasErrCtl) {
8283955d011SMarcel Moolenaar 		/*
8293955d011SMarcel Moolenaar 		 * we don't want the error-control commands showing
8303955d011SMarcel Moolenaar 		 * up either, so we turn off echoing while executing
8313955d011SMarcel Moolenaar 		 * them. We could put another field in the shell
8323955d011SMarcel Moolenaar 		 * structure to tell JobDoOutput to look for this
8333955d011SMarcel Moolenaar 		 * string too, but why make it any more complex than
8343955d011SMarcel Moolenaar 		 * it already is?
8353955d011SMarcel Moolenaar 		 */
8363955d011SMarcel Moolenaar 		if (!(job->flags & JOB_SILENT) && !shutUp &&
8373955d011SMarcel Moolenaar 		    commandShell->hasEchoCtl) {
8383955d011SMarcel Moolenaar 			DBPRINTF("%s\n", commandShell->echoOff);
8393955d011SMarcel Moolenaar 			DBPRINTF("%s\n", commandShell->ignErr);
8403955d011SMarcel Moolenaar 			DBPRINTF("%s\n", commandShell->echoOn);
8413955d011SMarcel Moolenaar 		} else {
8423955d011SMarcel Moolenaar 			DBPRINTF("%s\n", commandShell->ignErr);
8433955d011SMarcel Moolenaar 		}
8443955d011SMarcel Moolenaar 	    } else if (commandShell->ignErr &&
8453955d011SMarcel Moolenaar 		      (*commandShell->ignErr != '\0'))
8463955d011SMarcel Moolenaar 	    {
8473955d011SMarcel Moolenaar 		/*
8483955d011SMarcel Moolenaar 		 * The shell has no error control, so we need to be
8493955d011SMarcel Moolenaar 		 * weird to get it to ignore any errors from the command.
8503955d011SMarcel Moolenaar 		 * If echoing is turned on, we turn it off and use the
8513955d011SMarcel Moolenaar 		 * errCheck template to echo the command. Leave echoing
8523955d011SMarcel Moolenaar 		 * off so the user doesn't see the weirdness we go through
8533955d011SMarcel Moolenaar 		 * to ignore errors. Set cmdTemplate to use the weirdness
8543955d011SMarcel Moolenaar 		 * instead of the simple "%s\n" template.
8553955d011SMarcel Moolenaar 		 */
856db29cad8SSimon J. Gerraty 		job->flags |= JOB_IGNERR;
8573955d011SMarcel Moolenaar 		if (!(job->flags & JOB_SILENT) && !shutUp) {
8583955d011SMarcel Moolenaar 			if (commandShell->hasEchoCtl) {
8593955d011SMarcel Moolenaar 				DBPRINTF("%s\n", commandShell->echoOff);
8603955d011SMarcel Moolenaar 			}
8613955d011SMarcel Moolenaar 			DBPRINTF(commandShell->errCheck, escCmd);
8623955d011SMarcel Moolenaar 			shutUp = TRUE;
8633955d011SMarcel Moolenaar 		} else {
8643955d011SMarcel Moolenaar 			if (!shutUp) {
8653955d011SMarcel Moolenaar 				DBPRINTF(commandShell->errCheck, escCmd);
8663955d011SMarcel Moolenaar 			}
8673955d011SMarcel Moolenaar 		}
8683955d011SMarcel Moolenaar 		cmdTemplate = commandShell->ignErr;
8693955d011SMarcel Moolenaar 		/*
8703955d011SMarcel Moolenaar 		 * The error ignoration (hee hee) is already taken care
8713955d011SMarcel Moolenaar 		 * of by the ignErr template, so pretend error checking
8723955d011SMarcel Moolenaar 		 * is still on.
8733955d011SMarcel Moolenaar 		 */
8743955d011SMarcel Moolenaar 		errOff = FALSE;
8753955d011SMarcel Moolenaar 	    } else {
8763955d011SMarcel Moolenaar 		errOff = FALSE;
8773955d011SMarcel Moolenaar 	    }
8783955d011SMarcel Moolenaar 	} else {
8793955d011SMarcel Moolenaar 	    errOff = FALSE;
8803955d011SMarcel Moolenaar 	}
8813955d011SMarcel Moolenaar     } else {
8823955d011SMarcel Moolenaar 
8833955d011SMarcel Moolenaar 	/*
8843955d011SMarcel Moolenaar 	 * If errors are being checked and the shell doesn't have error control
8853955d011SMarcel Moolenaar 	 * but does supply an errOut template, then setup commands to run
8863955d011SMarcel Moolenaar 	 * through it.
8873955d011SMarcel Moolenaar 	 */
8883955d011SMarcel Moolenaar 
8893955d011SMarcel Moolenaar 	if (!commandShell->hasErrCtl && commandShell->errOut &&
8903955d011SMarcel Moolenaar 	    (*commandShell->errOut != '\0')) {
8913955d011SMarcel Moolenaar 		if (!(job->flags & JOB_SILENT) && !shutUp) {
8923955d011SMarcel Moolenaar 			if (commandShell->hasEchoCtl) {
8933955d011SMarcel Moolenaar 				DBPRINTF("%s\n", commandShell->echoOff);
8943955d011SMarcel Moolenaar 			}
8953955d011SMarcel Moolenaar 			DBPRINTF(commandShell->errCheck, escCmd);
8963955d011SMarcel Moolenaar 			shutUp = TRUE;
8973955d011SMarcel Moolenaar 		}
8983955d011SMarcel Moolenaar 		/* If it's a comment line or blank, treat as an ignored error */
8993955d011SMarcel Moolenaar 		if ((escCmd[0] == commandShell->commentChar) ||
9003955d011SMarcel Moolenaar 		    (escCmd[0] == 0))
9013955d011SMarcel Moolenaar 			cmdTemplate = commandShell->ignErr;
9023955d011SMarcel Moolenaar 		else
9033955d011SMarcel Moolenaar 			cmdTemplate = commandShell->errOut;
9043955d011SMarcel Moolenaar 		errOff = FALSE;
9053955d011SMarcel Moolenaar 	}
9063955d011SMarcel Moolenaar     }
9073955d011SMarcel Moolenaar 
9083955d011SMarcel Moolenaar     if (DEBUG(SHELL) && strcmp(shellName, "sh") == 0 &&
9093955d011SMarcel Moolenaar 	(job->flags & JOB_TRACED) == 0) {
9103955d011SMarcel Moolenaar 	    DBPRINTF("set -%s\n", "x");
9113955d011SMarcel Moolenaar 	    job->flags |= JOB_TRACED;
9123955d011SMarcel Moolenaar     }
9133955d011SMarcel Moolenaar 
9143955d011SMarcel Moolenaar     DBPRINTF(cmdTemplate, cmd);
9153955d011SMarcel Moolenaar     free(cmdStart);
9163955d011SMarcel Moolenaar     free(escCmd);
9173955d011SMarcel Moolenaar     if (errOff) {
9183955d011SMarcel Moolenaar 	/*
9193955d011SMarcel Moolenaar 	 * If echoing is already off, there's no point in issuing the
9203955d011SMarcel Moolenaar 	 * echoOff command. Otherwise we issue it and pretend it was on
9213955d011SMarcel Moolenaar 	 * for the whole command...
9223955d011SMarcel Moolenaar 	 */
9233955d011SMarcel Moolenaar 	if (!shutUp && !(job->flags & JOB_SILENT) && commandShell->hasEchoCtl){
9243955d011SMarcel Moolenaar 	    DBPRINTF("%s\n", commandShell->echoOff);
9253955d011SMarcel Moolenaar 	    shutUp = TRUE;
9263955d011SMarcel Moolenaar 	}
9273955d011SMarcel Moolenaar 	DBPRINTF("%s\n", commandShell->errCheck);
9283955d011SMarcel Moolenaar     }
9293955d011SMarcel Moolenaar     if (shutUp && commandShell->hasEchoCtl) {
9303955d011SMarcel Moolenaar 	DBPRINTF("%s\n", commandShell->echoOn);
9313955d011SMarcel Moolenaar     }
9323955d011SMarcel Moolenaar     return 0;
9333955d011SMarcel Moolenaar }
9343955d011SMarcel Moolenaar 
9353955d011SMarcel Moolenaar /*-
9363955d011SMarcel Moolenaar  *-----------------------------------------------------------------------
9373955d011SMarcel Moolenaar  * JobSaveCommand --
9383955d011SMarcel Moolenaar  *	Save a command to be executed when everything else is done.
9393955d011SMarcel Moolenaar  *	Callback function for JobFinish...
9403955d011SMarcel Moolenaar  *
9413955d011SMarcel Moolenaar  * Results:
9423955d011SMarcel Moolenaar  *	Always returns 0
9433955d011SMarcel Moolenaar  *
9443955d011SMarcel Moolenaar  * Side Effects:
9453955d011SMarcel Moolenaar  *	The command is tacked onto the end of postCommands's commands list.
9463955d011SMarcel Moolenaar  *
9473955d011SMarcel Moolenaar  *-----------------------------------------------------------------------
9483955d011SMarcel Moolenaar  */
9493955d011SMarcel Moolenaar static int
9503955d011SMarcel Moolenaar JobSaveCommand(void *cmd, void *gn)
9513955d011SMarcel Moolenaar {
952be19d90bSSimon J. Gerraty     cmd = Var_Subst(NULL, (char *)cmd, (GNode *)gn, VARF_WANTRES);
9533955d011SMarcel Moolenaar     (void)Lst_AtEnd(postCommands->commands, cmd);
9543955d011SMarcel Moolenaar     return(0);
9553955d011SMarcel Moolenaar }
9563955d011SMarcel Moolenaar 
9573955d011SMarcel Moolenaar 
9583955d011SMarcel Moolenaar /*-
9593955d011SMarcel Moolenaar  *-----------------------------------------------------------------------
9603955d011SMarcel Moolenaar  * JobClose --
9613955d011SMarcel Moolenaar  *	Called to close both input and output pipes when a job is finished.
9623955d011SMarcel Moolenaar  *
9633955d011SMarcel Moolenaar  * Results:
9643955d011SMarcel Moolenaar  *	Nada
9653955d011SMarcel Moolenaar  *
9663955d011SMarcel Moolenaar  * Side Effects:
9673955d011SMarcel Moolenaar  *	The file descriptors associated with the job are closed.
9683955d011SMarcel Moolenaar  *
9693955d011SMarcel Moolenaar  *-----------------------------------------------------------------------
9703955d011SMarcel Moolenaar  */
9713955d011SMarcel Moolenaar static void
9723955d011SMarcel Moolenaar JobClose(Job *job)
9733955d011SMarcel Moolenaar {
9743955d011SMarcel Moolenaar     clearfd(job);
9753955d011SMarcel Moolenaar     (void)close(job->outPipe);
9763955d011SMarcel Moolenaar     job->outPipe = -1;
9773955d011SMarcel Moolenaar 
9783955d011SMarcel Moolenaar     JobDoOutput(job, TRUE);
9793955d011SMarcel Moolenaar     (void)close(job->inPipe);
9803955d011SMarcel Moolenaar     job->inPipe = -1;
9813955d011SMarcel Moolenaar }
9823955d011SMarcel Moolenaar 
9833955d011SMarcel Moolenaar /*-
9843955d011SMarcel Moolenaar  *-----------------------------------------------------------------------
9853955d011SMarcel Moolenaar  * JobFinish  --
9863955d011SMarcel Moolenaar  *	Do final processing for the given job including updating
9873955d011SMarcel Moolenaar  *	parents and starting new jobs as available/necessary. Note
9883955d011SMarcel Moolenaar  *	that we pay no attention to the JOB_IGNERR flag here.
9893955d011SMarcel Moolenaar  *	This is because when we're called because of a noexecute flag
9903955d011SMarcel Moolenaar  *	or something, jstat.w_status is 0 and when called from
9913955d011SMarcel Moolenaar  *	Job_CatchChildren, the status is zeroed if it s/b ignored.
9923955d011SMarcel Moolenaar  *
9933955d011SMarcel Moolenaar  * Input:
9943955d011SMarcel Moolenaar  *	job		job to finish
9953955d011SMarcel Moolenaar  *	status		sub-why job went away
9963955d011SMarcel Moolenaar  *
9973955d011SMarcel Moolenaar  * Results:
9983955d011SMarcel Moolenaar  *	None
9993955d011SMarcel Moolenaar  *
10003955d011SMarcel Moolenaar  * Side Effects:
10013955d011SMarcel Moolenaar  *	Final commands for the job are placed on postCommands.
10023955d011SMarcel Moolenaar  *
10033955d011SMarcel Moolenaar  *	If we got an error and are aborting (aborting == ABORT_ERROR) and
10043955d011SMarcel Moolenaar  *	the job list is now empty, we are done for the day.
10053955d011SMarcel Moolenaar  *	If we recognized an error (errors !=0), we set the aborting flag
10063955d011SMarcel Moolenaar  *	to ABORT_ERROR so no more jobs will be started.
10073955d011SMarcel Moolenaar  *-----------------------------------------------------------------------
10083955d011SMarcel Moolenaar  */
10093955d011SMarcel Moolenaar /*ARGSUSED*/
10103955d011SMarcel Moolenaar static void
10113955d011SMarcel Moolenaar JobFinish (Job *job, WAIT_T status)
10123955d011SMarcel Moolenaar {
10133955d011SMarcel Moolenaar     Boolean 	 done, return_job_token;
10143955d011SMarcel Moolenaar 
10153955d011SMarcel Moolenaar     if (DEBUG(JOB)) {
10163955d011SMarcel Moolenaar 	fprintf(debug_file, "Jobfinish: %d [%s], status %d\n",
10173955d011SMarcel Moolenaar 				job->pid, job->node->name, status);
10183955d011SMarcel Moolenaar     }
10193955d011SMarcel Moolenaar 
10203955d011SMarcel Moolenaar     if ((WIFEXITED(status) &&
10213955d011SMarcel Moolenaar 	 (((WEXITSTATUS(status) != 0) && !(job->flags & JOB_IGNERR)))) ||
10223955d011SMarcel Moolenaar 	WIFSIGNALED(status))
10233955d011SMarcel Moolenaar     {
10243955d011SMarcel Moolenaar 	/*
10253955d011SMarcel Moolenaar 	 * If it exited non-zero and either we're doing things our
10263955d011SMarcel Moolenaar 	 * way or we're not ignoring errors, the job is finished.
10273955d011SMarcel Moolenaar 	 * Similarly, if the shell died because of a signal
10283955d011SMarcel Moolenaar 	 * the job is also finished. In these
10293955d011SMarcel Moolenaar 	 * cases, finish out the job's output before printing the exit
10303955d011SMarcel Moolenaar 	 * status...
10313955d011SMarcel Moolenaar 	 */
10323955d011SMarcel Moolenaar 	JobClose(job);
10333955d011SMarcel Moolenaar 	if (job->cmdFILE != NULL && job->cmdFILE != stdout) {
10343955d011SMarcel Moolenaar 	   (void)fclose(job->cmdFILE);
10353955d011SMarcel Moolenaar 	   job->cmdFILE = NULL;
10363955d011SMarcel Moolenaar 	}
10373955d011SMarcel Moolenaar 	done = TRUE;
10383955d011SMarcel Moolenaar     } else if (WIFEXITED(status)) {
10393955d011SMarcel Moolenaar 	/*
10403955d011SMarcel Moolenaar 	 * Deal with ignored errors in -B mode. We need to print a message
10413955d011SMarcel Moolenaar 	 * telling of the ignored error as well as setting status.w_status
10423955d011SMarcel Moolenaar 	 * to 0 so the next command gets run. To do this, we set done to be
10433955d011SMarcel Moolenaar 	 * TRUE if in -B mode and the job exited non-zero.
10443955d011SMarcel Moolenaar 	 */
10453955d011SMarcel Moolenaar 	done = WEXITSTATUS(status) != 0;
10463955d011SMarcel Moolenaar 	/*
10473955d011SMarcel Moolenaar 	 * Old comment said: "Note we don't
10483955d011SMarcel Moolenaar 	 * want to close down any of the streams until we know we're at the
10493955d011SMarcel Moolenaar 	 * end."
10503955d011SMarcel Moolenaar 	 * But we do. Otherwise when are we going to print the rest of the
10513955d011SMarcel Moolenaar 	 * stuff?
10523955d011SMarcel Moolenaar 	 */
10533955d011SMarcel Moolenaar 	JobClose(job);
10543955d011SMarcel Moolenaar     } else {
10553955d011SMarcel Moolenaar 	/*
10563955d011SMarcel Moolenaar 	 * No need to close things down or anything.
10573955d011SMarcel Moolenaar 	 */
10583955d011SMarcel Moolenaar 	done = FALSE;
10593955d011SMarcel Moolenaar     }
10603955d011SMarcel Moolenaar 
10613955d011SMarcel Moolenaar     if (done) {
10623955d011SMarcel Moolenaar 	if (WIFEXITED(status)) {
10633955d011SMarcel Moolenaar 	    if (DEBUG(JOB)) {
10643955d011SMarcel Moolenaar 		(void)fprintf(debug_file, "Process %d [%s] exited.\n",
10653955d011SMarcel Moolenaar 				job->pid, job->node->name);
10663955d011SMarcel Moolenaar 	    }
10673955d011SMarcel Moolenaar 	    if (WEXITSTATUS(status) != 0) {
10683955d011SMarcel Moolenaar 		if (job->node != lastNode) {
10693955d011SMarcel Moolenaar 		    MESSAGE(stdout, job->node);
10703955d011SMarcel Moolenaar 		    lastNode = job->node;
10713955d011SMarcel Moolenaar 		}
10723955d011SMarcel Moolenaar #ifdef USE_META
10733955d011SMarcel Moolenaar 		if (useMeta) {
10743955d011SMarcel Moolenaar 		    meta_job_error(job, job->node, job->flags, WEXITSTATUS(status));
10753955d011SMarcel Moolenaar 		}
10763955d011SMarcel Moolenaar #endif
10773955d011SMarcel Moolenaar 		(void)printf("*** [%s] Error code %d%s\n",
10783955d011SMarcel Moolenaar 				job->node->name,
10793955d011SMarcel Moolenaar 			       WEXITSTATUS(status),
10803955d011SMarcel Moolenaar 			       (job->flags & JOB_IGNERR) ? " (ignored)" : "");
10813955d011SMarcel Moolenaar 		if (job->flags & JOB_IGNERR) {
10823955d011SMarcel Moolenaar 		    WAIT_STATUS(status) = 0;
10833955d011SMarcel Moolenaar 		} else {
108445447996SSimon J. Gerraty 		    if (deleteOnError) {
108545447996SSimon J. Gerraty 			JobDeleteTarget(job->node);
108645447996SSimon J. Gerraty 		    }
10873955d011SMarcel Moolenaar 		    PrintOnError(job->node, NULL);
10883955d011SMarcel Moolenaar 		}
10893955d011SMarcel Moolenaar 	    } else if (DEBUG(JOB)) {
10903955d011SMarcel Moolenaar 		if (job->node != lastNode) {
10913955d011SMarcel Moolenaar 		    MESSAGE(stdout, job->node);
10923955d011SMarcel Moolenaar 		    lastNode = job->node;
10933955d011SMarcel Moolenaar 		}
10943955d011SMarcel Moolenaar 		(void)printf("*** [%s] Completed successfully\n",
10953955d011SMarcel Moolenaar 				job->node->name);
10963955d011SMarcel Moolenaar 	    }
10973955d011SMarcel Moolenaar 	} else {
10983955d011SMarcel Moolenaar 	    if (job->node != lastNode) {
10993955d011SMarcel Moolenaar 		MESSAGE(stdout, job->node);
11003955d011SMarcel Moolenaar 		lastNode = job->node;
11013955d011SMarcel Moolenaar 	    }
11023955d011SMarcel Moolenaar 	    (void)printf("*** [%s] Signal %d\n",
11033955d011SMarcel Moolenaar 			job->node->name, WTERMSIG(status));
110445447996SSimon J. Gerraty 	    if (deleteOnError) {
110545447996SSimon J. Gerraty 		JobDeleteTarget(job->node);
110645447996SSimon J. Gerraty 	    }
11073955d011SMarcel Moolenaar 	}
11083955d011SMarcel Moolenaar 	(void)fflush(stdout);
11093955d011SMarcel Moolenaar     }
11103955d011SMarcel Moolenaar 
11113955d011SMarcel Moolenaar #ifdef USE_META
11123955d011SMarcel Moolenaar     if (useMeta) {
1113e48f47ddSSimon J. Gerraty 	int x;
1114e48f47ddSSimon J. Gerraty 
1115e48f47ddSSimon J. Gerraty 	if ((x = meta_job_finish(job)) != 0 && status == 0) {
1116e48f47ddSSimon J. Gerraty 	    status = x;
1117e48f47ddSSimon J. Gerraty 	}
11183955d011SMarcel Moolenaar     }
11193955d011SMarcel Moolenaar #endif
11203955d011SMarcel Moolenaar 
11213955d011SMarcel Moolenaar     return_job_token = FALSE;
11223955d011SMarcel Moolenaar 
11233955d011SMarcel Moolenaar     Trace_Log(JOBEND, job);
11243955d011SMarcel Moolenaar     if (!(job->flags & JOB_SPECIAL)) {
11253955d011SMarcel Moolenaar 	if ((WAIT_STATUS(status) != 0) ||
11263955d011SMarcel Moolenaar 		(aborting == ABORT_ERROR) ||
11273955d011SMarcel Moolenaar 		(aborting == ABORT_INTERRUPT))
11283955d011SMarcel Moolenaar 	    return_job_token = TRUE;
11293955d011SMarcel Moolenaar     }
11303955d011SMarcel Moolenaar 
11313955d011SMarcel Moolenaar     if ((aborting != ABORT_ERROR) && (aborting != ABORT_INTERRUPT) &&
11323955d011SMarcel Moolenaar 	(WAIT_STATUS(status) == 0)) {
11333955d011SMarcel Moolenaar 	/*
11343955d011SMarcel Moolenaar 	 * As long as we aren't aborting and the job didn't return a non-zero
11353955d011SMarcel Moolenaar 	 * status that we shouldn't ignore, we call Make_Update to update
11363955d011SMarcel Moolenaar 	 * the parents. In addition, any saved commands for the node are placed
11373955d011SMarcel Moolenaar 	 * on the .END target.
11383955d011SMarcel Moolenaar 	 */
11393955d011SMarcel Moolenaar 	if (job->tailCmds != NULL) {
11403955d011SMarcel Moolenaar 	    Lst_ForEachFrom(job->node->commands, job->tailCmds,
11413955d011SMarcel Moolenaar 			     JobSaveCommand,
11423955d011SMarcel Moolenaar 			    job->node);
11433955d011SMarcel Moolenaar 	}
11443955d011SMarcel Moolenaar 	job->node->made = MADE;
11453955d011SMarcel Moolenaar 	if (!(job->flags & JOB_SPECIAL))
11463955d011SMarcel Moolenaar 	    return_job_token = TRUE;
11473955d011SMarcel Moolenaar 	Make_Update(job->node);
11483955d011SMarcel Moolenaar 	job->job_state = JOB_ST_FREE;
11493955d011SMarcel Moolenaar     } else if (WAIT_STATUS(status)) {
11503955d011SMarcel Moolenaar 	errors += 1;
11513955d011SMarcel Moolenaar 	job->job_state = JOB_ST_FREE;
11523955d011SMarcel Moolenaar     }
11533955d011SMarcel Moolenaar 
11543955d011SMarcel Moolenaar     /*
11553955d011SMarcel Moolenaar      * Set aborting if any error.
11563955d011SMarcel Moolenaar      */
11573955d011SMarcel Moolenaar     if (errors && !keepgoing && (aborting != ABORT_INTERRUPT)) {
11583955d011SMarcel Moolenaar 	/*
11593955d011SMarcel Moolenaar 	 * If we found any errors in this batch of children and the -k flag
11603955d011SMarcel Moolenaar 	 * wasn't given, we set the aborting flag so no more jobs get
11613955d011SMarcel Moolenaar 	 * started.
11623955d011SMarcel Moolenaar 	 */
11633955d011SMarcel Moolenaar 	aborting = ABORT_ERROR;
11643955d011SMarcel Moolenaar     }
11653955d011SMarcel Moolenaar 
11663955d011SMarcel Moolenaar     if (return_job_token)
11673955d011SMarcel Moolenaar 	Job_TokenReturn();
11683955d011SMarcel Moolenaar 
11693955d011SMarcel Moolenaar     if (aborting == ABORT_ERROR && jobTokensRunning == 0) {
11703955d011SMarcel Moolenaar 	/*
11713955d011SMarcel Moolenaar 	 * If we are aborting and the job table is now empty, we finish.
11723955d011SMarcel Moolenaar 	 */
11733955d011SMarcel Moolenaar 	Finish(errors);
11743955d011SMarcel Moolenaar     }
11753955d011SMarcel Moolenaar }
11763955d011SMarcel Moolenaar 
11773955d011SMarcel Moolenaar /*-
11783955d011SMarcel Moolenaar  *-----------------------------------------------------------------------
11793955d011SMarcel Moolenaar  * Job_Touch --
11803955d011SMarcel Moolenaar  *	Touch the given target. Called by JobStart when the -t flag was
11813955d011SMarcel Moolenaar  *	given
11823955d011SMarcel Moolenaar  *
11833955d011SMarcel Moolenaar  * Input:
11843955d011SMarcel Moolenaar  *	gn		the node of the file to touch
11853955d011SMarcel Moolenaar  *	silent		TRUE if should not print message
11863955d011SMarcel Moolenaar  *
11873955d011SMarcel Moolenaar  * Results:
11883955d011SMarcel Moolenaar  *	None
11893955d011SMarcel Moolenaar  *
11903955d011SMarcel Moolenaar  * Side Effects:
11913955d011SMarcel Moolenaar  *	The data modification of the file is changed. In addition, if the
11923955d011SMarcel Moolenaar  *	file did not exist, it is created.
11933955d011SMarcel Moolenaar  *-----------------------------------------------------------------------
11943955d011SMarcel Moolenaar  */
11953955d011SMarcel Moolenaar void
11963955d011SMarcel Moolenaar Job_Touch(GNode *gn, Boolean silent)
11973955d011SMarcel Moolenaar {
11983955d011SMarcel Moolenaar     int		  streamID;   	/* ID of stream opened to do the touch */
11993955d011SMarcel Moolenaar     struct utimbuf times;	/* Times for utime() call */
12003955d011SMarcel Moolenaar 
12013955d011SMarcel Moolenaar     if (gn->type & (OP_JOIN|OP_USE|OP_USEBEFORE|OP_EXEC|OP_OPTIONAL|
12023955d011SMarcel Moolenaar 	OP_SPECIAL|OP_PHONY)) {
12033955d011SMarcel Moolenaar 	/*
12043955d011SMarcel Moolenaar 	 * .JOIN, .USE, .ZEROTIME and .OPTIONAL targets are "virtual" targets
12053955d011SMarcel Moolenaar 	 * and, as such, shouldn't really be created.
12063955d011SMarcel Moolenaar 	 */
12073955d011SMarcel Moolenaar 	return;
12083955d011SMarcel Moolenaar     }
12093955d011SMarcel Moolenaar 
12103955d011SMarcel Moolenaar     if (!silent || NoExecute(gn)) {
12113955d011SMarcel Moolenaar 	(void)fprintf(stdout, "touch %s\n", gn->name);
12123955d011SMarcel Moolenaar 	(void)fflush(stdout);
12133955d011SMarcel Moolenaar     }
12143955d011SMarcel Moolenaar 
12153955d011SMarcel Moolenaar     if (NoExecute(gn)) {
12163955d011SMarcel Moolenaar 	return;
12173955d011SMarcel Moolenaar     }
12183955d011SMarcel Moolenaar 
12193955d011SMarcel Moolenaar     if (gn->type & OP_ARCHV) {
12203955d011SMarcel Moolenaar 	Arch_Touch(gn);
12213955d011SMarcel Moolenaar     } else if (gn->type & OP_LIB) {
12223955d011SMarcel Moolenaar 	Arch_TouchLib(gn);
12233955d011SMarcel Moolenaar     } else {
12243955d011SMarcel Moolenaar 	char	*file = gn->path ? gn->path : gn->name;
12253955d011SMarcel Moolenaar 
12263955d011SMarcel Moolenaar 	times.actime = times.modtime = now;
12273955d011SMarcel Moolenaar 	if (utime(file, &times) < 0){
12283955d011SMarcel Moolenaar 	    streamID = open(file, O_RDWR | O_CREAT, 0666);
12293955d011SMarcel Moolenaar 
12303955d011SMarcel Moolenaar 	    if (streamID >= 0) {
12313955d011SMarcel Moolenaar 		char	c;
12323955d011SMarcel Moolenaar 
12333955d011SMarcel Moolenaar 		/*
12343955d011SMarcel Moolenaar 		 * Read and write a byte to the file to change the
12353955d011SMarcel Moolenaar 		 * modification time, then close the file.
12363955d011SMarcel Moolenaar 		 */
12373955d011SMarcel Moolenaar 		if (read(streamID, &c, 1) == 1) {
12383955d011SMarcel Moolenaar 		    (void)lseek(streamID, (off_t)0, SEEK_SET);
12393cbdda60SSimon J. Gerraty 		    while (write(streamID, &c, 1) == -1 && errno == EAGAIN)
12403cbdda60SSimon J. Gerraty 			continue;
12413955d011SMarcel Moolenaar 		}
12423955d011SMarcel Moolenaar 
12433955d011SMarcel Moolenaar 		(void)close(streamID);
12443955d011SMarcel Moolenaar 	    } else {
12453955d011SMarcel Moolenaar 		(void)fprintf(stdout, "*** couldn't touch %s: %s",
12463955d011SMarcel Moolenaar 			       file, strerror(errno));
12473955d011SMarcel Moolenaar 		(void)fflush(stdout);
12483955d011SMarcel Moolenaar 	    }
12493955d011SMarcel Moolenaar 	}
12503955d011SMarcel Moolenaar     }
12513955d011SMarcel Moolenaar }
12523955d011SMarcel Moolenaar 
12533955d011SMarcel Moolenaar /*-
12543955d011SMarcel Moolenaar  *-----------------------------------------------------------------------
12553955d011SMarcel Moolenaar  * Job_CheckCommands --
12563955d011SMarcel Moolenaar  *	Make sure the given node has all the commands it needs.
12573955d011SMarcel Moolenaar  *
12583955d011SMarcel Moolenaar  * Input:
12593955d011SMarcel Moolenaar  *	gn		The target whose commands need verifying
12603955d011SMarcel Moolenaar  *	abortProc	Function to abort with message
12613955d011SMarcel Moolenaar  *
12623955d011SMarcel Moolenaar  * Results:
12633955d011SMarcel Moolenaar  *	TRUE if the commands list is/was ok.
12643955d011SMarcel Moolenaar  *
12653955d011SMarcel Moolenaar  * Side Effects:
12663955d011SMarcel Moolenaar  *	The node will have commands from the .DEFAULT rule added to it
12673955d011SMarcel Moolenaar  *	if it needs them.
12683955d011SMarcel Moolenaar  *-----------------------------------------------------------------------
12693955d011SMarcel Moolenaar  */
12703955d011SMarcel Moolenaar Boolean
12713955d011SMarcel Moolenaar Job_CheckCommands(GNode *gn, void (*abortProc)(const char *, ...))
12723955d011SMarcel Moolenaar {
12733955d011SMarcel Moolenaar     if (OP_NOP(gn->type) && Lst_IsEmpty(gn->commands) &&
12743955d011SMarcel Moolenaar 	((gn->type & OP_LIB) == 0 || Lst_IsEmpty(gn->children))) {
12753955d011SMarcel Moolenaar 	/*
12763955d011SMarcel Moolenaar 	 * No commands. Look for .DEFAULT rule from which we might infer
12773955d011SMarcel Moolenaar 	 * commands
12783955d011SMarcel Moolenaar 	 */
12793955d011SMarcel Moolenaar 	if ((DEFAULT != NULL) && !Lst_IsEmpty(DEFAULT->commands) &&
12803955d011SMarcel Moolenaar 		(gn->type & OP_SPECIAL) == 0) {
12813955d011SMarcel Moolenaar 	    char *p1;
12823955d011SMarcel Moolenaar 	    /*
12833955d011SMarcel Moolenaar 	     * Make only looks for a .DEFAULT if the node was never the
12843955d011SMarcel Moolenaar 	     * target of an operator, so that's what we do too. If
12853955d011SMarcel Moolenaar 	     * a .DEFAULT was given, we substitute its commands for gn's
12863955d011SMarcel Moolenaar 	     * commands and set the IMPSRC variable to be the target's name
12873955d011SMarcel Moolenaar 	     * The DEFAULT node acts like a transformation rule, in that
12883955d011SMarcel Moolenaar 	     * gn also inherits any attributes or sources attached to
12893955d011SMarcel Moolenaar 	     * .DEFAULT itself.
12903955d011SMarcel Moolenaar 	     */
12913955d011SMarcel Moolenaar 	    Make_HandleUse(DEFAULT, gn);
12923955d011SMarcel Moolenaar 	    Var_Set(IMPSRC, Var_Value(TARGET, gn, &p1), gn, 0);
12933955d011SMarcel Moolenaar 	    free(p1);
12943955d011SMarcel Moolenaar 	} else if (Dir_MTime(gn, 0) == 0 && (gn->type & OP_SPECIAL) == 0) {
12953955d011SMarcel Moolenaar 	    /*
12963955d011SMarcel Moolenaar 	     * The node wasn't the target of an operator we have no .DEFAULT
12973955d011SMarcel Moolenaar 	     * rule to go on and the target doesn't already exist. There's
12983955d011SMarcel Moolenaar 	     * nothing more we can do for this branch. If the -k flag wasn't
12993955d011SMarcel Moolenaar 	     * given, we stop in our tracks, otherwise we just don't update
13003955d011SMarcel Moolenaar 	     * this node's parents so they never get examined.
13013955d011SMarcel Moolenaar 	     */
13023955d011SMarcel Moolenaar 	    static const char msg[] = ": don't know how to make";
13033955d011SMarcel Moolenaar 
13043955d011SMarcel Moolenaar 	    if (gn->flags & FROM_DEPEND) {
13051748de26SSimon J. Gerraty 		if (!Job_RunTarget(".STALE", gn->fname))
13061748de26SSimon J. Gerraty 		    fprintf(stdout, "%s: %s, %d: ignoring stale %s for %s\n",
13071748de26SSimon J. Gerraty 			progname, gn->fname, gn->lineno, makeDependfile,
13081748de26SSimon J. Gerraty 			gn->name);
13093955d011SMarcel Moolenaar 		return TRUE;
13103955d011SMarcel Moolenaar 	    }
13113955d011SMarcel Moolenaar 
13123955d011SMarcel Moolenaar 	    if (gn->type & OP_OPTIONAL) {
13133955d011SMarcel Moolenaar 		(void)fprintf(stdout, "%s%s %s (ignored)\n", progname,
13143955d011SMarcel Moolenaar 		    msg, gn->name);
13153955d011SMarcel Moolenaar 		(void)fflush(stdout);
13163955d011SMarcel Moolenaar 	    } else if (keepgoing) {
13173955d011SMarcel Moolenaar 		(void)fprintf(stdout, "%s%s %s (continuing)\n", progname,
13183955d011SMarcel Moolenaar 		    msg, gn->name);
13193955d011SMarcel Moolenaar 		(void)fflush(stdout);
13203955d011SMarcel Moolenaar   		return FALSE;
13213955d011SMarcel Moolenaar 	    } else {
13223955d011SMarcel Moolenaar 		(*abortProc)("%s%s %s. Stop", progname, msg, gn->name);
13233955d011SMarcel Moolenaar 		return FALSE;
13243955d011SMarcel Moolenaar 	    }
13253955d011SMarcel Moolenaar 	}
13263955d011SMarcel Moolenaar     }
13273955d011SMarcel Moolenaar     return TRUE;
13283955d011SMarcel Moolenaar }
13293955d011SMarcel Moolenaar 
13303955d011SMarcel Moolenaar /*-
13313955d011SMarcel Moolenaar  *-----------------------------------------------------------------------
13323955d011SMarcel Moolenaar  * JobExec --
13333955d011SMarcel Moolenaar  *	Execute the shell for the given job. Called from JobStart
13343955d011SMarcel Moolenaar  *
13353955d011SMarcel Moolenaar  * Input:
13363955d011SMarcel Moolenaar  *	job		Job to execute
13373955d011SMarcel Moolenaar  *
13383955d011SMarcel Moolenaar  * Results:
13393955d011SMarcel Moolenaar  *	None.
13403955d011SMarcel Moolenaar  *
13413955d011SMarcel Moolenaar  * Side Effects:
13423955d011SMarcel Moolenaar  *	A shell is executed, outputs is altered and the Job structure added
13433955d011SMarcel Moolenaar  *	to the job table.
13443955d011SMarcel Moolenaar  *
13453955d011SMarcel Moolenaar  *-----------------------------------------------------------------------
13463955d011SMarcel Moolenaar  */
13473955d011SMarcel Moolenaar static void
13483955d011SMarcel Moolenaar JobExec(Job *job, char **argv)
13493955d011SMarcel Moolenaar {
13503955d011SMarcel Moolenaar     int	    	  cpid;	    	/* ID of new child */
13513955d011SMarcel Moolenaar     sigset_t	  mask;
13523955d011SMarcel Moolenaar 
13533955d011SMarcel Moolenaar     job->flags &= ~JOB_TRACED;
13543955d011SMarcel Moolenaar 
13553955d011SMarcel Moolenaar     if (DEBUG(JOB)) {
13563955d011SMarcel Moolenaar 	int 	  i;
13573955d011SMarcel Moolenaar 
13583955d011SMarcel Moolenaar 	(void)fprintf(debug_file, "Running %s %sly\n", job->node->name, "local");
13593955d011SMarcel Moolenaar 	(void)fprintf(debug_file, "\tCommand: ");
13603955d011SMarcel Moolenaar 	for (i = 0; argv[i] != NULL; i++) {
13613955d011SMarcel Moolenaar 	    (void)fprintf(debug_file, "%s ", argv[i]);
13623955d011SMarcel Moolenaar 	}
13633955d011SMarcel Moolenaar  	(void)fprintf(debug_file, "\n");
13643955d011SMarcel Moolenaar     }
13653955d011SMarcel Moolenaar 
13663955d011SMarcel Moolenaar     /*
13673955d011SMarcel Moolenaar      * Some jobs produce no output and it's disconcerting to have
13683955d011SMarcel Moolenaar      * no feedback of their running (since they produce no output, the
13693955d011SMarcel Moolenaar      * banner with their name in it never appears). This is an attempt to
13703955d011SMarcel Moolenaar      * provide that feedback, even if nothing follows it.
13713955d011SMarcel Moolenaar      */
13723955d011SMarcel Moolenaar     if ((lastNode != job->node) && !(job->flags & JOB_SILENT)) {
13733955d011SMarcel Moolenaar 	MESSAGE(stdout, job->node);
13743955d011SMarcel Moolenaar 	lastNode = job->node;
13753955d011SMarcel Moolenaar     }
13763955d011SMarcel Moolenaar 
13773955d011SMarcel Moolenaar     /* No interruptions until this job is on the `jobs' list */
13783955d011SMarcel Moolenaar     JobSigLock(&mask);
13793955d011SMarcel Moolenaar 
13803955d011SMarcel Moolenaar     /* Pre-emptively mark job running, pid still zero though */
13813955d011SMarcel Moolenaar     job->job_state = JOB_ST_RUNNING;
13823955d011SMarcel Moolenaar 
13833955d011SMarcel Moolenaar     cpid = vFork();
13843955d011SMarcel Moolenaar     if (cpid == -1)
13853955d011SMarcel Moolenaar 	Punt("Cannot vfork: %s", strerror(errno));
13863955d011SMarcel Moolenaar 
13873955d011SMarcel Moolenaar     if (cpid == 0) {
13883955d011SMarcel Moolenaar 	/* Child */
13893955d011SMarcel Moolenaar 	sigset_t tmask;
13903955d011SMarcel Moolenaar 
13913955d011SMarcel Moolenaar #ifdef USE_META
13923955d011SMarcel Moolenaar 	if (useMeta) {
13933955d011SMarcel Moolenaar 	    meta_job_child(job);
13943955d011SMarcel Moolenaar 	}
13953955d011SMarcel Moolenaar #endif
13963955d011SMarcel Moolenaar 	/*
13973955d011SMarcel Moolenaar 	 * Reset all signal handlers; this is necessary because we also
13983955d011SMarcel Moolenaar 	 * need to unblock signals before we exec(2).
13993955d011SMarcel Moolenaar 	 */
14003955d011SMarcel Moolenaar 	JobSigReset();
14013955d011SMarcel Moolenaar 
14023955d011SMarcel Moolenaar 	/* Now unblock signals */
14033955d011SMarcel Moolenaar 	sigemptyset(&tmask);
14043955d011SMarcel Moolenaar 	JobSigUnlock(&tmask);
14053955d011SMarcel Moolenaar 
14063955d011SMarcel Moolenaar 	/*
14073955d011SMarcel Moolenaar 	 * Must duplicate the input stream down to the child's input and
14083955d011SMarcel Moolenaar 	 * reset it to the beginning (again). Since the stream was marked
14093955d011SMarcel Moolenaar 	 * close-on-exec, we must clear that bit in the new input.
14103955d011SMarcel Moolenaar 	 */
14113955d011SMarcel Moolenaar 	if (dup2(FILENO(job->cmdFILE), 0) == -1) {
14123955d011SMarcel Moolenaar 	    execError("dup2", "job->cmdFILE");
14133955d011SMarcel Moolenaar 	    _exit(1);
14143955d011SMarcel Moolenaar 	}
1415e1cee40dSSimon J. Gerraty 	if (fcntl(0, F_SETFD, 0) == -1) {
1416e1cee40dSSimon J. Gerraty 	    execError("fcntl clear close-on-exec", "stdin");
1417e1cee40dSSimon J. Gerraty 	    _exit(1);
1418e1cee40dSSimon J. Gerraty 	}
1419e1cee40dSSimon J. Gerraty 	if (lseek(0, (off_t)0, SEEK_SET) == -1) {
1420e1cee40dSSimon J. Gerraty 	    execError("lseek to 0", "stdin");
1421e1cee40dSSimon J. Gerraty 	    _exit(1);
1422e1cee40dSSimon J. Gerraty 	}
14233955d011SMarcel Moolenaar 
1424db29cad8SSimon J. Gerraty 	if (Always_pass_job_queue ||
1425db29cad8SSimon J. Gerraty 	    (job->node->type & (OP_MAKE | OP_SUBMAKE))) {
14263955d011SMarcel Moolenaar 		/*
14273955d011SMarcel Moolenaar 		 * Pass job token pipe to submakes.
14283955d011SMarcel Moolenaar 		 */
1429e1cee40dSSimon J. Gerraty 		if (fcntl(tokenWaitJob.inPipe, F_SETFD, 0) == -1) {
1430e1cee40dSSimon J. Gerraty 		    execError("clear close-on-exec", "tokenWaitJob.inPipe");
1431e1cee40dSSimon J. Gerraty 		    _exit(1);
1432e1cee40dSSimon J. Gerraty 		}
1433e1cee40dSSimon J. Gerraty 		if (fcntl(tokenWaitJob.outPipe, F_SETFD, 0) == -1) {
1434e1cee40dSSimon J. Gerraty 		    execError("clear close-on-exec", "tokenWaitJob.outPipe");
1435e1cee40dSSimon J. Gerraty 		    _exit(1);
1436e1cee40dSSimon J. Gerraty 		}
14373955d011SMarcel Moolenaar 	}
14383955d011SMarcel Moolenaar 
14393955d011SMarcel Moolenaar 	/*
14403955d011SMarcel Moolenaar 	 * Set up the child's output to be routed through the pipe
14413955d011SMarcel Moolenaar 	 * we've created for it.
14423955d011SMarcel Moolenaar 	 */
14433955d011SMarcel Moolenaar 	if (dup2(job->outPipe, 1) == -1) {
14443955d011SMarcel Moolenaar 	    execError("dup2", "job->outPipe");
14453955d011SMarcel Moolenaar 	    _exit(1);
14463955d011SMarcel Moolenaar 	}
14473955d011SMarcel Moolenaar 	/*
14483955d011SMarcel Moolenaar 	 * The output channels are marked close on exec. This bit was
14493955d011SMarcel Moolenaar 	 * duplicated by the dup2(on some systems), so we have to clear
14503955d011SMarcel Moolenaar 	 * it before routing the shell's error output to the same place as
14513955d011SMarcel Moolenaar 	 * its standard output.
14523955d011SMarcel Moolenaar 	 */
1453e1cee40dSSimon J. Gerraty 	if (fcntl(1, F_SETFD, 0) == -1) {
1454e1cee40dSSimon J. Gerraty 	    execError("clear close-on-exec", "stdout");
1455e1cee40dSSimon J. Gerraty 	    _exit(1);
1456e1cee40dSSimon J. Gerraty 	}
14573955d011SMarcel Moolenaar 	if (dup2(1, 2) == -1) {
14583955d011SMarcel Moolenaar 	    execError("dup2", "1, 2");
14593955d011SMarcel Moolenaar 	    _exit(1);
14603955d011SMarcel Moolenaar 	}
14613955d011SMarcel Moolenaar 
14623955d011SMarcel Moolenaar 	/*
14633955d011SMarcel Moolenaar 	 * We want to switch the child into a different process family so
14643955d011SMarcel Moolenaar 	 * we can kill it and all its descendants in one fell swoop,
14653955d011SMarcel Moolenaar 	 * by killing its process family, but not commit suicide.
14663955d011SMarcel Moolenaar 	 */
14673955d011SMarcel Moolenaar #if defined(HAVE_SETPGID)
14683955d011SMarcel Moolenaar 	(void)setpgid(0, getpid());
14693955d011SMarcel Moolenaar #else
14703955d011SMarcel Moolenaar #if defined(HAVE_SETSID)
14713955d011SMarcel Moolenaar 	/* XXX: dsl - I'm sure this should be setpgrp()... */
14723955d011SMarcel Moolenaar 	(void)setsid();
14733955d011SMarcel Moolenaar #else
14743955d011SMarcel Moolenaar 	(void)setpgrp(0, getpid());
14753955d011SMarcel Moolenaar #endif
14763955d011SMarcel Moolenaar #endif
14773955d011SMarcel Moolenaar 
14783955d011SMarcel Moolenaar 	Var_ExportVars();
14793955d011SMarcel Moolenaar 
14803955d011SMarcel Moolenaar 	(void)execv(shellPath, argv);
14813955d011SMarcel Moolenaar 	execError("exec", shellPath);
14823955d011SMarcel Moolenaar 	_exit(1);
14833955d011SMarcel Moolenaar     }
14843955d011SMarcel Moolenaar 
14853955d011SMarcel Moolenaar     /* Parent, continuing after the child exec */
14863955d011SMarcel Moolenaar     job->pid = cpid;
14873955d011SMarcel Moolenaar 
14883955d011SMarcel Moolenaar     Trace_Log(JOBSTART, job);
14893955d011SMarcel Moolenaar 
1490*49caa483SSimon J. Gerraty #ifdef USE_META
1491*49caa483SSimon J. Gerraty     if (useMeta) {
1492*49caa483SSimon J. Gerraty 	meta_job_parent(job, cpid);
1493*49caa483SSimon J. Gerraty     }
1494*49caa483SSimon J. Gerraty #endif
1495*49caa483SSimon J. Gerraty 
14963955d011SMarcel Moolenaar     /*
14973955d011SMarcel Moolenaar      * Set the current position in the buffer to the beginning
14983955d011SMarcel Moolenaar      * and mark another stream to watch in the outputs mask
14993955d011SMarcel Moolenaar      */
15003955d011SMarcel Moolenaar     job->curPos = 0;
15013955d011SMarcel Moolenaar 
15023955d011SMarcel Moolenaar     watchfd(job);
15033955d011SMarcel Moolenaar 
15043955d011SMarcel Moolenaar     if (job->cmdFILE != NULL && job->cmdFILE != stdout) {
15053955d011SMarcel Moolenaar 	(void)fclose(job->cmdFILE);
15063955d011SMarcel Moolenaar 	job->cmdFILE = NULL;
15073955d011SMarcel Moolenaar     }
15083955d011SMarcel Moolenaar 
15093955d011SMarcel Moolenaar     /*
15103955d011SMarcel Moolenaar      * Now the job is actually running, add it to the table.
15113955d011SMarcel Moolenaar      */
15123955d011SMarcel Moolenaar     if (DEBUG(JOB)) {
15133955d011SMarcel Moolenaar 	fprintf(debug_file, "JobExec(%s): pid %d added to jobs table\n",
15143955d011SMarcel Moolenaar 		job->node->name, job->pid);
15153955d011SMarcel Moolenaar 	job_table_dump("job started");
15163955d011SMarcel Moolenaar     }
15173955d011SMarcel Moolenaar     JobSigUnlock(&mask);
15183955d011SMarcel Moolenaar }
15193955d011SMarcel Moolenaar 
15203955d011SMarcel Moolenaar /*-
15213955d011SMarcel Moolenaar  *-----------------------------------------------------------------------
15223955d011SMarcel Moolenaar  * JobMakeArgv --
15233955d011SMarcel Moolenaar  *	Create the argv needed to execute the shell for a given job.
15243955d011SMarcel Moolenaar  *
15253955d011SMarcel Moolenaar  *
15263955d011SMarcel Moolenaar  * Results:
15273955d011SMarcel Moolenaar  *
15283955d011SMarcel Moolenaar  * Side Effects:
15293955d011SMarcel Moolenaar  *
15303955d011SMarcel Moolenaar  *-----------------------------------------------------------------------
15313955d011SMarcel Moolenaar  */
15323955d011SMarcel Moolenaar static void
15333955d011SMarcel Moolenaar JobMakeArgv(Job *job, char **argv)
15343955d011SMarcel Moolenaar {
15353955d011SMarcel Moolenaar     int	    	  argc;
15363955d011SMarcel Moolenaar     static char args[10]; 	/* For merged arguments */
15373955d011SMarcel Moolenaar 
15383955d011SMarcel Moolenaar     argv[0] = UNCONST(shellName);
15393955d011SMarcel Moolenaar     argc = 1;
15403955d011SMarcel Moolenaar 
15413955d011SMarcel Moolenaar     if ((commandShell->exit && (*commandShell->exit != '-')) ||
15423955d011SMarcel Moolenaar 	(commandShell->echo && (*commandShell->echo != '-')))
15433955d011SMarcel Moolenaar     {
15443955d011SMarcel Moolenaar 	/*
15453955d011SMarcel Moolenaar 	 * At least one of the flags doesn't have a minus before it, so
15463955d011SMarcel Moolenaar 	 * merge them together. Have to do this because the *(&(@*#*&#$#
15473955d011SMarcel Moolenaar 	 * Bourne shell thinks its second argument is a file to source.
15483955d011SMarcel Moolenaar 	 * Grrrr. Note the ten-character limitation on the combined arguments.
15493955d011SMarcel Moolenaar 	 */
15503955d011SMarcel Moolenaar 	(void)snprintf(args, sizeof(args), "-%s%s",
15513955d011SMarcel Moolenaar 		      ((job->flags & JOB_IGNERR) ? "" :
15523955d011SMarcel Moolenaar 		       (commandShell->exit ? commandShell->exit : "")),
15533955d011SMarcel Moolenaar 		      ((job->flags & JOB_SILENT) ? "" :
15543955d011SMarcel Moolenaar 		       (commandShell->echo ? commandShell->echo : "")));
15553955d011SMarcel Moolenaar 
15563955d011SMarcel Moolenaar 	if (args[1]) {
15573955d011SMarcel Moolenaar 	    argv[argc] = args;
15583955d011SMarcel Moolenaar 	    argc++;
15593955d011SMarcel Moolenaar 	}
15603955d011SMarcel Moolenaar     } else {
15613955d011SMarcel Moolenaar 	if (!(job->flags & JOB_IGNERR) && commandShell->exit) {
15623955d011SMarcel Moolenaar 	    argv[argc] = UNCONST(commandShell->exit);
15633955d011SMarcel Moolenaar 	    argc++;
15643955d011SMarcel Moolenaar 	}
15653955d011SMarcel Moolenaar 	if (!(job->flags & JOB_SILENT) && commandShell->echo) {
15663955d011SMarcel Moolenaar 	    argv[argc] = UNCONST(commandShell->echo);
15673955d011SMarcel Moolenaar 	    argc++;
15683955d011SMarcel Moolenaar 	}
15693955d011SMarcel Moolenaar     }
15703955d011SMarcel Moolenaar     argv[argc] = NULL;
15713955d011SMarcel Moolenaar }
15723955d011SMarcel Moolenaar 
15733955d011SMarcel Moolenaar /*-
15743955d011SMarcel Moolenaar  *-----------------------------------------------------------------------
15753955d011SMarcel Moolenaar  * JobStart  --
15763955d011SMarcel Moolenaar  *	Start a target-creation process going for the target described
15773955d011SMarcel Moolenaar  *	by the graph node gn.
15783955d011SMarcel Moolenaar  *
15793955d011SMarcel Moolenaar  * Input:
15803955d011SMarcel Moolenaar  *	gn		target to create
15813955d011SMarcel Moolenaar  *	flags		flags for the job to override normal ones.
15823955d011SMarcel Moolenaar  *			e.g. JOB_SPECIAL or JOB_IGNDOTS
15833955d011SMarcel Moolenaar  *	previous	The previous Job structure for this node, if any.
15843955d011SMarcel Moolenaar  *
15853955d011SMarcel Moolenaar  * Results:
15863955d011SMarcel Moolenaar  *	JOB_ERROR if there was an error in the commands, JOB_FINISHED
15873955d011SMarcel Moolenaar  *	if there isn't actually anything left to do for the job and
15883955d011SMarcel Moolenaar  *	JOB_RUNNING if the job has been started.
15893955d011SMarcel Moolenaar  *
15903955d011SMarcel Moolenaar  * Side Effects:
15913955d011SMarcel Moolenaar  *	A new Job node is created and added to the list of running
15923955d011SMarcel Moolenaar  *	jobs. PMake is forked and a child shell created.
15933955d011SMarcel Moolenaar  *
15943955d011SMarcel Moolenaar  * NB: I'm fairly sure that this code is never called with JOB_SPECIAL set
15953955d011SMarcel Moolenaar  *     JOB_IGNDOTS is never set (dsl)
15963955d011SMarcel Moolenaar  *     Also the return value is ignored by everyone.
15973955d011SMarcel Moolenaar  *-----------------------------------------------------------------------
15983955d011SMarcel Moolenaar  */
15993955d011SMarcel Moolenaar static int
16003955d011SMarcel Moolenaar JobStart(GNode *gn, int flags)
16013955d011SMarcel Moolenaar {
16023955d011SMarcel Moolenaar     Job		  *job;       /* new job descriptor */
16033955d011SMarcel Moolenaar     char	  *argv[10];  /* Argument vector to shell */
16043955d011SMarcel Moolenaar     Boolean	  cmdsOK;     /* true if the nodes commands were all right */
16053955d011SMarcel Moolenaar     Boolean 	  noExec;     /* Set true if we decide not to run the job */
16063955d011SMarcel Moolenaar     int		  tfd;	      /* File descriptor to the temp file */
16073955d011SMarcel Moolenaar 
16083955d011SMarcel Moolenaar     for (job = job_table; job < job_table_end; job++) {
16093955d011SMarcel Moolenaar 	if (job->job_state == JOB_ST_FREE)
16103955d011SMarcel Moolenaar 	    break;
16113955d011SMarcel Moolenaar     }
16123955d011SMarcel Moolenaar     if (job >= job_table_end)
16133955d011SMarcel Moolenaar 	Punt("JobStart no job slots vacant");
16143955d011SMarcel Moolenaar 
16153955d011SMarcel Moolenaar     memset(job, 0, sizeof *job);
16163955d011SMarcel Moolenaar     job->job_state = JOB_ST_SETUP;
16173955d011SMarcel Moolenaar     if (gn->type & OP_SPECIAL)
16183955d011SMarcel Moolenaar 	flags |= JOB_SPECIAL;
16193955d011SMarcel Moolenaar 
16203955d011SMarcel Moolenaar     job->node = gn;
16213955d011SMarcel Moolenaar     job->tailCmds = NULL;
16223955d011SMarcel Moolenaar 
16233955d011SMarcel Moolenaar     /*
16243955d011SMarcel Moolenaar      * Set the initial value of the flags for this job based on the global
16253955d011SMarcel Moolenaar      * ones and the node's attributes... Any flags supplied by the caller
16263955d011SMarcel Moolenaar      * are also added to the field.
16273955d011SMarcel Moolenaar      */
16283955d011SMarcel Moolenaar     job->flags = 0;
16293955d011SMarcel Moolenaar     if (Targ_Ignore(gn)) {
16303955d011SMarcel Moolenaar 	job->flags |= JOB_IGNERR;
16313955d011SMarcel Moolenaar     }
16323955d011SMarcel Moolenaar     if (Targ_Silent(gn)) {
16333955d011SMarcel Moolenaar 	job->flags |= JOB_SILENT;
16343955d011SMarcel Moolenaar     }
16353955d011SMarcel Moolenaar     job->flags |= flags;
16363955d011SMarcel Moolenaar 
16373955d011SMarcel Moolenaar     /*
16383955d011SMarcel Moolenaar      * Check the commands now so any attributes from .DEFAULT have a chance
16393955d011SMarcel Moolenaar      * to migrate to the node
16403955d011SMarcel Moolenaar      */
16413955d011SMarcel Moolenaar     cmdsOK = Job_CheckCommands(gn, Error);
16423955d011SMarcel Moolenaar 
16433955d011SMarcel Moolenaar     job->inPollfd = NULL;
16443955d011SMarcel Moolenaar     /*
16453955d011SMarcel Moolenaar      * If the -n flag wasn't given, we open up OUR (not the child's)
16463955d011SMarcel Moolenaar      * temporary file to stuff commands in it. The thing is rd/wr so we don't
16473955d011SMarcel Moolenaar      * need to reopen it to feed it to the shell. If the -n flag *was* given,
16483955d011SMarcel Moolenaar      * we just set the file to be stdout. Cute, huh?
16493955d011SMarcel Moolenaar      */
16503955d011SMarcel Moolenaar     if (((gn->type & OP_MAKE) && !(noRecursiveExecute)) ||
16513955d011SMarcel Moolenaar 	    (!noExecute && !touchFlag)) {
16523955d011SMarcel Moolenaar 	/*
16533955d011SMarcel Moolenaar 	 * tfile is the name of a file into which all shell commands are
16543955d011SMarcel Moolenaar 	 * put. It is removed before the child shell is executed, unless
16553955d011SMarcel Moolenaar 	 * DEBUG(SCRIPT) is set.
16563955d011SMarcel Moolenaar 	 */
16573955d011SMarcel Moolenaar 	char *tfile;
16583955d011SMarcel Moolenaar 	sigset_t mask;
16593955d011SMarcel Moolenaar 	/*
16603955d011SMarcel Moolenaar 	 * We're serious here, but if the commands were bogus, we're
16613955d011SMarcel Moolenaar 	 * also dead...
16623955d011SMarcel Moolenaar 	 */
16633955d011SMarcel Moolenaar 	if (!cmdsOK) {
16643955d011SMarcel Moolenaar 	    PrintOnError(gn, NULL);	/* provide some clue */
16653955d011SMarcel Moolenaar 	    DieHorribly();
16663955d011SMarcel Moolenaar 	}
16673955d011SMarcel Moolenaar 
16683955d011SMarcel Moolenaar 	JobSigLock(&mask);
16693955d011SMarcel Moolenaar 	tfd = mkTempFile(TMPPAT, &tfile);
16703955d011SMarcel Moolenaar 	if (!DEBUG(SCRIPT))
16713955d011SMarcel Moolenaar 		(void)eunlink(tfile);
16723955d011SMarcel Moolenaar 	JobSigUnlock(&mask);
16733955d011SMarcel Moolenaar 
16743955d011SMarcel Moolenaar 	job->cmdFILE = fdopen(tfd, "w+");
16753955d011SMarcel Moolenaar 	if (job->cmdFILE == NULL) {
16763955d011SMarcel Moolenaar 	    Punt("Could not fdopen %s", tfile);
16773955d011SMarcel Moolenaar 	}
1678be19d90bSSimon J. Gerraty 	(void)fcntl(FILENO(job->cmdFILE), F_SETFD, FD_CLOEXEC);
16793955d011SMarcel Moolenaar 	/*
16803955d011SMarcel Moolenaar 	 * Send the commands to the command file, flush all its buffers then
16813955d011SMarcel Moolenaar 	 * rewind and remove the thing.
16823955d011SMarcel Moolenaar 	 */
16833955d011SMarcel Moolenaar 	noExec = FALSE;
16843955d011SMarcel Moolenaar 
16853955d011SMarcel Moolenaar #ifdef USE_META
16863955d011SMarcel Moolenaar 	if (useMeta) {
16873955d011SMarcel Moolenaar 	    meta_job_start(job, gn);
16883955d011SMarcel Moolenaar 	    if (Targ_Silent(gn)) {	/* might have changed */
16893955d011SMarcel Moolenaar 		job->flags |= JOB_SILENT;
16903955d011SMarcel Moolenaar 	    }
16913955d011SMarcel Moolenaar 	}
16923955d011SMarcel Moolenaar #endif
16933955d011SMarcel Moolenaar 	/*
16943955d011SMarcel Moolenaar 	 * We can do all the commands at once. hooray for sanity
16953955d011SMarcel Moolenaar 	 */
16963955d011SMarcel Moolenaar 	numCommands = 0;
16973955d011SMarcel Moolenaar 	Lst_ForEach(gn->commands, JobPrintCommand, job);
16983955d011SMarcel Moolenaar 
16993955d011SMarcel Moolenaar 	/*
17003955d011SMarcel Moolenaar 	 * If we didn't print out any commands to the shell script,
17013955d011SMarcel Moolenaar 	 * there's not much point in executing the shell, is there?
17023955d011SMarcel Moolenaar 	 */
17033955d011SMarcel Moolenaar 	if (numCommands == 0) {
17043955d011SMarcel Moolenaar 	    noExec = TRUE;
17053955d011SMarcel Moolenaar 	}
17063955d011SMarcel Moolenaar 
17073955d011SMarcel Moolenaar 	free(tfile);
17083955d011SMarcel Moolenaar     } else if (NoExecute(gn)) {
17093955d011SMarcel Moolenaar 	/*
17103955d011SMarcel Moolenaar 	 * Not executing anything -- just print all the commands to stdout
17113955d011SMarcel Moolenaar 	 * in one fell swoop. This will still set up job->tailCmds correctly.
17123955d011SMarcel Moolenaar 	 */
17133955d011SMarcel Moolenaar 	if (lastNode != gn) {
17143955d011SMarcel Moolenaar 	    MESSAGE(stdout, gn);
17153955d011SMarcel Moolenaar 	    lastNode = gn;
17163955d011SMarcel Moolenaar 	}
17173955d011SMarcel Moolenaar 	job->cmdFILE = stdout;
17183955d011SMarcel Moolenaar 	/*
17193955d011SMarcel Moolenaar 	 * Only print the commands if they're ok, but don't die if they're
17203955d011SMarcel Moolenaar 	 * not -- just let the user know they're bad and keep going. It
17213955d011SMarcel Moolenaar 	 * doesn't do any harm in this case and may do some good.
17223955d011SMarcel Moolenaar 	 */
17233955d011SMarcel Moolenaar 	if (cmdsOK) {
17243955d011SMarcel Moolenaar 	    Lst_ForEach(gn->commands, JobPrintCommand, job);
17253955d011SMarcel Moolenaar 	}
17263955d011SMarcel Moolenaar 	/*
17273955d011SMarcel Moolenaar 	 * Don't execute the shell, thank you.
17283955d011SMarcel Moolenaar 	 */
17293955d011SMarcel Moolenaar 	noExec = TRUE;
17303955d011SMarcel Moolenaar     } else {
17313955d011SMarcel Moolenaar 	/*
17323955d011SMarcel Moolenaar 	 * Just touch the target and note that no shell should be executed.
17333955d011SMarcel Moolenaar 	 * Set cmdFILE to stdout to make life easier. Check the commands, too,
17343955d011SMarcel Moolenaar 	 * but don't die if they're no good -- it does no harm to keep working
17353955d011SMarcel Moolenaar 	 * up the graph.
17363955d011SMarcel Moolenaar 	 */
17373955d011SMarcel Moolenaar 	job->cmdFILE = stdout;
17383955d011SMarcel Moolenaar     	Job_Touch(gn, job->flags&JOB_SILENT);
17393955d011SMarcel Moolenaar 	noExec = TRUE;
17403955d011SMarcel Moolenaar     }
17413955d011SMarcel Moolenaar     /* Just in case it isn't already... */
17423955d011SMarcel Moolenaar     (void)fflush(job->cmdFILE);
17433955d011SMarcel Moolenaar 
17443955d011SMarcel Moolenaar     /*
17453955d011SMarcel Moolenaar      * If we're not supposed to execute a shell, don't.
17463955d011SMarcel Moolenaar      */
17473955d011SMarcel Moolenaar     if (noExec) {
17483955d011SMarcel Moolenaar 	if (!(job->flags & JOB_SPECIAL))
17493955d011SMarcel Moolenaar 	    Job_TokenReturn();
17503955d011SMarcel Moolenaar 	/*
17513955d011SMarcel Moolenaar 	 * Unlink and close the command file if we opened one
17523955d011SMarcel Moolenaar 	 */
17533955d011SMarcel Moolenaar 	if (job->cmdFILE != stdout) {
17543955d011SMarcel Moolenaar 	    if (job->cmdFILE != NULL) {
17553955d011SMarcel Moolenaar 		(void)fclose(job->cmdFILE);
17563955d011SMarcel Moolenaar 		job->cmdFILE = NULL;
17573955d011SMarcel Moolenaar 	    }
17583955d011SMarcel Moolenaar 	}
17593955d011SMarcel Moolenaar 
17603955d011SMarcel Moolenaar 	/*
17613955d011SMarcel Moolenaar 	 * We only want to work our way up the graph if we aren't here because
17623955d011SMarcel Moolenaar 	 * the commands for the job were no good.
17633955d011SMarcel Moolenaar 	 */
17643955d011SMarcel Moolenaar 	if (cmdsOK && aborting == 0) {
17653955d011SMarcel Moolenaar 	    if (job->tailCmds != NULL) {
17663955d011SMarcel Moolenaar 		Lst_ForEachFrom(job->node->commands, job->tailCmds,
17673955d011SMarcel Moolenaar 				JobSaveCommand,
17683955d011SMarcel Moolenaar 			       job->node);
17693955d011SMarcel Moolenaar 	    }
17703955d011SMarcel Moolenaar 	    job->node->made = MADE;
17713955d011SMarcel Moolenaar 	    Make_Update(job->node);
17723955d011SMarcel Moolenaar 	}
17733955d011SMarcel Moolenaar 	job->job_state = JOB_ST_FREE;
17743955d011SMarcel Moolenaar 	return cmdsOK ? JOB_FINISHED : JOB_ERROR;
17753955d011SMarcel Moolenaar     }
17763955d011SMarcel Moolenaar 
17773955d011SMarcel Moolenaar     /*
17783955d011SMarcel Moolenaar      * Set up the control arguments to the shell. This is based on the flags
17793955d011SMarcel Moolenaar      * set earlier for this job.
17803955d011SMarcel Moolenaar      */
17813955d011SMarcel Moolenaar     JobMakeArgv(job, argv);
17823955d011SMarcel Moolenaar 
17833955d011SMarcel Moolenaar     /* Create the pipe by which we'll get the shell's output.  */
17843955d011SMarcel Moolenaar     JobCreatePipe(job, 3);
17853955d011SMarcel Moolenaar 
17863955d011SMarcel Moolenaar     JobExec(job, argv);
17873955d011SMarcel Moolenaar     return(JOB_RUNNING);
17883955d011SMarcel Moolenaar }
17893955d011SMarcel Moolenaar 
17903955d011SMarcel Moolenaar static char *
17913955d011SMarcel Moolenaar JobOutput(Job *job, char *cp, char *endp, int msg)
17923955d011SMarcel Moolenaar {
17933955d011SMarcel Moolenaar     char *ecp;
17943955d011SMarcel Moolenaar 
17953955d011SMarcel Moolenaar     if (commandShell->noPrint) {
17963955d011SMarcel Moolenaar 	ecp = Str_FindSubstring(cp, commandShell->noPrint);
17973955d011SMarcel Moolenaar 	while (ecp != NULL) {
17983955d011SMarcel Moolenaar 	    if (cp != ecp) {
17993955d011SMarcel Moolenaar 		*ecp = '\0';
18003955d011SMarcel Moolenaar 		if (!beSilent && msg && job->node != lastNode) {
18013955d011SMarcel Moolenaar 		    MESSAGE(stdout, job->node);
18023955d011SMarcel Moolenaar 		    lastNode = job->node;
18033955d011SMarcel Moolenaar 		}
18043955d011SMarcel Moolenaar 		/*
18053955d011SMarcel Moolenaar 		 * The only way there wouldn't be a newline after
18063955d011SMarcel Moolenaar 		 * this line is if it were the last in the buffer.
18073955d011SMarcel Moolenaar 		 * however, since the non-printable comes after it,
18083955d011SMarcel Moolenaar 		 * there must be a newline, so we don't print one.
18093955d011SMarcel Moolenaar 		 */
18103955d011SMarcel Moolenaar 		(void)fprintf(stdout, "%s", cp);
18113955d011SMarcel Moolenaar 		(void)fflush(stdout);
18123955d011SMarcel Moolenaar 	    }
18133955d011SMarcel Moolenaar 	    cp = ecp + commandShell->noPLen;
18143955d011SMarcel Moolenaar 	    if (cp != endp) {
18153955d011SMarcel Moolenaar 		/*
18163955d011SMarcel Moolenaar 		 * Still more to print, look again after skipping
18173955d011SMarcel Moolenaar 		 * the whitespace following the non-printable
18183955d011SMarcel Moolenaar 		 * command....
18193955d011SMarcel Moolenaar 		 */
18203955d011SMarcel Moolenaar 		cp++;
18213955d011SMarcel Moolenaar 		while (*cp == ' ' || *cp == '\t' || *cp == '\n') {
18223955d011SMarcel Moolenaar 		    cp++;
18233955d011SMarcel Moolenaar 		}
18243955d011SMarcel Moolenaar 		ecp = Str_FindSubstring(cp, commandShell->noPrint);
18253955d011SMarcel Moolenaar 	    } else {
18263955d011SMarcel Moolenaar 		return cp;
18273955d011SMarcel Moolenaar 	    }
18283955d011SMarcel Moolenaar 	}
18293955d011SMarcel Moolenaar     }
18303955d011SMarcel Moolenaar     return cp;
18313955d011SMarcel Moolenaar }
18323955d011SMarcel Moolenaar 
18333955d011SMarcel Moolenaar /*-
18343955d011SMarcel Moolenaar  *-----------------------------------------------------------------------
18353955d011SMarcel Moolenaar  * JobDoOutput  --
18363955d011SMarcel Moolenaar  *	This function is called at different times depending on
18373955d011SMarcel Moolenaar  *	whether the user has specified that output is to be collected
18383955d011SMarcel Moolenaar  *	via pipes or temporary files. In the former case, we are called
18393955d011SMarcel Moolenaar  *	whenever there is something to read on the pipe. We collect more
18403955d011SMarcel Moolenaar  *	output from the given job and store it in the job's outBuf. If
18413955d011SMarcel Moolenaar  *	this makes up a line, we print it tagged by the job's identifier,
18423955d011SMarcel Moolenaar  *	as necessary.
18433955d011SMarcel Moolenaar  *	If output has been collected in a temporary file, we open the
18443955d011SMarcel Moolenaar  *	file and read it line by line, transfering it to our own
18453955d011SMarcel Moolenaar  *	output channel until the file is empty. At which point we
18463955d011SMarcel Moolenaar  *	remove the temporary file.
18473955d011SMarcel Moolenaar  *	In both cases, however, we keep our figurative eye out for the
18483955d011SMarcel Moolenaar  *	'noPrint' line for the shell from which the output came. If
18493955d011SMarcel Moolenaar  *	we recognize a line, we don't print it. If the command is not
18503955d011SMarcel Moolenaar  *	alone on the line (the character after it is not \0 or \n), we
18513955d011SMarcel Moolenaar  *	do print whatever follows it.
18523955d011SMarcel Moolenaar  *
18533955d011SMarcel Moolenaar  * Input:
18543955d011SMarcel Moolenaar  *	job		the job whose output needs printing
18553955d011SMarcel Moolenaar  *	finish		TRUE if this is the last time we'll be called
18563955d011SMarcel Moolenaar  *			for this job
18573955d011SMarcel Moolenaar  *
18583955d011SMarcel Moolenaar  * Results:
18593955d011SMarcel Moolenaar  *	None
18603955d011SMarcel Moolenaar  *
18613955d011SMarcel Moolenaar  * Side Effects:
18623955d011SMarcel Moolenaar  *	curPos may be shifted as may the contents of outBuf.
18633955d011SMarcel Moolenaar  *-----------------------------------------------------------------------
18643955d011SMarcel Moolenaar  */
18653955d011SMarcel Moolenaar STATIC void
18663955d011SMarcel Moolenaar JobDoOutput(Job *job, Boolean finish)
18673955d011SMarcel Moolenaar {
18683955d011SMarcel Moolenaar     Boolean       gotNL = FALSE;  /* true if got a newline */
18693955d011SMarcel Moolenaar     Boolean       fbuf;  	  /* true if our buffer filled up */
18703955d011SMarcel Moolenaar     int		  nr;	      	  /* number of bytes read */
18713955d011SMarcel Moolenaar     int		  i;	      	  /* auxiliary index into outBuf */
18723955d011SMarcel Moolenaar     int		  max;	      	  /* limit for i (end of current data) */
18733955d011SMarcel Moolenaar     int		  nRead;      	  /* (Temporary) number of bytes read */
18743955d011SMarcel Moolenaar 
18753955d011SMarcel Moolenaar     /*
18763955d011SMarcel Moolenaar      * Read as many bytes as will fit in the buffer.
18773955d011SMarcel Moolenaar      */
18783955d011SMarcel Moolenaar end_loop:
18793955d011SMarcel Moolenaar     gotNL = FALSE;
18803955d011SMarcel Moolenaar     fbuf = FALSE;
18813955d011SMarcel Moolenaar 
18823955d011SMarcel Moolenaar     nRead = read(job->inPipe, &job->outBuf[job->curPos],
18833955d011SMarcel Moolenaar 		     JOB_BUFSIZE - job->curPos);
18843955d011SMarcel Moolenaar     if (nRead < 0) {
18853955d011SMarcel Moolenaar 	if (errno == EAGAIN)
18863955d011SMarcel Moolenaar 	    return;
18873955d011SMarcel Moolenaar 	if (DEBUG(JOB)) {
18883955d011SMarcel Moolenaar 	    perror("JobDoOutput(piperead)");
18893955d011SMarcel Moolenaar 	}
18903955d011SMarcel Moolenaar 	nr = 0;
18913955d011SMarcel Moolenaar     } else {
18923955d011SMarcel Moolenaar 	nr = nRead;
18933955d011SMarcel Moolenaar     }
18943955d011SMarcel Moolenaar 
18953955d011SMarcel Moolenaar     /*
18963955d011SMarcel Moolenaar      * If we hit the end-of-file (the job is dead), we must flush its
18973955d011SMarcel Moolenaar      * remaining output, so pretend we read a newline if there's any
18983955d011SMarcel Moolenaar      * output remaining in the buffer.
18993955d011SMarcel Moolenaar      * Also clear the 'finish' flag so we stop looping.
19003955d011SMarcel Moolenaar      */
19013955d011SMarcel Moolenaar     if ((nr == 0) && (job->curPos != 0)) {
19023955d011SMarcel Moolenaar 	job->outBuf[job->curPos] = '\n';
19033955d011SMarcel Moolenaar 	nr = 1;
19043955d011SMarcel Moolenaar 	finish = FALSE;
19053955d011SMarcel Moolenaar     } else if (nr == 0) {
19063955d011SMarcel Moolenaar 	finish = FALSE;
19073955d011SMarcel Moolenaar     }
19083955d011SMarcel Moolenaar 
19093955d011SMarcel Moolenaar     /*
19103955d011SMarcel Moolenaar      * Look for the last newline in the bytes we just got. If there is
19113955d011SMarcel Moolenaar      * one, break out of the loop with 'i' as its index and gotNL set
19123955d011SMarcel Moolenaar      * TRUE.
19133955d011SMarcel Moolenaar      */
19143955d011SMarcel Moolenaar     max = job->curPos + nr;
19153955d011SMarcel Moolenaar     for (i = job->curPos + nr - 1; i >= job->curPos; i--) {
19163955d011SMarcel Moolenaar 	if (job->outBuf[i] == '\n') {
19173955d011SMarcel Moolenaar 	    gotNL = TRUE;
19183955d011SMarcel Moolenaar 	    break;
19193955d011SMarcel Moolenaar 	} else if (job->outBuf[i] == '\0') {
19203955d011SMarcel Moolenaar 	    /*
19213955d011SMarcel Moolenaar 	     * Why?
19223955d011SMarcel Moolenaar 	     */
19233955d011SMarcel Moolenaar 	    job->outBuf[i] = ' ';
19243955d011SMarcel Moolenaar 	}
19253955d011SMarcel Moolenaar     }
19263955d011SMarcel Moolenaar 
19273955d011SMarcel Moolenaar     if (!gotNL) {
19283955d011SMarcel Moolenaar 	job->curPos += nr;
19293955d011SMarcel Moolenaar 	if (job->curPos == JOB_BUFSIZE) {
19303955d011SMarcel Moolenaar 	    /*
19313955d011SMarcel Moolenaar 	     * If we've run out of buffer space, we have no choice
19323955d011SMarcel Moolenaar 	     * but to print the stuff. sigh.
19333955d011SMarcel Moolenaar 	     */
19343955d011SMarcel Moolenaar 	    fbuf = TRUE;
19353955d011SMarcel Moolenaar 	    i = job->curPos;
19363955d011SMarcel Moolenaar 	}
19373955d011SMarcel Moolenaar     }
19383955d011SMarcel Moolenaar     if (gotNL || fbuf) {
19393955d011SMarcel Moolenaar 	/*
19403955d011SMarcel Moolenaar 	 * Need to send the output to the screen. Null terminate it
19413955d011SMarcel Moolenaar 	 * first, overwriting the newline character if there was one.
19423955d011SMarcel Moolenaar 	 * So long as the line isn't one we should filter (according
19433955d011SMarcel Moolenaar 	 * to the shell description), we print the line, preceded
19443955d011SMarcel Moolenaar 	 * by a target banner if this target isn't the same as the
19453955d011SMarcel Moolenaar 	 * one for which we last printed something.
19463955d011SMarcel Moolenaar 	 * The rest of the data in the buffer are then shifted down
19473955d011SMarcel Moolenaar 	 * to the start of the buffer and curPos is set accordingly.
19483955d011SMarcel Moolenaar 	 */
19493955d011SMarcel Moolenaar 	job->outBuf[i] = '\0';
19503955d011SMarcel Moolenaar 	if (i >= job->curPos) {
19513955d011SMarcel Moolenaar 	    char *cp;
19523955d011SMarcel Moolenaar 
19533955d011SMarcel Moolenaar 	    cp = JobOutput(job, job->outBuf, &job->outBuf[i], FALSE);
19543955d011SMarcel Moolenaar 
19553955d011SMarcel Moolenaar 	    /*
19563955d011SMarcel Moolenaar 	     * There's still more in that thar buffer. This time, though,
19573955d011SMarcel Moolenaar 	     * we know there's no newline at the end, so we add one of
19583955d011SMarcel Moolenaar 	     * our own free will.
19593955d011SMarcel Moolenaar 	     */
19603955d011SMarcel Moolenaar 	    if (*cp != '\0') {
19613955d011SMarcel Moolenaar 		if (!beSilent && job->node != lastNode) {
19623955d011SMarcel Moolenaar 		    MESSAGE(stdout, job->node);
19633955d011SMarcel Moolenaar 		    lastNode = job->node;
19643955d011SMarcel Moolenaar 		}
19653955d011SMarcel Moolenaar #ifdef USE_META
19663955d011SMarcel Moolenaar 		if (useMeta) {
19673955d011SMarcel Moolenaar 		    meta_job_output(job, cp, gotNL ? "\n" : "");
19683955d011SMarcel Moolenaar 		}
19693955d011SMarcel Moolenaar #endif
19703955d011SMarcel Moolenaar 		(void)fprintf(stdout, "%s%s", cp, gotNL ? "\n" : "");
19713955d011SMarcel Moolenaar 		(void)fflush(stdout);
19723955d011SMarcel Moolenaar 	    }
19733955d011SMarcel Moolenaar 	}
19743955d011SMarcel Moolenaar 	/*
1975db29cad8SSimon J. Gerraty 	 * max is the last offset still in the buffer. Move any remaining
1976db29cad8SSimon J. Gerraty 	 * characters to the start of the buffer and update the end marker
1977db29cad8SSimon J. Gerraty 	 * curPos.
19783955d011SMarcel Moolenaar 	 */
1979db29cad8SSimon J. Gerraty 	if (i < max) {
1980db29cad8SSimon J. Gerraty 	    (void)memmove(job->outBuf, &job->outBuf[i + 1], max - (i + 1));
1981db29cad8SSimon J. Gerraty 	    job->curPos = max - (i + 1);
1982db29cad8SSimon J. Gerraty 	} else {
1983db29cad8SSimon J. Gerraty 	    assert(i == max);
19843955d011SMarcel Moolenaar 	    job->curPos = 0;
19853955d011SMarcel Moolenaar 	}
19863955d011SMarcel Moolenaar     }
19873955d011SMarcel Moolenaar     if (finish) {
19883955d011SMarcel Moolenaar 	/*
19893955d011SMarcel Moolenaar 	 * If the finish flag is true, we must loop until we hit
19903955d011SMarcel Moolenaar 	 * end-of-file on the pipe. This is guaranteed to happen
19913955d011SMarcel Moolenaar 	 * eventually since the other end of the pipe is now closed
19923955d011SMarcel Moolenaar 	 * (we closed it explicitly and the child has exited). When
19933955d011SMarcel Moolenaar 	 * we do get an EOF, finish will be set FALSE and we'll fall
19943955d011SMarcel Moolenaar 	 * through and out.
19953955d011SMarcel Moolenaar 	 */
19963955d011SMarcel Moolenaar 	goto end_loop;
19973955d011SMarcel Moolenaar     }
19983955d011SMarcel Moolenaar }
19993955d011SMarcel Moolenaar 
20003955d011SMarcel Moolenaar static void
20013955d011SMarcel Moolenaar JobRun(GNode *targ)
20023955d011SMarcel Moolenaar {
20033955d011SMarcel Moolenaar #ifdef notyet
20043955d011SMarcel Moolenaar     /*
20053955d011SMarcel Moolenaar      * Unfortunately it is too complicated to run .BEGIN, .END,
20063955d011SMarcel Moolenaar      * and .INTERRUPT job in the parallel job module. This has
20073955d011SMarcel Moolenaar      * the nice side effect that it avoids a lot of other problems.
20083955d011SMarcel Moolenaar      */
20093955d011SMarcel Moolenaar     Lst lst = Lst_Init(FALSE);
20103955d011SMarcel Moolenaar     Lst_AtEnd(lst, targ);
20113955d011SMarcel Moolenaar     (void)Make_Run(lst);
20123955d011SMarcel Moolenaar     Lst_Destroy(lst, NULL);
20133955d011SMarcel Moolenaar     JobStart(targ, JOB_SPECIAL);
20143955d011SMarcel Moolenaar     while (jobTokensRunning) {
20153955d011SMarcel Moolenaar 	Job_CatchOutput();
20163955d011SMarcel Moolenaar     }
20173955d011SMarcel Moolenaar #else
20183955d011SMarcel Moolenaar     Compat_Make(targ, targ);
20193955d011SMarcel Moolenaar     if (targ->made == ERROR) {
20203955d011SMarcel Moolenaar 	PrintOnError(targ, "\n\nStop.");
20213955d011SMarcel Moolenaar 	exit(1);
20223955d011SMarcel Moolenaar     }
20233955d011SMarcel Moolenaar #endif
20243955d011SMarcel Moolenaar }
20253955d011SMarcel Moolenaar 
20263955d011SMarcel Moolenaar /*-
20273955d011SMarcel Moolenaar  *-----------------------------------------------------------------------
20283955d011SMarcel Moolenaar  * Job_CatchChildren --
20293955d011SMarcel Moolenaar  *	Handle the exit of a child. Called from Make_Make.
20303955d011SMarcel Moolenaar  *
20313955d011SMarcel Moolenaar  * Input:
20323955d011SMarcel Moolenaar  *	block		TRUE if should block on the wait
20333955d011SMarcel Moolenaar  *
20343955d011SMarcel Moolenaar  * Results:
20353955d011SMarcel Moolenaar  *	none.
20363955d011SMarcel Moolenaar  *
20373955d011SMarcel Moolenaar  * Side Effects:
20383955d011SMarcel Moolenaar  *	The job descriptor is removed from the list of children.
20393955d011SMarcel Moolenaar  *
20403955d011SMarcel Moolenaar  * Notes:
20413955d011SMarcel Moolenaar  *	We do waits, blocking or not, according to the wisdom of our
20423955d011SMarcel Moolenaar  *	caller, until there are no more children to report. For each
20433955d011SMarcel Moolenaar  *	job, call JobFinish to finish things off.
20443955d011SMarcel Moolenaar  *
20453955d011SMarcel Moolenaar  *-----------------------------------------------------------------------
20463955d011SMarcel Moolenaar  */
20473955d011SMarcel Moolenaar 
20483955d011SMarcel Moolenaar void
20493955d011SMarcel Moolenaar Job_CatchChildren(void)
20503955d011SMarcel Moolenaar {
20513955d011SMarcel Moolenaar     int    	  pid;	    	/* pid of dead child */
20523955d011SMarcel Moolenaar     WAIT_T	  status;   	/* Exit/termination status */
20533955d011SMarcel Moolenaar 
20543955d011SMarcel Moolenaar     /*
20553955d011SMarcel Moolenaar      * Don't even bother if we know there's no one around.
20563955d011SMarcel Moolenaar      */
20573955d011SMarcel Moolenaar     if (jobTokensRunning == 0)
20583955d011SMarcel Moolenaar 	return;
20593955d011SMarcel Moolenaar 
20603955d011SMarcel Moolenaar     while ((pid = waitpid((pid_t) -1, &status, WNOHANG | WUNTRACED)) > 0) {
20613955d011SMarcel Moolenaar 	if (DEBUG(JOB)) {
20623955d011SMarcel Moolenaar 	    (void)fprintf(debug_file, "Process %d exited/stopped status %x.\n", pid,
20633955d011SMarcel Moolenaar 	      WAIT_STATUS(status));
20643955d011SMarcel Moolenaar 	}
20653955d011SMarcel Moolenaar 	JobReapChild(pid, status, TRUE);
20663955d011SMarcel Moolenaar     }
20673955d011SMarcel Moolenaar }
20683955d011SMarcel Moolenaar 
20693955d011SMarcel Moolenaar /*
20703955d011SMarcel Moolenaar  * It is possible that wait[pid]() was called from elsewhere,
20713955d011SMarcel Moolenaar  * this lets us reap jobs regardless.
20723955d011SMarcel Moolenaar  */
20733955d011SMarcel Moolenaar void
20743955d011SMarcel Moolenaar JobReapChild(pid_t pid, WAIT_T status, Boolean isJobs)
20753955d011SMarcel Moolenaar {
20763955d011SMarcel Moolenaar     Job		  *job;	    	/* job descriptor for dead child */
20773955d011SMarcel Moolenaar 
20783955d011SMarcel Moolenaar     /*
20793955d011SMarcel Moolenaar      * Don't even bother if we know there's no one around.
20803955d011SMarcel Moolenaar      */
20813955d011SMarcel Moolenaar     if (jobTokensRunning == 0)
20823955d011SMarcel Moolenaar 	return;
20833955d011SMarcel Moolenaar 
20843955d011SMarcel Moolenaar     job = JobFindPid(pid, JOB_ST_RUNNING, isJobs);
20853955d011SMarcel Moolenaar     if (job == NULL) {
20863955d011SMarcel Moolenaar 	if (isJobs) {
20873955d011SMarcel Moolenaar 	    if (!lurking_children)
20883955d011SMarcel Moolenaar 		Error("Child (%d) status %x not in table?", pid, status);
20893955d011SMarcel Moolenaar 	}
20903955d011SMarcel Moolenaar 	return;				/* not ours */
20913955d011SMarcel Moolenaar     }
20923955d011SMarcel Moolenaar     if (WIFSTOPPED(status)) {
20933955d011SMarcel Moolenaar 	if (DEBUG(JOB)) {
20943955d011SMarcel Moolenaar 	    (void)fprintf(debug_file, "Process %d (%s) stopped.\n",
20953955d011SMarcel Moolenaar 			  job->pid, job->node->name);
20963955d011SMarcel Moolenaar 	}
20973955d011SMarcel Moolenaar 	if (!make_suspended) {
20983955d011SMarcel Moolenaar 	    switch (WSTOPSIG(status)) {
20993955d011SMarcel Moolenaar 	    case SIGTSTP:
21003955d011SMarcel Moolenaar 		(void)printf("*** [%s] Suspended\n", job->node->name);
21013955d011SMarcel Moolenaar 		break;
21023955d011SMarcel Moolenaar 	    case SIGSTOP:
21033955d011SMarcel Moolenaar 		(void)printf("*** [%s] Stopped\n", job->node->name);
21043955d011SMarcel Moolenaar 		break;
21053955d011SMarcel Moolenaar 	    default:
21063955d011SMarcel Moolenaar 		(void)printf("*** [%s] Stopped -- signal %d\n",
21073955d011SMarcel Moolenaar 			     job->node->name, WSTOPSIG(status));
21083955d011SMarcel Moolenaar 	    }
21093955d011SMarcel Moolenaar 	    job->job_suspended = 1;
21103955d011SMarcel Moolenaar 	}
21113955d011SMarcel Moolenaar 	(void)fflush(stdout);
21123955d011SMarcel Moolenaar 	return;
21133955d011SMarcel Moolenaar     }
21143955d011SMarcel Moolenaar 
21153955d011SMarcel Moolenaar     job->job_state = JOB_ST_FINISHED;
21163955d011SMarcel Moolenaar     job->exit_status = WAIT_STATUS(status);
21173955d011SMarcel Moolenaar 
21183955d011SMarcel Moolenaar     JobFinish(job, status);
21193955d011SMarcel Moolenaar }
21203955d011SMarcel Moolenaar 
21213955d011SMarcel Moolenaar /*-
21223955d011SMarcel Moolenaar  *-----------------------------------------------------------------------
21233955d011SMarcel Moolenaar  * Job_CatchOutput --
21243955d011SMarcel Moolenaar  *	Catch the output from our children, if we're using
21253955d011SMarcel Moolenaar  *	pipes do so. Otherwise just block time until we get a
21263955d011SMarcel Moolenaar  *	signal(most likely a SIGCHLD) since there's no point in
21273955d011SMarcel Moolenaar  *	just spinning when there's nothing to do and the reaping
21283955d011SMarcel Moolenaar  *	of a child can wait for a while.
21293955d011SMarcel Moolenaar  *
21303955d011SMarcel Moolenaar  * Results:
21313955d011SMarcel Moolenaar  *	None
21323955d011SMarcel Moolenaar  *
21333955d011SMarcel Moolenaar  * Side Effects:
21343955d011SMarcel Moolenaar  *	Output is read from pipes if we're piping.
21353955d011SMarcel Moolenaar  * -----------------------------------------------------------------------
21363955d011SMarcel Moolenaar  */
21373955d011SMarcel Moolenaar void
21383955d011SMarcel Moolenaar Job_CatchOutput(void)
21393955d011SMarcel Moolenaar {
21403955d011SMarcel Moolenaar     int nready;
21413955d011SMarcel Moolenaar     Job *job;
2142329d20deSMatt Macy     int i;
21433955d011SMarcel Moolenaar 
21443955d011SMarcel Moolenaar     (void)fflush(stdout);
21453955d011SMarcel Moolenaar 
21463955d011SMarcel Moolenaar     /* The first fd in the list is the job token pipe */
21471748de26SSimon J. Gerraty     do {
2148329d20deSMatt Macy 	nready = poll(fds + 1 - wantToken, nfds - 1 + wantToken, POLL_MSEC);
21491748de26SSimon J. Gerraty     } while (nready < 0 && errno == EINTR);
21503955d011SMarcel Moolenaar 
21511748de26SSimon J. Gerraty     if (nready < 0)
21521748de26SSimon J. Gerraty 	Punt("poll: %s", strerror(errno));
21531748de26SSimon J. Gerraty 
21541748de26SSimon J. Gerraty     if (nready > 0 && readyfd(&childExitJob)) {
21553955d011SMarcel Moolenaar 	char token = 0;
21561748de26SSimon J. Gerraty 	ssize_t count;
21571748de26SSimon J. Gerraty 	count = read(childExitJob.inPipe, &token, 1);
21581748de26SSimon J. Gerraty 	switch (count) {
21591748de26SSimon J. Gerraty 	case 0:
21601748de26SSimon J. Gerraty 	    Punt("unexpected eof on token pipe");
21611748de26SSimon J. Gerraty 	case -1:
21621748de26SSimon J. Gerraty 	    Punt("token pipe read: %s", strerror(errno));
21631748de26SSimon J. Gerraty 	case 1:
21643955d011SMarcel Moolenaar 	    if (token == DO_JOB_RESUME[0])
21653955d011SMarcel Moolenaar 		/* Complete relay requested from our SIGCONT handler */
21663955d011SMarcel Moolenaar 		JobRestartJobs();
21671748de26SSimon J. Gerraty 	    break;
21681748de26SSimon J. Gerraty 	default:
21691748de26SSimon J. Gerraty 	    abort();
21701748de26SSimon J. Gerraty 	}
21711748de26SSimon J. Gerraty 	--nready;
21723955d011SMarcel Moolenaar     }
21733955d011SMarcel Moolenaar 
21741748de26SSimon J. Gerraty     Job_CatchChildren();
21751748de26SSimon J. Gerraty     if (nready == 0)
21763955d011SMarcel Moolenaar 	    return;
21773955d011SMarcel Moolenaar 
2178*49caa483SSimon J. Gerraty     for (i = npseudojobs*nfds_per_job(); i < nfds; i++) {
21793955d011SMarcel Moolenaar 	if (!fds[i].revents)
21803955d011SMarcel Moolenaar 	    continue;
21813955d011SMarcel Moolenaar 	job = jobfds[i];
21821748de26SSimon J. Gerraty 	if (job->job_state == JOB_ST_RUNNING)
21833955d011SMarcel Moolenaar 	    JobDoOutput(job, FALSE);
2184*49caa483SSimon J. Gerraty #if defined(USE_FILEMON) && !defined(USE_FILEMON_DEV)
2185*49caa483SSimon J. Gerraty 	/*
2186*49caa483SSimon J. Gerraty 	 * With meta mode, we may have activity on the job's filemon
2187*49caa483SSimon J. Gerraty 	 * descriptor too, which at the moment is any pollfd other than
2188*49caa483SSimon J. Gerraty 	 * job->inPollfd.
2189*49caa483SSimon J. Gerraty 	 */
2190*49caa483SSimon J. Gerraty 	if (useMeta && job->inPollfd != &fds[i]) {
2191*49caa483SSimon J. Gerraty 	    if (meta_job_event(job) <= 0) {
2192*49caa483SSimon J. Gerraty 		fds[i].events = 0; /* never mind */
2193*49caa483SSimon J. Gerraty 	    }
2194*49caa483SSimon J. Gerraty 	}
2195*49caa483SSimon J. Gerraty #endif
21961748de26SSimon J. Gerraty 	if (--nready == 0)
21971748de26SSimon J. Gerraty 		return;
21983955d011SMarcel Moolenaar     }
21993955d011SMarcel Moolenaar }
22003955d011SMarcel Moolenaar 
22013955d011SMarcel Moolenaar /*-
22023955d011SMarcel Moolenaar  *-----------------------------------------------------------------------
22033955d011SMarcel Moolenaar  * Job_Make --
22043955d011SMarcel Moolenaar  *	Start the creation of a target. Basically a front-end for
22053955d011SMarcel Moolenaar  *	JobStart used by the Make module.
22063955d011SMarcel Moolenaar  *
22073955d011SMarcel Moolenaar  * Results:
22083955d011SMarcel Moolenaar  *	None.
22093955d011SMarcel Moolenaar  *
22103955d011SMarcel Moolenaar  * Side Effects:
22113955d011SMarcel Moolenaar  *	Another job is started.
22123955d011SMarcel Moolenaar  *
22133955d011SMarcel Moolenaar  *-----------------------------------------------------------------------
22143955d011SMarcel Moolenaar  */
22153955d011SMarcel Moolenaar void
22163955d011SMarcel Moolenaar Job_Make(GNode *gn)
22173955d011SMarcel Moolenaar {
22183955d011SMarcel Moolenaar     (void)JobStart(gn, 0);
22193955d011SMarcel Moolenaar }
22203955d011SMarcel Moolenaar 
22213955d011SMarcel Moolenaar void
22223955d011SMarcel Moolenaar Shell_Init(void)
22233955d011SMarcel Moolenaar {
22243955d011SMarcel Moolenaar     if (shellPath == NULL) {
22253955d011SMarcel Moolenaar 	/*
22263955d011SMarcel Moolenaar 	 * We are using the default shell, which may be an absolute
22273955d011SMarcel Moolenaar 	 * path if DEFSHELL_CUSTOM is defined.
22283955d011SMarcel Moolenaar 	 */
22293955d011SMarcel Moolenaar 	shellName = commandShell->name;
22303955d011SMarcel Moolenaar #ifdef DEFSHELL_CUSTOM
22313955d011SMarcel Moolenaar 	if (*shellName == '/') {
22323955d011SMarcel Moolenaar 	    shellPath = shellName;
22333955d011SMarcel Moolenaar 	    shellName = strrchr(shellPath, '/');
22343955d011SMarcel Moolenaar 	    shellName++;
22353955d011SMarcel Moolenaar 	} else
22363955d011SMarcel Moolenaar #endif
22373955d011SMarcel Moolenaar 	shellPath = str_concat(_PATH_DEFSHELLDIR, shellName, STR_ADDSLASH);
22383955d011SMarcel Moolenaar     }
22393955d011SMarcel Moolenaar     if (commandShell->exit == NULL) {
22403955d011SMarcel Moolenaar 	commandShell->exit = "";
22413955d011SMarcel Moolenaar     }
22423955d011SMarcel Moolenaar     if (commandShell->echo == NULL) {
22433955d011SMarcel Moolenaar 	commandShell->echo = "";
22443955d011SMarcel Moolenaar     }
224551ee2c1cSSimon J. Gerraty     if (commandShell->hasErrCtl && *commandShell->exit) {
224651ee2c1cSSimon J. Gerraty 	if (shellErrFlag &&
224751ee2c1cSSimon J. Gerraty 	    strcmp(commandShell->exit, &shellErrFlag[1]) != 0) {
224851ee2c1cSSimon J. Gerraty 	    free(shellErrFlag);
224951ee2c1cSSimon J. Gerraty 	    shellErrFlag = NULL;
225051ee2c1cSSimon J. Gerraty 	}
225151ee2c1cSSimon J. Gerraty 	if (!shellErrFlag) {
225251ee2c1cSSimon J. Gerraty 	    int n = strlen(commandShell->exit) + 2;
225351ee2c1cSSimon J. Gerraty 
225451ee2c1cSSimon J. Gerraty 	    shellErrFlag = bmake_malloc(n);
225551ee2c1cSSimon J. Gerraty 	    if (shellErrFlag) {
225651ee2c1cSSimon J. Gerraty 		snprintf(shellErrFlag, n, "-%s", commandShell->exit);
225751ee2c1cSSimon J. Gerraty 	    }
225851ee2c1cSSimon J. Gerraty 	}
225951ee2c1cSSimon J. Gerraty     } else if (shellErrFlag) {
226051ee2c1cSSimon J. Gerraty 	free(shellErrFlag);
226151ee2c1cSSimon J. Gerraty 	shellErrFlag = NULL;
226251ee2c1cSSimon J. Gerraty     }
22633955d011SMarcel Moolenaar }
22643955d011SMarcel Moolenaar 
22653955d011SMarcel Moolenaar /*-
22663955d011SMarcel Moolenaar  * Returns the string literal that is used in the current command shell
22673955d011SMarcel Moolenaar  * to produce a newline character.
22683955d011SMarcel Moolenaar  */
22693955d011SMarcel Moolenaar const char *
22703955d011SMarcel Moolenaar Shell_GetNewline(void)
22713955d011SMarcel Moolenaar {
22723955d011SMarcel Moolenaar 
22733955d011SMarcel Moolenaar     return commandShell->newline;
22743955d011SMarcel Moolenaar }
22753955d011SMarcel Moolenaar 
22763955d011SMarcel Moolenaar void
22773955d011SMarcel Moolenaar Job_SetPrefix(void)
22783955d011SMarcel Moolenaar {
22793955d011SMarcel Moolenaar 
22803955d011SMarcel Moolenaar     if (targPrefix) {
22813955d011SMarcel Moolenaar 	free(targPrefix);
22823955d011SMarcel Moolenaar     } else if (!Var_Exists(MAKE_JOB_PREFIX, VAR_GLOBAL)) {
22833955d011SMarcel Moolenaar 	Var_Set(MAKE_JOB_PREFIX, "---", VAR_GLOBAL, 0);
22843955d011SMarcel Moolenaar     }
22853955d011SMarcel Moolenaar 
22864c620fe5SSimon J. Gerraty     targPrefix = Var_Subst(NULL, "${" MAKE_JOB_PREFIX "}",
2287be19d90bSSimon J. Gerraty 			   VAR_GLOBAL, VARF_WANTRES);
22883955d011SMarcel Moolenaar }
22893955d011SMarcel Moolenaar 
22903955d011SMarcel Moolenaar /*-
22913955d011SMarcel Moolenaar  *-----------------------------------------------------------------------
22923955d011SMarcel Moolenaar  * Job_Init --
22933955d011SMarcel Moolenaar  *	Initialize the process module
22943955d011SMarcel Moolenaar  *
22953955d011SMarcel Moolenaar  * Input:
22963955d011SMarcel Moolenaar  *
22973955d011SMarcel Moolenaar  * Results:
22983955d011SMarcel Moolenaar  *	none
22993955d011SMarcel Moolenaar  *
23003955d011SMarcel Moolenaar  * Side Effects:
23013955d011SMarcel Moolenaar  *	lists and counters are initialized
23023955d011SMarcel Moolenaar  *-----------------------------------------------------------------------
23033955d011SMarcel Moolenaar  */
23043955d011SMarcel Moolenaar void
23053955d011SMarcel Moolenaar Job_Init(void)
23063955d011SMarcel Moolenaar {
2307d191243dSSimon J. Gerraty     Job_SetPrefix();
23083955d011SMarcel Moolenaar     /* Allocate space for all the job info */
23093955d011SMarcel Moolenaar     job_table = bmake_malloc(maxJobs * sizeof *job_table);
23103955d011SMarcel Moolenaar     memset(job_table, 0, maxJobs * sizeof *job_table);
23113955d011SMarcel Moolenaar     job_table_end = job_table + maxJobs;
23123955d011SMarcel Moolenaar     wantToken =	0;
23133955d011SMarcel Moolenaar 
23143955d011SMarcel Moolenaar     aborting = 	  0;
23153955d011SMarcel Moolenaar     errors = 	  0;
23163955d011SMarcel Moolenaar 
23173955d011SMarcel Moolenaar     lastNode =	  NULL;
23183955d011SMarcel Moolenaar 
23199a4bc556SSimon J. Gerraty     Always_pass_job_queue = getBoolean(MAKE_ALWAYS_PASS_JOB_QUEUE,
23209a4bc556SSimon J. Gerraty 				       Always_pass_job_queue);
23219a4bc556SSimon J. Gerraty 
23222d395cb5SSimon J. Gerraty     Job_error_token = getBoolean(MAKE_JOB_ERROR_TOKEN, Job_error_token);
23232d395cb5SSimon J. Gerraty 
23242d395cb5SSimon J. Gerraty 
23253955d011SMarcel Moolenaar     /*
23263955d011SMarcel Moolenaar      * There is a non-zero chance that we already have children.
23273955d011SMarcel Moolenaar      * eg after 'make -f- <<EOF'
23283955d011SMarcel Moolenaar      * Since their termination causes a 'Child (pid) not in table' message,
23293955d011SMarcel Moolenaar      * Collect the status of any that are already dead, and suppress the
23303955d011SMarcel Moolenaar      * error message if there are any undead ones.
23313955d011SMarcel Moolenaar      */
23323955d011SMarcel Moolenaar     for (;;) {
23333955d011SMarcel Moolenaar 	int rval, status;
23343955d011SMarcel Moolenaar 	rval = waitpid((pid_t) -1, &status, WNOHANG);
23353955d011SMarcel Moolenaar 	if (rval > 0)
23363955d011SMarcel Moolenaar 	    continue;
23373955d011SMarcel Moolenaar 	if (rval == 0)
23383955d011SMarcel Moolenaar 	    lurking_children = 1;
23393955d011SMarcel Moolenaar 	break;
23403955d011SMarcel Moolenaar     }
23413955d011SMarcel Moolenaar 
23423955d011SMarcel Moolenaar     Shell_Init();
23433955d011SMarcel Moolenaar 
23443955d011SMarcel Moolenaar     JobCreatePipe(&childExitJob, 3);
23453955d011SMarcel Moolenaar 
2346*49caa483SSimon J. Gerraty     /* Preallocate enough for the maximum number of jobs.  */
2347*49caa483SSimon J. Gerraty     fds = bmake_malloc(sizeof(*fds) *
2348*49caa483SSimon J. Gerraty 	(npseudojobs + maxJobs) * nfds_per_job());
2349*49caa483SSimon J. Gerraty     jobfds = bmake_malloc(sizeof(*jobfds) *
2350*49caa483SSimon J. Gerraty 	(npseudojobs + maxJobs) * nfds_per_job());
23513955d011SMarcel Moolenaar 
23523955d011SMarcel Moolenaar     /* These are permanent entries and take slots 0 and 1 */
23533955d011SMarcel Moolenaar     watchfd(&tokenWaitJob);
23543955d011SMarcel Moolenaar     watchfd(&childExitJob);
23553955d011SMarcel Moolenaar 
23563955d011SMarcel Moolenaar     sigemptyset(&caught_signals);
23573955d011SMarcel Moolenaar     /*
23583955d011SMarcel Moolenaar      * Install a SIGCHLD handler.
23593955d011SMarcel Moolenaar      */
23603955d011SMarcel Moolenaar     (void)bmake_signal(SIGCHLD, JobChildSig);
23613955d011SMarcel Moolenaar     sigaddset(&caught_signals, SIGCHLD);
23623955d011SMarcel Moolenaar 
23633955d011SMarcel Moolenaar #define ADDSIG(s,h)				\
23643955d011SMarcel Moolenaar     if (bmake_signal(s, SIG_IGN) != SIG_IGN) {	\
23653955d011SMarcel Moolenaar 	sigaddset(&caught_signals, s);		\
23663955d011SMarcel Moolenaar 	(void)bmake_signal(s, h);			\
23673955d011SMarcel Moolenaar     }
23683955d011SMarcel Moolenaar 
23693955d011SMarcel Moolenaar     /*
23703955d011SMarcel Moolenaar      * Catch the four signals that POSIX specifies if they aren't ignored.
23713955d011SMarcel Moolenaar      * JobPassSig will take care of calling JobInterrupt if appropriate.
23723955d011SMarcel Moolenaar      */
23733955d011SMarcel Moolenaar     ADDSIG(SIGINT, JobPassSig_int)
23743955d011SMarcel Moolenaar     ADDSIG(SIGHUP, JobPassSig_term)
23753955d011SMarcel Moolenaar     ADDSIG(SIGTERM, JobPassSig_term)
23763955d011SMarcel Moolenaar     ADDSIG(SIGQUIT, JobPassSig_term)
23773955d011SMarcel Moolenaar 
23783955d011SMarcel Moolenaar     /*
23793955d011SMarcel Moolenaar      * There are additional signals that need to be caught and passed if
23803955d011SMarcel Moolenaar      * either the export system wants to be told directly of signals or if
23813955d011SMarcel Moolenaar      * we're giving each job its own process group (since then it won't get
23823955d011SMarcel Moolenaar      * signals from the terminal driver as we own the terminal)
23833955d011SMarcel Moolenaar      */
23843955d011SMarcel Moolenaar     ADDSIG(SIGTSTP, JobPassSig_suspend)
23853955d011SMarcel Moolenaar     ADDSIG(SIGTTOU, JobPassSig_suspend)
23863955d011SMarcel Moolenaar     ADDSIG(SIGTTIN, JobPassSig_suspend)
23873955d011SMarcel Moolenaar     ADDSIG(SIGWINCH, JobCondPassSig)
23883955d011SMarcel Moolenaar     ADDSIG(SIGCONT, JobContinueSig)
23893955d011SMarcel Moolenaar #undef ADDSIG
23903955d011SMarcel Moolenaar 
23911748de26SSimon J. Gerraty     (void)Job_RunTarget(".BEGIN", NULL);
23923955d011SMarcel Moolenaar     postCommands = Targ_FindNode(".END", TARG_CREATE);
23933955d011SMarcel Moolenaar }
23943955d011SMarcel Moolenaar 
23953955d011SMarcel Moolenaar static void JobSigReset(void)
23963955d011SMarcel Moolenaar {
23973955d011SMarcel Moolenaar #define DELSIG(s)					\
23983955d011SMarcel Moolenaar     if (sigismember(&caught_signals, s)) {		\
23993955d011SMarcel Moolenaar 	(void)bmake_signal(s, SIG_DFL);			\
24003955d011SMarcel Moolenaar     }
24013955d011SMarcel Moolenaar 
24023955d011SMarcel Moolenaar     DELSIG(SIGINT)
24033955d011SMarcel Moolenaar     DELSIG(SIGHUP)
24043955d011SMarcel Moolenaar     DELSIG(SIGQUIT)
24053955d011SMarcel Moolenaar     DELSIG(SIGTERM)
24063955d011SMarcel Moolenaar     DELSIG(SIGTSTP)
24073955d011SMarcel Moolenaar     DELSIG(SIGTTOU)
24083955d011SMarcel Moolenaar     DELSIG(SIGTTIN)
24093955d011SMarcel Moolenaar     DELSIG(SIGWINCH)
24103955d011SMarcel Moolenaar     DELSIG(SIGCONT)
24113955d011SMarcel Moolenaar #undef DELSIG
24123955d011SMarcel Moolenaar     (void)bmake_signal(SIGCHLD, SIG_DFL);
24133955d011SMarcel Moolenaar }
24143955d011SMarcel Moolenaar 
24153955d011SMarcel Moolenaar /*-
24163955d011SMarcel Moolenaar  *-----------------------------------------------------------------------
24173955d011SMarcel Moolenaar  * JobMatchShell --
24183955d011SMarcel Moolenaar  *	Find a shell in 'shells' given its name.
24193955d011SMarcel Moolenaar  *
24203955d011SMarcel Moolenaar  * Results:
24213955d011SMarcel Moolenaar  *	A pointer to the Shell structure.
24223955d011SMarcel Moolenaar  *
24233955d011SMarcel Moolenaar  * Side Effects:
24243955d011SMarcel Moolenaar  *	None.
24253955d011SMarcel Moolenaar  *
24263955d011SMarcel Moolenaar  *-----------------------------------------------------------------------
24273955d011SMarcel Moolenaar  */
24283955d011SMarcel Moolenaar static Shell *
24293955d011SMarcel Moolenaar JobMatchShell(const char *name)
24303955d011SMarcel Moolenaar {
24313955d011SMarcel Moolenaar     Shell	*sh;
24323955d011SMarcel Moolenaar 
24333955d011SMarcel Moolenaar     for (sh = shells; sh->name != NULL; sh++) {
24343955d011SMarcel Moolenaar 	if (strcmp(name, sh->name) == 0)
24353955d011SMarcel Moolenaar 		return (sh);
24363955d011SMarcel Moolenaar     }
24373955d011SMarcel Moolenaar     return NULL;
24383955d011SMarcel Moolenaar }
24393955d011SMarcel Moolenaar 
24403955d011SMarcel Moolenaar /*-
24413955d011SMarcel Moolenaar  *-----------------------------------------------------------------------
24423955d011SMarcel Moolenaar  * Job_ParseShell --
24433955d011SMarcel Moolenaar  *	Parse a shell specification and set up commandShell, shellPath
24443955d011SMarcel Moolenaar  *	and shellName appropriately.
24453955d011SMarcel Moolenaar  *
24463955d011SMarcel Moolenaar  * Input:
24473955d011SMarcel Moolenaar  *	line		The shell spec
24483955d011SMarcel Moolenaar  *
24493955d011SMarcel Moolenaar  * Results:
24503955d011SMarcel Moolenaar  *	FAILURE if the specification was incorrect.
24513955d011SMarcel Moolenaar  *
24523955d011SMarcel Moolenaar  * Side Effects:
24533955d011SMarcel Moolenaar  *	commandShell points to a Shell structure (either predefined or
24543955d011SMarcel Moolenaar  *	created from the shell spec), shellPath is the full path of the
24553955d011SMarcel Moolenaar  *	shell described by commandShell, while shellName is just the
24563955d011SMarcel Moolenaar  *	final component of shellPath.
24573955d011SMarcel Moolenaar  *
24583955d011SMarcel Moolenaar  * Notes:
24593955d011SMarcel Moolenaar  *	A shell specification consists of a .SHELL target, with dependency
24603955d011SMarcel Moolenaar  *	operator, followed by a series of blank-separated words. Double
24613955d011SMarcel Moolenaar  *	quotes can be used to use blanks in words. A backslash escapes
24623955d011SMarcel Moolenaar  *	anything (most notably a double-quote and a space) and
24633955d011SMarcel Moolenaar  *	provides the functionality it does in C. Each word consists of
24643955d011SMarcel Moolenaar  *	keyword and value separated by an equal sign. There should be no
24653955d011SMarcel Moolenaar  *	unnecessary spaces in the word. The keywords are as follows:
24663955d011SMarcel Moolenaar  *	    name  	    Name of shell.
24673955d011SMarcel Moolenaar  *	    path  	    Location of shell.
24683955d011SMarcel Moolenaar  *	    quiet 	    Command to turn off echoing.
24693955d011SMarcel Moolenaar  *	    echo  	    Command to turn echoing on
24703955d011SMarcel Moolenaar  *	    filter	    Result of turning off echoing that shouldn't be
24713955d011SMarcel Moolenaar  *	    	  	    printed.
24723955d011SMarcel Moolenaar  *	    echoFlag	    Flag to turn echoing on at the start
24733955d011SMarcel Moolenaar  *	    errFlag	    Flag to turn error checking on at the start
24743955d011SMarcel Moolenaar  *	    hasErrCtl	    True if shell has error checking control
24753955d011SMarcel Moolenaar  *	    newline	    String literal to represent a newline char
24763955d011SMarcel Moolenaar  *	    check 	    Command to turn on error checking if hasErrCtl
24773955d011SMarcel Moolenaar  *	    	  	    is TRUE or template of command to echo a command
24783955d011SMarcel Moolenaar  *	    	  	    for which error checking is off if hasErrCtl is
24793955d011SMarcel Moolenaar  *	    	  	    FALSE.
24803955d011SMarcel Moolenaar  *	    ignore	    Command to turn off error checking if hasErrCtl
24813955d011SMarcel Moolenaar  *	    	  	    is TRUE or template of command to execute a
24823955d011SMarcel Moolenaar  *	    	  	    command so as to ignore any errors it returns if
24833955d011SMarcel Moolenaar  *	    	  	    hasErrCtl is FALSE.
24843955d011SMarcel Moolenaar  *
24853955d011SMarcel Moolenaar  *-----------------------------------------------------------------------
24863955d011SMarcel Moolenaar  */
24873955d011SMarcel Moolenaar ReturnStatus
24883955d011SMarcel Moolenaar Job_ParseShell(char *line)
24893955d011SMarcel Moolenaar {
24903955d011SMarcel Moolenaar     char	**words;
24913955d011SMarcel Moolenaar     char	**argv;
24923955d011SMarcel Moolenaar     int		argc;
24933955d011SMarcel Moolenaar     char	*path;
24943955d011SMarcel Moolenaar     Shell	newShell;
24953955d011SMarcel Moolenaar     Boolean	fullSpec = FALSE;
24963955d011SMarcel Moolenaar     Shell	*sh;
24973955d011SMarcel Moolenaar 
24983955d011SMarcel Moolenaar     while (isspace((unsigned char)*line)) {
24993955d011SMarcel Moolenaar 	line++;
25003955d011SMarcel Moolenaar     }
25013955d011SMarcel Moolenaar 
25023955d011SMarcel Moolenaar     free(UNCONST(shellArgv));
25033955d011SMarcel Moolenaar 
25043955d011SMarcel Moolenaar     memset(&newShell, 0, sizeof(newShell));
25053955d011SMarcel Moolenaar 
25063955d011SMarcel Moolenaar     /*
25073955d011SMarcel Moolenaar      * Parse the specification by keyword
25083955d011SMarcel Moolenaar      */
25093955d011SMarcel Moolenaar     words = brk_string(line, &argc, TRUE, &path);
25103955d011SMarcel Moolenaar     if (words == NULL) {
25113955d011SMarcel Moolenaar 	Error("Unterminated quoted string [%s]", line);
25123955d011SMarcel Moolenaar 	return FAILURE;
25133955d011SMarcel Moolenaar     }
25143955d011SMarcel Moolenaar     shellArgv = path;
25153955d011SMarcel Moolenaar 
25163955d011SMarcel Moolenaar     for (path = NULL, argv = words; argc != 0; argc--, argv++) {
25173955d011SMarcel Moolenaar 	    if (strncmp(*argv, "path=", 5) == 0) {
25183955d011SMarcel Moolenaar 		path = &argv[0][5];
25193955d011SMarcel Moolenaar 	    } else if (strncmp(*argv, "name=", 5) == 0) {
25203955d011SMarcel Moolenaar 		newShell.name = &argv[0][5];
25213955d011SMarcel Moolenaar 	    } else {
25223955d011SMarcel Moolenaar 		if (strncmp(*argv, "quiet=", 6) == 0) {
25233955d011SMarcel Moolenaar 		    newShell.echoOff = &argv[0][6];
25243955d011SMarcel Moolenaar 		} else if (strncmp(*argv, "echo=", 5) == 0) {
25253955d011SMarcel Moolenaar 		    newShell.echoOn = &argv[0][5];
25263955d011SMarcel Moolenaar 		} else if (strncmp(*argv, "filter=", 7) == 0) {
25273955d011SMarcel Moolenaar 		    newShell.noPrint = &argv[0][7];
25283955d011SMarcel Moolenaar 		    newShell.noPLen = strlen(newShell.noPrint);
25293955d011SMarcel Moolenaar 		} else if (strncmp(*argv, "echoFlag=", 9) == 0) {
25303955d011SMarcel Moolenaar 		    newShell.echo = &argv[0][9];
25313955d011SMarcel Moolenaar 		} else if (strncmp(*argv, "errFlag=", 8) == 0) {
25323955d011SMarcel Moolenaar 		    newShell.exit = &argv[0][8];
25333955d011SMarcel Moolenaar 		} else if (strncmp(*argv, "hasErrCtl=", 10) == 0) {
25343955d011SMarcel Moolenaar 		    char c = argv[0][10];
25353955d011SMarcel Moolenaar 		    newShell.hasErrCtl = !((c != 'Y') && (c != 'y') &&
25363955d011SMarcel Moolenaar 					   (c != 'T') && (c != 't'));
25373955d011SMarcel Moolenaar 		} else if (strncmp(*argv, "newline=", 8) == 0) {
25383955d011SMarcel Moolenaar 		    newShell.newline = &argv[0][8];
25393955d011SMarcel Moolenaar 		} else if (strncmp(*argv, "check=", 6) == 0) {
25403955d011SMarcel Moolenaar 		    newShell.errCheck = &argv[0][6];
25413955d011SMarcel Moolenaar 		} else if (strncmp(*argv, "ignore=", 7) == 0) {
25423955d011SMarcel Moolenaar 		    newShell.ignErr = &argv[0][7];
25433955d011SMarcel Moolenaar 		} else if (strncmp(*argv, "errout=", 7) == 0) {
25443955d011SMarcel Moolenaar 		    newShell.errOut = &argv[0][7];
25453955d011SMarcel Moolenaar 		} else if (strncmp(*argv, "comment=", 8) == 0) {
25463955d011SMarcel Moolenaar 		    newShell.commentChar = argv[0][8];
25473955d011SMarcel Moolenaar 		} else {
25483955d011SMarcel Moolenaar 		    Parse_Error(PARSE_FATAL, "Unknown keyword \"%s\"",
25493955d011SMarcel Moolenaar 				*argv);
25503955d011SMarcel Moolenaar 		    free(words);
25513955d011SMarcel Moolenaar 		    return(FAILURE);
25523955d011SMarcel Moolenaar 		}
25533955d011SMarcel Moolenaar 		fullSpec = TRUE;
25543955d011SMarcel Moolenaar 	    }
25553955d011SMarcel Moolenaar     }
25563955d011SMarcel Moolenaar 
25573955d011SMarcel Moolenaar     if (path == NULL) {
25583955d011SMarcel Moolenaar 	/*
25593955d011SMarcel Moolenaar 	 * If no path was given, the user wants one of the pre-defined shells,
25603955d011SMarcel Moolenaar 	 * yes? So we find the one s/he wants with the help of JobMatchShell
25613955d011SMarcel Moolenaar 	 * and set things up the right way. shellPath will be set up by
25623955d011SMarcel Moolenaar 	 * Shell_Init.
25633955d011SMarcel Moolenaar 	 */
25643955d011SMarcel Moolenaar 	if (newShell.name == NULL) {
25653955d011SMarcel Moolenaar 	    Parse_Error(PARSE_FATAL, "Neither path nor name specified");
25663955d011SMarcel Moolenaar 	    free(words);
25673955d011SMarcel Moolenaar 	    return(FAILURE);
25683955d011SMarcel Moolenaar 	} else {
25693955d011SMarcel Moolenaar 	    if ((sh = JobMatchShell(newShell.name)) == NULL) {
25703955d011SMarcel Moolenaar 		    Parse_Error(PARSE_WARNING, "%s: No matching shell",
25713955d011SMarcel Moolenaar 				newShell.name);
25723955d011SMarcel Moolenaar 		    free(words);
25733955d011SMarcel Moolenaar 		    return(FAILURE);
25743955d011SMarcel Moolenaar 	    }
25753955d011SMarcel Moolenaar 	    commandShell = sh;
25763955d011SMarcel Moolenaar 	    shellName = newShell.name;
25773955d011SMarcel Moolenaar 	    if (shellPath) {
25783955d011SMarcel Moolenaar 		/* Shell_Init has already been called!  Do it again. */
25793955d011SMarcel Moolenaar 		free(UNCONST(shellPath));
25803955d011SMarcel Moolenaar 		shellPath = NULL;
25813955d011SMarcel Moolenaar 		Shell_Init();
25823955d011SMarcel Moolenaar 	    }
25833955d011SMarcel Moolenaar 	}
25843955d011SMarcel Moolenaar     } else {
25853955d011SMarcel Moolenaar 	/*
25863955d011SMarcel Moolenaar 	 * The user provided a path. If s/he gave nothing else (fullSpec is
25873955d011SMarcel Moolenaar 	 * FALSE), try and find a matching shell in the ones we know of.
25883955d011SMarcel Moolenaar 	 * Else we just take the specification at its word and copy it
25893955d011SMarcel Moolenaar 	 * to a new location. In either case, we need to record the
25903955d011SMarcel Moolenaar 	 * path the user gave for the shell.
25913955d011SMarcel Moolenaar 	 */
25923955d011SMarcel Moolenaar 	shellPath = path;
25933955d011SMarcel Moolenaar 	path = strrchr(path, '/');
25943955d011SMarcel Moolenaar 	if (path == NULL) {
25953955d011SMarcel Moolenaar 	    path = UNCONST(shellPath);
25963955d011SMarcel Moolenaar 	} else {
25973955d011SMarcel Moolenaar 	    path += 1;
25983955d011SMarcel Moolenaar 	}
25993955d011SMarcel Moolenaar 	if (newShell.name != NULL) {
26003955d011SMarcel Moolenaar 	    shellName = newShell.name;
26013955d011SMarcel Moolenaar 	} else {
26023955d011SMarcel Moolenaar 	    shellName = path;
26033955d011SMarcel Moolenaar 	}
26043955d011SMarcel Moolenaar 	if (!fullSpec) {
26053955d011SMarcel Moolenaar 	    if ((sh = JobMatchShell(shellName)) == NULL) {
26063955d011SMarcel Moolenaar 		    Parse_Error(PARSE_WARNING, "%s: No matching shell",
26073955d011SMarcel Moolenaar 				shellName);
26083955d011SMarcel Moolenaar 		    free(words);
26093955d011SMarcel Moolenaar 		    return(FAILURE);
26103955d011SMarcel Moolenaar 	    }
26113955d011SMarcel Moolenaar 	    commandShell = sh;
26123955d011SMarcel Moolenaar 	} else {
26133955d011SMarcel Moolenaar 	    commandShell = bmake_malloc(sizeof(Shell));
26143955d011SMarcel Moolenaar 	    *commandShell = newShell;
26153955d011SMarcel Moolenaar 	}
261651ee2c1cSSimon J. Gerraty 	/* this will take care of shellErrFlag */
261751ee2c1cSSimon J. Gerraty 	Shell_Init();
26183955d011SMarcel Moolenaar     }
26193955d011SMarcel Moolenaar 
26203955d011SMarcel Moolenaar     if (commandShell->echoOn && commandShell->echoOff) {
26213955d011SMarcel Moolenaar 	commandShell->hasEchoCtl = TRUE;
26223955d011SMarcel Moolenaar     }
26233955d011SMarcel Moolenaar 
26243955d011SMarcel Moolenaar     if (!commandShell->hasErrCtl) {
26253955d011SMarcel Moolenaar 	if (commandShell->errCheck == NULL) {
26263955d011SMarcel Moolenaar 	    commandShell->errCheck = "";
26273955d011SMarcel Moolenaar 	}
26283955d011SMarcel Moolenaar 	if (commandShell->ignErr == NULL) {
26293955d011SMarcel Moolenaar 	    commandShell->ignErr = "%s\n";
26303955d011SMarcel Moolenaar 	}
26313955d011SMarcel Moolenaar     }
26323955d011SMarcel Moolenaar 
26333955d011SMarcel Moolenaar     /*
26343955d011SMarcel Moolenaar      * Do not free up the words themselves, since they might be in use by the
26353955d011SMarcel Moolenaar      * shell specification.
26363955d011SMarcel Moolenaar      */
26373955d011SMarcel Moolenaar     free(words);
26383955d011SMarcel Moolenaar     return SUCCESS;
26393955d011SMarcel Moolenaar }
26403955d011SMarcel Moolenaar 
26413955d011SMarcel Moolenaar /*-
26423955d011SMarcel Moolenaar  *-----------------------------------------------------------------------
26433955d011SMarcel Moolenaar  * JobInterrupt --
26443955d011SMarcel Moolenaar  *	Handle the receipt of an interrupt.
26453955d011SMarcel Moolenaar  *
26463955d011SMarcel Moolenaar  * Input:
26473955d011SMarcel Moolenaar  *	runINTERRUPT	Non-zero if commands for the .INTERRUPT target
26483955d011SMarcel Moolenaar  *			should be executed
26493955d011SMarcel Moolenaar  *	signo		signal received
26503955d011SMarcel Moolenaar  *
26513955d011SMarcel Moolenaar  * Results:
26523955d011SMarcel Moolenaar  *	None
26533955d011SMarcel Moolenaar  *
26543955d011SMarcel Moolenaar  * Side Effects:
26553955d011SMarcel Moolenaar  *	All children are killed. Another job will be started if the
26563955d011SMarcel Moolenaar  *	.INTERRUPT target was given.
26573955d011SMarcel Moolenaar  *-----------------------------------------------------------------------
26583955d011SMarcel Moolenaar  */
26593955d011SMarcel Moolenaar static void
26603955d011SMarcel Moolenaar JobInterrupt(int runINTERRUPT, int signo)
26613955d011SMarcel Moolenaar {
26623955d011SMarcel Moolenaar     Job		*job;		/* job descriptor in that element */
26633955d011SMarcel Moolenaar     GNode	*interrupt;	/* the node describing the .INTERRUPT target */
26643955d011SMarcel Moolenaar     sigset_t	mask;
26653955d011SMarcel Moolenaar     GNode	*gn;
26663955d011SMarcel Moolenaar 
26673955d011SMarcel Moolenaar     aborting = ABORT_INTERRUPT;
26683955d011SMarcel Moolenaar 
26693955d011SMarcel Moolenaar     JobSigLock(&mask);
26703955d011SMarcel Moolenaar 
26713955d011SMarcel Moolenaar     for (job = job_table; job < job_table_end; job++) {
26723955d011SMarcel Moolenaar 	if (job->job_state != JOB_ST_RUNNING)
26733955d011SMarcel Moolenaar 	    continue;
26743955d011SMarcel Moolenaar 
26753955d011SMarcel Moolenaar 	gn = job->node;
26763955d011SMarcel Moolenaar 
267745447996SSimon J. Gerraty 	JobDeleteTarget(gn);
26783955d011SMarcel Moolenaar 	if (job->pid) {
26793955d011SMarcel Moolenaar 	    if (DEBUG(JOB)) {
26803955d011SMarcel Moolenaar 		(void)fprintf(debug_file,
26813955d011SMarcel Moolenaar 			   "JobInterrupt passing signal %d to child %d.\n",
26823955d011SMarcel Moolenaar 			   signo, job->pid);
26833955d011SMarcel Moolenaar 	    }
26843955d011SMarcel Moolenaar 	    KILLPG(job->pid, signo);
26853955d011SMarcel Moolenaar 	}
26863955d011SMarcel Moolenaar     }
26873955d011SMarcel Moolenaar 
26883955d011SMarcel Moolenaar     JobSigUnlock(&mask);
26893955d011SMarcel Moolenaar 
26903955d011SMarcel Moolenaar     if (runINTERRUPT && !touchFlag) {
26913955d011SMarcel Moolenaar 	interrupt = Targ_FindNode(".INTERRUPT", TARG_NOCREATE);
26923955d011SMarcel Moolenaar 	if (interrupt != NULL) {
26933955d011SMarcel Moolenaar 	    ignoreErrors = FALSE;
26943955d011SMarcel Moolenaar 	    JobRun(interrupt);
26953955d011SMarcel Moolenaar 	}
26963955d011SMarcel Moolenaar     }
26973955d011SMarcel Moolenaar     Trace_Log(MAKEINTR, 0);
26983955d011SMarcel Moolenaar     exit(signo);
26993955d011SMarcel Moolenaar }
27003955d011SMarcel Moolenaar 
27013955d011SMarcel Moolenaar /*
27023955d011SMarcel Moolenaar  *-----------------------------------------------------------------------
27033955d011SMarcel Moolenaar  * Job_Finish --
27043955d011SMarcel Moolenaar  *	Do final processing such as the running of the commands
27053955d011SMarcel Moolenaar  *	attached to the .END target.
27063955d011SMarcel Moolenaar  *
27073955d011SMarcel Moolenaar  * Results:
27083955d011SMarcel Moolenaar  *	Number of errors reported.
27093955d011SMarcel Moolenaar  *
27103955d011SMarcel Moolenaar  * Side Effects:
27113955d011SMarcel Moolenaar  *	None.
27123955d011SMarcel Moolenaar  *-----------------------------------------------------------------------
27133955d011SMarcel Moolenaar  */
27143955d011SMarcel Moolenaar int
27153955d011SMarcel Moolenaar Job_Finish(void)
27163955d011SMarcel Moolenaar {
27173955d011SMarcel Moolenaar     if (postCommands != NULL &&
27183955d011SMarcel Moolenaar 	(!Lst_IsEmpty(postCommands->commands) ||
27193955d011SMarcel Moolenaar 	 !Lst_IsEmpty(postCommands->children))) {
27203955d011SMarcel Moolenaar 	if (errors) {
27213955d011SMarcel Moolenaar 	    Error("Errors reported so .END ignored");
27223955d011SMarcel Moolenaar 	} else {
27233955d011SMarcel Moolenaar 	    JobRun(postCommands);
27243955d011SMarcel Moolenaar 	}
27253955d011SMarcel Moolenaar     }
27263955d011SMarcel Moolenaar     return(errors);
27273955d011SMarcel Moolenaar }
27283955d011SMarcel Moolenaar 
27293955d011SMarcel Moolenaar /*-
27303955d011SMarcel Moolenaar  *-----------------------------------------------------------------------
27313955d011SMarcel Moolenaar  * Job_End --
27323955d011SMarcel Moolenaar  *	Cleanup any memory used by the jobs module
27333955d011SMarcel Moolenaar  *
27343955d011SMarcel Moolenaar  * Results:
27353955d011SMarcel Moolenaar  *	None.
27363955d011SMarcel Moolenaar  *
27373955d011SMarcel Moolenaar  * Side Effects:
27383955d011SMarcel Moolenaar  *	Memory is freed
27393955d011SMarcel Moolenaar  *-----------------------------------------------------------------------
27403955d011SMarcel Moolenaar  */
27413955d011SMarcel Moolenaar void
27423955d011SMarcel Moolenaar Job_End(void)
27433955d011SMarcel Moolenaar {
27443955d011SMarcel Moolenaar #ifdef CLEANUP
27453955d011SMarcel Moolenaar     free(shellArgv);
27463955d011SMarcel Moolenaar #endif
27473955d011SMarcel Moolenaar }
27483955d011SMarcel Moolenaar 
27493955d011SMarcel Moolenaar /*-
27503955d011SMarcel Moolenaar  *-----------------------------------------------------------------------
27513955d011SMarcel Moolenaar  * Job_Wait --
27523955d011SMarcel Moolenaar  *	Waits for all running jobs to finish and returns. Sets 'aborting'
27533955d011SMarcel Moolenaar  *	to ABORT_WAIT to prevent other jobs from starting.
27543955d011SMarcel Moolenaar  *
27553955d011SMarcel Moolenaar  * Results:
27563955d011SMarcel Moolenaar  *	None.
27573955d011SMarcel Moolenaar  *
27583955d011SMarcel Moolenaar  * Side Effects:
27593955d011SMarcel Moolenaar  *	Currently running jobs finish.
27603955d011SMarcel Moolenaar  *
27613955d011SMarcel Moolenaar  *-----------------------------------------------------------------------
27623955d011SMarcel Moolenaar  */
27633955d011SMarcel Moolenaar void
27643955d011SMarcel Moolenaar Job_Wait(void)
27653955d011SMarcel Moolenaar {
27663955d011SMarcel Moolenaar     aborting = ABORT_WAIT;
27673955d011SMarcel Moolenaar     while (jobTokensRunning != 0) {
27683955d011SMarcel Moolenaar 	Job_CatchOutput();
27693955d011SMarcel Moolenaar     }
27703955d011SMarcel Moolenaar     aborting = 0;
27713955d011SMarcel Moolenaar }
27723955d011SMarcel Moolenaar 
27733955d011SMarcel Moolenaar /*-
27743955d011SMarcel Moolenaar  *-----------------------------------------------------------------------
27753955d011SMarcel Moolenaar  * Job_AbortAll --
27763955d011SMarcel Moolenaar  *	Abort all currently running jobs without handling output or anything.
27773955d011SMarcel Moolenaar  *	This function is to be called only in the event of a major
27783955d011SMarcel Moolenaar  *	error. Most definitely NOT to be called from JobInterrupt.
27793955d011SMarcel Moolenaar  *
27803955d011SMarcel Moolenaar  * Results:
27813955d011SMarcel Moolenaar  *	None
27823955d011SMarcel Moolenaar  *
27833955d011SMarcel Moolenaar  * Side Effects:
27843955d011SMarcel Moolenaar  *	All children are killed, not just the firstborn
27853955d011SMarcel Moolenaar  *-----------------------------------------------------------------------
27863955d011SMarcel Moolenaar  */
27873955d011SMarcel Moolenaar void
27883955d011SMarcel Moolenaar Job_AbortAll(void)
27893955d011SMarcel Moolenaar {
27903955d011SMarcel Moolenaar     Job		*job;	/* the job descriptor in that element */
27913955d011SMarcel Moolenaar     WAIT_T	foo;
27923955d011SMarcel Moolenaar 
27933955d011SMarcel Moolenaar     aborting = ABORT_ERROR;
27943955d011SMarcel Moolenaar 
27953955d011SMarcel Moolenaar     if (jobTokensRunning) {
27963955d011SMarcel Moolenaar 	for (job = job_table; job < job_table_end; job++) {
27973955d011SMarcel Moolenaar 	    if (job->job_state != JOB_ST_RUNNING)
27983955d011SMarcel Moolenaar 		continue;
27993955d011SMarcel Moolenaar 	    /*
28003955d011SMarcel Moolenaar 	     * kill the child process with increasingly drastic signals to make
28013955d011SMarcel Moolenaar 	     * darn sure it's dead.
28023955d011SMarcel Moolenaar 	     */
28033955d011SMarcel Moolenaar 	    KILLPG(job->pid, SIGINT);
28043955d011SMarcel Moolenaar 	    KILLPG(job->pid, SIGKILL);
28053955d011SMarcel Moolenaar 	}
28063955d011SMarcel Moolenaar     }
28073955d011SMarcel Moolenaar 
28083955d011SMarcel Moolenaar     /*
28093955d011SMarcel Moolenaar      * Catch as many children as want to report in at first, then give up
28103955d011SMarcel Moolenaar      */
28113955d011SMarcel Moolenaar     while (waitpid((pid_t) -1, &foo, WNOHANG) > 0)
28123955d011SMarcel Moolenaar 	continue;
28133955d011SMarcel Moolenaar }
28143955d011SMarcel Moolenaar 
28153955d011SMarcel Moolenaar 
28163955d011SMarcel Moolenaar /*-
28173955d011SMarcel Moolenaar  *-----------------------------------------------------------------------
28183955d011SMarcel Moolenaar  * JobRestartJobs --
28193955d011SMarcel Moolenaar  *	Tries to restart stopped jobs if there are slots available.
28203955d011SMarcel Moolenaar  *	Called in process context in response to a SIGCONT.
28213955d011SMarcel Moolenaar  *
28223955d011SMarcel Moolenaar  * Results:
28233955d011SMarcel Moolenaar  *	None.
28243955d011SMarcel Moolenaar  *
28253955d011SMarcel Moolenaar  * Side Effects:
28263955d011SMarcel Moolenaar  *	Resumes jobs.
28273955d011SMarcel Moolenaar  *
28283955d011SMarcel Moolenaar  *-----------------------------------------------------------------------
28293955d011SMarcel Moolenaar  */
28303955d011SMarcel Moolenaar static void
28313955d011SMarcel Moolenaar JobRestartJobs(void)
28323955d011SMarcel Moolenaar {
28333955d011SMarcel Moolenaar     Job *job;
28343955d011SMarcel Moolenaar 
28353955d011SMarcel Moolenaar     for (job = job_table; job < job_table_end; job++) {
28363955d011SMarcel Moolenaar 	if (job->job_state == JOB_ST_RUNNING &&
28373955d011SMarcel Moolenaar 		(make_suspended || job->job_suspended)) {
28383955d011SMarcel Moolenaar 	    if (DEBUG(JOB)) {
28393955d011SMarcel Moolenaar 		(void)fprintf(debug_file, "Restarting stopped job pid %d.\n",
28403955d011SMarcel Moolenaar 			job->pid);
28413955d011SMarcel Moolenaar 	    }
28423955d011SMarcel Moolenaar 	    if (job->job_suspended) {
28433955d011SMarcel Moolenaar 		    (void)printf("*** [%s] Continued\n", job->node->name);
28443955d011SMarcel Moolenaar 		    (void)fflush(stdout);
28453955d011SMarcel Moolenaar 	    }
28463955d011SMarcel Moolenaar 	    job->job_suspended = 0;
28473955d011SMarcel Moolenaar 	    if (KILLPG(job->pid, SIGCONT) != 0 && DEBUG(JOB)) {
28483955d011SMarcel Moolenaar 		fprintf(debug_file, "Failed to send SIGCONT to %d\n", job->pid);
28493955d011SMarcel Moolenaar 	    }
28503955d011SMarcel Moolenaar 	}
28513955d011SMarcel Moolenaar 	if (job->job_state == JOB_ST_FINISHED)
28523955d011SMarcel Moolenaar 	    /* Job exit deferred after calling waitpid() in a signal handler */
28533955d011SMarcel Moolenaar 	    JobFinish(job, job->exit_status);
28543955d011SMarcel Moolenaar     }
28553955d011SMarcel Moolenaar     make_suspended = 0;
28563955d011SMarcel Moolenaar }
28573955d011SMarcel Moolenaar 
28583955d011SMarcel Moolenaar static void
28593955d011SMarcel Moolenaar watchfd(Job *job)
28603955d011SMarcel Moolenaar {
28613955d011SMarcel Moolenaar     if (job->inPollfd != NULL)
28623955d011SMarcel Moolenaar 	Punt("Watching watched job");
28633955d011SMarcel Moolenaar 
28643955d011SMarcel Moolenaar     fds[nfds].fd = job->inPipe;
28653955d011SMarcel Moolenaar     fds[nfds].events = POLLIN;
28663955d011SMarcel Moolenaar     jobfds[nfds] = job;
28673955d011SMarcel Moolenaar     job->inPollfd = &fds[nfds];
28683955d011SMarcel Moolenaar     nfds++;
2869*49caa483SSimon J. Gerraty #if defined(USE_FILEMON) && !defined(USE_FILEMON_DEV)
2870*49caa483SSimon J. Gerraty     if (useMeta) {
2871*49caa483SSimon J. Gerraty 	fds[nfds].fd = meta_job_fd(job);
2872*49caa483SSimon J. Gerraty 	fds[nfds].events = fds[nfds].fd == -1 ? 0 : POLLIN;
2873*49caa483SSimon J. Gerraty 	jobfds[nfds] = job;
2874*49caa483SSimon J. Gerraty 	nfds++;
2875*49caa483SSimon J. Gerraty     }
2876*49caa483SSimon J. Gerraty #endif
28773955d011SMarcel Moolenaar }
28783955d011SMarcel Moolenaar 
28793955d011SMarcel Moolenaar static void
28803955d011SMarcel Moolenaar clearfd(Job *job)
28813955d011SMarcel Moolenaar {
28823955d011SMarcel Moolenaar     int i;
28833955d011SMarcel Moolenaar     if (job->inPollfd == NULL)
28843955d011SMarcel Moolenaar 	Punt("Unwatching unwatched job");
28853955d011SMarcel Moolenaar     i = job->inPollfd - fds;
28863955d011SMarcel Moolenaar     nfds--;
2887*49caa483SSimon J. Gerraty #if defined(USE_FILEMON) && !defined(USE_FILEMON_DEV)
2888*49caa483SSimon J. Gerraty     if (useMeta) {
2889*49caa483SSimon J. Gerraty 	/*
2890*49caa483SSimon J. Gerraty 	 * Sanity check: there should be two fds per job, so the job's
2891*49caa483SSimon J. Gerraty 	 * pollfd number should be even.
2892*49caa483SSimon J. Gerraty 	 */
2893*49caa483SSimon J. Gerraty 	assert(nfds_per_job() == 2);
2894*49caa483SSimon J. Gerraty 	if (i % 2)
2895*49caa483SSimon J. Gerraty 	    Punt("odd-numbered fd with meta");
2896*49caa483SSimon J. Gerraty 	nfds--;
2897*49caa483SSimon J. Gerraty     }
2898*49caa483SSimon J. Gerraty #endif
28993955d011SMarcel Moolenaar     /*
29003955d011SMarcel Moolenaar      * Move last job in table into hole made by dead job.
29013955d011SMarcel Moolenaar      */
29023955d011SMarcel Moolenaar     if (nfds != i) {
29033955d011SMarcel Moolenaar 	fds[i] = fds[nfds];
29043955d011SMarcel Moolenaar 	jobfds[i] = jobfds[nfds];
29053955d011SMarcel Moolenaar 	jobfds[i]->inPollfd = &fds[i];
2906*49caa483SSimon J. Gerraty #if defined(USE_FILEMON) && !defined(USE_FILEMON_DEV)
2907*49caa483SSimon J. Gerraty 	if (useMeta) {
2908*49caa483SSimon J. Gerraty 	    fds[i + 1] = fds[nfds + 1];
2909*49caa483SSimon J. Gerraty 	    jobfds[i + 1] = jobfds[nfds + 1];
2910*49caa483SSimon J. Gerraty 	}
2911*49caa483SSimon J. Gerraty #endif
29123955d011SMarcel Moolenaar     }
29133955d011SMarcel Moolenaar     job->inPollfd = NULL;
29143955d011SMarcel Moolenaar }
29153955d011SMarcel Moolenaar 
29163955d011SMarcel Moolenaar static int
29173955d011SMarcel Moolenaar readyfd(Job *job)
29183955d011SMarcel Moolenaar {
29193955d011SMarcel Moolenaar     if (job->inPollfd == NULL)
29203955d011SMarcel Moolenaar 	Punt("Polling unwatched job");
29213955d011SMarcel Moolenaar     return (job->inPollfd->revents & POLLIN) != 0;
29223955d011SMarcel Moolenaar }
29233955d011SMarcel Moolenaar 
29243955d011SMarcel Moolenaar /*-
29253955d011SMarcel Moolenaar  *-----------------------------------------------------------------------
29263955d011SMarcel Moolenaar  * JobTokenAdd --
29273955d011SMarcel Moolenaar  *	Put a token into the job pipe so that some make process can start
29283955d011SMarcel Moolenaar  *	another job.
29293955d011SMarcel Moolenaar  *
29303955d011SMarcel Moolenaar  * Side Effects:
29313955d011SMarcel Moolenaar  *	Allows more build jobs to be spawned somewhere.
29323955d011SMarcel Moolenaar  *
29333955d011SMarcel Moolenaar  *-----------------------------------------------------------------------
29343955d011SMarcel Moolenaar  */
29353955d011SMarcel Moolenaar 
29363955d011SMarcel Moolenaar static void
29373955d011SMarcel Moolenaar JobTokenAdd(void)
29383955d011SMarcel Moolenaar {
29393955d011SMarcel Moolenaar     char tok = JOB_TOKENS[aborting], tok1;
29403955d011SMarcel Moolenaar 
29412d395cb5SSimon J. Gerraty     if (!Job_error_token && aborting == ABORT_ERROR) {
29422d395cb5SSimon J. Gerraty 	if (jobTokensRunning == 0)
29432d395cb5SSimon J. Gerraty 	    return;
29442d395cb5SSimon J. Gerraty 	tok = '+';			/* no error token */
29452d395cb5SSimon J. Gerraty     }
29462d395cb5SSimon J. Gerraty 
29473955d011SMarcel Moolenaar     /* If we are depositing an error token flush everything else */
29483955d011SMarcel Moolenaar     while (tok != '+' && read(tokenWaitJob.inPipe, &tok1, 1) == 1)
29493955d011SMarcel Moolenaar 	continue;
29503955d011SMarcel Moolenaar 
29513955d011SMarcel Moolenaar     if (DEBUG(JOB))
29523955d011SMarcel Moolenaar 	fprintf(debug_file, "(%d) aborting %d, deposit token %c\n",
29532d395cb5SSimon J. Gerraty 	    getpid(), aborting, tok);
29543cbdda60SSimon J. Gerraty     while (write(tokenWaitJob.outPipe, &tok, 1) == -1 && errno == EAGAIN)
29553cbdda60SSimon J. Gerraty 	continue;
29563955d011SMarcel Moolenaar }
29573955d011SMarcel Moolenaar 
29583955d011SMarcel Moolenaar /*-
29593955d011SMarcel Moolenaar  *-----------------------------------------------------------------------
29603955d011SMarcel Moolenaar  * Job_ServerStartTokenAdd --
29613955d011SMarcel Moolenaar  *	Prep the job token pipe in the root make process.
29623955d011SMarcel Moolenaar  *
29633955d011SMarcel Moolenaar  *-----------------------------------------------------------------------
29643955d011SMarcel Moolenaar  */
29653955d011SMarcel Moolenaar 
29663955d011SMarcel Moolenaar void
29673955d011SMarcel Moolenaar Job_ServerStart(int max_tokens, int jp_0, int jp_1)
29683955d011SMarcel Moolenaar {
29693955d011SMarcel Moolenaar     int i;
29703955d011SMarcel Moolenaar     char jobarg[64];
29713955d011SMarcel Moolenaar 
29723955d011SMarcel Moolenaar     if (jp_0 >= 0 && jp_1 >= 0) {
29733955d011SMarcel Moolenaar 	/* Pipe passed in from parent */
29743955d011SMarcel Moolenaar 	tokenWaitJob.inPipe = jp_0;
29753955d011SMarcel Moolenaar 	tokenWaitJob.outPipe = jp_1;
2976be19d90bSSimon J. Gerraty 	(void)fcntl(jp_0, F_SETFD, FD_CLOEXEC);
2977be19d90bSSimon J. Gerraty 	(void)fcntl(jp_1, F_SETFD, FD_CLOEXEC);
29783955d011SMarcel Moolenaar 	return;
29793955d011SMarcel Moolenaar     }
29803955d011SMarcel Moolenaar 
29813955d011SMarcel Moolenaar     JobCreatePipe(&tokenWaitJob, 15);
29823955d011SMarcel Moolenaar 
29833955d011SMarcel Moolenaar     snprintf(jobarg, sizeof(jobarg), "%d,%d",
29843955d011SMarcel Moolenaar 	    tokenWaitJob.inPipe, tokenWaitJob.outPipe);
29853955d011SMarcel Moolenaar 
29863955d011SMarcel Moolenaar     Var_Append(MAKEFLAGS, "-J", VAR_GLOBAL);
29873955d011SMarcel Moolenaar     Var_Append(MAKEFLAGS, jobarg, VAR_GLOBAL);
29883955d011SMarcel Moolenaar 
29893955d011SMarcel Moolenaar     /*
29903955d011SMarcel Moolenaar      * Preload the job pipe with one token per job, save the one
29913955d011SMarcel Moolenaar      * "extra" token for the primary job.
29923955d011SMarcel Moolenaar      *
29933955d011SMarcel Moolenaar      * XXX should clip maxJobs against PIPE_BUF -- if max_tokens is
29943955d011SMarcel Moolenaar      * larger than the write buffer size of the pipe, we will
29953955d011SMarcel Moolenaar      * deadlock here.
29963955d011SMarcel Moolenaar      */
29973955d011SMarcel Moolenaar     for (i = 1; i < max_tokens; i++)
29983955d011SMarcel Moolenaar 	JobTokenAdd();
29993955d011SMarcel Moolenaar }
30003955d011SMarcel Moolenaar 
30013955d011SMarcel Moolenaar /*-
30023955d011SMarcel Moolenaar  *-----------------------------------------------------------------------
30033955d011SMarcel Moolenaar  * Job_TokenReturn --
30043955d011SMarcel Moolenaar  *	Return a withdrawn token to the pool.
30053955d011SMarcel Moolenaar  *
30063955d011SMarcel Moolenaar  *-----------------------------------------------------------------------
30073955d011SMarcel Moolenaar  */
30083955d011SMarcel Moolenaar 
30093955d011SMarcel Moolenaar void
30103955d011SMarcel Moolenaar Job_TokenReturn(void)
30113955d011SMarcel Moolenaar {
30123955d011SMarcel Moolenaar     jobTokensRunning--;
30133955d011SMarcel Moolenaar     if (jobTokensRunning < 0)
30143955d011SMarcel Moolenaar 	Punt("token botch");
30153955d011SMarcel Moolenaar     if (jobTokensRunning || JOB_TOKENS[aborting] != '+')
30163955d011SMarcel Moolenaar 	JobTokenAdd();
30173955d011SMarcel Moolenaar }
30183955d011SMarcel Moolenaar 
30193955d011SMarcel Moolenaar /*-
30203955d011SMarcel Moolenaar  *-----------------------------------------------------------------------
30213955d011SMarcel Moolenaar  * Job_TokenWithdraw --
30223955d011SMarcel Moolenaar  *	Attempt to withdraw a token from the pool.
30233955d011SMarcel Moolenaar  *
30243955d011SMarcel Moolenaar  * Results:
30253955d011SMarcel Moolenaar  *	Returns TRUE if a token was withdrawn, and FALSE if the pool
30263955d011SMarcel Moolenaar  *	is currently empty.
30273955d011SMarcel Moolenaar  *
30283955d011SMarcel Moolenaar  * Side Effects:
30293955d011SMarcel Moolenaar  * 	If pool is empty, set wantToken so that we wake up
30303955d011SMarcel Moolenaar  *	when a token is released.
30313955d011SMarcel Moolenaar  *
30323955d011SMarcel Moolenaar  *-----------------------------------------------------------------------
30333955d011SMarcel Moolenaar  */
30343955d011SMarcel Moolenaar 
30353955d011SMarcel Moolenaar 
30363955d011SMarcel Moolenaar Boolean
30373955d011SMarcel Moolenaar Job_TokenWithdraw(void)
30383955d011SMarcel Moolenaar {
30393955d011SMarcel Moolenaar     char tok, tok1;
30403955d011SMarcel Moolenaar     int count;
30413955d011SMarcel Moolenaar 
30423955d011SMarcel Moolenaar     wantToken = 0;
30433955d011SMarcel Moolenaar     if (DEBUG(JOB))
30443955d011SMarcel Moolenaar 	fprintf(debug_file, "Job_TokenWithdraw(%d): aborting %d, running %d\n",
30453955d011SMarcel Moolenaar 		getpid(), aborting, jobTokensRunning);
30463955d011SMarcel Moolenaar 
30473955d011SMarcel Moolenaar     if (aborting || (jobTokensRunning >= maxJobs))
30483955d011SMarcel Moolenaar 	return FALSE;
30493955d011SMarcel Moolenaar 
30503955d011SMarcel Moolenaar     count = read(tokenWaitJob.inPipe, &tok, 1);
30513955d011SMarcel Moolenaar     if (count == 0)
30523955d011SMarcel Moolenaar 	Fatal("eof on job pipe!");
30533955d011SMarcel Moolenaar     if (count < 0 && jobTokensRunning != 0) {
30543955d011SMarcel Moolenaar 	if (errno != EAGAIN) {
30553955d011SMarcel Moolenaar 	    Fatal("job pipe read: %s", strerror(errno));
30563955d011SMarcel Moolenaar 	}
30573955d011SMarcel Moolenaar 	if (DEBUG(JOB))
30583955d011SMarcel Moolenaar 	    fprintf(debug_file, "(%d) blocked for token\n", getpid());
30593955d011SMarcel Moolenaar 	return FALSE;
30603955d011SMarcel Moolenaar     }
30613955d011SMarcel Moolenaar 
30623955d011SMarcel Moolenaar     if (count == 1 && tok != '+') {
30633955d011SMarcel Moolenaar 	/* make being abvorted - remove any other job tokens */
30643955d011SMarcel Moolenaar 	if (DEBUG(JOB))
30653955d011SMarcel Moolenaar 	    fprintf(debug_file, "(%d) aborted by token %c\n", getpid(), tok);
30663955d011SMarcel Moolenaar 	while (read(tokenWaitJob.inPipe, &tok1, 1) == 1)
30673955d011SMarcel Moolenaar 	    continue;
30683955d011SMarcel Moolenaar 	/* And put the stopper back */
30693cbdda60SSimon J. Gerraty 	while (write(tokenWaitJob.outPipe, &tok, 1) == -1 && errno == EAGAIN)
30703cbdda60SSimon J. Gerraty 	    continue;
30713955d011SMarcel Moolenaar 	Fatal("A failure has been detected in another branch of the parallel make");
30723955d011SMarcel Moolenaar     }
30733955d011SMarcel Moolenaar 
30743955d011SMarcel Moolenaar     if (count == 1 && jobTokensRunning == 0)
30753955d011SMarcel Moolenaar 	/* We didn't want the token really */
30763cbdda60SSimon J. Gerraty 	while (write(tokenWaitJob.outPipe, &tok, 1) == -1 && errno == EAGAIN)
30773cbdda60SSimon J. Gerraty 	    continue;
30783955d011SMarcel Moolenaar 
30793955d011SMarcel Moolenaar     jobTokensRunning++;
30803955d011SMarcel Moolenaar     if (DEBUG(JOB))
30813955d011SMarcel Moolenaar 	fprintf(debug_file, "(%d) withdrew token\n", getpid());
30823955d011SMarcel Moolenaar     return TRUE;
30833955d011SMarcel Moolenaar }
30843955d011SMarcel Moolenaar 
30851748de26SSimon J. Gerraty /*-
30861748de26SSimon J. Gerraty  *-----------------------------------------------------------------------
30871748de26SSimon J. Gerraty  * Job_RunTarget --
30881748de26SSimon J. Gerraty  *	Run the named target if found. If a filename is specified, then
30891748de26SSimon J. Gerraty  *	set that to the sources.
30901748de26SSimon J. Gerraty  *
30911748de26SSimon J. Gerraty  * Results:
30921748de26SSimon J. Gerraty  *	None
30931748de26SSimon J. Gerraty  *
30941748de26SSimon J. Gerraty  * Side Effects:
30951748de26SSimon J. Gerraty  * 	exits if the target fails.
30961748de26SSimon J. Gerraty  *
30971748de26SSimon J. Gerraty  *-----------------------------------------------------------------------
30981748de26SSimon J. Gerraty  */
30991748de26SSimon J. Gerraty Boolean
31001748de26SSimon J. Gerraty Job_RunTarget(const char *target, const char *fname) {
31011748de26SSimon J. Gerraty     GNode *gn = Targ_FindNode(target, TARG_NOCREATE);
31021748de26SSimon J. Gerraty 
31031748de26SSimon J. Gerraty     if (gn == NULL)
31041748de26SSimon J. Gerraty 	return FALSE;
31051748de26SSimon J. Gerraty 
31061748de26SSimon J. Gerraty     if (fname)
31071748de26SSimon J. Gerraty 	Var_Set(ALLSRC, fname, gn, 0);
31081748de26SSimon J. Gerraty 
31091748de26SSimon J. Gerraty     JobRun(gn);
31101748de26SSimon J. Gerraty     if (gn->made == ERROR) {
31111748de26SSimon J. Gerraty 	PrintOnError(gn, "\n\nStop.");
31121748de26SSimon J. Gerraty 	exit(1);
31131748de26SSimon J. Gerraty     }
31141748de26SSimon J. Gerraty     return TRUE;
31151748de26SSimon J. Gerraty }
31161748de26SSimon J. Gerraty 
31173955d011SMarcel Moolenaar #ifdef USE_SELECT
31183955d011SMarcel Moolenaar int
31193955d011SMarcel Moolenaar emul_poll(struct pollfd *fd, int nfd, int timeout)
31203955d011SMarcel Moolenaar {
31213955d011SMarcel Moolenaar     fd_set rfds, wfds;
31223955d011SMarcel Moolenaar     int i, maxfd, nselect, npoll;
31233955d011SMarcel Moolenaar     struct timeval tv, *tvp;
31243955d011SMarcel Moolenaar     long usecs;
31253955d011SMarcel Moolenaar 
31263955d011SMarcel Moolenaar     FD_ZERO(&rfds);
31273955d011SMarcel Moolenaar     FD_ZERO(&wfds);
31283955d011SMarcel Moolenaar 
31293955d011SMarcel Moolenaar     maxfd = -1;
31303955d011SMarcel Moolenaar     for (i = 0; i < nfd; i++) {
31313955d011SMarcel Moolenaar 	fd[i].revents = 0;
31323955d011SMarcel Moolenaar 
31333955d011SMarcel Moolenaar 	if (fd[i].events & POLLIN)
31343955d011SMarcel Moolenaar 	    FD_SET(fd[i].fd, &rfds);
31353955d011SMarcel Moolenaar 
31363955d011SMarcel Moolenaar 	if (fd[i].events & POLLOUT)
31373955d011SMarcel Moolenaar 	    FD_SET(fd[i].fd, &wfds);
31383955d011SMarcel Moolenaar 
31393955d011SMarcel Moolenaar 	if (fd[i].fd > maxfd)
31403955d011SMarcel Moolenaar 	    maxfd = fd[i].fd;
31413955d011SMarcel Moolenaar     }
31423955d011SMarcel Moolenaar 
31433955d011SMarcel Moolenaar     if (maxfd >= FD_SETSIZE) {
31443955d011SMarcel Moolenaar 	Punt("Ran out of fd_set slots; "
31453955d011SMarcel Moolenaar 	     "recompile with a larger FD_SETSIZE.");
31463955d011SMarcel Moolenaar     }
31473955d011SMarcel Moolenaar 
31483955d011SMarcel Moolenaar     if (timeout < 0) {
31493955d011SMarcel Moolenaar 	tvp = NULL;
31503955d011SMarcel Moolenaar     } else {
31513955d011SMarcel Moolenaar 	usecs = timeout * 1000;
31523955d011SMarcel Moolenaar 	tv.tv_sec = usecs / 1000000;
31533955d011SMarcel Moolenaar 	tv.tv_usec = usecs % 1000000;
31543955d011SMarcel Moolenaar         tvp = &tv;
31553955d011SMarcel Moolenaar     }
31563955d011SMarcel Moolenaar 
31573955d011SMarcel Moolenaar     nselect = select(maxfd + 1, &rfds, &wfds, 0, tvp);
31583955d011SMarcel Moolenaar 
31593955d011SMarcel Moolenaar     if (nselect <= 0)
31603955d011SMarcel Moolenaar 	return nselect;
31613955d011SMarcel Moolenaar 
31623955d011SMarcel Moolenaar     npoll = 0;
31633955d011SMarcel Moolenaar     for (i = 0; i < nfd; i++) {
31643955d011SMarcel Moolenaar 	if (FD_ISSET(fd[i].fd, &rfds))
31653955d011SMarcel Moolenaar 	    fd[i].revents |= POLLIN;
31663955d011SMarcel Moolenaar 
31673955d011SMarcel Moolenaar 	if (FD_ISSET(fd[i].fd, &wfds))
31683955d011SMarcel Moolenaar 	    fd[i].revents |= POLLOUT;
31693955d011SMarcel Moolenaar 
31703955d011SMarcel Moolenaar 	if (fd[i].revents)
31713955d011SMarcel Moolenaar 	    npoll++;
31723955d011SMarcel Moolenaar     }
31733955d011SMarcel Moolenaar 
31743955d011SMarcel Moolenaar     return npoll;
31753955d011SMarcel Moolenaar }
31763955d011SMarcel Moolenaar #endif /* USE_SELECT */
3177