Fix Savannah bug # 1328: if stdout is redirected to a full filesystem, we
authorPaul Smith <psmith@gnu.org>
Sun, 12 Jun 2005 22:22:07 +0000 (22:22 +0000)
committerPaul Smith <psmith@gnu.org>
Sun, 12 Jun 2005 22:22:07 +0000 (22:22 +0000)
check for this and exit with an error.
The closeout.c version from gnulib pulls in too much other stuff, and
gnulib requires an ANSI C 89 compliant compiler, while GNU make (so far)
still wants to work on K&R.

ChangeLog
configure.in
doc/make.texi
main.c
make.h
misc.c
tests/ChangeLog
tests/scripts/misc/close_stdout [new file with mode: 0644]

index 86966eb..227c0b4 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,7 +1,18 @@
+2005-06-12  Paul D. Smith  <psmith@gnu.org>
+
+       Fix Savannah bug # 1328.
+
+       * configure.in: Check for atexit().
+       * misc.c (close_stdout): Test stdout to see if writes to it have
+       failed.  If so, be sure to exit with a non-0 error code.  Based on
+       code found in gnulib.
+       * make.h: Prototype.
+       * main.c (main): Install close_stdout() with atexit().
+
 2005-06-10  Paul D. Smith  <psmith@gnu.org>
-        
+
         VMS build updates from Hartmut Becker <Hartmut.Becker@hp.com>:
-        
+
         * vmsjobs.c [VMS]: Updates to compile on VMS: add some missing
         headers; make vmsWaitForChildren() static; extern vmsify().
         * job.c [VMS]: Move vmsWaitForChildren() prototype to be global.
index 783d180..2c981c9 100644 (file)
@@ -137,7 +137,7 @@ AC_CHECK_FUNCS(     memcpy memmove strchr strdup mkstemp mktemp fdopen \
                bsd_signal dup2 getcwd realpath sigsetmask sigaction \
                 getgroups seteuid setegid setlinebuf setreuid setregid \
                 getrlimit setrlimit setvbuf pipe strerror strsignal \
-               lstat readlink)
+               lstat readlink atexit)
 
 AC_FUNC_SETVBUF_REVERSED
 
index 4ffefb9..f17a5d4 100644 (file)
@@ -9385,7 +9385,7 @@ foolib(hack.o) : hack.o
 @end example
 
 In fact, nearly all archive member targets are updated in just this way
-and there is an implicit rule to do it for you.  @strong{Note:} The
+and there is an implicit rule to do it for you.  @strong{Please note:} The
 @samp{c} flag to @code{ar} is required if the archive file does not
 already exist.
 
diff --git a/main.c b/main.c
index 5536190..60d11ab 100644 (file)
--- a/main.c
+++ b/main.c
@@ -914,6 +914,10 @@ main (int argc, char **argv, char **envp)
   }
 #endif
 
+#ifdef HAVE_ATEXIT
+  atexit (close_stdout);
+#endif
+
   /* Needed for OS/2 */
   initialize_main(&argc, &argv);
 
@@ -2176,6 +2180,7 @@ main (int argc, char **argv, char **envp)
     die (status);
   }
 
+  /* NOTREACHED */
   return 0;
 }
 \f
diff --git a/make.h b/make.h
index 5696965..699a6e4 100644 (file)
--- a/make.h
+++ b/make.h
@@ -459,8 +459,9 @@ extern void user_access PARAMS ((void));
 extern void make_access PARAMS ((void));
 extern void child_access PARAMS ((void));
 
-extern char *
-strip_whitespace PARAMS ((const char **begpp, const char **endpp));
+extern void close_stdout PARAMS ((void));
+
+extern char *strip_whitespace PARAMS ((const char **begpp, const char **endpp));
 
 
 #ifdef  HAVE_VFORK_H
diff --git a/misc.c b/misc.c
index 63936f7..995fd20 100644 (file)
--- a/misc.c
+++ b/misc.c
@@ -823,3 +823,50 @@ get_path_max (void)
   return value;
 }
 #endif
+\f
+
+/* This code is stolen from gnulib.
+   If/when we abandon the requirement to work with K&R compilers, we can
+   remove this (and perhaps other parts of GNU make!) and migrate to using
+   gnulib directly.
+
+   This is called only through atexit(), which means die() has already been
+   invoked.  So, call exit() here directly.  Apparently that works...?
+*/
+
+/* Close standard output, exiting with status 'exit_failure' on failure.
+   If a program writes *anything* to stdout, that program should close
+   stdout and make sure that it succeeds before exiting.  Otherwise,
+   suppose that you go to the extreme of checking the return status
+   of every function that does an explicit write to stdout.  The last
+   printf can succeed in writing to the internal stream buffer, and yet
+   the fclose(stdout) could still fail (due e.g., to a disk full error)
+   when it tries to write out that buffered data.  Thus, you would be
+   left with an incomplete output file and the offending program would
+   exit successfully.  Even calling fflush is not always sufficient,
+   since some file systems (NFS and CODA) buffer written/flushed data
+   until an actual close call.
+
+   Besides, it's wasteful to check the return value from every call
+   that writes to stdout -- just let the internal stream state record
+   the failure.  That's what the ferror test is checking below.
+
+   It's important to detect such failures and exit nonzero because many
+   tools (most notably `make' and other build-management systems) depend
+   on being able to detect failure in other tools via their exit status.  */
+
+void
+close_stdout (void)
+{
+  int prev_fail = ferror (stdout);
+  int fclose_fail = fclose (stdout);
+
+  if (prev_fail || fclose_fail)
+    {
+      if (fclose_fail)
+        error (NILF, _("write error: %s"), strerror (errno));
+      else
+        error (NILF, _("write error"));
+      exit (EXIT_FAILURE);
+    }
+}
index ffb3fe0..2e22195 100644 (file)
@@ -1,3 +1,8 @@
+2005-06-12  Paul D. Smith  <psmith@gnu.org>
+
+       * scripts/misc/close_stdout: Add a test for Savannah bug #1328.
+       This test only works on systems that have /dev/full (e.g., Linux).
+
 2005-06-09  Paul D. Smith  <psmith@gnu.org>
 
         * scripts/functions/foreach: Add a test for Savannah bug #11913.
diff --git a/tests/scripts/misc/close_stdout b/tests/scripts/misc/close_stdout
new file mode 100644 (file)
index 0000000..688942e
--- /dev/null
@@ -0,0 +1,9 @@
+#                                                                    -*-perl-*-
+
+$description = "Make sure make exits with an error if stdout is full.";
+
+if (-e '/dev/full') {
+  run_make_test('', '-v > /dev/full', '#MAKE#: write error', 256);
+}
+
+1;