summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorPaul Eggert <eggert@cs.ucla.edu>2013-07-07 11:00:14 -0700
committerPaul Eggert <eggert@cs.ucla.edu>2013-07-07 11:00:14 -0700
commit067428c1717acd28f205c2cff93f0583eb347f4c (patch)
tree5937b119187f9900840e2c1174b408e86bb8d12b /lib
parent9aff9b3864085addb02b699f9648e547a8c00e54 (diff)
downloademacs-067428c1717acd28f205c2cff93f0583eb347f4c.tar.gz
Make file descriptors close-on-exec when possible.
This simplifies Emacs a bit, since it no longer needs to worry about closing file descriptors by hand in some cases. It also fixes some unlikely races. Not all such races, as libraries often open files internally without setting close-on-exec, but it's an improvement. * admin/merge-gnulib (GNULIB_MODULES): Add fcntl, pipe2. (GNULIB_TOOL_FLAGS): Avoid binary-io, close. Do not avoid fcntl. * configure.ac (mkostemp): New function to check for. (PTY_OPEN): Pass O_CLOEXEC to posix_openpt. * lib/fcntl.c, lib/getdtablesize.c, lib/pipe2.c, m4/fcntl.m4: * m4/getdtablesize.m4, m4/pipe2.m4: New files, taken from gnulib. * lib/gnulib.mk, m4/gnulib-comp.m4: Regenerate. * nt/gnulib.mk: Remove empty gl_GNULIB_ENABLED_verify section; otherwise, gnulib-tool complains given close-on-exec changes. * nt/inc/ms-w32.h (pipe): Remove. * nt/mingw-cfg.site (ac_cv_func_fcntl, gl_cv_func_fcntl_f_dupfd_cloexec) (gl_cv_func_fcntl_f_dupfd_works, ac_cv_func_pipe2): New vars. * src/alloc.c (valid_pointer_p) [!WINDOWSNT]: * src/callproc.c (Fcall_process) [!MSDOS]: * src/emacs.c (main) [!DOS_NT]: * src/nsterm.m (ns_term_init): * src/process.c (create_process): Use 'pipe2' with O_CLOEXEC instead of 'pipe'. * src/emacs.c (Fcall_process_region) [HAVE_MKOSTEMP]: * src/filelock.c (create_lock_file) [HAVE_MKOSTEMP]: Prefer mkostemp with O_CLOEXEC to mkstemp. * src/callproc.c (relocate_fd) [!WINDOWSNT]: * src/emacs.c (main): Use F_DUPFD_CLOEXEC, not plain F_DUPFD. No need to use fcntl (..., F_SETFD, FD_CLOEXEC), since we're now using pipe2. * src/filelock.c (create_lock_file) [! HAVE_MKOSTEMP]: Make the resulting file descriptor close-on-exec. * src/lisp.h, src/lread.c, src/process.c (close_load_descs, close_process_descs): * src/lread.c (load_descriptor_list, load_descriptor_unwind): Remove; no longer needed. All uses removed. * src/process.c (SOCK_CLOEXEC): Define to 0 if not supplied by system. (close_on_exec, accept4, process_socket) [!SOCK_CLOEXEC]: New functions. (socket) [!SOCK_CLOEXEC]: Supply a substitute. (Fmake_network_process, Fnetwork_interface_list): (Fnetwork_interface_info, server_accept_connection): Make newly-created socket close-on-exec. * src/sysdep.c (emacs_open, emacs_fopen): Make new-created descriptor close-on-exec. * src/w32.c (fcntl): Support F_DUPFD_CLOEXEC well enough for Emacs. * src/w32.c, src/w32.h (pipe2): Rename from 'pipe', with new flags arg. Fixes: debbugs:14803
Diffstat (limited to 'lib')
-rw-r--r--lib/fcntl.c311
-rw-r--r--lib/getdtablesize.c86
-rw-r--r--lib/gnulib.mk30
-rw-r--r--lib/pipe2.c171
4 files changed, 595 insertions, 3 deletions
diff --git a/lib/fcntl.c b/lib/fcntl.c
new file mode 100644
index 00000000000..735fa66f4d7
--- /dev/null
+++ b/lib/fcntl.c
@@ -0,0 +1,311 @@
+/* Provide file descriptor control.
+
+ Copyright (C) 2009-2013 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 3 of the License, 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, see <http://www.gnu.org/licenses/>. */
+
+/* Written by Eric Blake <ebb9@byu.net>. */
+
+#include <config.h>
+
+/* Specification. */
+#include <fcntl.h>
+
+#include <errno.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <unistd.h>
+
+#if !HAVE_FCNTL
+# define rpl_fcntl fcntl
+#endif
+#undef fcntl
+
+#if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
+/* Get declarations of the native Windows API functions. */
+# define WIN32_LEAN_AND_MEAN
+# include <windows.h>
+
+/* Get _get_osfhandle. */
+# include "msvc-nothrow.h"
+
+/* Upper bound on getdtablesize(). See lib/getdtablesize.c. */
+# define OPEN_MAX_MAX 0x10000
+
+/* Duplicate OLDFD into the first available slot of at least NEWFD,
+ which must be positive, with FLAGS determining whether the duplicate
+ will be inheritable. */
+static int
+dupfd (int oldfd, int newfd, int flags)
+{
+ /* Mingw has no way to create an arbitrary fd. 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;
+ BOOL inherit = flags & O_CLOEXEC ? FALSE : TRUE;
+ int mode;
+
+ if (newfd < 0 || getdtablesize () <= newfd)
+ {
+ errno = EINVAL;
+ return -1;
+ }
+ if (old_handle == INVALID_HANDLE_VALUE
+ || (mode = setmode (oldfd, O_BINARY)) == -1)
+ {
+ /* oldfd is not open, or is an unassigned standard file
+ descriptor. */
+ errno = EBADF;
+ return -1;
+ }
+ setmode (oldfd, mode);
+ flags |= mode;
+
+ 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 */
+ inherit, /* InheritHandle */
+ DUPLICATE_SAME_ACCESS)) /* Options */
+ {
+ /* TODO: Translate GetLastError () into errno. */
+ errno = EMFILE;
+ result = -1;
+ break;
+ }
+ duplicated_fd = _open_osfhandle ((intptr_t) new_handle, flags);
+ if (duplicated_fd < 0)
+ {
+ CloseHandle (new_handle);
+ errno = EMFILE;
+ result = -1;
+ break;
+ }
+ if (newfd <= duplicated_fd)
+ {
+ result = duplicated_fd;
+ break;
+ }
+
+ /* Set the bit duplicated_fd in fds_to_close[]. */
+ index = (unsigned int) duplicated_fd / CHAR_BIT;
+ if (fds_to_close_bound <= index)
+ {
+ if (sizeof fds_to_close <= index)
+ /* 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;
+ }
+
+# if REPLACE_FCHDIR
+ if (0 <= result)
+ result = _gl_register_dup (oldfd, result);
+# endif
+ return result;
+}
+#endif /* W32 */
+
+/* Perform the specified ACTION on the file descriptor FD, possibly
+ using the argument ARG further described below. This replacement
+ handles the following actions, and forwards all others on to the
+ native fcntl. An unrecognized ACTION returns -1 with errno set to
+ EINVAL.
+
+ F_DUPFD - duplicate FD, with int ARG being the minimum target fd.
+ If successful, return the duplicate, which will be inheritable;
+ otherwise return -1 and set errno.
+
+ F_DUPFD_CLOEXEC - duplicate FD, with int ARG being the minimum
+ target fd. If successful, return the duplicate, which will not be
+ inheritable; otherwise return -1 and set errno.
+
+ F_GETFD - ARG need not be present. If successful, return a
+ non-negative value containing the descriptor flags of FD (only
+ FD_CLOEXEC is portable, but other flags may be present); otherwise
+ return -1 and set errno. */
+
+int
+rpl_fcntl (int fd, int action, /* arg */...)
+{
+ va_list arg;
+ int result = -1;
+ va_start (arg, action);
+ switch (action)
+ {
+
+#if !HAVE_FCNTL
+ case F_DUPFD:
+ {
+ int target = va_arg (arg, int);
+ result = dupfd (fd, target, 0);
+ break;
+ }
+#elif FCNTL_DUPFD_BUGGY || REPLACE_FCHDIR
+ case F_DUPFD:
+ {
+ int target = va_arg (arg, int);
+ /* Detect invalid target; needed for cygwin 1.5.x. */
+ if (target < 0 || getdtablesize () <= target)
+ errno = EINVAL;
+ else
+ {
+ /* Haiku alpha 2 loses fd flags on original. */
+ int flags = fcntl (fd, F_GETFD);
+ if (flags < 0)
+ {
+ result = -1;
+ break;
+ }
+ result = fcntl (fd, action, target);
+ if (0 <= result && fcntl (fd, F_SETFD, flags) == -1)
+ {
+ int saved_errno = errno;
+ close (result);
+ result = -1;
+ errno = saved_errno;
+ }
+# if REPLACE_FCHDIR
+ if (0 <= result)
+ result = _gl_register_dup (fd, result);
+# endif
+ }
+ break;
+ } /* F_DUPFD */
+#endif /* FCNTL_DUPFD_BUGGY || REPLACE_FCHDIR */
+
+ case F_DUPFD_CLOEXEC:
+ {
+ int target = va_arg (arg, int);
+
+#if !HAVE_FCNTL
+ result = dupfd (fd, target, O_CLOEXEC);
+ break;
+#else /* HAVE_FCNTL */
+ /* Try the system call first, if the headers claim it exists
+ (that is, if GNULIB_defined_F_DUPFD_CLOEXEC is 0), since we
+ may be running with a glibc that has the macro but with an
+ older kernel that does not support it. Cache the
+ information on whether the system call really works, but
+ avoid caching failure if the corresponding F_DUPFD fails
+ for any reason. 0 = unknown, 1 = yes, -1 = no. */
+ static int have_dupfd_cloexec = GNULIB_defined_F_DUPFD_CLOEXEC ? -1 : 0;
+ if (0 <= have_dupfd_cloexec)
+ {
+ result = fcntl (fd, action, target);
+ if (0 <= result || errno != EINVAL)
+ {
+ have_dupfd_cloexec = 1;
+# if REPLACE_FCHDIR
+ if (0 <= result)
+ result = _gl_register_dup (fd, result);
+# endif
+ }
+ else
+ {
+ result = rpl_fcntl (fd, F_DUPFD, target);
+ if (result < 0)
+ break;
+ have_dupfd_cloexec = -1;
+ }
+ }
+ else
+ result = rpl_fcntl (fd, F_DUPFD, target);
+ if (0 <= result && have_dupfd_cloexec == -1)
+ {
+ int flags = fcntl (result, F_GETFD);
+ if (flags < 0 || fcntl (result, F_SETFD, flags | FD_CLOEXEC) == -1)
+ {
+ int saved_errno = errno;
+ close (result);
+ errno = saved_errno;
+ result = -1;
+ }
+ }
+ break;
+#endif /* HAVE_FCNTL */
+ } /* F_DUPFD_CLOEXEC */
+
+#if !HAVE_FCNTL
+ case F_GETFD:
+ {
+# if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
+ HANDLE handle = (HANDLE) _get_osfhandle (fd);
+ DWORD flags;
+ if (handle == INVALID_HANDLE_VALUE
+ || GetHandleInformation (handle, &flags) == 0)
+ errno = EBADF;
+ else
+ result = (flags & HANDLE_FLAG_INHERIT) ? 0 : FD_CLOEXEC;
+# else /* !W32 */
+ /* Use dup2 to reject invalid file descriptors. No way to
+ access this information, so punt. */
+ if (0 <= dup2 (fd, fd))
+ result = 0;
+# endif /* !W32 */
+ break;
+ } /* F_GETFD */
+#endif /* !HAVE_FCNTL */
+
+ /* Implementing F_SETFD on mingw is not trivial - there is no
+ API for changing the O_NOINHERIT bit on an fd, and merely
+ changing the HANDLE_FLAG_INHERIT bit on the underlying handle
+ can lead to odd state. It may be possible by duplicating the
+ handle, using _open_osfhandle with the right flags, then
+ using dup2 to move the duplicate onto the original, but that
+ is not supported for now. */
+
+ default:
+ {
+#if HAVE_FCNTL
+ void *p = va_arg (arg, void *);
+ result = fcntl (fd, action, p);
+#else
+ errno = EINVAL;
+#endif
+ break;
+ }
+ }
+ va_end (arg);
+ return result;
+}
diff --git a/lib/getdtablesize.c b/lib/getdtablesize.c
new file mode 100644
index 00000000000..9947405af61
--- /dev/null
+++ b/lib/getdtablesize.c
@@ -0,0 +1,86 @@
+/* getdtablesize() function for platforms that don't have it.
+ Copyright (C) 2008-2013 Free Software Foundation, Inc.
+ Written by Bruno Haible <bruno@clisp.org>, 2008.
+
+ 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 3 of the License, 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, see <http://www.gnu.org/licenses/>. */
+
+#include <config.h>
+
+/* Specification. */
+#include <unistd.h>
+
+#if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
+
+#include <stdio.h>
+
+#include "msvc-inval.h"
+
+#if HAVE_MSVC_INVALID_PARAMETER_HANDLER
+static int
+_setmaxstdio_nothrow (int newmax)
+{
+ int result;
+
+ TRY_MSVC_INVAL
+ {
+ result = _setmaxstdio (newmax);
+ }
+ CATCH_MSVC_INVAL
+ {
+ result = -1;
+ }
+ DONE_MSVC_INVAL;
+
+ return result;
+}
+# define _setmaxstdio _setmaxstdio_nothrow
+#endif
+
+/* Cache for the previous getdtablesize () result. */
+static int dtablesize;
+
+int
+getdtablesize (void)
+{
+ if (dtablesize == 0)
+ {
+ /* We are looking for the number N such that the valid file descriptors
+ are 0..N-1. It can be obtained through a loop as follows:
+ {
+ int fd;
+ for (fd = 3; fd < 65536; fd++)
+ if (dup2 (0, fd) == -1)
+ break;
+ return fd;
+ }
+ On Windows XP, the result is 2048.
+ The drawback of this loop is that it allocates memory for a libc
+ internal array that is never freed.
+
+ The number N can also be obtained as the upper bound for
+ _getmaxstdio (). _getmaxstdio () returns the maximum number of open
+ FILE objects. The sanity check in _setmaxstdio reveals the maximum
+ number of file descriptors. This too allocates memory, but it is
+ freed when we call _setmaxstdio with the original value. */
+ int orig_max_stdio = _getmaxstdio ();
+ unsigned int bound;
+ for (bound = 0x10000; _setmaxstdio (bound) < 0; bound = bound / 2)
+ ;
+ _setmaxstdio (orig_max_stdio);
+ dtablesize = bound;
+ }
+ return dtablesize;
+}
+
+#endif
diff --git a/lib/gnulib.mk b/lib/gnulib.mk
index 4a84b1db261..9f9ac6c0583 100644
--- a/lib/gnulib.mk
+++ b/lib/gnulib.mk
@@ -21,7 +21,7 @@
# the same distribution terms as the rest of that program.
#
# Generated by gnulib-tool.
-# Reproduce by: gnulib-tool --import --dir=. --lib=libgnu --source-base=lib --m4-base=m4 --doc-base=doc --tests-base=tests --aux-dir=build-aux --avoid=dup --avoid=fchdir --avoid=fcntl --avoid=fstat --avoid=malloc-posix --avoid=msvc-inval --avoid=msvc-nothrow --avoid=open --avoid=openat-die --avoid=opendir --avoid=raise --avoid=save-cwd --avoid=select --avoid=sigprocmask --avoid=sys_types --avoid=threadlib --makefile-name=gnulib.mk --conditional-dependencies --no-libtool --macro-prefix=gl --no-vc-files alloca-opt c-ctype c-strcase careadlinkat close-stream crypto/md5 crypto/sha1 crypto/sha256 crypto/sha512 dtoastr dtotimespec dup2 environ execinfo faccessat fcntl-h fdatasync fdopendir filemode fstatat fsync getloadavg getopt-gnu gettime gettimeofday ignore-value intprops largefile lstat manywarnings memrchr mktime pselect pthread_sigmask putenv qacl readlink readlinkat sig2str socklen stat-time stdalign stdarg stdbool stdio strftime strtoimax strtoumax symlink sys_stat sys_time time timer-time timespec-add timespec-sub unsetenv utimens warnings
+# Reproduce by: gnulib-tool --import --dir=. --lib=libgnu --source-base=lib --m4-base=m4 --doc-base=doc --tests-base=tests --aux-dir=build-aux --avoid=binary-io --avoid=close --avoid=dup --avoid=fchdir --avoid=fstat --avoid=malloc-posix --avoid=msvc-inval --avoid=msvc-nothrow --avoid=open --avoid=openat-die --avoid=opendir --avoid=raise --avoid=save-cwd --avoid=select --avoid=sigprocmask --avoid=sys_types --avoid=threadlib --makefile-name=gnulib.mk --conditional-dependencies --no-libtool --macro-prefix=gl --no-vc-files alloca-opt c-ctype c-strcase careadlinkat close-stream crypto/md5 crypto/sha1 crypto/sha256 crypto/sha512 dtoastr dtotimespec dup2 environ execinfo faccessat fcntl fcntl-h fdatasync fdopendir filemode fstatat fsync getloadavg getopt-gnu gettime gettimeofday ignore-value intprops largefile lstat manywarnings memrchr mktime pipe2 pselect pthread_sigmask putenv qacl readlink readlinkat sig2str socklen stat-time stdalign stdarg stdbool stdio strftime strtoimax strtoumax symlink sys_stat sys_time time timer-time timespec-add timespec-sub unsetenv utimens warnings
MOSTLYCLEANFILES += core *.stackdump
@@ -296,6 +296,15 @@ EXTRA_libgnu_a_SOURCES += at-func.c faccessat.c
## end gnulib module faccessat
+## begin gnulib module fcntl
+
+
+EXTRA_DIST += fcntl.c
+
+EXTRA_libgnu_a_SOURCES += fcntl.c
+
+## end gnulib module fcntl
+
## begin gnulib module fcntl-h
BUILT_SOURCES += fcntl.h
@@ -384,6 +393,17 @@ EXTRA_libgnu_a_SOURCES += fsync.c
## end gnulib module fsync
+## begin gnulib module getdtablesize
+
+if gl_GNULIB_ENABLED_getdtablesize
+
+endif
+EXTRA_DIST += getdtablesize.c
+
+EXTRA_libgnu_a_SOURCES += getdtablesize.c
+
+## end gnulib module getdtablesize
+
## begin gnulib module getgroups
if gl_GNULIB_ENABLED_getgroups
@@ -568,6 +588,12 @@ EXTRA_DIST += pathmax.h
## end gnulib module pathmax
+## begin gnulib module pipe2
+
+libgnu_a_SOURCES += pipe2.c
+
+## end gnulib module pipe2
+
## begin gnulib module pselect
@@ -1704,9 +1730,7 @@ EXTRA_DIST += utimens.h
## begin gnulib module verify
-if gl_GNULIB_ENABLED_verify
-endif
EXTRA_DIST += verify.h
## end gnulib module verify
diff --git a/lib/pipe2.c b/lib/pipe2.c
new file mode 100644
index 00000000000..3858c328f76
--- /dev/null
+++ b/lib/pipe2.c
@@ -0,0 +1,171 @@
+/* Create a pipe, with specific opening flags.
+ Copyright (C) 2009-2013 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 3, 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, see <http://www.gnu.org/licenses/>. */
+
+#include <config.h>
+
+/* Specification. */
+#include <unistd.h>
+
+#include <errno.h>
+#include <fcntl.h>
+
+#if GNULIB_BINARY_IO
+# include "binary-io.h"
+#endif
+
+#include "verify.h"
+
+#if GNULIB_defined_O_NONBLOCK
+# include "nonblocking.h"
+#endif
+
+#if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
+/* Native Windows API. */
+
+# include <io.h>
+
+#endif
+
+int
+pipe2 (int fd[2], int flags)
+{
+ /* Mingw _pipe() corrupts fd on failure; also, if we succeed at
+ creating the pipe but later fail at changing fcntl, we want
+ to leave fd unchanged: http://austingroupbugs.net/view.php?id=467 */
+ int tmp[2];
+ tmp[0] = fd[0];
+ tmp[1] = fd[1];
+
+#if HAVE_PIPE2
+# undef pipe2
+ /* Try the system call first, if it exists. (We may be running with a glibc
+ that has the function but with an older kernel that lacks it.) */
+ {
+ /* Cache the information whether the system call really exists. */
+ static int have_pipe2_really; /* 0 = unknown, 1 = yes, -1 = no */
+ if (have_pipe2_really >= 0)
+ {
+ int result = pipe2 (fd, flags);
+ if (!(result < 0 && errno == ENOSYS))
+ {
+ have_pipe2_really = 1;
+ return result;
+ }
+ have_pipe2_really = -1;
+ }
+ }
+#endif
+
+ /* Check the supported flags. */
+ if ((flags & ~(O_CLOEXEC | O_NONBLOCK | O_BINARY | O_TEXT)) != 0)
+ {
+ errno = EINVAL;
+ return -1;
+ }
+
+#if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
+/* Native Windows API. */
+
+ if (_pipe (fd, 4096, flags & ~O_NONBLOCK) < 0)
+ {
+ fd[0] = tmp[0];
+ fd[1] = tmp[1];
+ return -1;
+ }
+
+ /* O_NONBLOCK handling.
+ On native Windows platforms, O_NONBLOCK is defined by gnulib. Use the
+ functions defined by the gnulib module 'nonblocking'. */
+# if GNULIB_defined_O_NONBLOCK
+ if (flags & O_NONBLOCK)
+ {
+ if (set_nonblocking_flag (fd[0], true) != 0
+ || set_nonblocking_flag (fd[1], true) != 0)
+ goto fail;
+ }
+# else
+ {
+ verify (O_NONBLOCK == 0);
+ }
+# endif
+
+ return 0;
+
+#else
+/* Unix API. */
+
+ if (pipe (fd) < 0)
+ return -1;
+
+ /* POSIX <http://www.opengroup.org/onlinepubs/9699919799/functions/pipe.html>
+ says that initially, the O_NONBLOCK and FD_CLOEXEC flags are cleared on
+ both fd[0] and fd[1]. */
+
+ /* O_NONBLOCK handling.
+ On Unix platforms, O_NONBLOCK is defined by the system. Use fcntl(). */
+ if (flags & O_NONBLOCK)
+ {
+ int fcntl_flags;
+
+ if ((fcntl_flags = fcntl (fd[1], F_GETFL, 0)) < 0
+ || fcntl (fd[1], F_SETFL, fcntl_flags | O_NONBLOCK) == -1
+ || (fcntl_flags = fcntl (fd[0], F_GETFL, 0)) < 0
+ || fcntl (fd[0], F_SETFL, fcntl_flags | O_NONBLOCK) == -1)
+ goto fail;
+ }
+
+ if (flags & O_CLOEXEC)
+ {
+ int fcntl_flags;
+
+ if ((fcntl_flags = fcntl (fd[1], F_GETFD, 0)) < 0
+ || fcntl (fd[1], F_SETFD, fcntl_flags | FD_CLOEXEC) == -1
+ || (fcntl_flags = fcntl (fd[0], F_GETFD, 0)) < 0
+ || fcntl (fd[0], F_SETFD, fcntl_flags | FD_CLOEXEC) == -1)
+ goto fail;
+ }
+
+# if O_BINARY
+ if (flags & O_BINARY)
+ {
+ setmode (fd[1], O_BINARY);
+ setmode (fd[0], O_BINARY);
+ }
+ else if (flags & O_TEXT)
+ {
+ setmode (fd[1], O_TEXT);
+ setmode (fd[0], O_TEXT);
+ }
+# endif
+
+ return 0;
+
+#endif
+
+#if GNULIB_defined_O_NONBLOCK || \
+ !((defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__)
+ fail:
+ {
+ int saved_errno = errno;
+ close (fd[0]);
+ close (fd[1]);
+ fd[0] = tmp[0];
+ fd[1] = tmp[1];
+ errno = saved_errno;
+ return -1;
+ }
+#endif
+}