Annotation of src/usr.bin/make/compat.c, Revision 1.60
1.36 espie 1: /* $OpenPackages$ */
1.60 ! espie 2: /* $OpenBSD: compat.c,v 1.59 2007/09/17 09:28:36 espie Exp $ */
1.8 millert 3: /* $NetBSD: compat.c,v 1.14 1996/11/06 17:59:01 christos Exp $ */
1.1 deraadt 4:
5: /*
6: * Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
7: * Copyright (c) 1988, 1989 by Adam de Boor
8: * Copyright (c) 1989 by Berkeley Softworks
9: * All rights reserved.
10: *
11: * This code is derived from software contributed to Berkeley by
12: * Adam de Boor.
13: *
14: * Redistribution and use in source and binary forms, with or without
15: * modification, are permitted provided that the following conditions
16: * are met:
17: * 1. Redistributions of source code must retain the above copyright
18: * notice, this list of conditions and the following disclaimer.
19: * 2. Redistributions in binary form must reproduce the above copyright
20: * notice, this list of conditions and the following disclaimer in the
21: * documentation and/or other materials provided with the distribution.
1.49 millert 22: * 3. Neither the name of the University nor the names of its contributors
1.1 deraadt 23: * may be used to endorse or promote products derived from this software
24: * without specific prior written permission.
25: *
26: * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
27: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29: * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
30: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
31: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
32: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
35: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36: * SUCH DAMAGE.
37: */
38:
1.37 espie 39: #include <sys/types.h>
40: #include <sys/stat.h>
41: #include <sys/wait.h>
42: #include <ctype.h>
43: #include <errno.h>
1.39 espie 44: #include <limits.h>
1.37 espie 45: #include <signal.h>
46: #include <stddef.h>
47: #include <stdio.h>
1.39 espie 48: #include <stdlib.h>
1.38 espie 49: #include <string.h>
1.37 espie 50: #include <unistd.h>
51: #include "config.h"
52: #include "defines.h"
53: #include "dir.h"
1.55 espie 54: #include "engine.h"
1.37 espie 55: #include "compat.h"
56: #include "suff.h"
57: #include "var.h"
58: #include "targ.h"
59: #include "error.h"
60: #include "str.h"
61: #include "extern.h"
62: #include "memory.h"
63: #include "gnode.h"
64: #include "make.h"
65: #include "timestamp.h"
66: #include "lst.h"
67: #include "pathnames.h"
1.1 deraadt 68:
1.36 espie 69: static void CompatInterrupt(int);
1.51 espie 70: static int CompatRunCommand(LstNode, void *);
1.36 espie 71: static void CompatMake(void *, void *);
1.60 ! espie 72: static int run_gnode(GNode *);
! 73: static void setup_meta(void);
! 74: static char **recheck_command_for_shell(char **);
! 75: static void run_command(const char *, bool);
1.1 deraadt 76:
1.45 espie 77: static volatile sig_atomic_t interrupted;
78:
1.59 espie 79: static void
1.50 espie 80: CompatInterrupt(int signo)
1.1 deraadt 81: {
1.57 espie 82: if (interrupted != SIGINT)
83: interrupted = signo;
1.1 deraadt 84: }
1.36 espie 85:
1.60 ! espie 86: /* The following array is used to make a fast determination of which
! 87: * characters are interpreted specially by the shell. If a command
! 88: * contains any of these characters, it is executed by the shell, not
! 89: * directly by us. */
! 90: static char meta[256];
! 91:
! 92: static void
! 93: setup_meta(void)
! 94: {
! 95: char *p;
! 96:
! 97: for (p = "#=|^(){};&<>*?[]:$`\\\n"; *p != '\0'; p++)
! 98: meta[(unsigned char) *p] = 1;
! 99: /* The null character serves as a sentinel in the string. */
! 100: meta[0] = 1;
! 101: }
! 102:
! 103: static char **
! 104: recheck_command_for_shell(char **av)
1.10 deraadt 105: {
1.36 espie 106: char *runsh[] = {
1.60 ! espie 107: "alias", "cd", "eval", "exit", "read", "set", "ulimit",
! 108: "unalias", "unset", "wait", "umask", NULL
1.11 deraadt 109: };
110:
1.12 espie 111: char **p;
1.11 deraadt 112:
1.60 ! espie 113: /* optimization: if exec cmd, we avoid the intermediate shell */
! 114: if (strcmp(av[0], "exec") == 0)
! 115: av++;
! 116:
1.11 deraadt 117: for (p = runsh; *p; p++)
1.12 espie 118: if (strcmp(av[0], *p) == 0)
1.60 ! espie 119: return NULL;
! 120:
! 121: return av;
! 122: }
! 123:
! 124: static void
! 125: run_command(const char *cmd, bool errCheck)
! 126: {
! 127: const char *p;
! 128: char *shargv[4];
! 129: char **todo;
! 130:
! 131: shargv[0] = _PATH_BSHELL;
! 132:
! 133: shargv[1] = errCheck ? "-ec" : "-c";
! 134: shargv[2] = (char *)cmd;
! 135: shargv[3] = NULL;
! 136:
! 137: todo = shargv;
1.11 deraadt 138:
1.10 deraadt 139:
1.60 ! espie 140: /* Search for meta characters in the command. If there are no meta
! 141: * characters, there's no need to execute a shell to execute the
! 142: * command. */
! 143: for (p = cmd; !meta[(unsigned char)*p]; p++)
! 144: continue;
! 145: if (*p == '\0') {
! 146: char *bp;
! 147: char **av;
! 148: int argc;
! 149: /* No meta-characters, so probably no need to exec a shell.
! 150: * Break the command into words to form an argument vector
! 151: * we can execute. */
! 152: av = brk_string(cmd, &argc, &bp);
! 153: av = recheck_command_for_shell(av);
! 154: if (av != NULL)
! 155: todo = av;
! 156: }
! 157: execvp(todo[0], todo);
! 158:
! 159: if (errno == ENOENT)
! 160: fprintf(stderr, "%s: not found\n", todo[0]);
! 161: else
! 162: perror(todo[0]);
! 163: _exit(1);
1.10 deraadt 164: }
1.36 espie 165:
1.10 deraadt 166: /*-
167: *-----------------------------------------------------------------------
1.1 deraadt 168: * CompatRunCommand --
169: * Execute the next command for a target. If the command returns an
170: * error, the node's made field is set to ERROR and creation stops.
171: *
172: * Results:
1.36 espie 173: * 0 in case of error, 1 if ok.
1.1 deraadt 174: *
175: * Side Effects:
176: * The node's 'made' field may be set to ERROR.
177: *-----------------------------------------------------------------------
178: */
179: static int
1.60 ! espie 180: CompatRunCommand(LstNode cmdNode, /* Command to execute */
! 181: void *gnp) /* Node from which the command came */
1.1 deraadt 182: {
1.60 ! espie 183: char *cmdStart; /* Start of expanded command */
! 184: bool silent; /* Don't print command */
! 185: bool doExecute; /* Execute the command */
! 186: bool errCheck; /* Check errors */
! 187: int reason; /* Reason for child's death */
! 188: int status; /* Description of child's death */
! 189: pid_t cpid; /* Child actually found */
! 190: pid_t stat; /* Status of fork */
1.57 espie 191: char *cmd = (char *)Lst_Datum(cmdNode);
192: GNode *gn = (GNode *)gnp;
193:
194: silent = gn->type & OP_SILENT;
195: errCheck = !(gn->type & OP_IGNORE);
196: doExecute = !noExecute;
197:
198: cmdStart = Var_Subst(cmd, &gn->context, false);
199:
1.60 ! espie 200: /* How can we execute a null command ? we warn the user that the
! 201: * command expanded to nothing (is this the right thing to do?). */
1.57 espie 202: if (*cmdStart == '\0') {
203: free(cmdStart);
204: Error("%s expands to empty string", cmd);
205: return 1;
206: } else
207: cmd = cmdStart;
208: Lst_Replace(cmdNode, cmdStart);
209:
1.60 ! espie 210: if ((gn->type & OP_SAVE_CMDS) && (gn != end_node)) {
! 211: Lst_AtEnd(&end_node->commands, cmdStart);
! 212: end_node->type &= ~OP_DUMMY;
1.57 espie 213: return 1;
214: } else if (strcmp(cmdStart, "...") == 0) {
215: gn->type |= OP_SAVE_CMDS;
216: return 1;
217: }
218: for (;; cmd++) {
219: if (*cmd == '@')
220: silent = DEBUG(LOUD) ? false : true;
221: else if (*cmd == '-')
222: errCheck = false;
223: else if (*cmd == '+')
224: doExecute = true;
225: else
226: break;
227: }
228: while (isspace(*cmd))
229: cmd++;
230: /* Print the command before echoing if we're not supposed to be quiet
231: * for this one. We also print the command if -n given. */
232: if (!silent || noExecute) {
233: printf("%s\n", cmd);
234: fflush(stdout);
235: }
236: /* If we're not supposed to execute any commands, this is as far as
237: * we go... */
238: if (!doExecute)
1.25 espie 239: return 1;
1.57 espie 240:
241: /* Fork and execute the single command. If the fork fails, we abort. */
1.60 ! espie 242: switch (cpid = fork()) {
! 243: case -1:
1.57 espie 244: Fatal("Could not fork");
1.60 ! espie 245: /*NOTREACHED*/
! 246: case 0:
! 247: run_command(cmd, errCheck);
! 248: /*NOTREACHED*/
! 249: default:
! 250: break;
1.57 espie 251: }
252: free(cmdStart);
253: Lst_Replace(cmdNode, NULL);
254:
255: /* The child is off and running. Now all we can do is wait... */
256: while (1) {
257:
258: while ((stat = wait(&reason)) != cpid) {
259: if (stat == -1 && errno != EINTR)
260: break;
1.1 deraadt 261: }
1.57 espie 262:
263: if (interrupted)
264: break;
265:
266: if (stat != -1) {
267: if (WIFSTOPPED(reason))
268: status = WSTOPSIG(reason); /* stopped */
269: else if (WIFEXITED(reason)) {
270: status = WEXITSTATUS(reason); /* exited */
271: if (status != 0)
1.60 ! espie 272: printf("*** Error code %d", status);
1.57 espie 273: } else {
274: status = WTERMSIG(reason); /* signaled */
275: printf("*** Signal %d", status);
276: }
277:
278:
279: if (!WIFEXITED(reason) || status != 0) {
280: if (errCheck) {
281: gn->made = ERROR;
282: if (keepgoing)
283: /* Abort the current target,
284: * but let others continue. */
285: printf(" (continuing)\n");
286: } else {
1.60 ! espie 287: /* Continue executing commands for
! 288: * this target. If we return 0,
! 289: * this will happen... */
1.57 espie 290: printf(" (ignored)\n");
291: status = 0;
292: }
293: }
294: return !status;
295: } else
296: Fatal("error in wait: %d", stat);
297: /*NOTREACHED*/
298: }
299:
300: /* This is reached only if interrupted */
301: if (!Targ_Precious(gn)) {
1.60 ! espie 302: char *file = Varq_Value(TARGET_INDEX, gn);
1.57 espie 303:
304: if (!noExecute && eunlink(file) != -1)
305: Error("*** %s removed\n", file);
306: }
307: if (interrupted == SIGINT) {
308: signal(SIGINT, SIG_IGN);
309: signal(SIGTERM, SIG_IGN);
310: signal(SIGHUP, SIG_IGN);
311: signal(SIGQUIT, SIG_IGN);
312: interrupted = 0;
1.60 ! espie 313: run_gnode(interrupt_node);
1.57 espie 314: exit(SIGINT);
315: }
316: exit(interrupted);
1.1 deraadt 317: }
1.36 espie 318:
1.60 ! espie 319: static int
! 320: run_gnode(GNode *gn)
! 321: {
! 322: if (gn != NULL && (gn->type & OP_DUMMY) == 0) {
! 323: Lst_ForEachNodeWhile(&gn->commands, CompatRunCommand, gn);
! 324: return gn->made;
! 325: } else
! 326: return NOSUCHNODE;
! 327: }
! 328:
1.1 deraadt 329: /*-
330: *-----------------------------------------------------------------------
331: * CompatMake --
332: * Make a target.
333: *
334: * Side Effects:
335: * If an error is detected and not being ignored, the process exits.
336: *-----------------------------------------------------------------------
337: */
1.26 espie 338: static void
1.50 espie 339: CompatMake(void *gnp, /* The node to make */
340: void *pgnp) /* Parent to abort if necessary */
1.1 deraadt 341: {
1.57 espie 342: GNode *gn = (GNode *)gnp;
343: GNode *pgn = (GNode *)pgnp;
344:
345: if (pgn->type & OP_MADE) {
346: (void)Dir_MTime(gn);
347: gn->made = UPTODATE;
348: }
349:
350: if (gn->type & OP_USE) {
351: Make_HandleUse(gn, pgn);
352: } else if (gn->made == UNMADE) {
353: /* First mark ourselves to be made, then apply whatever
1.60 ! espie 354: * transformations the suffix module thinks are necessary.
! 355: * Once that's done, we can descend and make all our children.
! 356: * If any of them has an error but the -k flag was given,
! 357: * our 'make' field will be set false again. This is our
! 358: * signal to not attempt to do anything but abort our
! 359: * parent as well. */
1.57 espie 360: gn->make = true;
361: gn->made = BEINGMADE;
362: Suff_FindDeps(gn);
363: Lst_ForEach(&gn->children, CompatMake, gn);
364: if (!gn->make) {
365: gn->made = ABORTED;
366: pgn->make = false;
367: return;
368: }
1.5 millert 369:
1.57 espie 370: if (Lst_Member(&gn->iParents, pgn) != NULL) {
1.59 espie 371: Varq_Set(IMPSRC_INDEX, Varq_Value(TARGET_INDEX, gn),
1.57 espie 372: pgn);
373: }
1.1 deraadt 374:
1.57 espie 375: /* All the children were made ok. Now cmtime contains the
376: * modification time of the newest child, we need to find out
1.60 ! espie 377: * if we exist and when we were modified last. The criteria
! 378: * for datedness are defined by the Make_OODate function. */
1.57 espie 379: if (DEBUG(MAKE))
380: printf("Examining %s...", gn->name);
1.60 ! espie 381: if (!Make_OODate(gn)) {
1.57 espie 382: gn->made = UPTODATE;
383: if (DEBUG(MAKE))
384: printf("up-to-date.\n");
385: return;
386: } else if (DEBUG(MAKE))
387: printf("out-of-date.\n");
388:
1.60 ! espie 389: /* If the user is just seeing if something is out-of-date,
! 390: * exit now to tell him/her "yes". */
1.57 espie 391: if (queryFlag)
392: exit(-1);
393:
1.60 ! espie 394: /* We need to be re-made. We also have to make sure we've
! 395: * got a $? variable. To be nice, we also define the $>
! 396: * variable using Make_DoAllVar(). */
1.57 espie 397: Make_DoAllVar(gn);
398:
399: /* Alter our type to tell if errors should be ignored or things
400: * should not be printed so CompatRunCommand knows what to do.
1.60 ! espie 401: */
1.57 espie 402: if (Targ_Ignore(gn))
403: gn->type |= OP_IGNORE;
404: if (Targ_Silent(gn))
405: gn->type |= OP_SILENT;
406:
407: if (Job_CheckCommands(gn, Fatal)) {
1.60 ! espie 408: /* Our commands are ok, but we still have to worry
! 409: * about the -t flag... */
! 410: if (!touchFlag)
! 411: run_gnode(gn);
! 412: else
! 413: Job_Touch(gn, gn->type & OP_SILENT);
1.57 espie 414: } else
415: gn->made = ERROR;
416:
417: if (gn->made != ERROR) {
418: /* If the node was made successfully, mark it so,
1.60 ! espie 419: * update its modification time and timestamp all
! 420: * its parents.
! 421: * This is to keep its state from affecting that of
! 422: * its parent. */
1.57 espie 423: gn->made = MADE;
424: /* This is what Make does and it's actually a good
425: * thing, as it allows rules like
426: *
427: * cmp -s y.tab.h parse.h || cp y.tab.h parse.h
428: *
429: * to function as intended. Unfortunately, thanks to
1.60 ! espie 430: * the stateless nature of NFS (and the speed of
! 431: * this program), there are times when the
! 432: * modification time of a file created on a remote
! 433: * machine will not be modified before the stat()
! 434: * implied by the Dir_MTime occurs, thus leading us
! 435: * to believe that the file is unchanged, wreaking
! 436: * havoc with files that depend on this one.
1.57 espie 437: */
438: if (noExecute || is_out_of_date(Dir_MTime(gn)))
439: gn->mtime = now;
440: if (is_strictly_before(gn->mtime, gn->cmtime))
441: gn->mtime = gn->cmtime;
442: if (DEBUG(MAKE))
1.59 espie 443: printf("update time: %s\n",
1.57 espie 444: time_to_string(gn->mtime));
445: if (!(gn->type & OP_EXEC)) {
446: pgn->childMade = true;
447: Make_TimeStamp(pgn, gn);
448: }
449: } else if (keepgoing)
450: pgn->make = false;
451: else {
1.36 espie 452:
1.57 espie 453: if (gn->lineno)
454: printf("\n\nStop in %s (line %lu of %s).\n",
455: Var_Value(".CURDIR"),
456: (unsigned long)gn->lineno,
457: gn->fname);
458: else
1.59 espie 459: printf("\n\nStop in %s.\n",
1.57 espie 460: Var_Value(".CURDIR"));
461: exit(1);
462: }
463: } else if (gn->made == ERROR)
1.60 ! espie 464: /* Already had an error when making this beastie. Tell the parent
! 465: * to abort. */
1.37 espie 466: pgn->make = false;
1.57 espie 467: else {
468: if (Lst_Member(&gn->iParents, pgn) != NULL) {
1.59 espie 469: Varq_Set(IMPSRC_INDEX, Varq_Value(TARGET_INDEX, gn),
1.57 espie 470: pgn);
471: }
472: switch (gn->made) {
473: case BEINGMADE:
474: Error("Graph cycles through %s\n", gn->name);
475: gn->made = ERROR;
476: pgn->make = false;
477: break;
478: case MADE:
479: if ((gn->type & OP_EXEC) == 0) {
480: pgn->childMade = true;
481: Make_TimeStamp(pgn, gn);
482: }
483: break;
484: case UPTODATE:
485: if ((gn->type & OP_EXEC) == 0)
486: Make_TimeStamp(pgn, gn);
487: break;
488: default:
489: break;
490: }
1.1 deraadt 491: }
492: }
1.36 espie 493:
1.1 deraadt 494: void
1.50 espie 495: Compat_Run(Lst targs) /* List of target nodes to re-create */
1.1 deraadt 496: {
1.60 ! espie 497: GNode *gn = NULL; /* Current root target */
! 498: int errors; /* Number of targets not remade due to errors */
1.57 espie 499:
500: signal(SIGINT, CompatInterrupt);
501: signal(SIGTERM, CompatInterrupt);
502: signal(SIGHUP, CompatInterrupt);
503: signal(SIGQUIT, CompatInterrupt);
504:
1.60 ! espie 505: setup_meta();
1.57 espie 506: /* If the user has defined a .BEGIN target, execute the commands
507: * attached to it. */
508: if (!queryFlag) {
1.60 ! espie 509: if (run_gnode(begin_node) == ERROR) {
! 510: printf("\n\nStop.\n");
! 511: exit(1);
1.57 espie 512: }
513: }
514:
515: /* For each entry in the list of targets to create, call CompatMake on
516: * it to create the thing. CompatMake will leave the 'made' field of gn
517: * in one of several states:
518: * UPTODATE gn was already up-to-date
519: * MADE gn was recreated successfully
1.60 ! espie 520: * ERROR An error occurred while gn was being
! 521: * created
1.59 espie 522: * ABORTED gn was not remade because one of its
1.60 ! espie 523: * inferiors could not be made due to errors.
1.57 espie 524: */
525: errors = 0;
526: while ((gn = (GNode *)Lst_DeQueue(targs)) != NULL) {
527: CompatMake(gn, gn);
528:
529: if (gn->made == UPTODATE)
530: printf("`%s' is up to date.\n", gn->name);
531: else if (gn->made == ABORTED) {
1.59 espie 532: printf("`%s' not remade because of errors.\n",
1.57 espie 533: gn->name);
1.58 espie 534: errors++;
1.57 espie 535: }
536: }
537:
538: /* If the user has defined a .END target, run its commands. */
539: if (errors == 0)
1.60 ! espie 540: run_gnode(end_node);
1.1 deraadt 541: }