diff options
author | harti <harti@FreeBSD.org> | 2004-11-12 07:57:17 +0000 |
---|---|---|
committer | harti <harti@FreeBSD.org> | 2004-11-12 07:57:17 +0000 |
commit | 24fe3160f6855d1e448487cea73895c8f79a7edd (patch) | |
tree | be0ceb4db8551f113ae779cd72720370744ff2ab /usr.bin | |
parent | 2fe0d6951dd03633e4a8010dfefbbb51b24b735b (diff) | |
download | FreeBSD-src-24fe3160f6855d1e448487cea73895c8f79a7edd.zip FreeBSD-src-24fe3160f6855d1e448487cea73895c8f79a7edd.tar.gz |
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
Diffstat (limited to 'usr.bin')
-rw-r--r-- | usr.bin/make/compat.c | 57 | ||||
-rw-r--r-- | usr.bin/make/job.c | 74 |
2 files changed, 102 insertions, 29 deletions
diff --git a/usr.bin/make/compat.c b/usr.bin/make/compat.c index b9ba32b..7fc3cf2 100644 --- a/usr.bin/make/compat.c +++ b/usr.bin/make/compat.c @@ -79,6 +79,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 *); @@ -101,6 +103,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 -- @@ -120,6 +132,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; @@ -129,18 +152,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); @@ -373,10 +398,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)) { @@ -660,16 +687,16 @@ Compat_Run(Lst targs) Shell_Init(); /* Set up shell. */ 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/job.c b/usr.bin/make/job.c index 6dfb28f..3979ecd 100644 --- a/usr.bin/make/job.c +++ b/usr.bin/make/job.c @@ -259,6 +259,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 @@ -306,6 +309,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 -- @@ -351,6 +367,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); @@ -377,10 +397,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; @@ -1366,6 +1384,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; @@ -1709,6 +1731,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; @@ -1921,6 +1951,8 @@ Job_CatchChildren(Boolean block) JobFinish(job, &status); } + if (interrupted) + JobPassSig(interrupted); } /*- @@ -1961,6 +1993,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) { @@ -1984,9 +2018,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"); } @@ -2055,6 +2091,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); @@ -2087,19 +2124,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 @@ -2109,16 +2151,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 @@ -2447,6 +2489,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; |