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