summaryrefslogtreecommitdiff
path: root/lib/freopen.c
diff options
context:
space:
mode:
authorPaul Eggert <eggert@cs.ucla.edu>2016-11-26 14:53:29 -0800
committerPaul Eggert <eggert@cs.ucla.edu>2016-11-26 14:54:38 -0800
commitb947d0524d64b5a139282fd48caa7a866e20513c (patch)
tree4719c46b1cefe2f15e1f1fd23fac8d09c091f112 /lib/freopen.c
parent0a2d091427da29e9d956b108801cdfdc27713a09 (diff)
downloadgnulib-b947d0524d64b5a139282fd48caa7a866e20513c.tar.gz
freopen: work around glibc bug with closed fd
Work around glibc bug#15589, where freopen mishandles the case where stdin etc. are already closed. * doc/posix-functions/freopen.texi (freopen): Document the bug. * lib/freopen.c (_GL_ALREADY_INCLUDING_STDIO_H): Define this instead of __need_FILE, as the latter does not work with glibc. Include <fcntl.h>, for open flags. (rpl_freopen): Work around glibc bug. * m4/freopen.m4 (gl_FUNC_FREOPEN): Check for bug. * modules/freopen (Depends-on): Add fcntl-h. * tests/test-freopen.c (main): Test for bug.
Diffstat (limited to 'lib/freopen.c')
-rw-r--r--lib/freopen.c44
1 files changed, 34 insertions, 10 deletions
diff --git a/lib/freopen.c b/lib/freopen.c
index 4cf7528e67..6855214adc 100644
--- a/lib/freopen.c
+++ b/lib/freopen.c
@@ -19,12 +19,12 @@
/* If the user's config.h happens to include <stdio.h>, let it include only
the system's <stdio.h> here, so that orig_freopen doesn't recurse to
rpl_freopen. */
-#define __need_FILE
+#define _GL_ALREADY_INCLUDING_STDIO_H
#include <config.h>
/* Get the original definition of freopen. It might be defined as a macro. */
#include <stdio.h>
-#undef __need_FILE
+#undef _GL_ALREADY_INCLUDING_STDIO_H
#include <errno.h>
@@ -39,29 +39,53 @@ orig_freopen (const char *filename, const char *mode, FILE *stream)
this include because of the preliminary #include <stdio.h> above. */
#include "stdio.h"
+#include <fcntl.h>
#include <string.h>
FILE *
rpl_freopen (const char *filename, const char *mode, FILE *stream)
{
FILE *result;
-
#if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
- if (filename != NULL && strcmp (filename, "/dev/null") == 0)
- filename = "NUL";
+ char const *null_device = "NUL";
+ if (filename && strcmp (filename, "/dev/null") == 0)
+ filename = null_device;
+#else
+ char const *null_device = "/dev/null";
#endif
- /* Clear errno to check the success of freopen() with it */
+#ifdef __KLIBC__
errno = 0;
+#endif
result = orig_freopen (filename, mode, stream);
+ if (!result)
+ {
#ifdef __KLIBC__
- /* On OS/2 kLIBC, freopen() returns NULL even if it is successful
- if filename is NULL. */
- if (!filename && !result && !errno)
- result = stream;
+ /* On OS/2 kLIBC, freopen returns NULL even if it is successful
+ if filename is NULL. */
+ if (!filename && !errno)
+ result = stream;
#endif
+ }
+ else if (filename)
+ {
+ int fd = fileno (result);
+ if (dup2 (fd, fd) < 0 && errno == EBADF)
+ {
+ int nullfd = open (null_device, O_RDONLY | O_CLOEXEC);
+ int err = 0;
+ if (nullfd != fd)
+ {
+ if (dup2 (nullfd, fd) < 0)
+ err = 1;
+ close (nullfd);
+ }
+ if (!err)
+ result = orig_freopen (filename, mode, result);
+ }
+ }
return result;
}