From 89a1b3970cb7ce36072eb22bd76b9be5e5d22b89 Mon Sep 17 00:00:00 2001 From: Joerg Sonnenberger Date: Wed, 1 Dec 2004 15:44:20 +0000 Subject: [PATCH] compat.c:1.38->1.39 job.c:1.55->1.56 Author: harti Log: Fix a (very) long standing bug in make (this has been there probably from the beginning). Make used to handle all its interrupt-time stuff directly from the signal handler, including calls to printf, accessing global data and so on. This is of course wrong and could provoke a core dump when interrupting make. Just set a flag in the signal handler and do everything else from the main thread. PR: bin/29103 config.h:1.14->1.15 main.c:1.97->1.98 parse.c:1.57->1.58 Author: harti Log: Eliminate the define for POSIX and build with Posix behaviour. Our make has been build with POSIX enabled from the first day and the ifdef'ed out code served no purpose. Obtained-from: FreeBSD Submitted-by: Max Okumoto --- usr.bin/make/compat.c | 59 ++++++++++++++++++++++++--------- usr.bin/make/config.h | 11 +------ usr.bin/make/job.c | 76 ++++++++++++++++++++++++++++++++++--------- usr.bin/make/main.c | 58 +++++++++++++++++---------------- usr.bin/make/parse.c | 59 ++++++++------------------------- 5 files changed, 148 insertions(+), 115 deletions(-) diff --git a/usr.bin/make/compat.c b/usr.bin/make/compat.c index 69fafc52e5..57da9e29fe 100644 --- a/usr.bin/make/compat.c +++ b/usr.bin/make/compat.c @@ -38,7 +38,7 @@ * * @(#)compat.c 8.2 (Berkeley) 3/19/94 * $FreeBSD: src/usr.bin/make/compat.c,v 1.16.2.2 2000/07/01 12:24:21 ps Exp $ - * $DragonFly: src/usr.bin/make/Attic/compat.c,v 1.13 2004/11/30 19:12:57 joerg Exp $ + * $DragonFly: src/usr.bin/make/Attic/compat.c,v 1.14 2004/12/01 15:44:20 joerg Exp $ */ /*- @@ -78,6 +78,8 @@ static char meta[256]; static GNode *curTarg = NULL; static GNode *ENDNode; +static sig_atomic_t interrupted; + static void CompatInterrupt(int); static int CompatMake(void *, void *); static int shellneed(char *); @@ -100,6 +102,16 @@ CompatInit(void) meta[0] = 1; } +/* + * Interrupt handler - set flag and defer handling to the main code + */ +static void +CompatCatchSig(int signo) +{ + + interrupted = signo; +} + /*- *----------------------------------------------------------------------- * CompatInterrupt -- @@ -119,6 +131,17 @@ static void CompatInterrupt (int signo) { GNode *gn; + sigset_t nmask, omask; + + sigemptyset(&nmask); + sigaddset(&nmask, SIGINT); + sigaddset(&nmask, SIGTERM); + sigaddset(&nmask, SIGHUP); + sigaddset(&nmask, SIGQUIT); + sigprocmask(SIG_SETMASK, &nmask, &omask); + + /* prevent recursion in evaluation of .INTERRUPT */ + interrupted = 0; if ((curTarg != NULL) && !Targ_Precious (curTarg)) { char *p1; @@ -128,18 +151,20 @@ CompatInterrupt (int signo) printf ("*** %s removed\n", file); } free(p1); + } - /* - * Run .INTERRUPT only if hit with interrupt signal - */ - if (signo == SIGINT) { - gn = Targ_FindNode(".INTERRUPT", TARG_NOCREATE); - if (gn != NULL) { - Lst_ForEach(gn->commands, Compat_RunCommand, (void *)gn); - } + /* + * Run .INTERRUPT only if hit with interrupt signal + */ + if (signo == SIGINT) { + gn = Targ_FindNode(".INTERRUPT", TARG_NOCREATE); + if (gn != NULL) { + Lst_ForEach(gn->commands, Compat_RunCommand, (void *)gn); } - } + + sigprocmask(SIG_SETMASK, &omask, NULL); + if (signo == SIGQUIT) exit(signo); (void) signal(signo, SIG_DFL); @@ -370,10 +395,12 @@ Compat_RunCommand (void *cmdp, void *gnp) while (1) { while ((rstat = wait(&reason)) != cpid) { - if (rstat == -1 && errno != EINTR) { - break; + if (interrupted || (rstat == -1 && errno != EINTR)) { + break; } } + if (interrupted) + CompatInterrupt(interrupted); if (rstat > -1) { if (WIFSTOPPED(reason)) { @@ -656,16 +683,16 @@ Compat_Run(Lst targs) CompatInit(); if (signal(SIGINT, SIG_IGN) != SIG_IGN) { - signal(SIGINT, CompatInterrupt); + signal(SIGINT, CompatCatchSig); } if (signal(SIGTERM, SIG_IGN) != SIG_IGN) { - signal(SIGTERM, CompatInterrupt); + signal(SIGTERM, CompatCatchSig); } if (signal(SIGHUP, SIG_IGN) != SIG_IGN) { - signal(SIGHUP, CompatInterrupt); + signal(SIGHUP, CompatCatchSig); } if (signal(SIGQUIT, SIG_IGN) != SIG_IGN) { - signal(SIGQUIT, CompatInterrupt); + signal(SIGQUIT, CompatCatchSig); } ENDNode = Targ_FindNode(".END", TARG_CREATE); diff --git a/usr.bin/make/config.h b/usr.bin/make/config.h index 04c2830aeb..89a2abdd4a 100644 --- a/usr.bin/make/config.h +++ b/usr.bin/make/config.h @@ -38,7 +38,7 @@ * * from: @(#)config.h 8.1 (Berkeley) 6/6/93 * $FreeBSD: src/usr.bin/make/config.h,v 1.9 1999/09/10 20:51:59 julian Exp $ - * $DragonFly: src/usr.bin/make/config.h,v 1.5 2004/12/01 02:02:14 joerg Exp $ + * $DragonFly: src/usr.bin/make/config.h,v 1.6 2004/12/01 15:44:20 joerg Exp $ */ #define DEFSHELL 1 /* Bourne shell */ @@ -80,15 +80,6 @@ #define LIBSUFF ".a" #define RECHECK -/* - * POSIX - * Adhere to the POSIX 1003.2 draft for the make(1) program. - * - Use MAKEFLAGS instead of MAKE to pick arguments from the - * environment. - * - Allow empty command lines if starting with tab. - */ -#define POSIX - /* * SYSVINCLUDE * Recognize system V like include directives [include "filename"] diff --git a/usr.bin/make/job.c b/usr.bin/make/job.c index c21c2cb74a..50bcdd2668 100644 --- a/usr.bin/make/job.c +++ b/usr.bin/make/job.c @@ -38,7 +38,7 @@ * * @(#)job.c 8.2 (Berkeley) 3/19/94 * $FreeBSD: src/usr.bin/make/job.c,v 1.17.2.2 2001/02/13 03:13:57 will Exp $ - * $DragonFly: src/usr.bin/make/job.c,v 1.21 2004/12/01 02:02:14 joerg Exp $ + * $DragonFly: src/usr.bin/make/job.c,v 1.22 2004/12/01 15:44:20 joerg Exp $ */ #ifndef OLD_JOKE @@ -248,6 +248,9 @@ STATIC Lst stoppedJobs; /* Lst of Job structures describing * limits or externally */ +static sig_atomic_t interrupted; + + #if defined(USE_PGRP) && defined(SYSV) # define KILL(pid, sig) killpg(-(pid), (sig)) #else @@ -295,6 +298,19 @@ static Shell *JobMatchShell(char *); static void JobInterrupt(int, int); static void JobRestartJobs(void); +/* + * JobCatchSignal + * + * Got a signal. Set global variables and hope that someone will + * handle it. + */ +static void +JobCatchSig(int signo) +{ + + interrupted = signo; +} + /*- *----------------------------------------------------------------------- * JobCondPassSig -- @@ -340,6 +356,10 @@ JobPassSig(int signo) sigset_t nmask, omask; struct sigaction act; + sigemptyset(&nmask); + sigaddset(&nmask, signo); + sigprocmask(SIG_SETMASK, &nmask, &omask); + DEBUGF(JOB, ("JobPassSig(%d) called.\n", signo)); Lst_ForEach(jobs, JobCondPassSig, (void *) &signo); @@ -366,10 +386,8 @@ JobPassSig(int signo) * Note we block everything else possible while we're getting the signal. * This ensures that all our jobs get continued when we wake up before * we take any other signal. + * XXX this comment seems wrong. */ - sigemptyset(&nmask); - sigaddset(&nmask, signo); - sigprocmask(SIG_SETMASK, &nmask, &omask); act.sa_handler = SIG_DFL; sigemptyset(&act.sa_mask); act.sa_flags = 0; @@ -1355,6 +1373,10 @@ JobStart(GNode *gn, int flags, Job *previous) Boolean noExec; /* Set true if we decide not to run the job */ int tfd; /* File descriptor for temp file */ + if (interrupted) { + JobPassSig(interrupted); + return (JOB_ERROR); + } if (previous != NULL) { previous->flags &= ~(JOB_FIRST|JOB_IGNERR|JOB_SILENT); job = previous; @@ -1698,6 +1720,14 @@ end_loop: nRead = read(job->inPipe, &job->outBuf[job->curPos], JOB_BUFSIZE - job->curPos); + /* + * Check for interrupt here too, because the above read may block + * when the child process is stopped. In this case the interrupt + * will unblock it (we don't use SA_RESTART). + */ + if (interrupted) + JobPassSig(interrupted); + if (nRead < 0) { DEBUGF(JOB, ("JobDoOutput(piperead)")); nr = 0; @@ -1910,6 +1940,8 @@ Job_CatchChildren(Boolean block) JobFinish(job, &status); } + if (interrupted) + JobPassSig(interrupted); } /*- @@ -1950,6 +1982,8 @@ Job_CatchOutput(void) if ((nfds = kevent(kqfd, NULL, 0, kev, KEV_SIZE, NULL)) == -1) { if (errno != EINTR) Punt("kevent: %s", strerror(errno)); + if (interrupted) + JobPassSig(interrupted); } else { for (i = 0; i < nfds; i++) { if (kev[i].flags & EV_ERROR) { @@ -1973,9 +2007,11 @@ Job_CatchOutput(void) timeout.tv_usec = SEL_USEC; if ((nfds = select(FD_SETSIZE, &readfds, (fd_set *) 0, - (fd_set *) 0, &timeout)) <= 0) + (fd_set *) 0, &timeout)) <= 0) { + if (interrupted) + JobPassSig(interrupted); return; - else { + } else { if (Lst_Open(jobs) == FAILURE) { Punt("Cannot open job table"); } @@ -2028,6 +2064,7 @@ void Job_Init(int maxproc) { GNode *begin; /* node for commands to do at the very start */ + struct sigaction sa; jobs = Lst_Init(FALSE); stoppedJobs = Lst_Init(FALSE); @@ -2071,19 +2108,24 @@ Job_Init(int maxproc) /* * Catch the four signals that POSIX specifies if they aren't ignored. - * JobPassSig will take care of calling JobInterrupt if appropriate. + * JobCatchSignal will just set global variables and hope someone + * else is going to handle the interrupt. */ + sa.sa_handler = JobCatchSig; + sigemptyset(&sa.sa_mask); + sa.sa_flags = 0; + if (signal(SIGINT, SIG_IGN) != SIG_IGN) { - (void) signal(SIGINT, JobPassSig); + (void) sigaction(SIGINT, &sa, NULL); } if (signal(SIGHUP, SIG_IGN) != SIG_IGN) { - (void) signal(SIGHUP, JobPassSig); + (void) sigaction(SIGHUP, &sa, NULL); } if (signal(SIGQUIT, SIG_IGN) != SIG_IGN) { - (void) signal(SIGQUIT, JobPassSig); + (void) sigaction(SIGQUIT, &sa, NULL); } if (signal(SIGTERM, SIG_IGN) != SIG_IGN) { - (void) signal(SIGTERM, JobPassSig); + (void) sigaction(SIGTERM, &sa, NULL); } /* * There are additional signals that need to be caught and passed if @@ -2093,16 +2135,16 @@ Job_Init(int maxproc) */ #if defined(USE_PGRP) if (signal(SIGTSTP, SIG_IGN) != SIG_IGN) { - (void) signal(SIGTSTP, JobPassSig); + (void) sigaction(SIGTSTP, &sa, NULL); } if (signal(SIGTTOU, SIG_IGN) != SIG_IGN) { - (void) signal(SIGTTOU, JobPassSig); + (void) sigaction(SIGTTOU, &sa, NULL); } if (signal(SIGTTIN, SIG_IGN) != SIG_IGN) { - (void) signal(SIGTTIN, JobPassSig); + (void) sigaction(SIGTTIN, &sa, NULL); } if (signal(SIGWINCH, SIG_IGN) != SIG_IGN) { - (void) signal(SIGWINCH, JobPassSig); + (void) sigaction(SIGWINCH, &sa, NULL); } #endif @@ -2420,6 +2462,10 @@ JobInterrupt(int runINTERRUPT, int signo) } if (runINTERRUPT && !touchFlag) { + /* clear the interrupted flag because we would get an + * infinite loop otherwise */ + interrupted = 0; + interrupt = Targ_FindNode(".INTERRUPT", TARG_NOCREATE); if (interrupt != NULL) { ignoreErrors = FALSE; diff --git a/usr.bin/make/main.c b/usr.bin/make/main.c index cbceb19631..20370a3bf4 100644 --- a/usr.bin/make/main.c +++ b/usr.bin/make/main.c @@ -38,7 +38,7 @@ * @(#) Copyright (c) 1988, 1989, 1990, 1993 The Regents of the University of California. All rights reserved. * @(#)main.c 8.3 (Berkeley) 3/19/94 * $FreeBSD: src/usr.bin/make/main.c,v 1.35.2.10 2003/12/16 08:34:11 des Exp $ - * $DragonFly: src/usr.bin/make/main.c,v 1.24 2004/12/01 02:02:14 joerg Exp $ + * $DragonFly: src/usr.bin/make/main.c,v 1.25 2004/12/01 15:44:20 joerg Exp $ */ /*- @@ -83,6 +83,8 @@ #include "pathnames.h" #define WANT_ENV_MKLVL 1 +#define MKLVL_MAXVAL 500 +#define MKLVL_ENVVAR "__MKLVL__" #define MAKEFLAGS ".MAKEFLAGS" @@ -393,6 +395,30 @@ chdir_verify_path(char *path, char *obpath) return 0; } +/* + * In lieu of a good way to prevent every possible looping in + * make(1), stop there from being more than MKLVL_MAXVAL processes forked + * by make(1), to prevent a forkbomb from happening, in a dumb and + * mechanical way. + */ +static void +check_make_level(void) +{ +#ifdef WANT_ENV_MKLVL + char *value = getenv(MKLVL_ENVVAR); + int level = (value == NULL) ? 0 : atoi(value); + + if (level < 0) { + errc(2, EAGAIN, "Invalid value for recursion level (%d).", level); + } else if (level > MKLVL_MAXVAL) { + errc(2, EAGAIN, "Max recursion level (%d) exceeded.", MKLVL_MAXVAL); + } else { + char new_value[32]; + sprintf(new_value, "%d", level + 1); + setenv(MKLVL_ENVVAR, new_value, 1); + } +#endif /* WANT_ENV_MKLVL */ +} /*- * main -- @@ -418,12 +444,6 @@ main(int argc, char **argv) Boolean outOfDate = TRUE; /* FALSE if all targets up to date */ struct stat sa; char *p, *p1, *path, *pathp; -#ifdef WANT_ENV_MKLVL -#define MKLVL_MAXVAL 500 -#define MKLVL_ENVVAR "__MKLVL__" - int iMkLvl = 0; - char *szMkLvl = getenv(MKLVL_ENVVAR); -#endif /* WANT_ENV_MKLVL */ char mdpath[MAXPATHLEN]; char obpath[MAXPATHLEN]; char cdpath[MAXPATHLEN]; @@ -445,25 +465,15 @@ main(int argc, char **argv) */ struct sigaction sa; -#ifdef WANT_ENV_MKLVL - if ((iMkLvl = szMkLvl ? atoi(szMkLvl) : 0) < 0) { - iMkLvl = 0; - } - if (iMkLvl++ > MKLVL_MAXVAL) { - errc(2, EAGAIN, - "Max recursion level (%d) exceeded.", MKLVL_MAXVAL); - } - bzero(szMkLvl = emalloc(32), 32); - sprintf(szMkLvl, "%d", iMkLvl); - setenv(MKLVL_ENVVAR, szMkLvl, 1); -#endif /* WANT_ENV_MKLVL */ - sigemptyset(&sa.sa_mask); sa.sa_flags = SA_RESTART | SA_NOCLDSTOP; sa.sa_handler = catch_child; sigaction(SIGCHLD, &sa, NULL); } + check_make_level(); + + #ifdef RLIMIT_NOFILE /* * get rid of resource limit on file descriptors @@ -595,11 +605,7 @@ main(int argc, char **argv) * (Note this is *not* MAKEFLAGS since /bin/make uses that and it's * in a different format). */ -#ifdef POSIX Main_ParseArgLine(getenv("MAKEFLAGS")); -#else - Main_ParseArgLine(getenv("MAKE")); -#endif MainParseArgs(argc, argv); @@ -748,11 +754,7 @@ main(int argc, char **argv) /* Install all the flags into the MAKE envariable. */ if (((p = Var_Value(MAKEFLAGS, VAR_GLOBAL, &p1)) != NULL) && *p) -#ifdef POSIX setenv("MAKEFLAGS", p, 1); -#else - setenv("MAKE", p, 1); -#endif free(p1); /* diff --git a/usr.bin/make/parse.c b/usr.bin/make/parse.c index 8877eb02e9..9d4ee98244 100644 --- a/usr.bin/make/parse.c +++ b/usr.bin/make/parse.c @@ -37,7 +37,7 @@ * * @(#)parse.c 8.3 (Berkeley) 3/19/94 * $FreeBSD: src/usr.bin/make/parse.c,v 1.22.2.2 2004/07/10 08:14:42 eik Exp $ - * $DragonFly: src/usr.bin/make/parse.c,v 1.18 2004/12/01 15:17:28 joerg Exp $ + * $DragonFly: src/usr.bin/make/parse.c,v 1.19 2004/12/01 15:44:20 joerg Exp $ */ /*- @@ -142,9 +142,7 @@ typedef enum { Parallel, /* .PARALLEL */ ExPath, /* .PATH */ Phony, /* .PHONY */ -#ifdef POSIX Posix, /* .POSIX */ -#endif Precious, /* .PRECIOUS */ ExShell, /* .SHELL */ Silent, /* .SILENT */ @@ -198,9 +196,7 @@ static struct { { ".PARALLEL", Parallel, 0 }, { ".PATH", ExPath, 0 }, { ".PHONY", Phony, OP_PHONY }, -#ifdef POSIX { ".POSIX", Posix, 0 }, -#endif { ".PRECIOUS", Precious, OP_PRECIOUS }, { ".RECURSIVE", Attribute, OP_MAKE }, { ".SHELL", ExShell, 0 }, @@ -1033,11 +1029,9 @@ ParseDoDependency (char *line) case ExPath: Lst_ForEach(paths, ParseClearPath, (void *)NULL); break; -#ifdef POSIX case Posix: Var_Set("%POSIX", "1003.2", VAR_GLOBAL); break; -#endif default: break; } @@ -2434,9 +2428,6 @@ Parse_File(char *name, FILE *stream) * If a line starts with a tab, it can only hope to be * a creation command. */ -#ifndef POSIX - shellCommand: -#endif for (cp = line + 1; isspace ((unsigned char) *cp); cp++) { continue; } @@ -2480,10 +2471,6 @@ Parse_File(char *name, FILE *stream) * line's script, we assume it's actually a shell command * and add it to the current list of targets. */ -#ifndef POSIX - Boolean nonSpace = FALSE; -#endif - cp = line; if (isspace((unsigned char) line[0])) { while ((*cp != '\0') && isspace((unsigned char) *cp)) { @@ -2492,44 +2479,24 @@ Parse_File(char *name, FILE *stream) if (*cp == '\0') { goto nextLine; } -#ifndef POSIX - while ((*cp != ':') && (*cp != '!') && (*cp != '\0')) { - nonSpace = TRUE; - cp++; - } -#endif } -#ifndef POSIX - if (*cp == '\0') { - if (inLine) { - Parse_Error (PARSE_WARNING, - "Shell command needs a leading tab"); - goto shellCommand; - } else if (nonSpace) { - Parse_Error (PARSE_FATAL, "Missing operator"); - } - } else { -#endif - ParseFinishLine(); + ParseFinishLine(); - cp = Var_Subst (NULL, line, VAR_CMD, TRUE); - free (line); - line = cp; + cp = Var_Subst (NULL, line, VAR_CMD, TRUE); + free (line); + line = cp; - /* - * Need a non-circular list for the target nodes - */ - if (targets) - Lst_Destroy(targets, NOFREE); + /* + * Need a non-circular list for the target nodes + */ + if (targets) + Lst_Destroy(targets, NOFREE); - targets = Lst_Init (FALSE); - inLine = TRUE; + targets = Lst_Init (FALSE); + inLine = TRUE; - ParseDoDependency (line); -#ifndef POSIX - } -#endif + ParseDoDependency (line); } nextLine: -- 2.41.0