Cygwin: encapsulate Winsock based fhandler_socket classes

Insert another class fhandler_socket_wsock between fhandler_socket
and fhandler_socket_inet/fhandler_socket_local.

Also, add a new method fhandler::is_wsock_socket to allow asking
for sockets in general (is_socket) vs. Winsock-based sockets
(is_wsock_socket).

This allows to develop a new handler_socket_unix class as derived
class from fhandler_socket without any trace of wsock code left
in fhandler_socket.

While this is basically a temporary measure at this time, it may
prove useful for later interoperability with the upcoming Windows 10
AF_UNIX implementation at one point.

Signed-off-by: Corinna Vinschen <corinna@vinschen.de>
This commit is contained in:
Corinna Vinschen
2018-02-23 15:24:18 +01:00
parent cc9fe2c716
commit b79018ee3a
8 changed files with 711 additions and 1157 deletions

View File

@@ -221,9 +221,10 @@ get_ext_funcptr (SOCKET sock, void *funcptr)
}
fhandler_socket_local::fhandler_socket_local () :
fhandler_socket (),
fhandler_socket_wsock (),
sun_path (NULL),
peer_sun_path (NULL)
peer_sun_path (NULL),
status ()
{
}
@@ -1044,73 +1045,7 @@ fhandler_socket_local::getpeername (struct sockaddr *name, int *namelen)
return res;
}
int
fhandler_socket_local::shutdown (int how)
{
int res = ::shutdown (get_socket (), how);
/* Linux allows to call shutdown for any socket, even if it's not connected.
This also disables to call accept on this socket, if shutdown has been
called with the SHUT_RD or SHUT_RDWR parameter. In contrast, WinSock
only allows to call shutdown on a connected socket. The accept function
is in no way affected. So, what we do here is to fake success, and to
change the event settings so that an FD_CLOSE event is triggered for the
calling Cygwin function. The evaluate_events method handles the call
from accept specially to generate a Linux-compatible behaviour. */
if (res && WSAGetLastError () != WSAENOTCONN)
set_winsock_errno ();
else
{
res = 0;
switch (how)
{
case SHUT_RD:
saw_shutdown_read (true);
wsock_events->events |= FD_CLOSE;
SetEvent (wsock_evt);
break;
case SHUT_WR:
saw_shutdown_write (true);
break;
case SHUT_RDWR:
saw_shutdown_read (true);
saw_shutdown_write (true);
wsock_events->events |= FD_CLOSE;
SetEvent (wsock_evt);
break;
}
}
return res;
}
int
fhandler_socket_local::close ()
{
int res = 0;
release_events ();
while ((res = ::closesocket (get_socket ())) != 0)
{
if (WSAGetLastError () != WSAEWOULDBLOCK)
{
set_winsock_errno ();
res = -1;
break;
}
if (cygwait (10) == WAIT_SIGNALED)
{
set_errno (EINTR);
res = -1;
break;
}
WSASetLastError (0);
}
debug_printf ("%d = fhandler_socket::close()", res);
return res;
}
inline ssize_t
ssize_t
fhandler_socket_local::recv_internal (LPWSAMSG wsamsg, bool use_recvmsg)
{
ssize_t res = 0;
@@ -1279,235 +1214,6 @@ fhandler_socket_local::recv_internal (LPWSAMSG wsamsg, bool use_recvmsg)
return ret;
}
ssize_t
fhandler_socket_local::recvfrom (void *in_ptr, size_t len, int flags,
struct sockaddr *from, int *fromlen)
{
char *ptr = (char *) in_ptr;
#ifdef __x86_64__
/* size_t is 64 bit, but the len member in WSABUF is 32 bit.
Split buffer if necessary. */
DWORD bufcnt = len / UINT32_MAX + ((!len || (len % UINT32_MAX)) ? 1 : 0);
WSABUF wsabuf[bufcnt];
WSAMSG wsamsg = { from, from && fromlen ? *fromlen : 0,
wsabuf, bufcnt,
{ 0, NULL },
(DWORD) flags };
/* Don't use len as loop condition, it could be 0. */
for (WSABUF *wsaptr = wsabuf; bufcnt--; ++wsaptr)
{
wsaptr->len = MIN (len, UINT32_MAX);
wsaptr->buf = ptr;
len -= wsaptr->len;
ptr += wsaptr->len;
}
#else
WSABUF wsabuf = { len, ptr };
WSAMSG wsamsg = { from, from && fromlen ? *fromlen : 0,
&wsabuf, 1,
{ 0, NULL},
(DWORD) flags };
#endif
ssize_t ret = recv_internal (&wsamsg, false);
if (fromlen)
*fromlen = wsamsg.namelen;
return ret;
}
ssize_t
fhandler_socket_local::recvmsg (struct msghdr *msg, int flags)
{
/* TODO: Descriptor passing on AF_LOCAL sockets. */
/* Disappointing but true: Even if WSARecvMsg is supported, it's only
supported for datagram and raw sockets. */
bool use_recvmsg = true;
if (get_socket_type () == SOCK_STREAM || get_addr_family () == AF_LOCAL)
{
use_recvmsg = false;
msg->msg_controllen = 0;
}
WSABUF wsabuf[msg->msg_iovlen];
WSABUF *wsaptr = wsabuf + msg->msg_iovlen;
const struct iovec *iovptr = msg->msg_iov + msg->msg_iovlen;
while (--wsaptr >= wsabuf)
{
wsaptr->len = (--iovptr)->iov_len;
wsaptr->buf = (char *) iovptr->iov_base;
}
WSAMSG wsamsg = { (struct sockaddr *) msg->msg_name, msg->msg_namelen,
wsabuf, (DWORD) msg->msg_iovlen,
{ (DWORD) msg->msg_controllen, (char *) msg->msg_control },
(DWORD) flags };
ssize_t ret = recv_internal (&wsamsg, use_recvmsg);
if (ret >= 0)
{
msg->msg_namelen = wsamsg.namelen;
msg->msg_controllen = wsamsg.Control.len;
if (!CYGWIN_VERSION_CHECK_FOR_USING_ANCIENT_MSGHDR)
msg->msg_flags = wsamsg.dwFlags;
}
return ret;
}
void __reg3
fhandler_socket_local::read (void *in_ptr, size_t& len)
{
char *ptr = (char *) in_ptr;
#ifdef __x86_64__
/* size_t is 64 bit, but the len member in WSABUF is 32 bit.
Split buffer if necessary. */
DWORD bufcnt = len / UINT32_MAX + ((!len || (len % UINT32_MAX)) ? 1 : 0);
WSABUF wsabuf[bufcnt];
WSAMSG wsamsg = { NULL, 0, wsabuf, bufcnt, { 0, NULL }, 0 };
/* Don't use len as loop condition, it could be 0. */
for (WSABUF *wsaptr = wsabuf; bufcnt--; ++wsaptr)
{
wsaptr->len = MIN (len, UINT32_MAX);
wsaptr->buf = ptr;
len -= wsaptr->len;
ptr += wsaptr->len;
}
#else
WSABUF wsabuf = { len, ptr };
WSAMSG wsamsg = { NULL, 0, &wsabuf, 1, { 0, NULL }, 0 };
#endif
len = recv_internal (&wsamsg, false);
}
ssize_t
fhandler_socket_local::readv (const struct iovec *const iov, const int iovcnt,
ssize_t tot)
{
WSABUF wsabuf[iovcnt];
WSABUF *wsaptr = wsabuf + iovcnt;
const struct iovec *iovptr = iov + iovcnt;
while (--wsaptr >= wsabuf)
{
wsaptr->len = (--iovptr)->iov_len;
wsaptr->buf = (char *) iovptr->iov_base;
}
WSAMSG wsamsg = { NULL, 0, wsabuf, (DWORD) iovcnt, { 0, NULL}, 0 };
return recv_internal (&wsamsg, false);
}
inline ssize_t
fhandler_socket_local::send_internal (struct _WSAMSG *wsamsg, int flags)
{
ssize_t res = 0;
DWORD ret = 0, sum = 0;
WSABUF out_buf[wsamsg->dwBufferCount];
bool use_sendmsg = false;
DWORD wait_flags = flags & MSG_DONTWAIT;
bool nosignal = !!(flags & MSG_NOSIGNAL);
flags &= (MSG_OOB | MSG_DONTROUTE);
if (wsamsg->Control.len > 0)
use_sendmsg = true;
/* Workaround for MSDN KB 823764: Split a message into chunks <= SO_SNDBUF.
in_idx is the index of the current lpBuffers from the input wsamsg buffer.
in_off is used to keep track of the next byte to write from a wsamsg
buffer which only gets partially written. */
for (DWORD in_idx = 0, in_off = 0;
in_idx < wsamsg->dwBufferCount;
in_off >= wsamsg->lpBuffers[in_idx].len && (++in_idx, in_off = 0))
{
/* Split a message into the least number of pieces to minimize the
number of WsaSendTo calls. Don't split datagram messages (bad idea).
out_idx is the index of the next buffer in the out_buf WSABUF,
also the number of buffers given to WSASendTo.
out_len is the number of bytes in the buffers given to WSASendTo.
Don't split datagram messages (very bad idea). */
DWORD out_idx = 0;
DWORD out_len = 0;
if (get_socket_type () == SOCK_STREAM)
{
do
{
out_buf[out_idx].buf = wsamsg->lpBuffers[in_idx].buf + in_off;
out_buf[out_idx].len = wsamsg->lpBuffers[in_idx].len - in_off;
out_len += out_buf[out_idx].len;
out_idx++;
}
while (out_len < (unsigned) wmem ()
&& (in_off = 0, ++in_idx < wsamsg->dwBufferCount));
/* Tweak len of the last out_buf buffer so the entire number of bytes
is (less than or) equal to wmem (). Fix out_len as well since it's
used in a subsequent test expression. */
if (out_len > (unsigned) wmem ())
{
out_buf[out_idx - 1].len -= out_len - (unsigned) wmem ();
out_len = (unsigned) wmem ();
}
/* Add the bytes written from the current last buffer to in_off,
so in_off points to the next byte to be written from that buffer,
or beyond which lets the outper loop skip to the next buffer. */
in_off += out_buf[out_idx - 1].len;
}
do
{
if (use_sendmsg)
res = WSASendMsg (get_socket (), wsamsg, flags, &ret, NULL, NULL);
else if (get_socket_type () == SOCK_STREAM)
res = WSASendTo (get_socket (), out_buf, out_idx, &ret, flags,
wsamsg->name, wsamsg->namelen, NULL, NULL);
else
res = WSASendTo (get_socket (), wsamsg->lpBuffers,
wsamsg->dwBufferCount, &ret, flags,
wsamsg->name, wsamsg->namelen, NULL, NULL);
if (res && (WSAGetLastError () == WSAEWOULDBLOCK))
{
LOCK_EVENTS;
wsock_events->events &= ~FD_WRITE;
UNLOCK_EVENTS;
}
}
while (res && (WSAGetLastError () == WSAEWOULDBLOCK)
&& !(res = wait_for_events (FD_WRITE | FD_CLOSE, wait_flags)));
if (!res)
{
sum += ret;
/* For streams, return to application if the number of bytes written
is less than the number of bytes we intended to write in a single
call to WSASendTo. Otherwise we would have to add code to
backtrack in the input buffers, which is questionable. There was
probably a good reason we couldn't write more. */
if (get_socket_type () != SOCK_STREAM || ret < out_len)
break;
}
else if (is_nonblocking () || WSAGetLastError() != WSAEWOULDBLOCK)
break;
}
if (sum)
res = sum;
else if (res == SOCKET_ERROR)
{
set_winsock_errno ();
/* Special handling for EPIPE and SIGPIPE.
EPIPE is generated if the local end has been shut down on a connection
oriented socket. In this case the process will also receive a SIGPIPE
unless MSG_NOSIGNAL is set. */
if ((get_errno () == ECONNABORTED || get_errno () == ESHUTDOWN)
&& get_socket_type () == SOCK_STREAM)
{
set_errno (EPIPE);
if (!nosignal)
raise (SIGPIPE);
}
}
return res;
}
ssize_t
fhandler_socket_local::sendto (const void *in_ptr, size_t len, int flags,
const struct sockaddr *to, int tolen)
@@ -1578,48 +1284,6 @@ fhandler_socket_local::sendmsg (const struct msghdr *msg, int flags)
return send_internal (&wsamsg, flags);
}
ssize_t
fhandler_socket_local::write (const void *in_ptr, size_t len)
{
char *ptr = (char *) in_ptr;
#ifdef __x86_64__
/* size_t is 64 bit, but the len member in WSABUF is 32 bit.
Split buffer if necessary. */
DWORD bufcnt = len / UINT32_MAX + ((!len || (len % UINT32_MAX)) ? 1 : 0);
WSABUF wsabuf[bufcnt];
WSAMSG wsamsg = { NULL, 0, wsabuf, bufcnt, { 0, NULL }, 0 };
/* Don't use len as loop condition, it could be 0. */
for (WSABUF *wsaptr = wsabuf; bufcnt--; ++wsaptr)
{
wsaptr->len = MIN (len, UINT32_MAX);
wsaptr->buf = ptr;
len -= wsaptr->len;
ptr += wsaptr->len;
}
#else
WSABUF wsabuf = { len, ptr };
WSAMSG wsamsg = { NULL, 0, &wsabuf, 1, { 0, NULL }, 0 };
#endif
return send_internal (&wsamsg, 0);
}
ssize_t
fhandler_socket_local::writev (const struct iovec *const iov, const int iovcnt,
ssize_t tot)
{
WSABUF wsabuf[iovcnt];
WSABUF *wsaptr = wsabuf;
const struct iovec *iovptr = iov;
for (int i = 0; i < iovcnt; ++i)
{
wsaptr->len = iovptr->iov_len;
(wsaptr++)->buf = (char *) (iovptr++)->iov_base;
}
WSAMSG wsamsg = { NULL, 0, wsabuf, (DWORD) iovcnt, { 0, NULL}, 0 };
return send_internal (&wsamsg, 0);
}
void
fhandler_socket_local::set_sun_path (const char *path)
{
@@ -1907,86 +1571,3 @@ fhandler_socket_local::getsockopt (int level, int optname, const void *optval,
return ret;
}
int
fhandler_socket_local::ioctl (unsigned int cmd, void *p)
{
int res;
switch (cmd)
{
/* FIXME: These have to be handled differently in future. */
case FIOASYNC:
#ifdef __x86_64__
case _IOW('f', 125, u_long):
#endif
res = WSAAsyncSelect (get_socket (), winmsg, WM_ASYNCIO,
*(int *) p ? ASYNC_MASK : 0);
syscall_printf ("Async I/O on socket %s",
*(int *) p ? "started" : "cancelled");
async_io (*(int *) p != 0);
/* If async_io is switched off, revert the event handling. */
if (*(int *) p == 0)
WSAEventSelect (get_socket (), wsock_evt, EVENT_MASK);
break;
case FIONREAD:
#ifdef __x86_64__
case _IOR('f', 127, u_long):
#endif
/* Make sure to use the Winsock definition of FIONREAD. */
res = ::ioctlsocket (get_socket (), _IOR('f', 127, u_long), (u_long *) p);
if (res == SOCKET_ERROR)
set_winsock_errno ();
break;
case FIONBIO:
case SIOCATMARK:
/* Sockets are always non-blocking internally. So we just note the
state here. */
#ifdef __x86_64__
/* Convert the different idea of u_long in the definition of cmd. */
if (((cmd >> 16) & IOCPARM_MASK) == sizeof (unsigned long))
cmd = (cmd & ~(IOCPARM_MASK << 16)) | (sizeof (u_long) << 16);
#endif
if (cmd == FIONBIO)
{
syscall_printf ("socket is now %sblocking",
*(int *) p ? "non" : "");
set_nonblocking (*(int *) p);
res = 0;
}
else
res = ::ioctlsocket (get_socket (), cmd, (u_long *) p);
break;
default:
res = fhandler_socket::ioctl (cmd, p);
break;
}
syscall_printf ("%d = ioctl_socket(%x, %p)", res, cmd, p);
return res;
}
int
fhandler_socket_local::fcntl (int cmd, intptr_t arg)
{
int res = 0;
switch (cmd)
{
case F_SETOWN:
{
pid_t pid = (pid_t) arg;
LOCK_EVENTS;
wsock_events->owner = pid;
UNLOCK_EVENTS;
debug_printf ("owner set to %d", pid);
}
break;
case F_GETOWN:
res = wsock_events->owner;
break;
default:
res = fhandler_socket::fcntl (cmd, arg);
break;
}
return res;
}