Back to index

nagios-plugins  1.4.16
fcntl.c
Go to the documentation of this file.
00001 /* Provide file descriptor control.
00002 
00003    Copyright (C) 2009, 2010 Free Software Foundation, Inc.
00004 
00005    This program is free software: you can redistribute it and/or modify
00006    it under the terms of the GNU General Public License as published by
00007    the Free Software Foundation; either version 3 of the License, or
00008    (at your option) any later version.
00009 
00010    This program is distributed in the hope that it will be useful,
00011    but WITHOUT ANY WARRANTY; without even the implied warranty of
00012    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00013    GNU General Public License for more details.
00014 
00015    You should have received a copy of the GNU General Public License
00016    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
00017 
00018 /* Written by Eric Blake <ebb9@byu.net>.  */
00019 
00020 #include <config.h>
00021 
00022 /* Specification.  */
00023 #include <fcntl.h>
00024 
00025 #include <errno.h>
00026 #include <limits.h>
00027 #include <stdarg.h>
00028 #include <unistd.h>
00029 
00030 #if !HAVE_FCNTL
00031 # define rpl_fcntl fcntl
00032 #endif
00033 #undef fcntl
00034 
00035 #if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
00036 /* Get declarations of the Win32 API functions.  */
00037 # define WIN32_LEAN_AND_MEAN
00038 # include <windows.h>
00039 
00040 /* Upper bound on getdtablesize().  See lib/getdtablesize.c.  */
00041 # define OPEN_MAX_MAX 0x10000
00042 
00043 /* Duplicate OLDFD into the first available slot of at least NEWFD,
00044    which must be positive, with FLAGS determining whether the duplicate
00045    will be inheritable.  */
00046 static int
00047 dupfd (int oldfd, int newfd, int flags)
00048 {
00049   /* Mingw has no way to create an arbitrary fd.  Iterate until all
00050      file descriptors less than newfd are filled up.  */
00051   HANDLE curr_process = GetCurrentProcess ();
00052   HANDLE old_handle = (HANDLE) _get_osfhandle (oldfd);
00053   unsigned char fds_to_close[OPEN_MAX_MAX / CHAR_BIT];
00054   unsigned int fds_to_close_bound = 0;
00055   int result;
00056   BOOL inherit = flags & O_CLOEXEC ? FALSE : TRUE;
00057   int mode;
00058 
00059   if (newfd < 0 || getdtablesize () <= newfd)
00060     {
00061       errno = EINVAL;
00062       return -1;
00063     }
00064   if (old_handle == INVALID_HANDLE_VALUE
00065       || (mode = setmode (oldfd, O_BINARY)) == -1)
00066     {
00067       /* oldfd is not open, or is an unassigned standard file
00068          descriptor.  */
00069       errno = EBADF;
00070       return -1;
00071     }
00072   setmode (oldfd, mode);
00073   flags |= mode;
00074 
00075   for (;;)
00076     {
00077       HANDLE new_handle;
00078       int duplicated_fd;
00079       unsigned int index;
00080 
00081       if (!DuplicateHandle (curr_process,           /* SourceProcessHandle */
00082                             old_handle,             /* SourceHandle */
00083                             curr_process,           /* TargetProcessHandle */
00084                             (PHANDLE) &new_handle,  /* TargetHandle */
00085                             (DWORD) 0,              /* DesiredAccess */
00086                             inherit,                /* InheritHandle */
00087                             DUPLICATE_SAME_ACCESS)) /* Options */
00088         {
00089           /* TODO: Translate GetLastError () into errno.  */
00090           errno = EMFILE;
00091           result = -1;
00092           break;
00093         }
00094       duplicated_fd = _open_osfhandle ((long) new_handle, flags);
00095       if (duplicated_fd < 0)
00096         {
00097           CloseHandle (new_handle);
00098           errno = EMFILE;
00099           result = -1;
00100           break;
00101         }
00102       if (newfd <= duplicated_fd)
00103         {
00104           result = duplicated_fd;
00105           break;
00106         }
00107 
00108       /* Set the bit duplicated_fd in fds_to_close[].  */
00109       index = (unsigned int) duplicated_fd / CHAR_BIT;
00110       if (fds_to_close_bound <= index)
00111         {
00112           if (sizeof fds_to_close <= index)
00113             /* Need to increase OPEN_MAX_MAX.  */
00114             abort ();
00115           memset (fds_to_close + fds_to_close_bound, '\0',
00116                   index + 1 - fds_to_close_bound);
00117           fds_to_close_bound = index + 1;
00118         }
00119       fds_to_close[index] |= 1 << ((unsigned int) duplicated_fd % CHAR_BIT);
00120     }
00121 
00122   /* Close the previous fds that turned out to be too small.  */
00123   {
00124     int saved_errno = errno;
00125     unsigned int duplicated_fd;
00126 
00127     for (duplicated_fd = 0;
00128          duplicated_fd < fds_to_close_bound * CHAR_BIT;
00129          duplicated_fd++)
00130       if ((fds_to_close[duplicated_fd / CHAR_BIT]
00131            >> (duplicated_fd % CHAR_BIT))
00132           & 1)
00133         close (duplicated_fd);
00134 
00135     errno = saved_errno;
00136   }
00137 
00138 # if REPLACE_FCHDIR
00139   if (0 <= result)
00140     result = _gl_register_dup (oldfd, result);
00141 # endif
00142   return result;
00143 }
00144 #endif /* W32 */
00145 
00146 /* Perform the specified ACTION on the file descriptor FD, possibly
00147    using the argument ARG further described below.  This replacement
00148    handles the following actions, and forwards all others on to the
00149    native fcntl.  An unrecognized ACTION returns -1 with errno set to
00150    EINVAL.
00151 
00152    F_DUPFD - duplicate FD, with int ARG being the minimum target fd.
00153    If successful, return the duplicate, which will be inheritable;
00154    otherwise return -1 and set errno.
00155 
00156    F_DUPFD_CLOEXEC - duplicate FD, with int ARG being the minimum
00157    target fd.  If successful, return the duplicate, which will not be
00158    inheritable; otherwise return -1 and set errno.
00159 
00160    F_GETFD - ARG need not be present.  If successful, return a
00161    non-negative value containing the descriptor flags of FD (only
00162    FD_CLOEXEC is portable, but other flags may be present); otherwise
00163    return -1 and set errno.  */
00164 
00165 int
00166 rpl_fcntl (int fd, int action, /* arg */...)
00167 {
00168   va_list arg;
00169   int result = -1;
00170   va_start (arg, action);
00171   switch (action)
00172     {
00173 
00174 #if !HAVE_FCNTL
00175     case F_DUPFD:
00176       {
00177         int target = va_arg (arg, int);
00178         result = dupfd (fd, target, 0);
00179         break;
00180       }
00181 #elif FCNTL_DUPFD_BUGGY || REPLACE_FCHDIR
00182     case F_DUPFD:
00183       {
00184         int target = va_arg (arg, int);
00185         /* Detect invalid target; needed for cygwin 1.5.x.  */
00186         if (target < 0 || getdtablesize () <= target)
00187           errno = EINVAL;
00188         else
00189           {
00190             result = fcntl (fd, action, target);
00191 # if REPLACE_FCHDIR
00192             if (0 <= result)
00193               result = _gl_register_dup (fd, result);
00194 # endif
00195           }
00196         break;
00197       } /* F_DUPFD */
00198 #endif /* FCNTL_DUPFD_BUGGY || REPLACE_FCHDIR */
00199 
00200     case F_DUPFD_CLOEXEC:
00201       {
00202         int target = va_arg (arg, int);
00203 
00204 #if !HAVE_FCNTL
00205         result = dupfd (fd, target, O_CLOEXEC);
00206         break;
00207 #else /* HAVE_FCNTL */
00208         /* Try the system call first, if the headers claim it exists
00209            (that is, if GNULIB_defined_F_DUPFD_CLOEXEC is 0), since we
00210            may be running with a glibc that has the macro but with an
00211            older kernel that does not support it.  Cache the
00212            information on whether the system call really works, but
00213            avoid caching failure if the corresponding F_DUPFD fails
00214            for any reason.  0 = unknown, 1 = yes, -1 = no.  */
00215         static int have_dupfd_cloexec = GNULIB_defined_F_DUPFD_CLOEXEC ? -1 : 0;
00216         if (0 <= have_dupfd_cloexec)
00217           {
00218             result = fcntl (fd, action, target);
00219             if (0 <= result || errno != EINVAL)
00220               {
00221                 have_dupfd_cloexec = 1;
00222 # if REPLACE_FCHDIR
00223                 if (0 <= result)
00224                   result = _gl_register_dup (fd, result);
00225 # endif
00226               }
00227             else
00228               {
00229                 result = rpl_fcntl (fd, F_DUPFD, target);
00230                 if (result < 0)
00231                   break;
00232                 have_dupfd_cloexec = -1;
00233               }
00234           }
00235         else
00236           result = rpl_fcntl (fd, F_DUPFD, target);
00237         if (0 <= result && have_dupfd_cloexec == -1)
00238           {
00239             int flags = fcntl (result, F_GETFD);
00240             if (flags < 0 || fcntl (result, F_SETFD, flags | FD_CLOEXEC) == -1)
00241               {
00242                 int saved_errno = errno;
00243                 close (result);
00244                 errno = saved_errno;
00245                 result = -1;
00246               }
00247           }
00248         break;
00249 #endif /* HAVE_FCNTL */
00250       } /* F_DUPFD_CLOEXEC */
00251 
00252 #if !HAVE_FCNTL
00253     case F_GETFD:
00254       {
00255 # if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
00256         HANDLE handle = (HANDLE) _get_osfhandle (fd);
00257         DWORD flags;
00258         if (handle == INVALID_HANDLE_VALUE
00259             || GetHandleInformation (handle, &flags) == 0)
00260           errno = EBADF;
00261         else
00262           result = (flags & HANDLE_FLAG_INHERIT) ? 0 : FD_CLOEXEC;
00263 # else /* !W32 */
00264         /* Use dup2 to reject invalid file descriptors.  No way to
00265            access this information, so punt.  */
00266         if (0 <= dup2 (fd, fd))
00267           result = 0;
00268 # endif /* !W32 */
00269         break;
00270       } /* F_GETFD */
00271 #endif /* !HAVE_FCNTL */
00272 
00273       /* Implementing F_SETFD on mingw is not trivial - there is no
00274          API for changing the O_NOINHERIT bit on an fd, and merely
00275          changing the HANDLE_FLAG_INHERIT bit on the underlying handle
00276          can lead to odd state.  It may be possible by duplicating the
00277          handle, using _open_osfhandle with the right flags, then
00278          using dup2 to move the duplicate onto the original, but that
00279          is not supported for now.  */
00280 
00281     default:
00282       {
00283 #if HAVE_FCNTL
00284         void *p = va_arg (arg, void *);
00285         result = fcntl (fd, action, p);
00286 #else
00287         errno = EINVAL;
00288 #endif
00289         break;
00290       }
00291     }
00292   va_end (arg);
00293   return result;
00294 }