1*84d9c625SLionel Sambuc /* $NetBSD: job.c,v 1.176 2013/08/04 16:48:15 sjg 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*84d9c625SLionel Sambuc static char rcsid[] = "$NetBSD: job.c,v 1.176 2013/08/04 16:48:15 sjg 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*84d9c625SLionel Sambuc __RCSID("$NetBSD: job.c,v 1.176 2013/08/04 16:48:15 sjg 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 142*84d9c625SLionel 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 */ 303*84d9c625SLionel 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) \ 335*84d9c625SLionel 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 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 */ 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 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 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 405*84d9c625SLionel Sambuc for (i = 0; i < 2; i++) { 406*84d9c625SLionel Sambuc /* Avoid using low numbered fds */ 407*84d9c625SLionel Sambuc fd = fcntl(job->jobPipe[i], F_DUPFD, minfd); 408*84d9c625SLionel Sambuc if (fd != -1) { 409*84d9c625SLionel Sambuc close(job->jobPipe[i]); 410*84d9c625SLionel Sambuc job->jobPipe[i] = fd; 411*84d9c625SLionel Sambuc } 412*84d9c625SLionel Sambuc } 413*84d9c625SLionel 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 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 4802bc7c627SLionel Sambuc JobChildSig(int signo MAKE_ATTR_UNUSED) 4812e2caf59SThomas Veerman { 482*84d9c625SLionel Sambuc while (write(childExitJob.outPipe, CHILD_EXIT, 1) == -1 && errno == EAGAIN) 483*84d9c625SLionel 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 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 */ 510*84d9c625SLionel Sambuc while (write(childExitJob.outPipe, DO_JOB_RESUME, 1) == -1 && 511*84d9c625SLionel Sambuc errno == EAGAIN) 512*84d9c625SLionel 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 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 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 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 * 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 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 job->flags |= JOB_IGNERR; 7192e2caf59SThomas Veerman errOff = TRUE; 7202e2caf59SThomas Veerman break; 7212e2caf59SThomas Veerman case '+': 7222e2caf59SThomas Veerman if (noSpecials) { 7232e2caf59SThomas Veerman /* 7242e2caf59SThomas Veerman * We're not actually executing anything... 7252e2caf59SThomas Veerman * but this one needs to be - use compat mode just for it. 7262e2caf59SThomas Veerman */ 7272e2caf59SThomas Veerman CompatRunCommand(cmdp, job->node); 7282e2caf59SThomas Veerman return 0; 7292e2caf59SThomas Veerman } 7302e2caf59SThomas Veerman break; 7312e2caf59SThomas Veerman } 7322e2caf59SThomas Veerman cmd++; 7332e2caf59SThomas Veerman } 7342e2caf59SThomas Veerman 7352e2caf59SThomas Veerman while (isspace((unsigned char) *cmd)) 7362e2caf59SThomas Veerman cmd++; 7372e2caf59SThomas Veerman 7382e2caf59SThomas Veerman /* 7392e2caf59SThomas Veerman * If the shell doesn't have error control the alternate echo'ing will 7402e2caf59SThomas Veerman * be done (to avoid showing additional error checking code) 7412e2caf59SThomas Veerman * and this will need the characters '$ ` \ "' escaped 7422e2caf59SThomas Veerman */ 7432e2caf59SThomas Veerman 7442e2caf59SThomas Veerman if (!commandShell->hasErrCtl) { 7452e2caf59SThomas Veerman /* Worst that could happen is every char needs escaping. */ 7462e2caf59SThomas Veerman escCmd = bmake_malloc((strlen(cmd) * 2) + 1); 7472e2caf59SThomas Veerman for (i = 0, j= 0; cmd[i] != '\0'; i++, j++) { 7482e2caf59SThomas Veerman if (cmd[i] == '$' || cmd[i] == '`' || cmd[i] == '\\' || 7492e2caf59SThomas Veerman cmd[i] == '"') 7502e2caf59SThomas Veerman escCmd[j++] = '\\'; 7512e2caf59SThomas Veerman escCmd[j] = cmd[i]; 7522e2caf59SThomas Veerman } 7532e2caf59SThomas Veerman escCmd[j] = 0; 7542e2caf59SThomas Veerman } 7552e2caf59SThomas Veerman 7562e2caf59SThomas Veerman if (shutUp) { 7572e2caf59SThomas Veerman if (!(job->flags & JOB_SILENT) && !noSpecials && 7582e2caf59SThomas Veerman commandShell->hasEchoCtl) { 7592e2caf59SThomas Veerman DBPRINTF("%s\n", commandShell->echoOff); 7602e2caf59SThomas Veerman } else { 7612e2caf59SThomas Veerman if (commandShell->hasErrCtl) 7622e2caf59SThomas Veerman shutUp = FALSE; 7632e2caf59SThomas Veerman } 7642e2caf59SThomas Veerman } 7652e2caf59SThomas Veerman 7662e2caf59SThomas Veerman if (errOff) { 7672e2caf59SThomas Veerman if (!noSpecials) { 7682e2caf59SThomas Veerman if (commandShell->hasErrCtl) { 7692e2caf59SThomas Veerman /* 7702e2caf59SThomas Veerman * we don't want the error-control commands showing 7712e2caf59SThomas Veerman * up either, so we turn off echoing while executing 7722e2caf59SThomas Veerman * them. We could put another field in the shell 7732e2caf59SThomas Veerman * structure to tell JobDoOutput to look for this 7742e2caf59SThomas Veerman * string too, but why make it any more complex than 7752e2caf59SThomas Veerman * it already is? 7762e2caf59SThomas Veerman */ 7772e2caf59SThomas Veerman if (!(job->flags & JOB_SILENT) && !shutUp && 7782e2caf59SThomas Veerman commandShell->hasEchoCtl) { 7792e2caf59SThomas Veerman DBPRINTF("%s\n", commandShell->echoOff); 7802e2caf59SThomas Veerman DBPRINTF("%s\n", commandShell->ignErr); 7812e2caf59SThomas Veerman DBPRINTF("%s\n", commandShell->echoOn); 7822e2caf59SThomas Veerman } else { 7832e2caf59SThomas Veerman DBPRINTF("%s\n", commandShell->ignErr); 7842e2caf59SThomas Veerman } 7852e2caf59SThomas Veerman } else if (commandShell->ignErr && 7862e2caf59SThomas Veerman (*commandShell->ignErr != '\0')) 7872e2caf59SThomas Veerman { 7882e2caf59SThomas Veerman /* 7892e2caf59SThomas Veerman * The shell has no error control, so we need to be 7902e2caf59SThomas Veerman * weird to get it to ignore any errors from the command. 7912e2caf59SThomas Veerman * If echoing is turned on, we turn it off and use the 7922e2caf59SThomas Veerman * errCheck template to echo the command. Leave echoing 7932e2caf59SThomas Veerman * off so the user doesn't see the weirdness we go through 7942e2caf59SThomas Veerman * to ignore errors. Set cmdTemplate to use the weirdness 7952e2caf59SThomas Veerman * instead of the simple "%s\n" template. 7962e2caf59SThomas Veerman */ 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 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 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 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 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); 1169*84d9c625SLionel Sambuc while (write(streamID, &c, 1) == -1 && errno == EAGAIN) 1170*84d9c625SLionel 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 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) { 1236*84d9c625SLionel Sambuc if (!Job_RunTarget(".STALE", gn->fname)) 1237*84d9c625SLionel Sambuc fprintf(stdout, "%s: %s, %d: ignoring stale %s for %s\n", 1238*84d9c625SLionel Sambuc progname, gn->fname, gn->lineno, makeDependfile, 1239*84d9c625SLionel 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 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 13492e2caf59SThomas Veerman if (job->node->type & OP_MAKE) { 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 */ 1382*84d9c625SLionel Sambuc #if defined(MAKE_NATIVE) || defined(HAVE_SETPGID) 13832e2caf59SThomas Veerman #if defined(SYSV) || defined(__minix) 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 1389*84d9c625SLionel 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 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 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 * 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 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 if (i < max - 1) { 18822e2caf59SThomas Veerman /* shift the remaining characters down */ 18832e2caf59SThomas Veerman (void)memcpy(job->outBuf, &job->outBuf[i + 1], max - (i + 1)); 18842e2caf59SThomas Veerman job->curPos = max - (i + 1); 18852e2caf59SThomas Veerman 18862e2caf59SThomas Veerman } else { 18872e2caf59SThomas Veerman /* 18882e2caf59SThomas Veerman * We have written everything out, so we just start over 18892e2caf59SThomas Veerman * from the start of the buffer. No copying. No nothing. 18902e2caf59SThomas Veerman */ 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 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 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 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 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 */ 2054*84d9c625SLionel Sambuc do { 20552e2caf59SThomas Veerman nready = poll(fds + 1 - wantToken, nfds - 1 + wantToken, POLL_MSEC); 2056*84d9c625SLionel Sambuc } while (nready < 0 && errno == EINTR); 20572e2caf59SThomas Veerman 2058*84d9c625SLionel Sambuc if (nready < 0) 2059*84d9c625SLionel Sambuc Punt("poll: %s", strerror(errno)); 2060*84d9c625SLionel Sambuc 2061*84d9c625SLionel Sambuc if (nready > 0 && readyfd(&childExitJob)) { 20622e2caf59SThomas Veerman char token = 0; 2063*84d9c625SLionel Sambuc ssize_t count; 2064*84d9c625SLionel Sambuc count = read(childExitJob.inPipe, &token, 1); 2065*84d9c625SLionel Sambuc switch (count) { 2066*84d9c625SLionel Sambuc case 0: 2067*84d9c625SLionel Sambuc Punt("unexpected eof on token pipe"); 2068*84d9c625SLionel Sambuc case -1: 2069*84d9c625SLionel Sambuc Punt("token pipe read: %s", strerror(errno)); 2070*84d9c625SLionel Sambuc case 1: 20712e2caf59SThomas Veerman if (token == DO_JOB_RESUME[0]) 20722e2caf59SThomas Veerman /* Complete relay requested from our SIGCONT handler */ 20732e2caf59SThomas Veerman JobRestartJobs(); 2074*84d9c625SLionel Sambuc break; 2075*84d9c625SLionel Sambuc default: 2076*84d9c625SLionel Sambuc abort(); 2077*84d9c625SLionel Sambuc } 2078*84d9c625SLionel Sambuc --nready; 20792e2caf59SThomas Veerman } 20802e2caf59SThomas Veerman 2081*84d9c625SLionel Sambuc Job_CatchChildren(); 2082*84d9c625SLionel Sambuc if (nready == 0) 20832e2caf59SThomas Veerman return; 20842e2caf59SThomas Veerman 20852e2caf59SThomas Veerman for (i = 2; i < nfds; i++) { 20862e2caf59SThomas Veerman if (!fds[i].revents) 20872e2caf59SThomas Veerman continue; 20882e2caf59SThomas Veerman job = jobfds[i]; 2089*84d9c625SLionel Sambuc if (job->job_state == JOB_ST_RUNNING) 20902e2caf59SThomas Veerman JobDoOutput(job, FALSE); 2091*84d9c625SLionel Sambuc if (--nready == 0) 2092*84d9c625SLionel Sambuc return; 20932e2caf59SThomas Veerman } 20942e2caf59SThomas Veerman } 20952e2caf59SThomas Veerman 20962e2caf59SThomas Veerman /*- 20972e2caf59SThomas Veerman *----------------------------------------------------------------------- 20982e2caf59SThomas Veerman * Job_Make -- 20992e2caf59SThomas Veerman * Start the creation of a target. Basically a front-end for 21002e2caf59SThomas Veerman * JobStart used by the Make module. 21012e2caf59SThomas Veerman * 21022e2caf59SThomas Veerman * Results: 21032e2caf59SThomas Veerman * None. 21042e2caf59SThomas Veerman * 21052e2caf59SThomas Veerman * Side Effects: 21062e2caf59SThomas Veerman * Another job is started. 21072e2caf59SThomas Veerman * 21082e2caf59SThomas Veerman *----------------------------------------------------------------------- 21092e2caf59SThomas Veerman */ 21102e2caf59SThomas Veerman void 21112e2caf59SThomas Veerman Job_Make(GNode *gn) 21122e2caf59SThomas Veerman { 21132e2caf59SThomas Veerman (void)JobStart(gn, 0); 21142e2caf59SThomas Veerman } 21152e2caf59SThomas Veerman 21162e2caf59SThomas Veerman void 21172e2caf59SThomas Veerman Shell_Init(void) 21182e2caf59SThomas Veerman { 21192e2caf59SThomas Veerman if (shellPath == NULL) { 21202e2caf59SThomas Veerman /* 21212e2caf59SThomas Veerman * We are using the default shell, which may be an absolute 21222e2caf59SThomas Veerman * path if DEFSHELL_CUSTOM is defined. 21232e2caf59SThomas Veerman */ 21242e2caf59SThomas Veerman shellName = commandShell->name; 21252e2caf59SThomas Veerman #ifdef DEFSHELL_CUSTOM 21262e2caf59SThomas Veerman if (*shellName == '/') { 21272e2caf59SThomas Veerman shellPath = shellName; 21282e2caf59SThomas Veerman shellName = strrchr(shellPath, '/'); 21292e2caf59SThomas Veerman shellName++; 21302e2caf59SThomas Veerman } else 21312e2caf59SThomas Veerman #endif 21322e2caf59SThomas Veerman shellPath = str_concat(_PATH_DEFSHELLDIR, shellName, STR_ADDSLASH); 21332e2caf59SThomas Veerman } 21342e2caf59SThomas Veerman if (commandShell->exit == NULL) { 21352e2caf59SThomas Veerman commandShell->exit = ""; 21362e2caf59SThomas Veerman } 21372e2caf59SThomas Veerman if (commandShell->echo == NULL) { 21382e2caf59SThomas Veerman commandShell->echo = ""; 21392e2caf59SThomas Veerman } 2140*84d9c625SLionel Sambuc if (commandShell->hasErrCtl && *commandShell->exit) { 2141*84d9c625SLionel Sambuc if (shellErrFlag && 2142*84d9c625SLionel Sambuc strcmp(commandShell->exit, &shellErrFlag[1]) != 0) { 2143*84d9c625SLionel Sambuc free(shellErrFlag); 2144*84d9c625SLionel Sambuc shellErrFlag = NULL; 2145*84d9c625SLionel Sambuc } 2146*84d9c625SLionel Sambuc if (!shellErrFlag) { 2147*84d9c625SLionel Sambuc int n = strlen(commandShell->exit) + 2; 2148*84d9c625SLionel Sambuc 2149*84d9c625SLionel Sambuc shellErrFlag = bmake_malloc(n); 2150*84d9c625SLionel Sambuc if (shellErrFlag) { 2151*84d9c625SLionel Sambuc snprintf(shellErrFlag, n, "-%s", commandShell->exit); 2152*84d9c625SLionel Sambuc } 2153*84d9c625SLionel Sambuc } 2154*84d9c625SLionel Sambuc } else if (shellErrFlag) { 2155*84d9c625SLionel Sambuc free(shellErrFlag); 2156*84d9c625SLionel Sambuc shellErrFlag = NULL; 2157*84d9c625SLionel Sambuc } 21582e2caf59SThomas Veerman } 21592e2caf59SThomas Veerman 21602e2caf59SThomas Veerman /*- 21612e2caf59SThomas Veerman * Returns the string literal that is used in the current command shell 21622e2caf59SThomas Veerman * to produce a newline character. 21632e2caf59SThomas Veerman */ 21642e2caf59SThomas Veerman const char * 21652e2caf59SThomas Veerman Shell_GetNewline(void) 21662e2caf59SThomas Veerman { 21672e2caf59SThomas Veerman 21682e2caf59SThomas Veerman return commandShell->newline; 21692e2caf59SThomas Veerman } 21702e2caf59SThomas Veerman 21712e2caf59SThomas Veerman void 21722e2caf59SThomas Veerman Job_SetPrefix(void) 21732e2caf59SThomas Veerman { 21742e2caf59SThomas Veerman 21752e2caf59SThomas Veerman if (targPrefix) { 21762e2caf59SThomas Veerman free(targPrefix); 21772e2caf59SThomas Veerman } else if (!Var_Exists(MAKE_JOB_PREFIX, VAR_GLOBAL)) { 21782e2caf59SThomas Veerman Var_Set(MAKE_JOB_PREFIX, "---", VAR_GLOBAL, 0); 21792e2caf59SThomas Veerman } 21802e2caf59SThomas Veerman 21812e2caf59SThomas Veerman targPrefix = Var_Subst(NULL, "${" MAKE_JOB_PREFIX "}", VAR_GLOBAL, 0); 21822e2caf59SThomas Veerman } 21832e2caf59SThomas Veerman 21842e2caf59SThomas Veerman /*- 21852e2caf59SThomas Veerman *----------------------------------------------------------------------- 21862e2caf59SThomas Veerman * Job_Init -- 21872e2caf59SThomas Veerman * Initialize the process module 21882e2caf59SThomas Veerman * 21892e2caf59SThomas Veerman * Input: 21902e2caf59SThomas Veerman * 21912e2caf59SThomas Veerman * Results: 21922e2caf59SThomas Veerman * none 21932e2caf59SThomas Veerman * 21942e2caf59SThomas Veerman * Side Effects: 21952e2caf59SThomas Veerman * lists and counters are initialized 21962e2caf59SThomas Veerman *----------------------------------------------------------------------- 21972e2caf59SThomas Veerman */ 21982e2caf59SThomas Veerman void 21992e2caf59SThomas Veerman Job_Init(void) 22002e2caf59SThomas Veerman { 2201*84d9c625SLionel Sambuc Job_SetPrefix(); 22022e2caf59SThomas Veerman /* Allocate space for all the job info */ 22032e2caf59SThomas Veerman job_table = bmake_malloc(maxJobs * sizeof *job_table); 22042e2caf59SThomas Veerman memset(job_table, 0, maxJobs * sizeof *job_table); 22052e2caf59SThomas Veerman job_table_end = job_table + maxJobs; 22062e2caf59SThomas Veerman wantToken = 0; 22072e2caf59SThomas Veerman 22082e2caf59SThomas Veerman aborting = 0; 22092e2caf59SThomas Veerman errors = 0; 22102e2caf59SThomas Veerman 22112e2caf59SThomas Veerman lastNode = NULL; 22122e2caf59SThomas Veerman 22132e2caf59SThomas Veerman /* 22142e2caf59SThomas Veerman * There is a non-zero chance that we already have children. 22152e2caf59SThomas Veerman * eg after 'make -f- <<EOF' 22162e2caf59SThomas Veerman * Since their termination causes a 'Child (pid) not in table' message, 22172e2caf59SThomas Veerman * Collect the status of any that are already dead, and suppress the 22182e2caf59SThomas Veerman * error message if there are any undead ones. 22192e2caf59SThomas Veerman */ 22202e2caf59SThomas Veerman for (;;) { 22212e2caf59SThomas Veerman int rval, status; 22222e2caf59SThomas Veerman rval = waitpid((pid_t) -1, &status, WNOHANG); 22232e2caf59SThomas Veerman if (rval > 0) 22242e2caf59SThomas Veerman continue; 22252e2caf59SThomas Veerman if (rval == 0) 22262e2caf59SThomas Veerman lurking_children = 1; 22272e2caf59SThomas Veerman break; 22282e2caf59SThomas Veerman } 22292e2caf59SThomas Veerman 22302e2caf59SThomas Veerman Shell_Init(); 22312e2caf59SThomas Veerman 22322e2caf59SThomas Veerman JobCreatePipe(&childExitJob, 3); 22332e2caf59SThomas Veerman 22342e2caf59SThomas Veerman /* We can only need to wait for tokens, children and output from each job */ 22352e2caf59SThomas Veerman fds = bmake_malloc(sizeof (*fds) * (2 + maxJobs)); 22362e2caf59SThomas Veerman jobfds = bmake_malloc(sizeof (*jobfds) * (2 + maxJobs)); 22372e2caf59SThomas Veerman 22382e2caf59SThomas Veerman /* These are permanent entries and take slots 0 and 1 */ 22392e2caf59SThomas Veerman watchfd(&tokenWaitJob); 22402e2caf59SThomas Veerman watchfd(&childExitJob); 22412e2caf59SThomas Veerman 22422e2caf59SThomas Veerman sigemptyset(&caught_signals); 22432e2caf59SThomas Veerman /* 22442e2caf59SThomas Veerman * Install a SIGCHLD handler. 22452e2caf59SThomas Veerman */ 22462e2caf59SThomas Veerman (void)bmake_signal(SIGCHLD, JobChildSig); 22472e2caf59SThomas Veerman sigaddset(&caught_signals, SIGCHLD); 22482e2caf59SThomas Veerman 22492e2caf59SThomas Veerman #define ADDSIG(s,h) \ 22502e2caf59SThomas Veerman if (bmake_signal(s, SIG_IGN) != SIG_IGN) { \ 22512e2caf59SThomas Veerman sigaddset(&caught_signals, s); \ 22522e2caf59SThomas Veerman (void)bmake_signal(s, h); \ 22532e2caf59SThomas Veerman } 22542e2caf59SThomas Veerman 22552e2caf59SThomas Veerman /* 22562e2caf59SThomas Veerman * Catch the four signals that POSIX specifies if they aren't ignored. 22572e2caf59SThomas Veerman * JobPassSig will take care of calling JobInterrupt if appropriate. 22582e2caf59SThomas Veerman */ 22592e2caf59SThomas Veerman ADDSIG(SIGINT, JobPassSig_int) 22602e2caf59SThomas Veerman ADDSIG(SIGHUP, JobPassSig_term) 22612e2caf59SThomas Veerman ADDSIG(SIGTERM, JobPassSig_term) 22622e2caf59SThomas Veerman ADDSIG(SIGQUIT, JobPassSig_term) 22632e2caf59SThomas Veerman 22642e2caf59SThomas Veerman /* 22652e2caf59SThomas Veerman * There are additional signals that need to be caught and passed if 22662e2caf59SThomas Veerman * either the export system wants to be told directly of signals or if 22672e2caf59SThomas Veerman * we're giving each job its own process group (since then it won't get 22682e2caf59SThomas Veerman * signals from the terminal driver as we own the terminal) 22692e2caf59SThomas Veerman */ 22702e2caf59SThomas Veerman ADDSIG(SIGTSTP, JobPassSig_suspend) 22712e2caf59SThomas Veerman ADDSIG(SIGTTOU, JobPassSig_suspend) 22722e2caf59SThomas Veerman ADDSIG(SIGTTIN, JobPassSig_suspend) 22732e2caf59SThomas Veerman ADDSIG(SIGWINCH, JobCondPassSig) 22742e2caf59SThomas Veerman ADDSIG(SIGCONT, JobContinueSig) 22752e2caf59SThomas Veerman #undef ADDSIG 22762e2caf59SThomas Veerman 2277*84d9c625SLionel Sambuc (void)Job_RunTarget(".BEGIN", NULL); 22782e2caf59SThomas Veerman postCommands = Targ_FindNode(".END", TARG_CREATE); 22792e2caf59SThomas Veerman } 22802e2caf59SThomas Veerman 22812e2caf59SThomas Veerman static void JobSigReset(void) 22822e2caf59SThomas Veerman { 22832e2caf59SThomas Veerman #define DELSIG(s) \ 22842e2caf59SThomas Veerman if (sigismember(&caught_signals, s)) { \ 22852e2caf59SThomas Veerman (void)bmake_signal(s, SIG_DFL); \ 22862e2caf59SThomas Veerman } 22872e2caf59SThomas Veerman 22882e2caf59SThomas Veerman DELSIG(SIGINT) 22892e2caf59SThomas Veerman DELSIG(SIGHUP) 22902e2caf59SThomas Veerman DELSIG(SIGQUIT) 22912e2caf59SThomas Veerman DELSIG(SIGTERM) 22922e2caf59SThomas Veerman DELSIG(SIGTSTP) 22932e2caf59SThomas Veerman DELSIG(SIGTTOU) 22942e2caf59SThomas Veerman DELSIG(SIGTTIN) 22952e2caf59SThomas Veerman DELSIG(SIGWINCH) 22962e2caf59SThomas Veerman DELSIG(SIGCONT) 22972e2caf59SThomas Veerman #undef DELSIG 22982e2caf59SThomas Veerman (void)bmake_signal(SIGCHLD, SIG_DFL); 22992e2caf59SThomas Veerman } 23002e2caf59SThomas Veerman 23012e2caf59SThomas Veerman /*- 23022e2caf59SThomas Veerman *----------------------------------------------------------------------- 23032e2caf59SThomas Veerman * JobMatchShell -- 23042e2caf59SThomas Veerman * Find a shell in 'shells' given its name. 23052e2caf59SThomas Veerman * 23062e2caf59SThomas Veerman * Results: 23072e2caf59SThomas Veerman * A pointer to the Shell structure. 23082e2caf59SThomas Veerman * 23092e2caf59SThomas Veerman * Side Effects: 23102e2caf59SThomas Veerman * None. 23112e2caf59SThomas Veerman * 23122e2caf59SThomas Veerman *----------------------------------------------------------------------- 23132e2caf59SThomas Veerman */ 23142e2caf59SThomas Veerman static Shell * 23152e2caf59SThomas Veerman JobMatchShell(const char *name) 23162e2caf59SThomas Veerman { 23172e2caf59SThomas Veerman Shell *sh; 23182e2caf59SThomas Veerman 23192e2caf59SThomas Veerman for (sh = shells; sh->name != NULL; sh++) { 23202e2caf59SThomas Veerman if (strcmp(name, sh->name) == 0) 23212e2caf59SThomas Veerman return (sh); 23222e2caf59SThomas Veerman } 23232e2caf59SThomas Veerman return NULL; 23242e2caf59SThomas Veerman } 23252e2caf59SThomas Veerman 23262e2caf59SThomas Veerman /*- 23272e2caf59SThomas Veerman *----------------------------------------------------------------------- 23282e2caf59SThomas Veerman * Job_ParseShell -- 23292e2caf59SThomas Veerman * Parse a shell specification and set up commandShell, shellPath 23302e2caf59SThomas Veerman * and shellName appropriately. 23312e2caf59SThomas Veerman * 23322e2caf59SThomas Veerman * Input: 23332e2caf59SThomas Veerman * line The shell spec 23342e2caf59SThomas Veerman * 23352e2caf59SThomas Veerman * Results: 23362e2caf59SThomas Veerman * FAILURE if the specification was incorrect. 23372e2caf59SThomas Veerman * 23382e2caf59SThomas Veerman * Side Effects: 23392e2caf59SThomas Veerman * commandShell points to a Shell structure (either predefined or 23402e2caf59SThomas Veerman * created from the shell spec), shellPath is the full path of the 23412e2caf59SThomas Veerman * shell described by commandShell, while shellName is just the 23422e2caf59SThomas Veerman * final component of shellPath. 23432e2caf59SThomas Veerman * 23442e2caf59SThomas Veerman * Notes: 23452e2caf59SThomas Veerman * A shell specification consists of a .SHELL target, with dependency 23462e2caf59SThomas Veerman * operator, followed by a series of blank-separated words. Double 23472e2caf59SThomas Veerman * quotes can be used to use blanks in words. A backslash escapes 23482e2caf59SThomas Veerman * anything (most notably a double-quote and a space) and 23492e2caf59SThomas Veerman * provides the functionality it does in C. Each word consists of 23502e2caf59SThomas Veerman * keyword and value separated by an equal sign. There should be no 23512e2caf59SThomas Veerman * unnecessary spaces in the word. The keywords are as follows: 23522e2caf59SThomas Veerman * name Name of shell. 23532e2caf59SThomas Veerman * path Location of shell. 23542e2caf59SThomas Veerman * quiet Command to turn off echoing. 23552e2caf59SThomas Veerman * echo Command to turn echoing on 23562e2caf59SThomas Veerman * filter Result of turning off echoing that shouldn't be 23572e2caf59SThomas Veerman * printed. 23582e2caf59SThomas Veerman * echoFlag Flag to turn echoing on at the start 23592e2caf59SThomas Veerman * errFlag Flag to turn error checking on at the start 23602e2caf59SThomas Veerman * hasErrCtl True if shell has error checking control 23612e2caf59SThomas Veerman * newline String literal to represent a newline char 23622e2caf59SThomas Veerman * check Command to turn on error checking if hasErrCtl 23632e2caf59SThomas Veerman * is TRUE or template of command to echo a command 23642e2caf59SThomas Veerman * for which error checking is off if hasErrCtl is 23652e2caf59SThomas Veerman * FALSE. 23662e2caf59SThomas Veerman * ignore Command to turn off error checking if hasErrCtl 23672e2caf59SThomas Veerman * is TRUE or template of command to execute a 23682e2caf59SThomas Veerman * command so as to ignore any errors it returns if 23692e2caf59SThomas Veerman * hasErrCtl is FALSE. 23702e2caf59SThomas Veerman * 23712e2caf59SThomas Veerman *----------------------------------------------------------------------- 23722e2caf59SThomas Veerman */ 23732e2caf59SThomas Veerman ReturnStatus 23742e2caf59SThomas Veerman Job_ParseShell(char *line) 23752e2caf59SThomas Veerman { 23762e2caf59SThomas Veerman char **words; 23772e2caf59SThomas Veerman char **argv; 23782e2caf59SThomas Veerman int argc; 23792e2caf59SThomas Veerman char *path; 23802e2caf59SThomas Veerman Shell newShell; 23812e2caf59SThomas Veerman Boolean fullSpec = FALSE; 23822e2caf59SThomas Veerman Shell *sh; 23832e2caf59SThomas Veerman 23842e2caf59SThomas Veerman while (isspace((unsigned char)*line)) { 23852e2caf59SThomas Veerman line++; 23862e2caf59SThomas Veerman } 23872e2caf59SThomas Veerman 23882e2caf59SThomas Veerman if (shellArgv) 23892e2caf59SThomas Veerman free(UNCONST(shellArgv)); 23902e2caf59SThomas Veerman 23912e2caf59SThomas Veerman memset(&newShell, 0, sizeof(newShell)); 23922e2caf59SThomas Veerman 23932e2caf59SThomas Veerman /* 23942e2caf59SThomas Veerman * Parse the specification by keyword 23952e2caf59SThomas Veerman */ 23962e2caf59SThomas Veerman words = brk_string(line, &argc, TRUE, &path); 23972e2caf59SThomas Veerman if (words == NULL) { 23982e2caf59SThomas Veerman Error("Unterminated quoted string [%s]", line); 23992e2caf59SThomas Veerman return FAILURE; 24002e2caf59SThomas Veerman } 24012e2caf59SThomas Veerman shellArgv = path; 24022e2caf59SThomas Veerman 24032e2caf59SThomas Veerman for (path = NULL, argv = words; argc != 0; argc--, argv++) { 24042e2caf59SThomas Veerman if (strncmp(*argv, "path=", 5) == 0) { 24052e2caf59SThomas Veerman path = &argv[0][5]; 24062e2caf59SThomas Veerman } else if (strncmp(*argv, "name=", 5) == 0) { 24072e2caf59SThomas Veerman newShell.name = &argv[0][5]; 24082e2caf59SThomas Veerman } else { 24092e2caf59SThomas Veerman if (strncmp(*argv, "quiet=", 6) == 0) { 24102e2caf59SThomas Veerman newShell.echoOff = &argv[0][6]; 24112e2caf59SThomas Veerman } else if (strncmp(*argv, "echo=", 5) == 0) { 24122e2caf59SThomas Veerman newShell.echoOn = &argv[0][5]; 24132e2caf59SThomas Veerman } else if (strncmp(*argv, "filter=", 7) == 0) { 24142e2caf59SThomas Veerman newShell.noPrint = &argv[0][7]; 24152e2caf59SThomas Veerman newShell.noPLen = strlen(newShell.noPrint); 24162e2caf59SThomas Veerman } else if (strncmp(*argv, "echoFlag=", 9) == 0) { 24172e2caf59SThomas Veerman newShell.echo = &argv[0][9]; 24182e2caf59SThomas Veerman } else if (strncmp(*argv, "errFlag=", 8) == 0) { 24192e2caf59SThomas Veerman newShell.exit = &argv[0][8]; 24202e2caf59SThomas Veerman } else if (strncmp(*argv, "hasErrCtl=", 10) == 0) { 24212e2caf59SThomas Veerman char c = argv[0][10]; 24222e2caf59SThomas Veerman newShell.hasErrCtl = !((c != 'Y') && (c != 'y') && 24232e2caf59SThomas Veerman (c != 'T') && (c != 't')); 24242e2caf59SThomas Veerman } else if (strncmp(*argv, "newline=", 8) == 0) { 24252e2caf59SThomas Veerman newShell.newline = &argv[0][8]; 24262e2caf59SThomas Veerman } else if (strncmp(*argv, "check=", 6) == 0) { 24272e2caf59SThomas Veerman newShell.errCheck = &argv[0][6]; 24282e2caf59SThomas Veerman } else if (strncmp(*argv, "ignore=", 7) == 0) { 24292e2caf59SThomas Veerman newShell.ignErr = &argv[0][7]; 24302e2caf59SThomas Veerman } else if (strncmp(*argv, "errout=", 7) == 0) { 24312e2caf59SThomas Veerman newShell.errOut = &argv[0][7]; 24322e2caf59SThomas Veerman } else if (strncmp(*argv, "comment=", 8) == 0) { 24332e2caf59SThomas Veerman newShell.commentChar = argv[0][8]; 24342e2caf59SThomas Veerman } else { 24352e2caf59SThomas Veerman Parse_Error(PARSE_FATAL, "Unknown keyword \"%s\"", 24362e2caf59SThomas Veerman *argv); 24372e2caf59SThomas Veerman free(words); 24382e2caf59SThomas Veerman return(FAILURE); 24392e2caf59SThomas Veerman } 24402e2caf59SThomas Veerman fullSpec = TRUE; 24412e2caf59SThomas Veerman } 24422e2caf59SThomas Veerman } 24432e2caf59SThomas Veerman 24442e2caf59SThomas Veerman if (path == NULL) { 24452e2caf59SThomas Veerman /* 24462e2caf59SThomas Veerman * If no path was given, the user wants one of the pre-defined shells, 24472e2caf59SThomas Veerman * yes? So we find the one s/he wants with the help of JobMatchShell 24482e2caf59SThomas Veerman * and set things up the right way. shellPath will be set up by 24492bc7c627SLionel Sambuc * Shell_Init. 24502e2caf59SThomas Veerman */ 24512e2caf59SThomas Veerman if (newShell.name == NULL) { 24522e2caf59SThomas Veerman Parse_Error(PARSE_FATAL, "Neither path nor name specified"); 24532e2caf59SThomas Veerman free(words); 24542e2caf59SThomas Veerman return(FAILURE); 24552e2caf59SThomas Veerman } else { 24562e2caf59SThomas Veerman if ((sh = JobMatchShell(newShell.name)) == NULL) { 24572e2caf59SThomas Veerman Parse_Error(PARSE_WARNING, "%s: No matching shell", 24582e2caf59SThomas Veerman newShell.name); 24592e2caf59SThomas Veerman free(words); 24602e2caf59SThomas Veerman return(FAILURE); 24612e2caf59SThomas Veerman } 24622e2caf59SThomas Veerman commandShell = sh; 24632e2caf59SThomas Veerman shellName = newShell.name; 24642bc7c627SLionel Sambuc if (shellPath) { 24652bc7c627SLionel Sambuc /* Shell_Init has already been called! Do it again. */ 24662bc7c627SLionel Sambuc free(UNCONST(shellPath)); 24672bc7c627SLionel Sambuc shellPath = NULL; 24682bc7c627SLionel Sambuc Shell_Init(); 24692bc7c627SLionel Sambuc } 24702e2caf59SThomas Veerman } 24712e2caf59SThomas Veerman } else { 24722e2caf59SThomas Veerman /* 24732e2caf59SThomas Veerman * The user provided a path. If s/he gave nothing else (fullSpec is 24742e2caf59SThomas Veerman * FALSE), try and find a matching shell in the ones we know of. 24752e2caf59SThomas Veerman * Else we just take the specification at its word and copy it 24762e2caf59SThomas Veerman * to a new location. In either case, we need to record the 24772e2caf59SThomas Veerman * path the user gave for the shell. 24782e2caf59SThomas Veerman */ 24792e2caf59SThomas Veerman shellPath = path; 24802e2caf59SThomas Veerman path = strrchr(path, '/'); 24812e2caf59SThomas Veerman if (path == NULL) { 24822e2caf59SThomas Veerman path = UNCONST(shellPath); 24832e2caf59SThomas Veerman } else { 24842e2caf59SThomas Veerman path += 1; 24852e2caf59SThomas Veerman } 24862e2caf59SThomas Veerman if (newShell.name != NULL) { 24872e2caf59SThomas Veerman shellName = newShell.name; 24882e2caf59SThomas Veerman } else { 24892e2caf59SThomas Veerman shellName = path; 24902e2caf59SThomas Veerman } 24912e2caf59SThomas Veerman if (!fullSpec) { 24922e2caf59SThomas Veerman if ((sh = JobMatchShell(shellName)) == NULL) { 24932e2caf59SThomas Veerman Parse_Error(PARSE_WARNING, "%s: No matching shell", 24942e2caf59SThomas Veerman shellName); 24952e2caf59SThomas Veerman free(words); 24962e2caf59SThomas Veerman return(FAILURE); 24972e2caf59SThomas Veerman } 24982e2caf59SThomas Veerman commandShell = sh; 24992e2caf59SThomas Veerman } else { 25002e2caf59SThomas Veerman commandShell = bmake_malloc(sizeof(Shell)); 25012e2caf59SThomas Veerman *commandShell = newShell; 25022e2caf59SThomas Veerman } 2503*84d9c625SLionel Sambuc /* this will take care of shellErrFlag */ 2504*84d9c625SLionel Sambuc Shell_Init(); 25052e2caf59SThomas Veerman } 25062e2caf59SThomas Veerman 25072e2caf59SThomas Veerman if (commandShell->echoOn && commandShell->echoOff) { 25082e2caf59SThomas Veerman commandShell->hasEchoCtl = TRUE; 25092e2caf59SThomas Veerman } 25102e2caf59SThomas Veerman 25112e2caf59SThomas Veerman if (!commandShell->hasErrCtl) { 25122e2caf59SThomas Veerman if (commandShell->errCheck == NULL) { 25132e2caf59SThomas Veerman commandShell->errCheck = ""; 25142e2caf59SThomas Veerman } 25152e2caf59SThomas Veerman if (commandShell->ignErr == NULL) { 25162e2caf59SThomas Veerman commandShell->ignErr = "%s\n"; 25172e2caf59SThomas Veerman } 25182e2caf59SThomas Veerman } 25192e2caf59SThomas Veerman 25202e2caf59SThomas Veerman /* 25212e2caf59SThomas Veerman * Do not free up the words themselves, since they might be in use by the 25222e2caf59SThomas Veerman * shell specification. 25232e2caf59SThomas Veerman */ 25242e2caf59SThomas Veerman free(words); 25252e2caf59SThomas Veerman return SUCCESS; 25262e2caf59SThomas Veerman } 25272e2caf59SThomas Veerman 25282e2caf59SThomas Veerman /*- 25292e2caf59SThomas Veerman *----------------------------------------------------------------------- 25302e2caf59SThomas Veerman * JobInterrupt -- 25312e2caf59SThomas Veerman * Handle the receipt of an interrupt. 25322e2caf59SThomas Veerman * 25332e2caf59SThomas Veerman * Input: 25342e2caf59SThomas Veerman * runINTERRUPT Non-zero if commands for the .INTERRUPT target 25352e2caf59SThomas Veerman * should be executed 25362e2caf59SThomas Veerman * signo signal received 25372e2caf59SThomas Veerman * 25382e2caf59SThomas Veerman * Results: 25392e2caf59SThomas Veerman * None 25402e2caf59SThomas Veerman * 25412e2caf59SThomas Veerman * Side Effects: 25422e2caf59SThomas Veerman * All children are killed. Another job will be started if the 25432e2caf59SThomas Veerman * .INTERRUPT target was given. 25442e2caf59SThomas Veerman *----------------------------------------------------------------------- 25452e2caf59SThomas Veerman */ 25462e2caf59SThomas Veerman static void 25472e2caf59SThomas Veerman JobInterrupt(int runINTERRUPT, int signo) 25482e2caf59SThomas Veerman { 25492e2caf59SThomas Veerman Job *job; /* job descriptor in that element */ 25502e2caf59SThomas Veerman GNode *interrupt; /* the node describing the .INTERRUPT target */ 25512e2caf59SThomas Veerman sigset_t mask; 25522e2caf59SThomas Veerman GNode *gn; 25532e2caf59SThomas Veerman 25542e2caf59SThomas Veerman aborting = ABORT_INTERRUPT; 25552e2caf59SThomas Veerman 25562e2caf59SThomas Veerman JobSigLock(&mask); 25572e2caf59SThomas Veerman 25582e2caf59SThomas Veerman for (job = job_table; job < job_table_end; job++) { 25592e2caf59SThomas Veerman if (job->job_state != JOB_ST_RUNNING) 25602e2caf59SThomas Veerman continue; 25612e2caf59SThomas Veerman 25622e2caf59SThomas Veerman gn = job->node; 25632e2caf59SThomas Veerman 25642e2caf59SThomas Veerman if ((gn->type & (OP_JOIN|OP_PHONY)) == 0 && !Targ_Precious(gn)) { 25652e2caf59SThomas Veerman char *file = (gn->path == NULL ? gn->name : gn->path); 25662e2caf59SThomas Veerman if (!noExecute && eunlink(file) != -1) { 25672e2caf59SThomas Veerman Error("*** %s removed", file); 25682e2caf59SThomas Veerman } 25692e2caf59SThomas Veerman } 25702e2caf59SThomas Veerman if (job->pid) { 25712e2caf59SThomas Veerman if (DEBUG(JOB)) { 25722e2caf59SThomas Veerman (void)fprintf(debug_file, 25732e2caf59SThomas Veerman "JobInterrupt passing signal %d to child %d.\n", 25742e2caf59SThomas Veerman signo, job->pid); 25752e2caf59SThomas Veerman } 25762e2caf59SThomas Veerman KILLPG(job->pid, signo); 25772e2caf59SThomas Veerman } 25782e2caf59SThomas Veerman } 25792e2caf59SThomas Veerman 25802e2caf59SThomas Veerman JobSigUnlock(&mask); 25812e2caf59SThomas Veerman 25822e2caf59SThomas Veerman if (runINTERRUPT && !touchFlag) { 25832e2caf59SThomas Veerman interrupt = Targ_FindNode(".INTERRUPT", TARG_NOCREATE); 25842e2caf59SThomas Veerman if (interrupt != NULL) { 25852e2caf59SThomas Veerman ignoreErrors = FALSE; 25862e2caf59SThomas Veerman JobRun(interrupt); 25872e2caf59SThomas Veerman } 25882e2caf59SThomas Veerman } 25892e2caf59SThomas Veerman Trace_Log(MAKEINTR, 0); 25902e2caf59SThomas Veerman exit(signo); 25912e2caf59SThomas Veerman } 25922e2caf59SThomas Veerman 25932e2caf59SThomas Veerman /* 25942e2caf59SThomas Veerman *----------------------------------------------------------------------- 25952e2caf59SThomas Veerman * Job_Finish -- 25962e2caf59SThomas Veerman * Do final processing such as the running of the commands 25972e2caf59SThomas Veerman * attached to the .END target. 25982e2caf59SThomas Veerman * 25992e2caf59SThomas Veerman * Results: 26002e2caf59SThomas Veerman * Number of errors reported. 26012e2caf59SThomas Veerman * 26022e2caf59SThomas Veerman * Side Effects: 26032e2caf59SThomas Veerman * None. 26042e2caf59SThomas Veerman *----------------------------------------------------------------------- 26052e2caf59SThomas Veerman */ 26062e2caf59SThomas Veerman int 26072e2caf59SThomas Veerman Job_Finish(void) 26082e2caf59SThomas Veerman { 26092e2caf59SThomas Veerman if (postCommands != NULL && 26102e2caf59SThomas Veerman (!Lst_IsEmpty(postCommands->commands) || 26112e2caf59SThomas Veerman !Lst_IsEmpty(postCommands->children))) { 26122e2caf59SThomas Veerman if (errors) { 26132e2caf59SThomas Veerman Error("Errors reported so .END ignored"); 26142e2caf59SThomas Veerman } else { 26152e2caf59SThomas Veerman JobRun(postCommands); 26162e2caf59SThomas Veerman } 26172e2caf59SThomas Veerman } 26182e2caf59SThomas Veerman return(errors); 26192e2caf59SThomas Veerman } 26202e2caf59SThomas Veerman 26212e2caf59SThomas Veerman /*- 26222e2caf59SThomas Veerman *----------------------------------------------------------------------- 26232e2caf59SThomas Veerman * Job_End -- 26242e2caf59SThomas Veerman * Cleanup any memory used by the jobs module 26252e2caf59SThomas Veerman * 26262e2caf59SThomas Veerman * Results: 26272e2caf59SThomas Veerman * None. 26282e2caf59SThomas Veerman * 26292e2caf59SThomas Veerman * Side Effects: 26302e2caf59SThomas Veerman * Memory is freed 26312e2caf59SThomas Veerman *----------------------------------------------------------------------- 26322e2caf59SThomas Veerman */ 26332e2caf59SThomas Veerman void 26342e2caf59SThomas Veerman Job_End(void) 26352e2caf59SThomas Veerman { 26362e2caf59SThomas Veerman #ifdef CLEANUP 26372e2caf59SThomas Veerman if (shellArgv) 26382e2caf59SThomas Veerman free(shellArgv); 26392e2caf59SThomas Veerman #endif 26402e2caf59SThomas Veerman } 26412e2caf59SThomas Veerman 26422e2caf59SThomas Veerman /*- 26432e2caf59SThomas Veerman *----------------------------------------------------------------------- 26442e2caf59SThomas Veerman * Job_Wait -- 26452e2caf59SThomas Veerman * Waits for all running jobs to finish and returns. Sets 'aborting' 26462e2caf59SThomas Veerman * to ABORT_WAIT to prevent other jobs from starting. 26472e2caf59SThomas Veerman * 26482e2caf59SThomas Veerman * Results: 26492e2caf59SThomas Veerman * None. 26502e2caf59SThomas Veerman * 26512e2caf59SThomas Veerman * Side Effects: 26522e2caf59SThomas Veerman * Currently running jobs finish. 26532e2caf59SThomas Veerman * 26542e2caf59SThomas Veerman *----------------------------------------------------------------------- 26552e2caf59SThomas Veerman */ 26562e2caf59SThomas Veerman void 26572e2caf59SThomas Veerman Job_Wait(void) 26582e2caf59SThomas Veerman { 26592e2caf59SThomas Veerman aborting = ABORT_WAIT; 26602e2caf59SThomas Veerman while (jobTokensRunning != 0) { 26612e2caf59SThomas Veerman Job_CatchOutput(); 26622e2caf59SThomas Veerman } 26632e2caf59SThomas Veerman aborting = 0; 26642e2caf59SThomas Veerman } 26652e2caf59SThomas Veerman 26662e2caf59SThomas Veerman /*- 26672e2caf59SThomas Veerman *----------------------------------------------------------------------- 26682e2caf59SThomas Veerman * Job_AbortAll -- 26692e2caf59SThomas Veerman * Abort all currently running jobs without handling output or anything. 26702e2caf59SThomas Veerman * This function is to be called only in the event of a major 26712e2caf59SThomas Veerman * error. Most definitely NOT to be called from JobInterrupt. 26722e2caf59SThomas Veerman * 26732e2caf59SThomas Veerman * Results: 26742e2caf59SThomas Veerman * None 26752e2caf59SThomas Veerman * 26762e2caf59SThomas Veerman * Side Effects: 26772e2caf59SThomas Veerman * All children are killed, not just the firstborn 26782e2caf59SThomas Veerman *----------------------------------------------------------------------- 26792e2caf59SThomas Veerman */ 26802e2caf59SThomas Veerman void 26812e2caf59SThomas Veerman Job_AbortAll(void) 26822e2caf59SThomas Veerman { 26832e2caf59SThomas Veerman Job *job; /* the job descriptor in that element */ 26842e2caf59SThomas Veerman int foo; 26852e2caf59SThomas Veerman 26862e2caf59SThomas Veerman aborting = ABORT_ERROR; 26872e2caf59SThomas Veerman 26882e2caf59SThomas Veerman if (jobTokensRunning) { 26892e2caf59SThomas Veerman for (job = job_table; job < job_table_end; job++) { 26902e2caf59SThomas Veerman if (job->job_state != JOB_ST_RUNNING) 26912e2caf59SThomas Veerman continue; 26922e2caf59SThomas Veerman /* 26932e2caf59SThomas Veerman * kill the child process with increasingly drastic signals to make 26942e2caf59SThomas Veerman * darn sure it's dead. 26952e2caf59SThomas Veerman */ 26962e2caf59SThomas Veerman KILLPG(job->pid, SIGINT); 26972e2caf59SThomas Veerman KILLPG(job->pid, SIGKILL); 26982e2caf59SThomas Veerman } 26992e2caf59SThomas Veerman } 27002e2caf59SThomas Veerman 27012e2caf59SThomas Veerman /* 27022e2caf59SThomas Veerman * Catch as many children as want to report in at first, then give up 27032e2caf59SThomas Veerman */ 27042e2caf59SThomas Veerman while (waitpid((pid_t) -1, &foo, WNOHANG) > 0) 27052e2caf59SThomas Veerman continue; 27062e2caf59SThomas Veerman } 27072e2caf59SThomas Veerman 27082e2caf59SThomas Veerman 27092e2caf59SThomas Veerman /*- 27102e2caf59SThomas Veerman *----------------------------------------------------------------------- 27112e2caf59SThomas Veerman * JobRestartJobs -- 27122e2caf59SThomas Veerman * Tries to restart stopped jobs if there are slots available. 27132e2caf59SThomas Veerman * Called in process context in response to a SIGCONT. 27142e2caf59SThomas Veerman * 27152e2caf59SThomas Veerman * Results: 27162e2caf59SThomas Veerman * None. 27172e2caf59SThomas Veerman * 27182e2caf59SThomas Veerman * Side Effects: 27192e2caf59SThomas Veerman * Resumes jobs. 27202e2caf59SThomas Veerman * 27212e2caf59SThomas Veerman *----------------------------------------------------------------------- 27222e2caf59SThomas Veerman */ 27232e2caf59SThomas Veerman static void 27242e2caf59SThomas Veerman JobRestartJobs(void) 27252e2caf59SThomas Veerman { 27262e2caf59SThomas Veerman Job *job; 27272e2caf59SThomas Veerman 27282e2caf59SThomas Veerman for (job = job_table; job < job_table_end; job++) { 27292e2caf59SThomas Veerman if (job->job_state == JOB_ST_RUNNING && 27302e2caf59SThomas Veerman (make_suspended || job->job_suspended)) { 27312e2caf59SThomas Veerman if (DEBUG(JOB)) { 27322e2caf59SThomas Veerman (void)fprintf(debug_file, "Restarting stopped job pid %d.\n", 27332e2caf59SThomas Veerman job->pid); 27342e2caf59SThomas Veerman } 27352e2caf59SThomas Veerman if (job->job_suspended) { 27362e2caf59SThomas Veerman (void)printf("*** [%s] Continued\n", job->node->name); 27372e2caf59SThomas Veerman (void)fflush(stdout); 27382e2caf59SThomas Veerman } 27392e2caf59SThomas Veerman job->job_suspended = 0; 27402e2caf59SThomas Veerman if (KILLPG(job->pid, SIGCONT) != 0 && DEBUG(JOB)) { 27412e2caf59SThomas Veerman fprintf(debug_file, "Failed to send SIGCONT to %d\n", job->pid); 27422e2caf59SThomas Veerman } 27432e2caf59SThomas Veerman } 27442e2caf59SThomas Veerman if (job->job_state == JOB_ST_FINISHED) 27452e2caf59SThomas Veerman /* Job exit deferred after calling waitpid() in a signal handler */ 27462e2caf59SThomas Veerman JobFinish(job, job->exit_status); 27472e2caf59SThomas Veerman } 27482e2caf59SThomas Veerman make_suspended = 0; 27492e2caf59SThomas Veerman } 27502e2caf59SThomas Veerman 27512e2caf59SThomas Veerman static void 27522e2caf59SThomas Veerman watchfd(Job *job) 27532e2caf59SThomas Veerman { 27542e2caf59SThomas Veerman if (job->inPollfd != NULL) 27552e2caf59SThomas Veerman Punt("Watching watched job"); 27562e2caf59SThomas Veerman 27572e2caf59SThomas Veerman fds[nfds].fd = job->inPipe; 27582e2caf59SThomas Veerman fds[nfds].events = POLLIN; 27592e2caf59SThomas Veerman jobfds[nfds] = job; 27602e2caf59SThomas Veerman job->inPollfd = &fds[nfds]; 27612e2caf59SThomas Veerman nfds++; 27622e2caf59SThomas Veerman } 27632e2caf59SThomas Veerman 27642e2caf59SThomas Veerman static void 27652e2caf59SThomas Veerman clearfd(Job *job) 27662e2caf59SThomas Veerman { 27672e2caf59SThomas Veerman int i; 27682e2caf59SThomas Veerman if (job->inPollfd == NULL) 27692e2caf59SThomas Veerman Punt("Unwatching unwatched job"); 27702e2caf59SThomas Veerman i = job->inPollfd - fds; 27712e2caf59SThomas Veerman nfds--; 27722e2caf59SThomas Veerman /* 27732e2caf59SThomas Veerman * Move last job in table into hole made by dead job. 27742e2caf59SThomas Veerman */ 27752e2caf59SThomas Veerman if (nfds != i) { 27762e2caf59SThomas Veerman fds[i] = fds[nfds]; 27772e2caf59SThomas Veerman jobfds[i] = jobfds[nfds]; 27782e2caf59SThomas Veerman jobfds[i]->inPollfd = &fds[i]; 27792e2caf59SThomas Veerman } 27802e2caf59SThomas Veerman job->inPollfd = NULL; 27812e2caf59SThomas Veerman } 27822e2caf59SThomas Veerman 27832e2caf59SThomas Veerman static int 27842e2caf59SThomas Veerman readyfd(Job *job) 27852e2caf59SThomas Veerman { 27862e2caf59SThomas Veerman if (job->inPollfd == NULL) 27872e2caf59SThomas Veerman Punt("Polling unwatched job"); 27882e2caf59SThomas Veerman return (job->inPollfd->revents & POLLIN) != 0; 27892e2caf59SThomas Veerman } 27902e2caf59SThomas Veerman 27912e2caf59SThomas Veerman /*- 27922e2caf59SThomas Veerman *----------------------------------------------------------------------- 27932e2caf59SThomas Veerman * JobTokenAdd -- 27942e2caf59SThomas Veerman * Put a token into the job pipe so that some make process can start 27952e2caf59SThomas Veerman * another job. 27962e2caf59SThomas Veerman * 27972e2caf59SThomas Veerman * Side Effects: 27982e2caf59SThomas Veerman * Allows more build jobs to be spawned somewhere. 27992e2caf59SThomas Veerman * 28002e2caf59SThomas Veerman *----------------------------------------------------------------------- 28012e2caf59SThomas Veerman */ 28022e2caf59SThomas Veerman 28032e2caf59SThomas Veerman static void 28042e2caf59SThomas Veerman JobTokenAdd(void) 28052e2caf59SThomas Veerman { 28062e2caf59SThomas Veerman char tok = JOB_TOKENS[aborting], tok1; 28072e2caf59SThomas Veerman 28082e2caf59SThomas Veerman /* If we are depositing an error token flush everything else */ 28092e2caf59SThomas Veerman while (tok != '+' && read(tokenWaitJob.inPipe, &tok1, 1) == 1) 28102e2caf59SThomas Veerman continue; 28112e2caf59SThomas Veerman 28122e2caf59SThomas Veerman if (DEBUG(JOB)) 28132e2caf59SThomas Veerman fprintf(debug_file, "(%d) aborting %d, deposit token %c\n", 28142e2caf59SThomas Veerman getpid(), aborting, JOB_TOKENS[aborting]); 2815*84d9c625SLionel Sambuc while (write(tokenWaitJob.outPipe, &tok, 1) == -1 && errno == EAGAIN) 2816*84d9c625SLionel Sambuc continue; 28172e2caf59SThomas Veerman } 28182e2caf59SThomas Veerman 28192e2caf59SThomas Veerman /*- 28202e2caf59SThomas Veerman *----------------------------------------------------------------------- 28212e2caf59SThomas Veerman * Job_ServerStartTokenAdd -- 28222e2caf59SThomas Veerman * Prep the job token pipe in the root make process. 28232e2caf59SThomas Veerman * 28242e2caf59SThomas Veerman *----------------------------------------------------------------------- 28252e2caf59SThomas Veerman */ 28262e2caf59SThomas Veerman 28272e2caf59SThomas Veerman void 28282e2caf59SThomas Veerman Job_ServerStart(int max_tokens, int jp_0, int jp_1) 28292e2caf59SThomas Veerman { 28302e2caf59SThomas Veerman int i; 28312e2caf59SThomas Veerman char jobarg[64]; 28322e2caf59SThomas Veerman 28332e2caf59SThomas Veerman if (jp_0 >= 0 && jp_1 >= 0) { 28342e2caf59SThomas Veerman /* Pipe passed in from parent */ 28352e2caf59SThomas Veerman tokenWaitJob.inPipe = jp_0; 28362e2caf59SThomas Veerman tokenWaitJob.outPipe = jp_1; 2837*84d9c625SLionel Sambuc (void)fcntl(jp_0, F_SETFD, 1); 2838*84d9c625SLionel Sambuc (void)fcntl(jp_1, F_SETFD, 1); 28392e2caf59SThomas Veerman return; 28402e2caf59SThomas Veerman } 28412e2caf59SThomas Veerman 28422e2caf59SThomas Veerman JobCreatePipe(&tokenWaitJob, 15); 28432e2caf59SThomas Veerman 28442e2caf59SThomas Veerman snprintf(jobarg, sizeof(jobarg), "%d,%d", 28452e2caf59SThomas Veerman tokenWaitJob.inPipe, tokenWaitJob.outPipe); 28462e2caf59SThomas Veerman 28472e2caf59SThomas Veerman Var_Append(MAKEFLAGS, "-J", VAR_GLOBAL); 28482e2caf59SThomas Veerman Var_Append(MAKEFLAGS, jobarg, VAR_GLOBAL); 28492e2caf59SThomas Veerman 28502e2caf59SThomas Veerman /* 28512e2caf59SThomas Veerman * Preload the job pipe with one token per job, save the one 28522e2caf59SThomas Veerman * "extra" token for the primary job. 28532e2caf59SThomas Veerman * 28542e2caf59SThomas Veerman * XXX should clip maxJobs against PIPE_BUF -- if max_tokens is 28552e2caf59SThomas Veerman * larger than the write buffer size of the pipe, we will 28562e2caf59SThomas Veerman * deadlock here. 28572e2caf59SThomas Veerman */ 28582e2caf59SThomas Veerman for (i = 1; i < max_tokens; i++) 28592e2caf59SThomas Veerman JobTokenAdd(); 28602e2caf59SThomas Veerman } 28612e2caf59SThomas Veerman 28622e2caf59SThomas Veerman /*- 28632e2caf59SThomas Veerman *----------------------------------------------------------------------- 28642e2caf59SThomas Veerman * Job_TokenReturn -- 28652e2caf59SThomas Veerman * Return a withdrawn token to the pool. 28662e2caf59SThomas Veerman * 28672e2caf59SThomas Veerman *----------------------------------------------------------------------- 28682e2caf59SThomas Veerman */ 28692e2caf59SThomas Veerman 28702e2caf59SThomas Veerman void 28712e2caf59SThomas Veerman Job_TokenReturn(void) 28722e2caf59SThomas Veerman { 28732e2caf59SThomas Veerman jobTokensRunning--; 28742e2caf59SThomas Veerman if (jobTokensRunning < 0) 28752e2caf59SThomas Veerman Punt("token botch"); 28762e2caf59SThomas Veerman if (jobTokensRunning || JOB_TOKENS[aborting] != '+') 28772e2caf59SThomas Veerman JobTokenAdd(); 28782e2caf59SThomas Veerman } 28792e2caf59SThomas Veerman 28802e2caf59SThomas Veerman /*- 28812e2caf59SThomas Veerman *----------------------------------------------------------------------- 28822e2caf59SThomas Veerman * Job_TokenWithdraw -- 28832e2caf59SThomas Veerman * Attempt to withdraw a token from the pool. 28842e2caf59SThomas Veerman * 28852e2caf59SThomas Veerman * Results: 28862e2caf59SThomas Veerman * Returns TRUE if a token was withdrawn, and FALSE if the pool 28872e2caf59SThomas Veerman * is currently empty. 28882e2caf59SThomas Veerman * 28892e2caf59SThomas Veerman * Side Effects: 28902e2caf59SThomas Veerman * If pool is empty, set wantToken so that we wake up 28912e2caf59SThomas Veerman * when a token is released. 28922e2caf59SThomas Veerman * 28932e2caf59SThomas Veerman *----------------------------------------------------------------------- 28942e2caf59SThomas Veerman */ 28952e2caf59SThomas Veerman 28962e2caf59SThomas Veerman 28972e2caf59SThomas Veerman Boolean 28982e2caf59SThomas Veerman Job_TokenWithdraw(void) 28992e2caf59SThomas Veerman { 29002e2caf59SThomas Veerman char tok, tok1; 29012e2caf59SThomas Veerman int count; 29022e2caf59SThomas Veerman 29032e2caf59SThomas Veerman wantToken = 0; 29042e2caf59SThomas Veerman if (DEBUG(JOB)) 29052e2caf59SThomas Veerman fprintf(debug_file, "Job_TokenWithdraw(%d): aborting %d, running %d\n", 29062e2caf59SThomas Veerman getpid(), aborting, jobTokensRunning); 29072e2caf59SThomas Veerman 29082e2caf59SThomas Veerman if (aborting || (jobTokensRunning >= maxJobs)) 29092e2caf59SThomas Veerman return FALSE; 29102e2caf59SThomas Veerman 29112e2caf59SThomas Veerman count = read(tokenWaitJob.inPipe, &tok, 1); 29122e2caf59SThomas Veerman if (count == 0) 29132e2caf59SThomas Veerman Fatal("eof on job pipe!"); 29142e2caf59SThomas Veerman if (count < 0 && jobTokensRunning != 0) { 29152e2caf59SThomas Veerman if (errno != EAGAIN) { 29162e2caf59SThomas Veerman Fatal("job pipe read: %s", strerror(errno)); 29172e2caf59SThomas Veerman } 29182e2caf59SThomas Veerman if (DEBUG(JOB)) 29192e2caf59SThomas Veerman fprintf(debug_file, "(%d) blocked for token\n", getpid()); 29202e2caf59SThomas Veerman wantToken = 1; 29212e2caf59SThomas Veerman return FALSE; 29222e2caf59SThomas Veerman } 29232e2caf59SThomas Veerman 29242e2caf59SThomas Veerman if (count == 1 && tok != '+') { 29252e2caf59SThomas Veerman /* make being abvorted - remove any other job tokens */ 29262e2caf59SThomas Veerman if (DEBUG(JOB)) 29272e2caf59SThomas Veerman fprintf(debug_file, "(%d) aborted by token %c\n", getpid(), tok); 29282e2caf59SThomas Veerman while (read(tokenWaitJob.inPipe, &tok1, 1) == 1) 29292e2caf59SThomas Veerman continue; 29302e2caf59SThomas Veerman /* And put the stopper back */ 2931*84d9c625SLionel Sambuc while (write(tokenWaitJob.outPipe, &tok, 1) == -1 && errno == EAGAIN) 2932*84d9c625SLionel Sambuc continue; 29332e2caf59SThomas Veerman Fatal("A failure has been detected in another branch of the parallel make"); 29342e2caf59SThomas Veerman } 29352e2caf59SThomas Veerman 29362e2caf59SThomas Veerman if (count == 1 && jobTokensRunning == 0) 29372e2caf59SThomas Veerman /* We didn't want the token really */ 2938*84d9c625SLionel Sambuc while (write(tokenWaitJob.outPipe, &tok, 1) == -1 && errno == EAGAIN) 2939*84d9c625SLionel Sambuc continue; 29402e2caf59SThomas Veerman 29412e2caf59SThomas Veerman jobTokensRunning++; 29422e2caf59SThomas Veerman if (DEBUG(JOB)) 29432e2caf59SThomas Veerman fprintf(debug_file, "(%d) withdrew token\n", getpid()); 29442e2caf59SThomas Veerman return TRUE; 29452e2caf59SThomas Veerman } 29462e2caf59SThomas Veerman 2947*84d9c625SLionel Sambuc /*- 2948*84d9c625SLionel Sambuc *----------------------------------------------------------------------- 2949*84d9c625SLionel Sambuc * Job_RunTarget -- 2950*84d9c625SLionel Sambuc * Run the named target if found. If a filename is specified, then 2951*84d9c625SLionel Sambuc * set that to the sources. 2952*84d9c625SLionel Sambuc * 2953*84d9c625SLionel Sambuc * Results: 2954*84d9c625SLionel Sambuc * None 2955*84d9c625SLionel Sambuc * 2956*84d9c625SLionel Sambuc * Side Effects: 2957*84d9c625SLionel Sambuc * exits if the target fails. 2958*84d9c625SLionel Sambuc * 2959*84d9c625SLionel Sambuc *----------------------------------------------------------------------- 2960*84d9c625SLionel Sambuc */ 2961*84d9c625SLionel Sambuc Boolean 2962*84d9c625SLionel Sambuc Job_RunTarget(const char *target, const char *fname) { 2963*84d9c625SLionel Sambuc GNode *gn = Targ_FindNode(target, TARG_NOCREATE); 2964*84d9c625SLionel Sambuc 2965*84d9c625SLionel Sambuc if (gn == NULL) 2966*84d9c625SLionel Sambuc return FALSE; 2967*84d9c625SLionel Sambuc 2968*84d9c625SLionel Sambuc if (fname) 2969*84d9c625SLionel Sambuc Var_Set(ALLSRC, fname, gn, 0); 2970*84d9c625SLionel Sambuc 2971*84d9c625SLionel Sambuc JobRun(gn); 2972*84d9c625SLionel Sambuc if (gn->made == ERROR) { 2973*84d9c625SLionel Sambuc PrintOnError(gn, "\n\nStop."); 2974*84d9c625SLionel Sambuc exit(1); 2975*84d9c625SLionel Sambuc } 2976*84d9c625SLionel Sambuc return TRUE; 2977*84d9c625SLionel Sambuc } 2978*84d9c625SLionel Sambuc 29792e2caf59SThomas Veerman #ifdef USE_SELECT 29802e2caf59SThomas Veerman int 29812e2caf59SThomas Veerman emul_poll(struct pollfd *fd, int nfd, int timeout) 29822e2caf59SThomas Veerman { 29832e2caf59SThomas Veerman fd_set rfds, wfds; 29842e2caf59SThomas Veerman int i, maxfd, nselect, npoll; 29852e2caf59SThomas Veerman struct timeval tv, *tvp; 29862e2caf59SThomas Veerman long usecs; 29872e2caf59SThomas Veerman 29882e2caf59SThomas Veerman FD_ZERO(&rfds); 29892e2caf59SThomas Veerman FD_ZERO(&wfds); 29902e2caf59SThomas Veerman 29912e2caf59SThomas Veerman maxfd = -1; 29922e2caf59SThomas Veerman for (i = 0; i < nfd; i++) { 29932e2caf59SThomas Veerman fd[i].revents = 0; 29942e2caf59SThomas Veerman 29952e2caf59SThomas Veerman if (fd[i].events & POLLIN) 29962e2caf59SThomas Veerman FD_SET(fd[i].fd, &rfds); 29972e2caf59SThomas Veerman 29982e2caf59SThomas Veerman if (fd[i].events & POLLOUT) 29992e2caf59SThomas Veerman FD_SET(fd[i].fd, &wfds); 30002e2caf59SThomas Veerman 30012e2caf59SThomas Veerman if (fd[i].fd > maxfd) 30022e2caf59SThomas Veerman maxfd = fd[i].fd; 30032e2caf59SThomas Veerman } 30042e2caf59SThomas Veerman 30052e2caf59SThomas Veerman if (maxfd >= FD_SETSIZE) { 30062e2caf59SThomas Veerman Punt("Ran out of fd_set slots; " 30072e2caf59SThomas Veerman "recompile with a larger FD_SETSIZE."); 30082e2caf59SThomas Veerman } 30092e2caf59SThomas Veerman 30102e2caf59SThomas Veerman if (timeout < 0) { 30112e2caf59SThomas Veerman tvp = NULL; 30122e2caf59SThomas Veerman } else { 30132e2caf59SThomas Veerman usecs = timeout * 1000; 30142e2caf59SThomas Veerman tv.tv_sec = usecs / 1000000; 30152e2caf59SThomas Veerman tv.tv_usec = usecs % 1000000; 30162e2caf59SThomas Veerman tvp = &tv; 30172e2caf59SThomas Veerman } 30182e2caf59SThomas Veerman 30192e2caf59SThomas Veerman nselect = select(maxfd + 1, &rfds, &wfds, 0, tvp); 30202e2caf59SThomas Veerman 30212e2caf59SThomas Veerman if (nselect <= 0) 30222e2caf59SThomas Veerman return nselect; 30232e2caf59SThomas Veerman 30242e2caf59SThomas Veerman npoll = 0; 30252e2caf59SThomas Veerman for (i = 0; i < nfd; i++) { 30262e2caf59SThomas Veerman if (FD_ISSET(fd[i].fd, &rfds)) 30272e2caf59SThomas Veerman fd[i].revents |= POLLIN; 30282e2caf59SThomas Veerman 30292e2caf59SThomas Veerman if (FD_ISSET(fd[i].fd, &wfds)) 30302e2caf59SThomas Veerman fd[i].revents |= POLLOUT; 30312e2caf59SThomas Veerman 30322e2caf59SThomas Veerman if (fd[i].revents) 30332e2caf59SThomas Veerman npoll++; 30342e2caf59SThomas Veerman } 30352e2caf59SThomas Veerman 30362e2caf59SThomas Veerman return npoll; 30372e2caf59SThomas Veerman } 30382e2caf59SThomas Veerman #endif /* USE_SELECT */ 3039