New module 'msvc-nothrow'. Makes _get_osfhandle safe on MSVC 9.
[gnulib.git] / lib / accept4.c
1 /* Accept a connection on a socket, with specific opening flags.
2    Copyright (C) 2009-2011 Free Software Foundation, Inc.
3
4    This program is free software; you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation; either version 2, or (at your option)
7    any later version.
8
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13
14    You should have received a copy of the GNU General Public License along
15    with this program; if not, write to the Free Software Foundation,
16    Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */
17
18 #include <config.h>
19
20 /* Specification.  */
21 #include <sys/socket.h>
22
23 #include <errno.h>
24 #include <fcntl.h>
25 #include "binary-io.h"
26 #include "msvc-nothrow.h"
27
28 #ifndef SOCK_CLOEXEC
29 # define SOCK_CLOEXEC 0
30 #endif
31
32 int
33 accept4 (int sockfd, struct sockaddr *addr, socklen_t *addrlen, int flags)
34 {
35   int fd;
36
37 #if HAVE_ACCEPT4
38 # undef accept4
39   /* Try the system call first, if it exists.  (We may be running with a glibc
40      that has the function but with an older kernel that lacks it.)  */
41   {
42     /* Cache the information whether the system call really exists.  */
43     static int have_accept4_really; /* 0 = unknown, 1 = yes, -1 = no */
44     if (have_accept4_really >= 0)
45       {
46         int result = accept4 (sockfd, addr, addrlen, flags);
47         if (!(result < 0 && errno == ENOSYS))
48           {
49             have_accept4_really = 1;
50             return result;
51           }
52         have_accept4_really = -1;
53       }
54   }
55 #endif
56
57   /* Check the supported flags.  */
58   if ((flags & ~(SOCK_CLOEXEC | O_TEXT | O_BINARY)) != 0)
59     {
60       errno = EINVAL;
61       return -1;
62     }
63
64   fd = accept (sockfd, addr, addrlen);
65   if (fd < 0)
66     return -1;
67
68 #if SOCK_CLOEXEC
69 # if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
70 /* Native Woe32 API.  */
71   if (flags & SOCK_CLOEXEC)
72     {
73       HANDLE curr_process = GetCurrentProcess ();
74       HANDLE old_handle = (HANDLE) _get_osfhandle (fd);
75       HANDLE new_handle;
76       int nfd;
77
78       if (!DuplicateHandle (curr_process,           /* SourceProcessHandle */
79                             old_handle,             /* SourceHandle */
80                             curr_process,           /* TargetProcessHandle */
81                             (PHANDLE) &new_handle,  /* TargetHandle */
82                             (DWORD) 0,              /* DesiredAccess */
83                             FALSE,                  /* InheritHandle */
84                             DUPLICATE_SAME_ACCESS)) /* Options */
85         {
86           close (fd);
87           errno = EBADF; /* arbitrary */
88           return -1;
89         }
90
91       /* Closing fd before allocating the new fd ensures that the new fd will
92          have the minimum possible value.  */
93       close (fd);
94       nfd = _open_osfhandle ((long) new_handle,
95                              O_NOINHERIT | (flags & (O_TEXT | O_BINARY)));
96       if (nfd < 0)
97         {
98           CloseHandle (new_handle);
99           return -1;
100         }
101       return nfd;
102     }
103 # else
104 /* Unix API.  */
105   if (flags & SOCK_CLOEXEC)
106     {
107       int fcntl_flags;
108
109       if ((fcntl_flags = fcntl (fd, F_GETFD, 0)) < 0
110           || fcntl (fd, F_SETFD, fcntl_flags | FD_CLOEXEC) == -1)
111         {
112           int saved_errno = errno;
113           close (fd);
114           errno = saved_errno;
115           return -1;
116         }
117     }
118 # endif
119 #endif
120
121 #if O_BINARY
122   if (flags & O_BINARY)
123     setmode (fd, O_BINARY);
124   else if (flags & O_TEXT)
125     setmode (fd, O_TEXT);
126 #endif
127
128   return fd;
129 }