[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: add some fd tests to head
From: |
Eric Blake |
Subject: |
Re: add some fd tests to head |
Date: |
Thu, 5 Oct 2006 23:06:59 +0000 (UTC) |
User-agent: |
Loom/3.14 (http://gmane.org/) |
Eric Blake <ebb9 <at> byu.net> writes:
>
> I still need to add more tests from the mailing lists, but here is a group of
> tests that expose current shortfalls in handling closed fds.
Indeed, here are some more tests, and some (but not all) fixes. In particular,
this will not completely work until gnulib's closeout module checks for write
errors on stderr, as was proposed a few days ago but not committed yet.
2006-10-05 Eric Blake <address@hidden>
* ltdl/m4/gnulib-cache.m4: Augment with gnulib-tool --import
closeout.
* Makefile.am (m4_libm4_la_DEPENDENCIES): Add missing dependency.
* src/main.c (main): Close stdout on exit.
* m4/debug.c (set_debug_file): Check for write failure.
* m4/m4.c (m4_delete): Don't mask write failure.
* tests/testsuite.at (AT_CHECK_M4): Allow tracing by expanding
macros before calling AT_CHECK.
(M4_ONE_MEG_DEFN): New helper macro.
* tests/others.at (stdout full): New test.
(stderr closed, stdin closed, stdout closed): Augment.
* tests/builtins.at (syscmd): New test.
(esyscmd): Augment.
Index: Makefile.am
===================================================================
RCS file: /sources/m4/m4/Makefile.am,v
retrieving revision 1.48
diff -u -r1.48 Makefile.am
--- Makefile.am 4 Oct 2006 03:57:00 -0000 1.48
+++ Makefile.am 5 Oct 2006 23:03:02 -0000
@@ -240,7 +240,7 @@
m4/utility.c
m4_libm4_la_LIBADD = gnu/libgnu.la \
$(LIBLTDL) $(LTLIBINTL) $(LIBADD_DL)
-m4_libm4_la_DEPENDENCIES = $(LIBLTDL)
+m4_libm4_la_DEPENDENCIES = $(LIBLTDL) gnu/libgnu.la
m4/pathconf.h:
echo "#define MODULE_PATH \"$(pkglibexecdir)\"" > m4/pathconf.h
Index: ltdl/m4/gnulib-cache.m4
===================================================================
RCS file: /sources/m4/m4/ltdl/m4/gnulib-cache.m4,v
retrieving revision 1.12
diff -u -r1.12 gnulib-cache.m4
--- ltdl/m4/gnulib-cache.m4 29 Sep 2006 12:26:06 -0000 1.12
+++ ltdl/m4/gnulib-cache.m4 5 Oct 2006 23:03:02 -0000
@@ -15,11 +15,11 @@
# Specification in the form of a command-line invocation:
-# gnulib-tool --import --dir=. --lib=libgnu --source-base=gnu --m4-
base=ltdl/m4 --doc-base=doc --aux-dir=ltdl/config --libtool --macro-prefix=M4
assert binary-io cloexec close-stream dirname error exit fdl filenamecat fopen-
safer free gendocs gettext gnupload mkstemp obstack progname regex regexprops-
generic stdbool stdlib-safer strnlen strtol unlocked-io verror xalloc xalloc-
die xstrndup xstrtol xvasprintf
+# gnulib-tool --import --dir=. --lib=libgnu --source-base=gnu --m4-
base=ltdl/m4 --doc-base=doc --aux-dir=ltdl/config --libtool --macro-prefix=M4
assert binary-io cloexec close-stream closeout dirname error exit fdl
filenamecat fopen-safer free gendocs gettext gnupload mkstemp obstack progname
regex regexprops-generic stdbool stdlib-safer strnlen strtol unlocked-io verror
xalloc xalloc-die xstrndup xstrtol xvasprintf
# Specification in the form of a few gnulib-tool.m4 macro invocations:
gl_LOCAL_DIR([])
-gl_MODULES([assert binary-io cloexec close-stream dirname error exit fdl
filenamecat fopen-safer free gendocs gettext gnupload mkstemp obstack progname
regex regexprops-generic stdbool stdlib-safer strnlen strtol unlocked-io verror
xalloc xalloc-die xstrndup xstrtol xvasprintf])
+gl_MODULES([assert binary-io cloexec close-stream closeout dirname error exit
fdl filenamecat fopen-safer free gendocs gettext gnupload mkstemp obstack
progname regex regexprops-generic stdbool stdlib-safer strnlen strtol unlocked-
io verror xalloc xalloc-die xstrndup xstrtol xvasprintf])
gl_AVOID([])
gl_SOURCE_BASE([gnu])
gl_M4_BASE([ltdl/m4])
Index: m4/debug.c
===================================================================
RCS file: /sources/m4/m4/m4/debug.c,v
retrieving revision 1.27
diff -u -r1.27 debug.c
--- m4/debug.c 27 Sep 2006 12:24:53 -0000 1.27
+++ m4/debug.c 5 Oct 2006 23:03:02 -0000
@@ -22,6 +22,7 @@
#include <stdarg.h>
#include "m4private.h"
+#include "close-stream.h"
static void set_debug_file (m4 *, FILE *);
@@ -144,8 +145,9 @@
assert (context);
debug_file = m4_get_debug_file (context);
- if (debug_file != NULL && debug_file != stderr && debug_file != stdout)
- fclose (debug_file);
+ if (debug_file != NULL && debug_file != stderr && debug_file != stdout
+ && close_stream (debug_file) != 0)
+ m4_error (context, 0, errno, _("error writing to debug stream"));
debug_file = fp;
m4_set_debug_file (context, fp);
@@ -157,11 +159,14 @@
if (fstat (fileno (debug_file), &debug_stat) < 0)
return;
+ /* mingw has a bug where fstat on a regular file reports st_ino
+ of 0. On normal system, st_ino should never be 0. */
if (stdout_stat.st_ino == debug_stat.st_ino
- && stdout_stat.st_dev == debug_stat.st_dev)
+ && stdout_stat.st_dev == debug_stat.st_dev
+ && stdout_stat.st_ino != 0)
{
- if (debug_file != stderr)
- fclose (debug_file);
+ if (debug_file != stderr && close_stream (debug_file) != 0)
+ m4_error (context, 0, errno, _("error writing to debug stream"));
m4_set_debug_file (context, stdout);
}
}
Index: m4/m4.c
===================================================================
RCS file: /sources/m4/m4/m4/m4.c,v
retrieving revision 1.19
diff -u -r1.19 m4.c
--- m4/m4.c 18 Sep 2006 13:16:44 -0000 1.19
+++ m4/m4.c 5 Oct 2006 23:03:02 -0000
@@ -53,8 +53,8 @@
if (context->syntax)
m4_syntax_delete (context->syntax);
- if (context->debug_file)
- fclose (context->debug_file);
+ /* debug_file should have been reset to stderr, which is closed later. */
+ assert (context->debug_file == stderr);
obstack_free (&context->trace_messages, NULL);
Index: src/main.c
===================================================================
RCS file: /sources/m4/m4/src/main.c,v
retrieving revision 1.92
diff -u -r1.92 main.c
--- src/main.c 4 Oct 2006 03:57:01 -0000 1.92
+++ src/main.c 5 Oct 2006 23:03:02 -0000
@@ -21,6 +21,7 @@
#include "m4.h"
#include "m4private.h"
+#include "closeout.h"
#include "getopt.h"
#include "version-etc.h"
#include "pathconf.h"
@@ -276,6 +277,7 @@
/* Initialize gnulib error module. */
m4_set_program_name (argv[0]);
+ atexit (close_stdout);
setlocale (LC_ALL, "");
#ifdef ENABLE_NLS
Index: tests/builtins.at
===================================================================
RCS file: /sources/m4/m4/tests/builtins.at,v
retrieving revision 1.20
diff -u -r1.20 builtins.at
--- tests/builtins.at 4 Oct 2006 23:30:46 -0000 1.20
+++ tests/builtins.at 5 Oct 2006 23:03:03 -0000
@@ -137,8 +137,37 @@
hostname = >>www<<
]])
-AT_CLEANUP
+dnl Ensure that esyscmd does not inherit any unnecessary fds from trace.
+AT_DATA([in.m4], [[esyscmd(`echo hi >&3')dnl
+]])
+AT_CHECK_M4([3>&-], [0], [], [stderr], [in.m4])
+mv stderr experr
+AT_CHECK_M4([--debugfile=trace -tdnl 3>&-], [0], [], [experr], [in.m4])
+AT_CHECK([cat trace], [0], [[m4trace: -1- dnl
+]])
+
+dnl Ensure that esyscmd does not inherit any unnecessary fds from diversions.
+AT_DATA([in.m4], [M4_ONE_MEG_DEFN[divert(`1')f
+world
+divert`'esyscmd(`echo hi >&3')
+hello
+]])
+AT_CHECK_M4([3>&-], [0], [[hello
+world
+]], [experr], [in.m4 | sed -ne '/./p'])
+
+dnl Ensure that esyscmd does not inherit any unnecessary fds from input files.
+AT_DATA([in.m4], [[hello esyscmd(`cat <&3')dnl
+dnl this line should not be read by cat
+world
+]])
+AT_CHECK_M4([3>&-], [0], [[hello world
+]], [stderr], [in.m4])
+mv stderr experr
+AT_CHECK_M4([in.m4 3>&-], [0], [[hello world
+]], [experr])
+AT_CLEANUP
## --- ##
@@ -594,6 +623,44 @@
AT_CLEANUP
+## ------ ##
+## syscmd ##
+## ------ ##
+
+AT_SETUP([syscmd])
+
+dnl Ensure that syscmd does not inherit any unnecessary fds from trace.
+AT_DATA([in.m4], [[syscmd(`echo hi >&3')dnl
+]])
+AT_CHECK_M4([3>&-], [0], [], [stderr], [in.m4])
+mv stderr experr
+AT_CHECK_M4([--debugfile=trace -tdnl 3>&-], [0], [], [experr], [in.m4])
+AT_CHECK([cat trace], [0], [[m4trace: -1- dnl
+]])
+
+dnl Ensure that syscmd does not inherit any unnecessary fds from diversions.
+AT_DATA([in.m4], [M4_ONE_MEG_DEFN[divert(`1')f
+world
+divert`'syscmd(`echo hi >&3')
+hello
+]])
+AT_CHECK_M4([3>&-], [0], [[hello
+world
+]], [experr], [in.m4 | sed -ne '/./p'])
+
+dnl Ensure that syscmd does not inherit any unnecessary fds from input files.
+AT_DATA([in.m4], [[hello syscmd(`cat <&3')dnl
+dnl this line should not be read by cat
+world
+]])
+AT_CHECK_M4([3>&-], [0], [[hello world
+]], [stderr], [in.m4])
+mv stderr experr
+AT_CHECK_M4([in.m4 3>&-], [0], [[hello world
+]], [experr])
+
+AT_CLEANUP
+
## -------- ##
## translit ##
Index: tests/others.at
===================================================================
RCS file: /sources/m4/m4/tests/others.at,v
retrieving revision 1.20
diff -u -r1.20 others.at
--- tests/others.at 4 Oct 2006 23:30:46 -0000 1.20
+++ tests/others.at 5 Oct 2006 23:03:03 -0000
@@ -16,7 +16,7 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
# 02110-1301, USA.
-AT_BANNER([Composite macros.])
+AT_BANNER([Composite macros and other tests.])
## ---------- ##
@@ -350,38 +350,69 @@
dnl no error when stderr is not used
AT_DATA([in.m4], [[hello world
]])
-AT_CHECK_M4([in.m4], [0], [[hello world
-]])
+AT_CHECK_M4([2>&-], [0], [[hello world
+]], [], [in.m4])
dnl must exit nonzero when error issued
-AT_CHECK([--unknown], [1])
+AT_CHECK_M4([--unknown 2>&-], [1])
dnl must exit nonzero if stderr used
AT_DATA([in.m4], [[errprint(`hello world
')dnl
]])
-AT_CHECK_M4([in.m4 2>&-], [1])
+AT_CHECK_M4([2>&-], [1], [], [], [in.m4])
dnl must exit nonzero if stderr used
-AT_DATA([in.m4], [[dnl(`hello world')
+AT_DATA([in.m4], [[hello
+dnl(`world')
]])
-AT_CHECK_M4([in.m4 2>&-], [1])
+AT_CHECK_M4([2>&-], [1], [[hello
+]], [], [in.m4])
-dnl must exit nonzero on error, in spite of m4exit
+dnl must exit nonzero on error, in spite of m4exit requesting 0
AT_DATA([in.m4], [[errprint(`hello world
')m4exit(`0')
]])
-AT_CHECK_M4([in.m4 2>&-], [1])
+AT_CHECK_M4([2>&-], [1], [], [], [in.m4])
+
+dnl preserve m4exit's failure value
+AT_DATA([in.m4], [[errprint(`hello world
+')m4exit(`2')
+]])
+AT_CHECK_M4([2>&-], [2], [], [], [in.m4])
dnl trace file must not collide with closed stderr
AT_DATA([in.m4], [[errprint(`hello world
')dnl
]])
-AT_CHECK_M4([--debugfile=trace -terrprint in.m4 2>&-], [1])
+AT_CHECK_M4([--debugfile=trace -terrprint 2>&-], [1], [], [], [in.m4])
AT_CHECK([cat trace], [0], [[m4trace: -1- errprint(`hello world
')
]])
+dnl spilled diversion file must not collide with closed stderr
+AT_DATA([in.m4], [M4_ONE_MEG_DEFN[divert(1)f
+and
+divert
+hello`'dnl(world)
+undivert
+goodbye
+]])
+AT_CHECK_M4([2>&-], [1],
+[[hello
+and
+goodbye
+]], [], [in.m4 | sed -ne '/./p'])
+
+dnl command line input file must not collide with closed stderr
+AT_DATA([in.m4], [[syscmd(`cat <&2')sysval
+dnl this line should not be read by cat
+]])
+AT_CHECK_M4([2>&-], [0], [[1
+]], [], [in.m4])
+AT_CHECK_M4([in.m4 2>&-], [0], [[1
+]])
+
AT_CLEANUP
@@ -415,6 +446,33 @@
]], [[m4:stdin:1: error reading file `stdin'
]], [-])
+dnl command line and trace file must not collide with stdin
+AT_CHECK([cat <&- && { echo "skipping: can't detect closed stdin"; exit 77; }],
+[1], [], [stderr])
+mv stderr experr
+AT_DATA([in.m4], [[syscmd(`cat')dnl
+]])
+AT_CHECK_M4([--debugfile=trace -tdnl in.m4], [0], [], [experr], [-])
+AT_CHECK([cat trace], [0], [[m4trace: -1- dnl
+]])
+
+dnl diversions must not collide with stdin
+AT_DATA([in.m4], [M4_ONE_MEG_DEFN[divert(`1')f
+syscmd(`cat')dnl
+divert(`-1')undivert
+]])
+AT_CHECK_M4([in.m4], [0], [], [experr], [-])
+
+dnl diversions must not collide with stdin
+AT_DATA([in.m4], [M4_ONE_MEG_DEFN[hello divert(`1')f
+]])
+AT_DATA([in2.m4], [[divert(`-1')undivert
+divert`'world
+]])
+AT_CHECK_M4([in.m4 - in2.m4], [1], [[hello world
+]], [[m4:stdin:1: error reading file `stdin'
+]], [-])
+
AT_CLEANUP
## ------------- ##
@@ -435,29 +493,124 @@
AT_DATA([in.m4], [[errprint(`hello world
')dnl
]])
-AT_CHECK_M4([in.m4 >&-], [0], [], [[hello world
-]])
+AT_CHECK_M4([>&-], [0], [], [[hello world
+]], [in.m4])
dnl error when stdout must be used
-AT_CHECK_M4([-P in.m4 >&-], [1], [],
+AT_CHECK_M4([-P >&-], [1], [],
[[m4: write error: Bad file descriptor
-]])
+]], [in.m4])
dnl error must occur in spite of m4exit requesting 0
AT_DATA([in.m4], [[hello world
m4exit(`0')
]])
-AT_CHECK_M4([in.m4 >&-], [1], [],
+AT_CHECK_M4([>&-], [1], [],
[[m4: write error: Bad file descriptor
+]], [in.m4])
+
+dnl preserve m4exit's failure value
+AT_DATA([in.m4], [[hello world
+m4exit(`2')
]])
+AT_CHECK_M4([>&-], [2], [],
+[[m4: write error: Bad file descriptor
+]], [in.m4])
dnl trace file must not collide with closed stdout
AT_DATA([in.m4], [[hello world
+dnl
]])
-AT_CHECK_M4([--debugfile=trace in.m4 >&-], [1], [],
+AT_CHECK_M4([--debugfile=trace -tdnl >&-], [1], [],
[[m4: write error: Bad file descriptor
+]], [in.m4])
+AT_CHECK([cat trace], [0], [[m4trace: -1- dnl
+]])
+
+dnl esyscmd always has valid stdout
+AT_DATA([in.m4], [[errprint(esyscmd(`echo hello'))dnl
+]])
+AT_CHECK_M4([>&-], [0], [], [[hello
+]], [in.m4])
+
+dnl syscmd inherits closed stdout
+AT_CHECK([cat >&- && { echo "skipping: can't detect closed stdout"; exit
77; }],
+[1], [], [stderr])
+mv stderr experr
+AT_DATA([in.m4], [[syscmd(`cat')dnl
+]])
+AT_CHECK_M4([>&-], [0], [], [experr], [in.m4])
+
+dnl command line input file must not collide with closed stdout
+AT_DATA([in.m4], [[syscmd(`cat <&1 >&2')
+dnl this line should not be read by cat
+]])
+AT_CHECK_M4([in.m4 >&-], [0], [], [stderr])
+AT_CHECK([sed -ne '/should not be read/p' stderr], [0])
+
+AT_CLEANUP
+
+
+## ----------- ##
+## stdout full ##
+## ----------- ##
+
+AT_SETUP([stdout full])
+AT_CHECK([test -w /dev/full && test -c /dev/full || {
+ echo "Skipping: no /dev/full support";
+ exit 77
+}])
+
+dnl detect write failures on --help
+AT_CHECK_M4([--help >/dev/full], [1], [],
+[[m4: write error: No space left on device
+]])
+
+dnl detect write failures on --version
+AT_CHECK_M4([--version >/dev/full], [1], [],
+[[m4: write error: No space left on device
+]])
+
+dnl detect ordinary write failures
+AT_DATA([in.m4], [[hello world
+]])
+AT_CHECK_M4([in.m4 >/dev/full], [1], [],
+[[m4: write error: No space left on device
+]])
+
+dnl detect stderr write failures
+AT_DATA([in.m4], [[dnl(hello world)
+]])
+AT_CHECK_M4([in.m4 2>/dev/full], [1])
+
+dnl detect trace write failures
+AT_DATA([in.m4], [[dnl
+]])
+AT_CHECK_M4([-tdnl in.m4 2>/dev/full], [1])
+
+dnl detect trace write failures
+AT_DATA([in.m4], [[dnl
+]])
+AT_CHECK_M4([--debugfile=/dev/full -tdnl in.m4], [1], [],
+[[m4: error writing to debug stream: No space left on device
+]])
+
+dnl too hard to test for spilled diversion failures, without requiring the
+dnl user to have a nearly full partition that we can assign to $TMPDIR.
+
+dnl write failures must override m4exit requesting 0
+AT_DATA([in.m4], [[hello world m4exit(`0')
+]])
+AT_CHECK_M4([in.m4 >/dev/full], [1], [],
+[[m4: write error: No space left on device
+]])
+
+dnl preserve m4exit's failure value
+AT_DATA([in.m4], [[hello world m4exit(`2')
+]])
+AT_CHECK_M4([in.m4 >/dev/full], [2], [],
+[[m4: write error: No space left on device
]])
-AT_CHECK([cat trace])
AT_CLEANUP
Index: tests/testsuite.at
===================================================================
RCS file: /sources/m4/m4/tests/testsuite.at,v
retrieving revision 1.24
diff -u -r1.24 testsuite.at
--- tests/testsuite.at 4 Oct 2006 23:30:46 -0000 1.24
+++ tests/testsuite.at 5 Oct 2006 23:03:03 -0000
@@ -47,8 +47,8 @@
# or m4:input.m4:7: cannot open module `no_such': can't open the module
# to m4:input.m4:7: cannot open module `no_such'
m4_define([AT_CHECK_M4],
-[AT_CHECK([m4 -b -d $1 m4_if([$5], [-], [<&-],
- [< m4_default([$5], [/dev/null])])],
+[AT_CHECK([m4 -b -d $1 ]m4_if([$5], [-], [<&-],
+ [< m4_default([$5], [/dev/null])]),
[$2], [$3], m4_ifval([$4], [stderr]))
m4_if([$4], [], [],
[$4], [ignore], [],
@@ -58,6 +58,37 @@
' stderr >&2]], [0], [], [$4])])
])
+# M4_ONE_MEG_DEFN
+# ---------------
+# emit a code snippet for use in AT_DATA that will define a macro `f' to
+# consist of 1M bytes of newlines. With that in place, it is then easy
+# to use divert and invoke `f' in the test file to force diversions to
+# spill into a temporary file.
+m4_define([M4_ONE_MEG_DEFN],
+[pushdef(`diversion', divnum)divert(-1)
+define(`f', `
+')
+define(`f', defn(`f')defn(`f'))
+define(`f', defn(`f')defn(`f'))
+define(`f', defn(`f')defn(`f'))
+define(`f', defn(`f')defn(`f'))
+define(`f', defn(`f')defn(`f'))
+define(`f', defn(`f')defn(`f'))
+define(`f', defn(`f')defn(`f'))
+define(`f', defn(`f')defn(`f'))
+define(`f', defn(`f')defn(`f'))
+define(`f', defn(`f')defn(`f'))
+define(`f', defn(`f')defn(`f'))
+define(`f', defn(`f')defn(`f'))
+define(`f', defn(`f')defn(`f'))
+define(`f', defn(`f')defn(`f'))
+define(`f', defn(`f')defn(`f'))
+define(`f', defn(`f')defn(`f'))
+define(`f', defn(`f')defn(`f'))
+define(`f', defn(`f')defn(`f'))
+define(`f', defn(`f')defn(`f'))
+divert(diversion)popdef(`diversion')])
+
# AT_TEST_M4(TITLE, INPUT, [STDOUT = `'], [STDERR = `'])
# ------------------------------------------------------
# Run m4 on INPUT, expecting a success.