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