bug-gnulib
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [patch] fix "broken pipe" message from lseek test


From: Bruno Haible
Subject: Re: [patch] fix "broken pipe" message from lseek test
Date: Sun, 21 Feb 2010 13:33:47 +0100
User-agent: KMail/1.9.9

Hi Ben,

Thanks for this analysis.

> Default handling of SIGPIPE
> ---------------------------
> 
>     address@hidden:~/tmp(0)$ strace -o foo -f bash
>     address@hidden:~/tmp(0)$ trap - SIGPIPE
>     address@hidden:~/tmp(0)$ grep ^Sig /proc/$$/status
>     SigQ:   1/16382
>     SigPnd: 0000000000000000
>     SigBlk: 0000000000010000
>     SigIgn: 0000000000384000
>     SigCgt: 000000004b813eff
> 
> SigIgn: ...4000 means SIGPIPE is not SIG_IGN.
> SigCgt: ...3eff means SIGPIPE has a registered handler.
> 
>     address@hidden:~/tmp(0)$ echo hi | ./a.out
>     bash: echo: write error: Broken pipe

An even easier way to reproduce it is to run
      $ echo hi | true
10 times at a bash prompt. In bash 3.2, it frequently prints the
error message.

> Your gnulib-tool comment refers to Chet Ramey's email at
> http://lists.gnu.org/archive/html/bug-bash/2008-12/msg00050.html.
> I think that he is saying the same thing, actually:
> 
>     The second is that the echo builtin in bash-3.2 displays a message on
>     a write error, instead of letting the exit status communicate the error.
>     When the shell receives SIGPIPE and handles it without exiting, writes
>     to that pipe return -1/EPIPE, and the echo builtin reports the error.  In
>     earlier versions, you wouldn't have seen the message.
> 
> The bash 3.2 "printf" builtin doesn't have this problem though.

Aha! So it's really a bug in the 'echo' built-in, and using 'printf' is a
work-around.

I'm updating gnulib-tool: The function func_reset_sigpipe had no effect, since
the bash bug occurs also in the _default_ situation (no inherited trap or 
ignored
signal). Also the comment "When "trap '' SIGPIPE" is in effect, the behaviour
(at least with bash) is to terminate the current process with an error message."
was wrong, because it applies only to the 'echo' built-in, not to other 
built-ins.


2010-02-21  Bruno Haible  <address@hidden>

        Really work around around "broken pipe" error message from bash 3.2.
        * gnulib-tool (func_reset_sigpipe): Remove function.
        (echo): In bash 3.2, define to a function that uses printf.
        Analyzed by Ralf Wildenhues, Chet Ramey, Ben Pfaff.

--- gnulib-tool.orig    Sun Feb 21 13:24:10 2010
+++ gnulib-tool Sun Feb 21 13:20:19 2010
@@ -661,39 +661,20 @@
   fi
 }
 
-# func_reset_sigpipe
-# Resets SIGPIPE to its default behaviour. SIGPIPE is signalled when a process
-# writes into a pipe with no readers, i.e. a pipe where all readers have
-# already closed their file descriptor that read from it or exited entirely.
-# The default behaviour is to terminate the current process without an error
-# message.
-# When "trap '' SIGPIPE" is in effect, the behaviour (at least with bash) is to
-# terminate the current process with an error message.
-# This function should be called at the beginning of a command that only
-# produces output to stdout (i.e. no side effects!), when the command that
-# will read from this pipe might prematurely exit or close its standard input
-# descriptor.
-if test -n "$BASH_VERSION"; then
-  # The problem has only been reported with bash. Probably it occurs only with
-  # bash-3.2. For the reasons, see
-  # <http://lists.gnu.org/archive/html/bug-bash/2008-12/msg00050.html>.
-  # Note that Solaris sh does not understand "trap - SIGPIPE".
-  func_reset_sigpipe ()
-  {
-    trap - SIGPIPE
-  }
-else
-  func_reset_sigpipe ()
-  {
-    :
-  }
-fi
-
-# Ensure an 'echo' command that does not interpret backslashes.
-# Test cases:
+# Ensure an 'echo' command that
+#   1. does not interpret backslashes and
+#   2. does not print an error message "broken pipe" when writing into a pipe
+#      with no writers.
+#
+# Test cases for problem 1:
 #   echo '\n' | wc -l                 prints 1 when OK, 2 when KO
 #   echo '\t' | grep t > /dev/null    has return code 0 when OK, 1 when KO
-# This problem is a weird heritage from SVR4. BSD got it right (except that
+# Test cases for problem 2:
+#   echo hi | true                    frequently prints
+#                                     "bash: echo: write error: Broken pipe"
+#                                     to standard error in bash 3.2.
+#
+# Problem 1 is a weird heritage from SVR4. BSD got it right (except that
 # BSD echo interprets '-n' as an option, which is also not desirable).
 # Nowadays the problem occurs in 4 situations:
 # - in bash, when the shell option xpg_echo is set (bash >= 2.04)
@@ -716,6 +697,12 @@
 # - otherwise: respawn using /bin/sh and rely on the workarounds.
 # When respawning, we pass --no-reexec as first argument, so as to avoid
 # turning this script into a fork bomb in unlucky situations.
+#
+# Problem 2 is specific to bash 3.2 and affects the 'echo' built-in, but not
+# the 'printf' built-in. See
+#   <http://lists.gnu.org/archive/html/bug-bash/2008-12/msg00050.html>
+#   <http://lists.gnu.org/archive/html/bug-gnulib/2010-02/msg00154.html>
+# The workaround is: define echo to a function that uses the printf built-in.
 have_echo=
 if echo '\t' | grep t > /dev/null; then
   have_echo=yes # Lucky!
@@ -823,6 +810,15 @@
   exec /bin/sh "$0" --no-reexec "$@"
   exit 127
 fi
+# Now handle problem 2, specific to bash 3.2.
+case "$BASH_VERSION" in
+  3.2*)
+    echo ()
+    {
+      printf '%s\n' "$*"
+    }
+    ;;
+esac
 if test -z "$have_echo"; then
   func_fatal_error "Shell does not support 'echo' correctly. Please install 
GNU bash and set the environment variable CONFIG_SHELL to point to it."
 fi
@@ -2029,8 +2025,7 @@
       func_filter_filelist lib_files "$nl" "$all_files" 'lib/' '' 'lib/' ''
       # Remove $already_mentioned_files from $lib_files.
       echo "$lib_files" | LC_ALL=C sort -u > "$tmp"/lib-files
-      extra_files=`func_reset_sigpipe; \
-                   for f in $already_mentioned_files; do echo $f; done \
+      extra_files=`for f in $already_mentioned_files; do echo $f; done \
                    | LC_ALL=C sort -u | LC_ALL=C join -v 2 - "$tmp"/lib-files`
       if test -n "$extra_files"; then
         echo "EXTRA_DIST +=" $extra_files
@@ -2266,7 +2261,7 @@
     handledmodules=`for m in $handledmodules $inmodules_this_round; do echo 
$m; done | LC_ALL=C sort -u`
     # Remove $handledmodules from $inmodules.
     for m in $inmodules; do echo $m; done | LC_ALL=C sort -u > 
"$tmp"/queued-modules
-    inmodules=`func_reset_sigpipe; echo "$handledmodules" | LC_ALL=C join -v 2 
- "$tmp"/queued-modules`
+    inmodules=`echo "$handledmodules" | LC_ALL=C join -v 2 - 
"$tmp"/queued-modules`
   done
   modules=`for m in $outmodules; do echo $m; done | LC_ALL=C sort -u`
   rm -f "$tmp"/queued-modules
@@ -3354,8 +3349,7 @@
   fi
   # Determine tests-related module list.
   echo "$final_modules" | LC_ALL=C sort -u > "$tmp"/final-modules
-  testsrelated_modules=`func_reset_sigpipe
-                        for module in $main_modules; do
+  testsrelated_modules=`for module in $main_modules; do
                           if test \`func_get_applicability $module\` = main; 
then
                             echo $module
                           fi
@@ -4256,8 +4250,7 @@
         if test -f "$destdir/$dir$ignore"; then
           if test -n "$dir_added" || test -n "$dir_removed"; then
             sed -e "s|^$anchor||" < "$destdir/$dir$ignore" | LC_ALL=C sort > 
"$tmp"/ignore
-            (func_reset_sigpipe
-             echo "$dir_added" | sed -e '/^$/d' | LC_ALL=C sort -u \
+            (echo "$dir_added" | sed -e '/^$/d' | LC_ALL=C sort -u \
                | LC_ALL=C join -v 1 - "$tmp"/ignore > "$tmp"/ignore-added
              echo "$dir_removed" | sed -e '/^$/d' | LC_ALL=C sort -u \
                | LC_ALL=C join -v 1 - "$tmp"/ignore > "$tmp"/ignore-removed




reply via email to

[Prev in Thread] Current Thread [Next in Thread]