Fix various things about SIGINT handling:

* exception handlers are now run with interrupts disabled, which avoids
  many race conditions
* fix some cases where SIGINT only aborts one command and continues the
  script, in particular if a SIGINT causes an EINTR error which trumped the
  interrupt.

Example:
  sh -c 'echo < /some/fifo; echo This should not be printed'
The fifo should not have writers. When pressing ctrl+c to abort the open,
the shell used to continue with the next command.

Example:
  sh -c '/bin/echo < /some/fifo; echo This should not be printed'
Similar. Note, however, that this particular case did not and does not work
in interactive mode with job control enabled.
This commit is contained in:
Jilles Tjoelker 2009-11-22 18:23:30 +00:00
parent b94f6865f9
commit 9922c6d2d5
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=199660
6 changed files with 25 additions and 6 deletions

View File

@ -73,11 +73,15 @@ static void exverror(int, const char *, va_list) __printf0like(2, 0);
* Called to raise an exception. Since C doesn't include exceptions, we
* just do a longjmp to the exception handler. The type of exception is
* stored in the global variable "exception".
*
* Interrupts are disabled; they should be reenabled when the exception is
* caught.
*/
void
exraise(int e)
{
INTOFF;
if (handler == NULL)
abort();
exception = e;
@ -138,8 +142,15 @@ onint(void)
static void
exverror(int cond, const char *msg, va_list ap)
{
CLEAR_PENDING_INT;
INTOFF;
/*
* An interrupt trumps an error. Certain places catch error
* exceptions or transform them to a plain nonzero exit code
* in child processes, and if an error exception can be handled,
* an interrupt can be handled as well.
*
* exraise() will disable interrupts for the exception handler.
*/
FORCEINTON;
#ifdef DEBUG
if (msg)

View File

@ -72,6 +72,8 @@ extern volatile sig_atomic_t intpending;
#define INTOFF suppressint++
#define INTON { if (--suppressint == 0 && intpending) onint(); }
#define is_int_on() suppressint
#define SETINTON(s) suppressint = (s)
#define FORCEINTON {suppressint = 0; if (intpending) onint();}
#define CLEAR_PENDING_INT intpending = 0
#define int_pending() intpending

View File

@ -782,7 +782,6 @@ evalcommand(union node *cmd, int flags, struct backcmd *backcmd)
savelocalvars = localvars;
localvars = NULL;
reffunc(cmdentry.u.func);
INTON;
savehandler = handler;
if (setjmp(jmploc.loc)) {
if (exception == EXSHELLPROC)
@ -798,6 +797,7 @@ evalcommand(union node *cmd, int flags, struct backcmd *backcmd)
longjmp(handler->loc, 1);
}
handler = &jmploc;
INTON;
for (sp = varlist.list ; sp ; sp = sp->next)
mklocal(sp->text);
funcnest++;

View File

@ -1312,6 +1312,7 @@ parsebackq: {
int saveprompt;
const int bq_startlinno = plinno;
str = NULL;
if (setjmp(jmploc.loc)) {
if (str)
ckfree(str);
@ -1323,7 +1324,6 @@ parsebackq: {
longjmp(handler->loc, 1);
}
INTOFF;
str = NULL;
savelen = out - stackblock();
if (savelen > 0) {
str = ckmalloc(savelen);

View File

@ -166,8 +166,11 @@ openredirect(union node *redir, char memory[10])
/*
* We suppress interrupts so that we won't leave open file
* descriptors around. This may not be such a good idea because
* an open of a device or a fifo can block indefinitely.
* descriptors around. Because the signal handler remains
* installed and we do not use system call restart, interrupts
* will still abort blocking opens such as fifos (they will fail
* with EINTR). There is, however, a race condition if an interrupt
* arrives after INTOFF and before open blocks.
*/
INTOFF;
memory[fd] = 0;

View File

@ -195,7 +195,9 @@ setvarsafe(char *name, char *val, int flags)
struct jmploc jmploc;
struct jmploc *const savehandler = handler;
int err = 0;
int inton;
inton = is_int_on();
if (setjmp(jmploc.loc))
err = 1;
else {
@ -203,6 +205,7 @@ setvarsafe(char *name, char *val, int flags)
setvar(name, val, flags);
}
handler = savehandler;
SETINTON(inton);
return err;
}