This implements the 'dup3' replacement function. Paolo's issue of use of older Linux kernels is not yet handled.
2009-08-23 Bruno Haible <br...@clisp.org> New module 'dup3'. * lib/unistd.in.h (dup3): New declaration. * lib/dup3.c: New file. * m4/dup3.m4: New file. * m4/unistd_h.m4 (gl_UNISTD_H_DEFAULTS): Initialize GNULIB_DUP3 and HAVE_DUP3. * modules/unistd (Makefile.am): Substitute GNULIB_DUP3 and HAVE_DUP3. * modules/dup3: New file. * doc/glibc-functions/dup3.texi: Mention the new module. ================================= lib/dup3.c ================================= /* Copy a file descriptor, applying specific flags. Copyright (C) 2009 Free Software Foundation, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include <config.h> /* Specification. */ #include <unistd.h> #include <errno.h> #include <fcntl.h> #include <limits.h> #include "binary-io.h" #if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__ /* Native Woe32 API. */ /* Get declarations of the Win32 API functions. */ # define WIN32_LEAN_AND_MEAN # include <windows.h> /* Upper bound on getdtablesize(). See lib/getdtablesize.c. */ # define OPEN_MAX_MAX 0x10000 #else /* Unix API. */ # ifndef O_CLOEXEC # define O_CLOEXEC 0 # endif #endif int dup3 (int oldfd, int newfd, int flags) { if (oldfd < 0 || newfd < 0 || newfd >= getdtablesize ()) { errno = EBADF; return -1; } if (newfd == oldfd) { errno = EINVAL; return -1; } /* Check the supported flags. Note that O_NONBLOCK is not supported, because setting it on newfd would implicitly also set it on oldfd. */ if ((flags & ~(O_CLOEXEC | O_BINARY | O_TEXT)) != 0) { errno = EINVAL; return -1; } #if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__ /* Native Woe32 API. */ if (flags & O_CLOEXEC) { /* Neither dup() nor dup2() can create a file descriptor with O_CLOEXEC = O_NOINHERIT set. We need to use the low-level function _open_osfhandle for this. Iterate until all file descriptors less than newfd are filled up. */ HANDLE curr_process = GetCurrentProcess (); HANDLE old_handle = (HANDLE) _get_osfhandle (oldfd); unsigned char fds_to_close[OPEN_MAX_MAX / CHAR_BIT]; unsigned int fds_to_close_bound = 0; int result; if (old_handle == INVALID_HANDLE_VALUE) { /* oldfd is not open, or is an unassigned standard file descriptor. */ errno = EBADF; return -1; } close (newfd); for (;;) { HANDLE new_handle; int duplicated_fd; unsigned int index; if (!DuplicateHandle (curr_process, /* SourceProcessHandle */ old_handle, /* SourceHandle */ curr_process, /* TargetProcessHandle */ (PHANDLE) &new_handle, /* TargetHandle */ (DWORD) 0, /* DesiredAccess */ FALSE, /* InheritHandle */ DUPLICATE_SAME_ACCESS)) /* Options */ { errno = EBADF; /* arbitrary */ result = -1; break; } duplicated_fd = _open_osfhandle ((long) new_handle, flags); if (duplicated_fd < 0) { CloseHandle (new_handle); result = -1; break; } if (duplicated_fd > newfd) /* Shouldn't happen, since newfd is still closed. */ abort (); if (duplicated_fd == newfd) { result = newfd; break; } /* Set the bit duplicated_fd in fds_to_close[]. */ index = (unsigned int) duplicated_fd / CHAR_BIT; if (index >= fds_to_close_bound) { if (index >= sizeof (fds_to_close)) /* Need to increase OPEN_MAX_MAX. */ abort (); memset (fds_to_close + fds_to_close_bound, '\0', index + 1 - fds_to_close_bound); fds_to_close_bound = index + 1; } fds_to_close[index] |= 1 << ((unsigned int) duplicated_fd % CHAR_BIT); } /* Close the previous fds that turned out to be too small. */ { int saved_errno = errno; unsigned int duplicated_fd; for (duplicated_fd = 0; duplicated_fd < fds_to_close_bound * CHAR_BIT; duplicated_fd++) if ((fds_to_close[duplicated_fd / CHAR_BIT] >> (duplicated_fd % CHAR_BIT)) & 1) close (duplicated_fd); errno = saved_errno; } return result; } if (dup2 (oldfd, newfd) < 0) return -1; #else /* Unix API. */ if (dup2 (oldfd, newfd) < 0) return -1; /* POSIX <http://www.opengroup.org/onlinepubs/9699919799/functions/dup.html> says that initially, the FD_CLOEXEC flag is cleared on newfd. */ if (flags & O_CLOEXEC) { int fcntl_flags; if ((fcntl_flags = fcntl (newfd, F_GETFD, 0)) < 0 || fcntl (newfd, F_SETFD, fcntl_flags | FD_CLOEXEC) == -1) { int saved_errno = errno; close (newfd); errno = saved_errno; return -1; } } #endif #if O_BINARY if (flags & O_BINARY) setmode (newfd, O_BINARY); else if (flags & O_TEXT) setmode (newfd, O_TEXT); #endif return newfd; } ================================= m4/dup3.m4 ================================= # dup3.m4 serial 1 dnl Copyright (C) 2009 Free Software Foundation, Inc. dnl This file is free software; the Free Software Foundation dnl gives unlimited permission to copy and/or distribute it, dnl with or without modifications, as long as this notice is preserved. AC_DEFUN([gl_FUNC_DUP3], [ AC_REQUIRE([gl_UNISTD_H_DEFAULTS]) dnl Persuade glibc <unistd.h> to declare dup3(). AC_REQUIRE([AC_USE_SYSTEM_EXTENSIONS]) AC_CHECK_FUNCS_ONCE([dup3]) if test $ac_cv_func_dup3 != yes; then HAVE_DUP3=0 AC_LIBOBJ([dup3]) fi ]) ================================ modules/dup3 ================================ Description: dup3() function: copy a file descriptor, applying specific flags. Files: lib/dup3.c m4/dup3.m4 Depends-on: unistd fcntl binary-io getdtablesize configure.ac: gl_FUNC_DUP3 gl_UNISTD_MODULE_INDICATOR([dup3]) Makefile.am: Include: <unistd.h> License: LGPL Maintainer: Bruno Haible, Jim Meyering ============================================================================== --- doc/glibc-functions/dup3.texi.orig 2009-08-24 00:11:26.000000000 +0200 +++ doc/glibc-functions/dup3.texi 2009-08-23 21:55:58.000000000 +0200 @@ -2,15 +2,15 @@ @subsection @code{dup3} @findex dup3 -Gnulib module: --- +Gnulib module: dup3 Portability problems fixed by Gnulib: @itemize +...@item +This function is missing on all non-glibc platforms: +MacOS X 10.3, FreeBSD 6.0, NetBSD 3.0, OpenBSD 3.8, AIX 5.1, HP-UX 11, IRIX 6.5, OSF/1 5.1, Solaris 10, Cygwin, mingw, Interix 3.5, BeOS. @end itemize Portability problems not fixed by Gnulib: @itemize -...@item -This function is missing on all non-glibc platforms: -MacOS X 10.3, FreeBSD 6.0, NetBSD 3.0, OpenBSD 3.8, AIX 5.1, HP-UX 11, IRIX 6.5, OSF/1 5.1, Solaris 10, Cygwin, mingw, Interix 3.5, BeOS. @end itemize --- lib/unistd.in.h.orig 2009-08-24 00:11:26.000000000 +0200 +++ lib/unistd.in.h 2009-08-24 00:11:22.000000000 +0200 @@ -178,6 +178,27 @@ #endif +#if @GNULIB_DUP3@ +# if !...@have_dup3@ +/* Copy the file descriptor OLDFD into file descriptor NEWFD, with the + specified flags. + The flags are a bitmask, possibly including O_CLOEXEC (defined in <fcntl.h>) + and O_TEXT, O_BINARY (defined in "binary-io.h"). + Close NEWFD first if it is open. + Return newfd if successful, otherwise -1 and errno set. + See the Linux man page at + <http://www.kernel.org/doc/man-pages/online/pages/man2/dup3.2.html>. */ +extern int dup3 (int oldfd, int newfd, int flags); +# endif +#elif defined GNULIB_POSIXCHECK +# undef dup3 +# define dup3(o,n,f) \ + (GL_LINK_WARNING ("dup3 is unportable - " \ + "use gnulib module dup3 for portability"), \ + dup3 (o, n, f)) +#endif + + #if @GNULIB_ENVIRON@ # if !...@have_decl_environ@ /* Set of environment variables and values. An array of strings of the form --- m4/unistd_h.m4.orig 2009-08-24 00:11:26.000000000 +0200 +++ m4/unistd_h.m4 2009-08-23 21:55:58.000000000 +0200 @@ -1,4 +1,4 @@ -# unistd_h.m4 serial 20 +# unistd_h.m4 serial 21 dnl Copyright (C) 2006-2009 Free Software Foundation, Inc. dnl This file is free software; the Free Software Foundation dnl gives unlimited permission to copy and/or distribute it, @@ -35,6 +35,7 @@ GNULIB_CHOWN=0; AC_SUBST([GNULIB_CHOWN]) GNULIB_CLOSE=0; AC_SUBST([GNULIB_CLOSE]) GNULIB_DUP2=0; AC_SUBST([GNULIB_DUP2]) + GNULIB_DUP3=0; AC_SUBST([GNULIB_DUP3]) GNULIB_ENVIRON=0; AC_SUBST([GNULIB_ENVIRON]) GNULIB_EUIDACCESS=0; AC_SUBST([GNULIB_EUIDACCESS]) GNULIB_FCHDIR=0; AC_SUBST([GNULIB_FCHDIR]) @@ -58,6 +59,7 @@ GNULIB_WRITE=0; AC_SUBST([GNULIB_WRITE]) dnl Assume proper GNU behavior unless another module says otherwise. HAVE_DUP2=1; AC_SUBST([HAVE_DUP2]) + HAVE_DUP3=1; AC_SUBST([HAVE_DUP3]) HAVE_EUIDACCESS=1; AC_SUBST([HAVE_EUIDACCESS]) HAVE_FSYNC=1; AC_SUBST([HAVE_FSYNC]) HAVE_FTRUNCATE=1; AC_SUBST([HAVE_FTRUNCATE]) --- modules/unistd.orig 2009-08-24 00:11:26.000000000 +0200 +++ modules/unistd 2009-08-23 21:55:58.000000000 +0200 @@ -28,6 +28,7 @@ -e 's|@''GNULIB_CHOWN''@|$(GNULIB_CHOWN)|g' \ -e 's|@''GNULIB_CLOSE''@|$(GNULIB_CLOSE)|g' \ -e 's|@''GNULIB_DUP2''@|$(GNULIB_DUP2)|g' \ + -e 's|@''GNULIB_DUP3''@|$(GNULIB_DUP3)|g' \ -e 's|@''GNULIB_ENVIRON''@|$(GNULIB_ENVIRON)|g' \ -e 's|@''GNULIB_EUIDACCESS''@|$(GNULIB_EUIDACCESS)|g' \ -e 's|@''GNULIB_FCHDIR''@|$(GNULIB_FCHDIR)|g' \ @@ -50,6 +51,7 @@ -e 's|@''GNULIB_UNISTD_H_SIGPIPE''@|$(GNULIB_UNISTD_H_SIGPIPE)|g' \ -e 's|@''GNULIB_WRITE''@|$(GNULIB_WRITE)|g' \ -e 's|@''HAVE_DUP2''@|$(HAVE_DUP2)|g' \ + -e 's|@''HAVE_DUP3''@|$(HAVE_DUP3)|g' \ -e 's|@''HAVE_EUIDACCESS''@|$(HAVE_EUIDACCESS)|g' \ -e 's|@''HAVE_FSYNC''@|$(HAVE_FSYNC)|g' \ -e 's|@''HAVE_FTRUNCATE''@|$(HAVE_FTRUNCATE)|g' \