Mercurial > hg > octave-nkf > gnulib-hg
view tests/test-fcntl.c @ 12454:f7624052e60d
fcntl: support F_DUPFD_CLOEXEC on systems with fcntl
Implement F_DUPFD_CLOEXEC. The unit test still fails on systems
with other fcntl bugs (such as cygwin 1.5 mishandling F_DUPFD,
or mingw lacking fcntl altogether). Passes on Linux, both with
and without kernel support, and on cygwin 1.7.
* modules/fcntl (Files): List new files.
(configure.ac): Run a test.
* m4/fcntl.m4 (gl_FUNC_FCNTL): New file.
* lib/fcntl.c (rpl_fcntl): Likewise.
* m4/fcntl_h.m4 (gl_FCNTL_H_DEFAULTS): Add witness defaults.
(gl_FCNTL_H): Always replace fcntl.h.
* modules/fcntl-h (Makefile.am): Substitute witnesses.
* lib/fcntl.in.h (fcntl): Declare replacement.
(F_DUPFD_CLOEXEC, GNULIB_defined_F_DUPFD_CLOEXEC): New macro when
needed, plus a witness.
* doc/posix-functions/fcntl.texi (fcntl): Document this.
* doc/posix-headers/fcntl.texi (fcntl.h): Likewise.
* tests/test-fcntl.c: New file.
* modules/fcntl-tests: Likewise.
Signed-off-by: Eric Blake <ebb9@byu.net>
author | Eric Blake <ebb9@byu.net> |
---|---|
date | Mon, 07 Dec 2009 11:50:59 -0700 |
parents | |
children | f3aceada3c52 |
line wrap: on
line source
/* Test of fcntl(2). 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 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>, 2009. */ #include <config.h> /* Specification. */ #include <fcntl.h> /* Helpers. */ #include <errno.h> #include <stdarg.h> #include <stdbool.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__ /* Get declarations of the Win32 API functions. */ # define WIN32_LEAN_AND_MEAN # include <windows.h> #endif #include "binary-io.h" /* Use O_CLOEXEC if available, but test works without it. */ #ifndef O_CLOEXEC # define O_CLOEXEC 0 #endif #if !O_BINARY # define setmode(f,m) zero () static int zero (void) { return 0; } #endif #define ASSERT(expr) \ do \ { \ if (!(expr)) \ { \ fprintf (stderr, "%s:%d: assertion failed\n", __FILE__, __LINE__); \ fflush (stderr); \ abort (); \ } \ } \ while (0) /* Return true if FD is open. */ static bool is_open (int fd) { #if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__ /* On Win32, the initial state of unassigned standard file descriptors is that they are open but point to an INVALID_HANDLE_VALUE, and there is no fcntl. */ return (HANDLE) _get_osfhandle (fd) != INVALID_HANDLE_VALUE; #else # ifndef F_GETFL # error Please port fcntl to your platform # endif return 0 <= fcntl (fd, F_GETFL); #endif } /* Return true if FD is open and inheritable across exec/spawn. */ static bool is_inheritable (int fd) { #if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__ /* On Win32, the initial state of unassigned standard file descriptors is that they are open but point to an INVALID_HANDLE_VALUE, and there is no fcntl. */ HANDLE h = (HANDLE) _get_osfhandle (fd); DWORD flags; if (h == INVALID_HANDLE_VALUE || GetHandleInformation (h, &flags) == 0) return false; return (flags & HANDLE_FLAG_INHERIT) != 0; #else # ifndef F_GETFD # error Please port fcntl to your platform # endif int i = fcntl (fd, F_GETFD); return 0 <= i && (i & FD_CLOEXEC) == 0; #endif } /* Return non-zero if FD is open in the given MODE, which is either O_TEXT or O_BINARY. */ static bool is_mode (int fd, int mode) { int value = setmode (fd, O_BINARY); setmode (fd, value); return mode == value; } /* Since native fcntl can have more supported operations than our replacement is aware of, and since various operations assign different types to the vararg argument, a wrapper around fcntl must be able to pass a vararg of unknown type on through to the original fcntl. Make sure that this works properly: func1 behaves like the original fcntl interpreting the vararg as an int or a pointer to a struct, and func2 behaves like rpl_fcntl that doesn't know what type to forward. */ struct dummy_struct { long filler; int value; }; static int func1 (int a, ...) { va_list arg; int i; va_start (arg, a); if (a < 4) i = va_arg (arg, int); else { struct dummy_struct *s = va_arg (arg, struct dummy_struct *); i = s->value; } va_end (arg); return i; } static int func2 (int a, ...) { va_list arg; void *p; va_start (arg, a); p = va_arg (arg, void *); va_end (arg); return func1 (a, p); } /* Ensure that all supported fcntl actions are distinct, and usable in preprocessor expressions. */ static void check_flags (void) { switch (0) { #ifdef F_DUPFD case F_DUPFD: # if F_DUPFD # endif #endif #ifdef F_DUPFD_CLOEXEC case F_DUPFD_CLOEXEC: # if F_DUPFD_CLOEXEC # endif #endif #ifdef F_GETFD case F_GETFD: # if F_GETFD # endif #endif #ifdef F_SETFD case F_SETFD: # if F_SETFD # endif #endif #ifdef F_GETFL case F_GETFL: # if F_GETFL # endif #endif #ifdef F_SETFL case F_SETFL: # if F_SETFL # endif #endif #ifdef F_GETOWN case F_GETOWN: # if F_GETOWN # endif #endif #ifdef F_SETOWN case F_SETOWN: # if F_SETOWN # endif #endif #ifdef F_GETLK case F_GETLK: # if F_GETLK # endif #endif #ifdef F_SETLK case F_SETLK: # if F_SETLK # endif #endif #ifdef F_SETLKW case F_SETLKW: # if F_SETLKW # endif #endif ; } } int main (int argc, char **argv) { const char *file = "test-fcntl.tmp"; int fd; /* Sanity check that rpl_fcntl is likely to work. */ ASSERT (func2 (1, 2) == 2); ASSERT (func2 (2, -2) == -2); ASSERT (func2 (3, 0x80000000) == 0x80000000); { struct dummy_struct s = { 0L, 4 }; ASSERT (func2 (4, &s) == 4); } check_flags (); #if HAVE_FCNTL /* Assume std descriptors were provided by invoker, and ignore fds that might have been inherited. */ fd = creat (file, 0600); ASSERT (STDERR_FILENO < fd); close (fd + 1); close (fd + 2); /* For F_DUPFD*, the source must be valid. */ errno = 0; ASSERT (fcntl (-1, F_DUPFD, 0) == -1); ASSERT (errno == EBADF); errno = 0; ASSERT (fcntl (fd + 1, F_DUPFD, 0) == -1); ASSERT (errno == EBADF); errno = 0; ASSERT (fcntl (10000000, F_DUPFD, 0) == -1); ASSERT (errno == EBADF); errno = 0; ASSERT (fcntl (-1, F_DUPFD_CLOEXEC, 0) == -1); ASSERT (errno == EBADF); errno = 0; ASSERT (fcntl (fd + 1, F_DUPFD_CLOEXEC, 0) == -1); ASSERT (errno == EBADF); errno = 0; ASSERT (fcntl (10000000, F_DUPFD_CLOEXEC, 0) == -1); ASSERT (errno == EBADF); /* For F_DUPFD*, the destination must be valid. */ ASSERT (getdtablesize () < 10000000); errno = 0; ASSERT (fcntl (fd, F_DUPFD, -1) == -1); ASSERT (errno == EINVAL); errno = 0; ASSERT (fcntl (fd, F_DUPFD, 10000000) == -1); ASSERT (errno == EINVAL); ASSERT (getdtablesize () < 10000000); errno = 0; ASSERT (fcntl (fd, F_DUPFD_CLOEXEC, -1) == -1); ASSERT (errno == EINVAL); errno = 0; ASSERT (fcntl (fd, F_DUPFD_CLOEXEC, 10000000) == -1); ASSERT (errno == EINVAL); /* For F_DUPFD*, check for correct inheritance, as well as preservation of text vs. binary. */ setmode (fd, O_BINARY); ASSERT (is_open (fd)); ASSERT (!is_open (fd + 1)); ASSERT (!is_open (fd + 2)); ASSERT (is_inheritable (fd)); ASSERT (is_mode (fd, O_BINARY)); ASSERT (fcntl (fd, F_DUPFD, fd) == fd + 1); ASSERT (is_open (fd)); ASSERT (is_open (fd + 1)); ASSERT (!is_open (fd + 2)); ASSERT (is_inheritable (fd + 1)); ASSERT (is_mode (fd, O_BINARY)); ASSERT (is_mode (fd + 1, O_BINARY)); ASSERT (close (fd + 1) == 0); ASSERT (fcntl (fd, F_DUPFD_CLOEXEC, fd + 2) == fd + 2); ASSERT (is_open (fd)); ASSERT (!is_open (fd + 1)); ASSERT (is_open (fd + 2)); ASSERT (is_inheritable (fd)); ASSERT (!is_inheritable (fd + 2)); ASSERT (is_mode (fd, O_BINARY)); ASSERT (is_mode (fd + 2, O_BINARY)); ASSERT (close (fd) == 0); setmode (fd + 2, O_TEXT); ASSERT (fcntl (fd + 2, F_DUPFD, fd + 1) == fd + 1); ASSERT (!is_open (fd)); ASSERT (is_open (fd + 1)); ASSERT (is_open (fd + 2)); ASSERT (is_inheritable (fd + 1)); ASSERT (!is_inheritable (fd + 2)); ASSERT (is_mode (fd + 1, O_TEXT)); ASSERT (is_mode (fd + 2, O_TEXT)); ASSERT (close (fd + 1) == 0); ASSERT (fcntl (fd + 2, F_DUPFD_CLOEXEC, 0) == fd); ASSERT (is_open (fd)); ASSERT (!is_open (fd + 1)); ASSERT (is_open (fd + 2)); ASSERT (!is_inheritable (fd)); ASSERT (!is_inheritable (fd + 2)); ASSERT (is_mode (fd, O_TEXT)); ASSERT (is_mode (fd + 2, O_TEXT)); ASSERT (close (fd + 2) == 0); /* Cleanup. */ ASSERT (close (fd) == 0); ASSERT (unlink (file) == 0); #endif /* HAVE_FCNTL */ return 0; }