SIGPIPE in the GHC runtime
Brian Bloniarz
brian.bloniarz at gmail.com
Wed Aug 18 00:21:41 EDT 2010
The GHC runtime ignores SIGPIPE by setting the signal
to SIG_IGN. This means that any subprocesses (created via
System.Process or otherwise) will also have their
SIGPIPE handler set to SIG_IGN; I think this might be
a bug. The Python runtime does the same thing,
there's a good explanation of the drawbacks in:
http://bugs.python.org/issue1652
IMHO the simplest fix is the patch below: simply
avoid SIG_IGN, instead install a handler which does nothing.
This way, an exec() restores the handler to SIG_DFL. I've
included a testcase too.
Do people think this is the way to go? I think the alternative
is the approach Python took: patch the various bits of code
that fork&exec and have them restore the signal handler
manually.
---
diff -rN old-ghc/rts/posix/Signals.c new-ghc/rts/posix/Signals.c
472a473,477
> static void
> ignoreSigPipe(int sig)
> {
> }
>
528,529c533,535
< // ignore SIGPIPE; see #1619
< action.sa_handler = SIG_IGN;
---
> // ignore SIGPIPE; see #1619. Don't use SIG_IGN since that'd
> // be inherited by any children that get fork&exec'd.
> action.sa_handler = &ignoreSigPipe;
531c537
< action.sa_flags = 0;
---
> action.sa_flags = SA_RESTART;
---
Testcase:
diff -rN old-testsuite/tests/ghc-regress/rts/all.T new-testsuite/tests/ghc-regress/rts/all.T
86a87,88
>
> test('exec_signals', [cmd_prefix('$MAKE exec_signals-prep && ./exec_signals_prepare')], compile_and_run, [''])
diff -rN old-testsuite/tests/ghc-regress/rts/exec_signals_child.c new-testsuite/tests/ghc-regress/rts/exec_signals_child.c
0a1,47
> #include <signal.h>
> #include <stdio.h>
> #include <errno.h>
>
> // Prints the state of the signal handlers to stdout
> int main()
> {
> int open = 0, i;
> sigset_t blockedsigs;
>
> printf("ChildInfo { masked = [");
>
> sigprocmask(SIG_BLOCK, NULL, &blockedsigs);
> for(i = 0; i < NSIG; ++i)
> {
> int ret = sigismember(&blockedsigs, i);
> if(ret >= 0)
> {
> if(!open)
> open=1;
> else
> printf(",");
> printf("(%d,%s)", i, ret == 1 ? "True" : "False");
> }
> }
> printf("], handlers = [");
>
> open = 0;
> for(i = 0; i < NSIG; ++i)
> {
> struct sigaction old;
> if(sigaction(i, NULL, &old) >= 0)
> {
> if(!open)
> open=1;
> else
> printf(",");
>
> printf("(%d,%s)", i,
> old.sa_handler == SIG_IGN ? "Ignored" :
> (old.sa_handler == SIG_DFL ? "Default" : "Handled"));
> }
> }
> printf("]}");
>
> return 0;
> }
diff -rN old-testsuite/tests/ghc-regress/rts/exec_signals.hs new-testsuite/tests/ghc-regress/rts/exec_signals.hs
0a1,20
> import System.Process
> import System.Posix.Signals
> import Control.Monad(when)
>
> data SigState = Ignored | Default | Handled
> deriving (Eq, Read, Show)
>
> data ChildInfo = ChildInfo {
> masked :: [(Int,Bool)],
> handlers :: [(Int, SigState)] }
> deriving (Read, Show)
>
> main = do out <- readProcess "./exec_signals_child" [] ""
> let ci = read out :: ChildInfo
> blockedSigs = [x | (x, True) <- masked ci]
> ignoredSigs = [x | (x, Ignored) <- handlers ci]
> when (not $ null blockedSigs) $
> putStrLn ("signals " ++ show blockedSigs ++ " are blocked")
> when (not $ null ignoredSigs) $
> putStrLn ("signals " ++ show ignoredSigs ++ " are ignored")
diff -rN old-testsuite/tests/ghc-regress/rts/exec_signals_prepare.c new-testsuite/tests/ghc-regress/rts/exec_signals_prepare.c
0a1,29
> #include <signal.h>
> #include <stdio.h>
> #include <errno.h>
> #include <string.h>
>
> // Invokes a process, making sure that the state of the signal
> // handlers has all been set back to the unix default.
> int main(int argc, char **argv)
> {
> int i;
> sigset_t blockedsigs;
> struct sigaction action;
>
> // unblock all signals
> sigemptyset(&blockedsigs);
> sigprocmask(SIG_BLOCK, NULL, NULL);
>
> // reset all signals to SIG_DFL
> memset(&action, 0, sizeof(action));
> action.sa_handler = SIG_DFL;
> action.sa_flags = 0;
> sigemptyset(&action.sa_mask);
> for(i = 0; i < NSIG; ++i)
> sigaction(i, &action, NULL);
>
> execv(argv[1], argv+1);
> fprintf(stderr, "failed to execv %s\n", argv[1]);
> return 0;
> }
diff -rN old-testsuite/tests/ghc-regress/rts/Makefile new-testsuite/tests/ghc-regress/rts/Makefile
29a30,32
> exec_signals-prep::
> $(CC) -o exec_signals_child exec_signals_child.c
> $(CC) -o exec_signals_prepare exec_signals_prepare.c
More information about the Glasgow-haskell-users
mailing list