Merge branch 'pr/use-default-sigpipe-setting'
authorJunio C Hamano <gitster@pobox.com>
Tue, 30 Sep 2014 05:17:20 +0000 (22:17 -0700)
committerJunio C Hamano <gitster@pobox.com>
Tue, 30 Sep 2014 05:17:20 +0000 (22:17 -0700)
We used to get confused when a process called us with SIGPIPE
ignored; we do want to die with SIGPIPE when the output is not
read by default, and do ignore the signal when appropriate.

* pr/use-default-sigpipe-setting:
mingw.h: add dummy functions for sigset_t operations
unblock and unignore SIGPIPE

compat/mingw.h
git.c
t/t0005-signals.sh
index df0e3203abed06374a789be191ff130e3ba728b6..5e499cfb71a00ea81013c1ed21e9a5e9dc9e2544 100644 (file)
@@ -69,7 +69,6 @@ struct sigaction {
        sig_handler_t sa_handler;
        unsigned sa_flags;
 };
-#define sigemptyset(x) (void)0
 #define SA_RESTART 0
 
 struct itimerval {
@@ -116,6 +115,12 @@ static inline int fcntl(int fd, int cmd, ...)
 }
 /* bash cannot reliably detect negative return codes as failure */
 #define exit(code) exit((code) & 0xff)
+#define sigemptyset(x) (void)0
+static inline int sigaddset(sigset_t *set, int signum)
+{ return 0; }
+#define SIG_UNBLOCK 0
+static inline int sigprocmask(int how, const sigset_t *set, sigset_t *oldset)
+{ return 0; }
 
 /*
  * simple adaptors
diff --git a/git.c b/git.c
index 4076b014aba6c488e51a9bae8e5cd4b26e4e2476..4cebf32126014938533b66cb57164c6602a1675c 100644 (file)
--- a/git.c
+++ b/git.c
@@ -592,6 +592,26 @@ static int run_argv(int *argcp, const char ***argv)
        return done_alias;
 }
 
+/*
+ * Many parts of Git have subprograms communicate via pipe, expect the
+ * upstream of a pipe to die with SIGPIPE when the downstream of a
+ * pipe does not need to read all that is written.  Some third-party
+ * programs that ignore or block SIGPIPE for their own reason forget
+ * to restore SIGPIPE handling to the default before spawning Git and
+ * break this carefully orchestrated machinery.
+ *
+ * Restore the way SIGPIPE is handled to default, which is what we
+ * expect.
+ */
+static void restore_sigpipe_to_default(void)
+{
+       sigset_t unblock;
+
+       sigemptyset(&unblock);
+       sigaddset(&unblock, SIGPIPE);
+       sigprocmask(SIG_UNBLOCK, &unblock, NULL);
+       signal(SIGPIPE, SIG_DFL);
+}
 
 int main(int argc, char **av)
 {
@@ -611,6 +631,8 @@ int main(int argc, char **av)
         */
        sanitize_stdfds();
 
+       restore_sigpipe_to_default();
+
        git_setup_gettext();
 
        trace_command_performance(argv);
index 981437b3a88b86ee1ddd26f842a0048b8dc2b57f..aeea50c6339ebd4a0d33ce03b7fad5948e69a8a8 100755 (executable)
@@ -27,4 +27,26 @@ test_expect_success !MINGW 'signals are propagated using shell convention' '
        test_expect_code 143 git sigterm
 '
 
+large_git () {
+       for i in $(test_seq 1 100)
+       do
+               git diff --cached --binary || return
+       done
+}
+
+test_expect_success 'create blob' '
+       test-genrandom foo 16384 >file &&
+       git add file
+'
+
+test_expect_success !MINGW 'a constipated git dies with SIGPIPE' '
+       OUT=$( ((large_git; echo $? 1>&3) | :) 3>&1 )
+       test "$OUT" -eq 141
+'
+
+test_expect_success !MINGW 'a constipated git dies with SIGPIPE even if parent ignores it' '
+       OUT=$( ((trap "" PIPE; large_git; echo $? 1>&3) | :) 3>&1 )
+       test "$OUT" -eq 141
+'
+
 test_done