git/list[1] front-page[2] threads[3] people[4] search[5] about
 

[WIP/PATCH v2 5/5] WIP pager: respect exit code of pager over SIGPIPE

From
Ævar Arnfjörð Bjarmason <avarab@gmail.com>
Date
Feb 2, 2021, 02:00 UTC
Message-ID
<20210202020001.31601-6-avarab@gmail.com>
In-Reply-To
<20210201144921.8664-1-avarab@gmail.com>

As discussed on-list starting with [1] I don't think this patch makes sense, but this "passes tests", at least on Debian with glibc, and is food for thought for those who like the approach of git not propagating the pager-induced SIGPIPE in git's own exit code.

The exit() here in wait_for_pager_atexit() isn't portable though[2], we could probably use _exit(1) instead, but then we're going to abruptly put a stop to further atexit handler processing. We're far from the only one, tempfile.c, run-command.c, gc.c etc. all rely on it, and that's just the git.git code.

If we drop the "if (code)" condition we can see that our pager exit code will override the exit code of other commands in t7006-pager.sh, causing numerous tests to fail. Of course if we don't do that all tests pass.

But that experiment suggests regressions introduced here that we just don't have good test coverage for. I.e. we're running code before the atexit() here which expects to exit() with a given status code, and we're clobbering it with ours because the pager also happened to fail as we were exiting.

So a real implementation of this would, I think, have to at least:
 A. Refactor all use of atexit() to use some git-specific registry,
    hard assert somehow that we're never going to have atexit() by
    anything else (a library we use might call it).
 B. Because we used some atexit() wrapper API we'd know if we were in
    the last atexit() handler, which would need to re-evaluate the
    decision about the "real" exit code.
 C. We could not call exit() anywhere, but would have to make a
    git_exit() wrapper. We'd then assign the desired exit code to a
    global variable, and then only override our "real" non-zero exit
    code with the pager's non-zero, in cases where the pager also
    failed.
 D. I haven't found whether calling _exit() in the atexit() handler
    even has defined behavior, but in any case using it would
    short-circuit the documented program exit behavior defined in the
    C standard, of which calling atexit() handlers is just the first
    step.
1. https://lore.kernel.org/git/8735yhq3lc.fsf@evledraar.gmail.com/
2. https://pubs.opengroup.org/onlinepubs/009695399/functions/exit.html
Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 pager.c          | 10 ++++++++--
 t/t7006-pager.sh |  8 ++++----
 2 files changed, 12 insertions(+), 6 deletions(-)
diff --git a/pager.c b/pager.c
index 3d37dd7adaa..2e743bc0b1e 100644
--- a/pager.c
+++ b/pager.c
@@ -20,18 +20,24 @@ static void close_pager_fds(void)
 
 static void wait_for_pager_atexit(void)
 {
+	int code;
 	fflush(stdout);
 	fflush(stderr);
 	close_pager_fds();
-	finish_command(&pager_process);
+	code = finish_command(&pager_process);
+	if (code)
+		exit(code);
 }
 
 static void wait_for_pager_signal(int signo)
 {
+	int code;
 	close_pager_fds();
-	finish_command_in_signal(&pager_process);
+	code = finish_command_in_signal(&pager_process);
 	sigchain_pop(signo);
 	raise(signo);
+	if (signo == SIGPIPE)
+		exit(code);
 }
 
 static int core_pager_config(const char *var, const char *value, void *data)
diff --git a/t/t7006-pager.sh b/t/t7006-pager.sh
index 0e7cf75435e..69997fa48f2 100755
--- a/t/t7006-pager.sh
+++ b/t/t7006-pager.sh
@@ -703,7 +703,7 @@ test_expect_success TTY 'git returns SIGPIPE on early pager non-zero exit' '
 	test_path_is_file pager-used
 '
 
-test_expect_success TTY 'git discards pager non-zero exit without SIGPIPE' '
+test_expect_success TTY 'git respects pager non-zero exit without SIGPIPE' '
 	test_when_finished "rm pager-used trace.normal" &&
 	test_config core.pager "wc >pager-used; exit 1" &&
 	GIT_TRACE2="$(pwd)/trace.normal" &&
@@ -713,7 +713,7 @@ test_expect_success TTY 'git discards pager non-zero exit without SIGPIPE' '
 	if test_have_prereq !MINGW
 	then
 		OUT=$( ((test_terminal git log; echo $? 1>&3) | :) 3>&1 ) &&
-		test "$OUT" -eq 0
+		test "$OUT" -eq 1
 	else
 		test_terminal git log
 	fi &&
@@ -724,7 +724,7 @@ test_expect_success TTY 'git discards pager non-zero exit without SIGPIPE' '
 	test_path_is_file pager-used
 '
 
-test_expect_success TTY 'git discards nonexisting pager without SIGPIPE' '
+test_expect_success TTY 'git respects nonexisting pager without SIGPIPE' '
 	test_when_finished "rm pager-used trace.normal" &&
 	test_config core.pager "wc >pager-used; does-not-exist" &&
 	GIT_TRACE2="$(pwd)/trace.normal" &&
@@ -734,7 +734,7 @@ test_expect_success TTY 'git discards nonexisting pager without SIGPIPE' '
 	if test_have_prereq !MINGW
 	then
 		OUT=$( ((test_terminal git log; echo $? 1>&3) | :) 3>&1 ) &&
-		test "$OUT" -eq 0
+		test "$OUT" -eq 127
 	else
 		test_terminal git log
 	fi &&
-- 
2.30.0.284.gd98b1dd5eaa7
Previous: Junio C HamanoNext: Vincent Lefevre
Message 51 of 60 in “git fails with a broken pipe when one quits the pager”
  1. Vincent LefevreJan 15, 2021
  2. pager: exit without error on SIGPIPEDenton Liu, Jan 29, 2021
  3. Johannes SixtJan 30, 2021
  4. Johannes SixtJan 30, 2021
  5. Ævar Arnfjörð BjarmasonFeb 1, 2021
  6. Junio C HamanoFeb 1, 2021
  7. Ævar Arnfjörð BjarmasonFeb 1, 2021
  8. Junio C HamanoFeb 1, 2021
  9. Ævar Arnfjörð BjarmasonFeb 2, 2021
  10. Junio C HamanoFeb 2, 2021
  11. Junio C HamanoFeb 2, 2021
  12. Johannes SixtFeb 2, 2021
  13. Junio C HamanoFeb 2, 2021
  14. Johannes SixtFeb 2, 2021
  15. Junio C HamanoFeb 2, 2021
  16. Johannes SixtFeb 3, 2021
  17. Junio C HamanoFeb 3, 2021
  18. Vincent LefevreFeb 4, 2021
  19. Ævar Arnfjörð BjarmasonFeb 3, 2021
  20. Junio C HamanoFeb 3, 2021
  21. Ævar Arnfjörð BjarmasonFeb 3, 2021
  22. Johannes SixtFeb 3, 2021
  23. Ævar Arnfjörð BjarmasonJan 31, 2021
  24. Vincent LefevreJan 31, 2021
  25. Vincent LefevreJan 31, 2021
  26. Ævar Arnfjörð BjarmasonJan 31, 2021
  27. Vincent LefevreFeb 1, 2021
  28. Chris TorekFeb 1, 2021
  29. Vincent LefevreFeb 1, 2021
  30. Chris TorekFeb 1, 2021
  31. Vincent LefevreFeb 1, 2021
  32. Ævar Arnfjörð BjarmasonFeb 1, 2021
  33. Ævar Arnfjörð BjarmasonFeb 1, 2021
  34. 3/3 pager: properly log pager exit code when signalledÆvar Arnfjörð Bjarmason, Feb 1, 2021
  35. Junio C HamanoFeb 1, 2021
  36. Ævar Arnfjörð BjarmasonFeb 1, 2021
  37. Junio C HamanoFeb 1, 2021
  38. Ævar Arnfjörð BjarmasonFeb 1, 2021
  39. 2/3 pager: refactor wait_for_pager() functionÆvar Arnfjörð Bjarmason, Feb 1, 2021
  40. 1/3 pager: test for exit codeÆvar Arnfjörð Bjarmason, Feb 1, 2021
  41. 0/3 pager: test for exit behavior & trace2 bug fixÆvar Arnfjörð Bjarmason, Feb 1, 2021
  42. 1/5 pager: refactor wait_for_pager() functionÆvar Arnfjörð Bjarmason, Feb 2, 2021
  43. 0/5 pager: test for exit behavior & trace2 bug fixÆvar Arnfjörð Bjarmason, Feb 2, 2021
  44. 3/5 run-command: add braces for "if" block in wait_or_whine()Ævar Arnfjörð Bjarmason, Feb 2, 2021
  45. 2/5 pager: test for exit code with and without SIGPIPEÆvar Arnfjörð Bjarmason, Feb 2, 2021
  46. Denton LiuFeb 2, 2021
  47. Johannes SixtFeb 5, 2021
  48. 4/5 pager: properly log pager exit code when signalledÆvar Arnfjörð Bjarmason, Feb 2, 2021
  49. Johannes SixtFeb 5, 2021
  50. Junio C HamanoFeb 5, 2021
  51. 5/5 WIP pager: respect exit code of pager over SIGPIPEÆvar Arnfjörð Bjarmason, Feb 2, 2021
  52. Vincent LefevreFeb 1, 2021
  53. Ævar Arnfjörð BjarmasonFeb 1, 2021
  54. Johannes SixtFeb 1, 2021
  55. Ævar Arnfjörð BjarmasonFeb 3, 2021
  56. Johannes SixtFeb 3, 2021
  57. Vincent LefevreFeb 3, 2021
  58. Ævar Arnfjörð BjarmasonFeb 4, 2021
  59. Vincent LefevreFeb 4, 2021
  60. Johannes SixtFeb 1, 2021

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.