1 /* $OpenBSD: compat.c,v 1.35 2000/09/14 13:52:41 espie Exp $ */ 2 /* $NetBSD: compat.c,v 1.14 1996/11/06 17:59:01 christos Exp $ */ 3 4 /* 5 * Copyright (c) 1988, 1989, 1990 The Regents of the University of California. 6 * Copyright (c) 1988, 1989 by Adam de Boor 7 * Copyright (c) 1989 by Berkeley Softworks 8 * All rights reserved. 9 * 10 * This code is derived from software contributed to Berkeley by 11 * Adam de Boor. 12 * 13 * Redistribution and use in source and binary forms, with or without 14 * modification, are permitted provided that the following conditions 15 * are met: 16 * 1. Redistributions of source code must retain the above copyright 17 * notice, this list of conditions and the following disclaimer. 18 * 2. Redistributions in binary form must reproduce the above copyright 19 * notice, this list of conditions and the following disclaimer in the 20 * documentation and/or other materials provided with the distribution. 21 * 3. All advertising materials mentioning features or use of this software 22 * must display the following acknowledgement: 23 * This product includes software developed by the University of 24 * California, Berkeley and its contributors. 25 * 4. Neither the name of the University nor the names of its contributors 26 * may be used to endorse or promote products derived from this software 27 * without specific prior written permission. 28 * 29 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 30 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 31 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 32 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 33 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 34 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 35 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 36 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 37 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 38 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 39 * SUCH DAMAGE. 40 */ 41 42 /*- 43 * compat.c -- 44 * The routines in this file implement the full-compatibility 45 * mode of PMake. Most of the special functionality of PMake 46 * is available in this mode. Things not supported: 47 * - different shells. 48 * - friendly variable substitution. 49 * 50 * Interface: 51 * Compat_Run Initialize things for this module and recreate 52 * thems as need creatin' 53 */ 54 55 #include <stddef.h> 56 #include <stdio.h> 57 #include <sys/types.h> 58 #include <sys/stat.h> 59 #include <sys/wait.h> 60 #include <ctype.h> 61 #include <errno.h> 62 #include <signal.h> 63 #include "make.h" 64 #include "ohash.h" 65 #include "dir.h" 66 #include "job.h" 67 68 #ifndef lint 69 #if 0 70 static char sccsid[] = "@(#)compat.c 8.2 (Berkeley) 3/19/94"; 71 #else 72 UNUSED 73 static char rcsid[] = "$OpenBSD: compat.c,v 1.35 2000/09/14 13:52:41 espie Exp $"; 74 #endif 75 #endif /* not lint */ 76 77 /* 78 * The following array is used to make a fast determination of which 79 * characters are interpreted specially by the shell. If a command 80 * contains any of these characters, it is executed by the shell, not 81 * directly by us. 82 */ 83 84 static char meta[256]; 85 86 static GNode *curTarg = NULL; 87 static GNode *ENDNode; 88 static void CompatInterrupt __P((int)); 89 static int CompatRunCommand __P((void *, void *)); 90 static void CompatMake __P((void *, void *)); 91 static int shellneed __P((char **av)); 92 93 /*- 94 *----------------------------------------------------------------------- 95 * CompatInterrupt -- 96 * Interrupt the creation of the current target and remove it if 97 * it ain't precious. 98 * 99 * Results: 100 * None. 101 * 102 * Side Effects: 103 * The target is removed and the process exits. If .INTERRUPT exists, 104 * its commands are run first WITH INTERRUPTS IGNORED.. 105 * 106 *----------------------------------------------------------------------- 107 */ 108 static void 109 CompatInterrupt (signo) 110 int signo; 111 { 112 GNode *gn; 113 114 if ((curTarg != NULL) && !Targ_Precious (curTarg)) { 115 char *file = Varq_Value(TARGET_INDEX, curTarg); 116 117 if (!noExecute && eunlink(file) != -1) { 118 Error("*** %s removed\n", file); 119 } 120 121 /* 122 * Run .INTERRUPT only if hit with interrupt signal 123 */ 124 if (signo == SIGINT) { 125 gn = Targ_FindNode(".INTERRUPT", TARG_NOCREATE); 126 if (gn != NULL) { 127 Lst_Find(&gn->commands, CompatRunCommand, gn); 128 } 129 } 130 131 } 132 exit (signo); 133 } 134 135 /*- 136 *----------------------------------------------------------------------- 137 * shellneed -- 138 * 139 * Results: 140 * Returns 1 if a specified set of arguments 141 * must be executed by the shell, 142 * 0 if it can be run via execve, and -1 if the command can be 143 * handled internally 144 * 145 * Side Effects: 146 * May modify the process umask 147 * 148 *----------------------------------------------------------------------- 149 */ 150 static int 151 shellneed (av) 152 char **av; 153 { 154 char *runsh[] = { 155 "alias", "cd", "eval", "exec", "exit", "read", "set", "ulimit", 156 "unalias", "unset", "wait", 157 NULL 158 }; 159 160 char **p; 161 162 /* FIXME most of these ARE actual no-ops */ 163 for (p = runsh; *p; p++) 164 if (strcmp(av[0], *p) == 0) 165 return (1); 166 167 if (strcmp(av[0], "umask") == 0) { 168 long umi; 169 char *ep = NULL; 170 mode_t um; 171 172 if (av[1] != NULL) { 173 umi = strtol(av[1], &ep, 8); 174 if (ep == NULL) 175 return (1); 176 um = umi; 177 } 178 else { 179 um = umask(0); 180 printf("%o\n", um); 181 } 182 (void) umask(um); 183 return (-1); 184 } 185 186 return (0); 187 } 188 189 /*- 190 *----------------------------------------------------------------------- 191 * CompatRunCommand -- 192 * Execute the next command for a target. If the command returns an 193 * error, the node's made field is set to ERROR and creation stops. 194 * 195 * Results: 196 * 1 if the command succeeded, 0 if an error occurred. 197 * 198 * Side Effects: 199 * The node's 'made' field may be set to ERROR. 200 * 201 *----------------------------------------------------------------------- 202 */ 203 static int 204 CompatRunCommand (cmdp, gnp) 205 void *cmdp; /* Command to execute */ 206 void *gnp; /* Node from which the command came */ 207 { 208 char *cmdStart; /* Start of expanded command */ 209 char *cp, *bp = NULL; 210 Boolean silent, /* Don't print command */ 211 errCheck, /* Check errors */ 212 doExecute; /* Execute the command */ 213 int reason; /* Reason for child's death */ 214 int status; /* Description of child's death */ 215 int cpid; /* Child actually found */ 216 ReturnStatus stat; /* Status of fork */ 217 LstNode cmdNode; /* Node where current command is located */ 218 char **av; /* Argument vector for thing to exec */ 219 int argc; /* Number of arguments in av or 0 if not 220 * dynamically allocated */ 221 Boolean local; /* TRUE if command should be executed 222 * locally */ 223 char *cmd = (char *) cmdp; 224 GNode *gn = (GNode *) gnp; 225 static char *shargv[4] = { "/bin/sh" }; 226 227 /* 228 * Avoid clobbered variable warnings by forcing the compiler 229 * to ``unregister'' variables 230 */ 231 #if __GNUC__ 232 (void) &av; 233 (void) &errCheck; 234 #endif 235 silent = gn->type & OP_SILENT; 236 errCheck = !(gn->type & OP_IGNORE); 237 doExecute = !noExecute; 238 239 cmdNode = Lst_Member(&gn->commands, cmd); 240 cmdStart = Var_Subst(cmd, &gn->context, FALSE); 241 242 /* 243 * brk_string will return an argv with a NULL in av[0], thus causing 244 * execvp to choke and die horribly. Besides, how can we execute a null 245 * command? In any case, we warn the user that the command expanded to 246 * nothing (is this the right thing to do?). 247 */ 248 249 if (*cmdStart == '\0') { 250 free(cmdStart); 251 Error("%s expands to empty string", cmd); 252 return 1; 253 } else { 254 cmd = cmdStart; 255 } 256 Lst_Replace(cmdNode, cmdStart); 257 258 if ((gn->type & OP_SAVE_CMDS) && (gn != ENDNode)) { 259 Lst_AtEnd(&ENDNode->commands, cmdStart); 260 return 1; 261 } else if (strcmp(cmdStart, "...") == 0) { 262 gn->type |= OP_SAVE_CMDS; 263 return 1; 264 } 265 266 for (;; cmd++) { 267 if (*cmd == '@') 268 silent = TRUE; 269 else if (*cmd == '-') 270 errCheck = FALSE; 271 else if (*cmd == '+') 272 doExecute = TRUE; 273 else 274 break; 275 } 276 277 while (isspace((unsigned char)*cmd)) 278 cmd++; 279 280 /* 281 * Search for meta characters in the command. If there are no meta 282 * characters, there's no need to execute a shell to execute the 283 * command. 284 */ 285 for (cp = cmd; !meta[(unsigned char)*cp]; cp++) { 286 continue; 287 } 288 289 /* 290 * Print the command before echoing if we're not supposed to be quiet for 291 * this one. We also print the command if -n given. 292 */ 293 if (!silent || noExecute) { 294 printf ("%s\n", cmd); 295 fflush(stdout); 296 } 297 298 /* 299 * If we're not supposed to execute any commands, this is as far as 300 * we go... 301 */ 302 if (!doExecute) 303 return 1; 304 305 if (*cp != '\0') { 306 /* 307 * If *cp isn't the null character, we hit a "meta" character and 308 * need to pass the command off to the shell. We give the shell the 309 * -e flag as well as -c if it's supposed to exit when it hits an 310 * error. 311 */ 312 313 shargv[1] = (errCheck ? "-ec" : "-c"); 314 shargv[2] = cmd; 315 shargv[3] = (char *)NULL; 316 av = shargv; 317 argc = 0; 318 } else { 319 /* 320 * No meta-characters, so probably no need to exec a shell. 321 * Break the command into words to form an argument vector 322 * we can execute. 323 */ 324 av = brk_string(cmd, &argc, TRUE, &bp); 325 switch(shellneed(av)) { 326 case -1: /* handled internally */ 327 free(bp); 328 free(av); 329 return 1; 330 case 1: 331 shargv[1] = (errCheck ? "-ec" : "-c"); 332 shargv[2] = cmd; 333 shargv[3] = (char *)NULL; 334 free(bp); 335 free(av); 336 bp = NULL; 337 av = shargv; 338 argc = 0; 339 break; 340 default: /* nothing needed */ 341 break; 342 } 343 } 344 345 local = TRUE; 346 347 /* 348 * Fork and execute the single command. If the fork fails, we abort. 349 */ 350 cpid = vfork(); 351 if (cpid < 0) { 352 Fatal("Could not fork"); 353 } 354 if (cpid == 0) { 355 if (local) { 356 execvp(av[0], av); 357 if (errno == ENOENT) 358 fprintf(stderr, "%s: not found\n", av[0]); 359 else 360 perror(av[0]); 361 } else { 362 (void)execv(av[0], av); 363 } 364 _exit(1); 365 } 366 if (bp) { 367 free(av); 368 free(bp); 369 } 370 free(cmdStart); 371 Lst_Replace(cmdNode, NULL); 372 373 /* 374 * The child is off and running. Now all we can do is wait... 375 */ 376 while (1) { 377 378 while ((stat = wait(&reason)) != cpid) { 379 if (stat == -1 && errno != EINTR) { 380 break; 381 } 382 } 383 384 if (stat > -1) { 385 if (WIFSTOPPED(reason)) { 386 status = WSTOPSIG(reason); /* stopped */ 387 } else if (WIFEXITED(reason)) { 388 status = WEXITSTATUS(reason); /* exited */ 389 if (status != 0) { 390 printf ("*** Error code %d", status); 391 } 392 } else { 393 status = WTERMSIG(reason); /* signaled */ 394 printf ("*** Signal %d", status); 395 } 396 397 398 if (!WIFEXITED(reason) || (status != 0)) { 399 if (errCheck) { 400 gn->made = ERROR; 401 if (keepgoing) { 402 /* 403 * Abort the current target, but let others 404 * continue. 405 */ 406 printf (" (continuing)\n"); 407 } 408 } else { 409 /* 410 * Continue executing commands for this target. 411 * If we return 0, this will happen... 412 */ 413 printf (" (ignored)\n"); 414 status = 0; 415 } 416 } 417 break; 418 } else { 419 Fatal ("error in wait: %d", stat); 420 /*NOTREACHED*/ 421 } 422 } 423 424 return !status; 425 } 426 427 /*- 428 *----------------------------------------------------------------------- 429 * CompatMake -- 430 * Make a target. 431 * 432 * Side Effects: 433 * If an error is detected and not being ignored, the process exits. 434 * 435 *----------------------------------------------------------------------- 436 */ 437 static void 438 CompatMake(gnp, pgnp) 439 void *gnp; /* The node to make */ 440 void *pgnp; /* Parent to abort if necessary */ 441 { 442 GNode *gn = (GNode *) gnp; 443 GNode *pgn = (GNode *) pgnp; 444 445 if (pgn->type & OP_MADE) { 446 (void)Dir_MTime(gn); 447 gn->made = UPTODATE; 448 } 449 450 if (gn->type & OP_USE) { 451 Make_HandleUse(gn, pgn); 452 } else if (gn->made == UNMADE) { 453 /* 454 * First mark ourselves to be made, then apply whatever transformations 455 * the suffix module thinks are necessary. Once that's done, we can 456 * descend and make all our children. If any of them has an error 457 * but the -k flag was given, our 'make' field will be set FALSE again. 458 * This is our signal to not attempt to do anything but abort our 459 * parent as well. 460 */ 461 gn->make = TRUE; 462 gn->made = BEINGMADE; 463 Suff_FindDeps (gn); 464 Lst_ForEach(&gn->children, CompatMake, gn); 465 if (!gn->make) { 466 gn->made = ABORTED; 467 pgn->make = FALSE; 468 return; 469 } 470 471 if (Lst_Member(&gn->iParents, pgn) != NULL) 472 Varq_Set(IMPSRC_INDEX, Varq_Value(TARGET_INDEX, gn), pgn); 473 474 /* 475 * All the children were made ok. Now cmtime contains the modification 476 * time of the newest child, we need to find out if we exist and when 477 * we were modified last. The criteria for datedness are defined by the 478 * Make_OODate function. 479 */ 480 if (DEBUG(MAKE)) { 481 printf("Examining %s...", gn->name); 482 } 483 if (! Make_OODate(gn)) { 484 gn->made = UPTODATE; 485 if (DEBUG(MAKE)) { 486 printf("up-to-date.\n"); 487 } 488 return; 489 } else if (DEBUG(MAKE)) { 490 printf("out-of-date.\n"); 491 } 492 493 /* 494 * If the user is just seeing if something is out-of-date, exit now 495 * to tell him/her "yes". 496 */ 497 if (queryFlag) { 498 exit (-1); 499 } 500 501 /* 502 * We need to be re-made. We also have to make sure we've got a $? 503 * variable. To be nice, we also define the $> variable using 504 * Make_DoAllVar(). 505 */ 506 Make_DoAllVar(gn); 507 508 /* 509 * Alter our type to tell if errors should be ignored or things 510 * should not be printed so CompatRunCommand knows what to do. 511 */ 512 if (Targ_Ignore (gn)) { 513 gn->type |= OP_IGNORE; 514 } 515 if (Targ_Silent (gn)) { 516 gn->type |= OP_SILENT; 517 } 518 519 if (Job_CheckCommands (gn, Fatal)) { 520 /* 521 * Our commands are ok, but we still have to worry about the -t 522 * flag... 523 */ 524 if (!touchFlag) { 525 curTarg = gn; 526 Lst_Find(&gn->commands, CompatRunCommand, gn); 527 curTarg = NULL; 528 } else { 529 Job_Touch (gn, gn->type & OP_SILENT); 530 } 531 } else { 532 gn->made = ERROR; 533 } 534 535 if (gn->made != ERROR) { 536 /* 537 * If the node was made successfully, mark it so, update 538 * its modification time and timestamp all its parents. Note 539 * that for .ZEROTIME targets, the timestamping isn't done. 540 * This is to keep its state from affecting that of its parent. 541 */ 542 gn->made = MADE; 543 #ifndef RECHECK 544 /* 545 * We can't re-stat the thing, but we can at least take care of 546 * rules where a target depends on a source that actually creates 547 * the target, but only if it has changed, e.g. 548 * 549 * parse.h : parse.o 550 * 551 * parse.o : parse.y 552 * yacc -d parse.y 553 * cc -c y.tab.c 554 * mv y.tab.o parse.o 555 * cmp -s y.tab.h parse.h || mv y.tab.h parse.h 556 * 557 * In this case, if the definitions produced by yacc haven't 558 * changed from before, parse.h won't have been updated and 559 * gn->mtime will reflect the current modification time for 560 * parse.h. This is something of a kludge, I admit, but it's a 561 * useful one.. 562 * 563 * XXX: People like to use a rule like 564 * 565 * FRC: 566 * 567 * To force things that depend on FRC to be made, so we have to 568 * check for gn->children being empty as well... 569 */ 570 if (!Lst_IsEmpty(&gn->commands) || Lst_IsEmpty(&gn->children)) { 571 gn->mtime = now; 572 } 573 #else 574 /* 575 * This is what Make does and it's actually a good thing, as it 576 * allows rules like 577 * 578 * cmp -s y.tab.h parse.h || cp y.tab.h parse.h 579 * 580 * to function as intended. Unfortunately, thanks to the stateless 581 * nature of NFS (and the speed of this program), there are times 582 * when the modification time of a file created on a remote 583 * machine will not be modified before the stat() implied by 584 * the Dir_MTime occurs, thus leading us to believe that the file 585 * is unchanged, wreaking havoc with files that depend on this one. 586 * 587 * I have decided it is better to make too much than to make too 588 * little, so this stuff is commented out unless you're sure it's 589 * ok. 590 * -- ardeb 1/12/88 591 */ 592 if (noExecute || is_out_of_date(Dir_MTime(gn))) 593 gn->mtime = now; 594 if (is_before(gn->mtime, gn->cmtime)) 595 gn->mtime = gn->cmtime; 596 if (DEBUG(MAKE)) { 597 printf("update time: %s\n", Targ_FmtTime(gn->mtime)); 598 } 599 #endif 600 if (!(gn->type & OP_EXEC)) { 601 pgn->childMade = TRUE; 602 Make_TimeStamp(pgn, gn); 603 } 604 } else if (keepgoing) { 605 pgn->make = FALSE; 606 } else { 607 if (gn->lineno) 608 printf("\n\nStop in %s (line %lu of %s).\n", 609 Var_Value(".CURDIR", VAR_GLOBAL), 610 (unsigned long)gn->lineno, 611 gn->fname); 612 else 613 printf("\n\nStop in %s.\n", Var_Value(".CURDIR", VAR_GLOBAL)); 614 exit (1); 615 } 616 } else if (gn->made == ERROR) { 617 /* 618 * Already had an error when making this beastie. Tell the parent 619 * to abort. 620 */ 621 pgn->make = FALSE; 622 } else { 623 if (Lst_Member(&gn->iParents, pgn) != NULL) 624 Varq_Set(IMPSRC_INDEX, Varq_Value(TARGET_INDEX, gn), pgn); 625 switch(gn->made) { 626 case BEINGMADE: 627 Error("Graph cycles through %s\n", gn->name); 628 gn->made = ERROR; 629 pgn->make = FALSE; 630 break; 631 case MADE: 632 if ((gn->type & OP_EXEC) == 0) { 633 pgn->childMade = TRUE; 634 Make_TimeStamp(pgn, gn); 635 } 636 break; 637 case UPTODATE: 638 if ((gn->type & OP_EXEC) == 0) { 639 Make_TimeStamp(pgn, gn); 640 } 641 break; 642 default: 643 break; 644 } 645 } 646 } 647 648 /*- 649 *----------------------------------------------------------------------- 650 * Compat_Run -- 651 * Initialize this mode and start making. 652 * 653 * Results: 654 * None. 655 * 656 * Side Effects: 657 * Guess what? 658 * 659 *----------------------------------------------------------------------- 660 */ 661 void 662 Compat_Run(targs) 663 Lst targs; /* List of target nodes to re-create */ 664 { 665 char *cp; /* Pointer to string of shell meta-characters */ 666 GNode *gn = NULL;/* Current root target */ 667 int errors; /* Number of targets not remade due to errors */ 668 669 if (signal(SIGINT, SIG_IGN) != SIG_IGN) { 670 signal(SIGINT, CompatInterrupt); 671 } 672 if (signal(SIGTERM, SIG_IGN) != SIG_IGN) { 673 signal(SIGTERM, CompatInterrupt); 674 } 675 if (signal(SIGHUP, SIG_IGN) != SIG_IGN) { 676 signal(SIGHUP, CompatInterrupt); 677 } 678 if (signal(SIGQUIT, SIG_IGN) != SIG_IGN) { 679 signal(SIGQUIT, CompatInterrupt); 680 } 681 682 for (cp = "#=|^(){};&<>*?[]:$`\\\n"; *cp != '\0'; cp++) { 683 meta[(unsigned char) *cp] = 1; 684 } 685 /* 686 * The null character serves as a sentinel in the string. 687 */ 688 meta[0] = 1; 689 690 ENDNode = Targ_FindNode(".END", TARG_CREATE); 691 /* 692 * If the user has defined a .BEGIN target, execute the commands attached 693 * to it. 694 */ 695 if (!queryFlag) { 696 gn = Targ_FindNode(".BEGIN", TARG_NOCREATE); 697 if (gn != NULL) { 698 Lst_Find(&gn->commands, CompatRunCommand, gn); 699 if (gn->made == ERROR) { 700 printf("\n\nStop.\n"); 701 exit(1); 702 } 703 } 704 } 705 706 /* 707 * For each entry in the list of targets to create, call CompatMake on 708 * it to create the thing. CompatMake will leave the 'made' field of gn 709 * in one of several states: 710 * UPTODATE gn was already up-to-date 711 * MADE gn was recreated successfully 712 * ERROR An error occurred while gn was being created 713 * ABORTED gn was not remade because one of its inferiors 714 * could not be made due to errors. 715 */ 716 errors = 0; 717 while ((gn = (GNode *)Lst_DeQueue(targs)) != NULL) { 718 CompatMake(gn, gn); 719 720 if (gn->made == UPTODATE) { 721 printf ("`%s' is up to date.\n", gn->name); 722 } else if (gn->made == ABORTED) { 723 printf ("`%s' not remade because of errors.\n", gn->name); 724 errors += 1; 725 } 726 } 727 728 /* 729 * If the user has defined a .END target, run its commands. 730 */ 731 if (errors == 0) { 732 Lst_Find(&ENDNode->commands, CompatRunCommand, gn); 733 } 734 } 735