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 _______________________________________________ Glasgow-haskell-users mailing list Glasgow-haskell-users@haskell.org http://www.haskell.org/mailman/listinfo/glasgow-haskell-users