diff --git a/winsup/cygwin/ChangeLog b/winsup/cygwin/ChangeLog index 6582ed7f4..83d332020 100644 --- a/winsup/cygwin/ChangeLog +++ b/winsup/cygwin/ChangeLog @@ -1,3 +1,13 @@ +2011-04-18 Corinna Vinschen + + * fhandler_socket.cc (fhandler_socket::evaluate_events): Handle the + FD_CLOSE event specially when called from accept. Explain why. + (fhandler_socket::shutdown): Fake success on not-connected socket and + trigger socket event if the read side of a socket is affected. Explain + why. + * poll.cc (poll): Check for saw_shutdown_read on sockets to generate + POLLHUP as well. + 2011-04-18 Corinna Vinschen * Fix various copyrights. diff --git a/winsup/cygwin/fhandler_socket.cc b/winsup/cygwin/fhandler_socket.cc index 0a7c5ddf4..d0cf205a6 100644 --- a/winsup/cygwin/fhandler_socket.cc +++ b/winsup/cygwin/fhandler_socket.cc @@ -596,6 +596,15 @@ fhandler_socket::evaluate_events (const long event_mask, long &events, wsock_events->events &= ~FD_CONNECT; wsock_events->connect_errorcode = 0; } + /* This test makes the accept function behave as on Linux when + accept is called on a socket for which shutdown for the read side + has been called. The second half of this code is in the shutdown + method. See there for more info. */ + if ((event_mask & FD_ACCEPT) && (events & FD_CLOSE)) + { + WSASetLastError (WSAEINVAL); + ret = SOCKET_ERROR; + } if (erase) wsock_events->events &= ~(events & ~(FD_WRITE | FD_CLOSE)); } @@ -1659,22 +1668,37 @@ fhandler_socket::shutdown (int how) { int res = ::shutdown (get_socket (), how); - if (res) + /* 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 - switch (how) - { - case SHUT_RD: - saw_shutdown_read (true); - break; - case SHUT_WR: - saw_shutdown_write (true); - break; - case SHUT_RDWR: - saw_shutdown_read (true); - saw_shutdown_write (true); - break; - } + { + 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; } diff --git a/winsup/cygwin/poll.cc b/winsup/cygwin/poll.cc index b7f452ddb..2e68f3249 100644 --- a/winsup/cygwin/poll.cc +++ b/winsup/cygwin/poll.cc @@ -1,7 +1,7 @@ /* poll.cc. Implements poll(2) via usage of select(2) call. - Copyright 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 - Red Hat, Inc. + Copyright 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, + 2011 Red Hat, Inc. This file is part of Cygwin. @@ -84,12 +84,16 @@ poll (struct pollfd *fds, nfds_t nfds, int timeout) { if (fds[i].fd >= 0) { - if (cygheap->fdtab.not_open (fds[i].fd)) + fhandler_socket *sock; + + /* Check if the descriptor has been closed, or if shutdown for the + read side has been called on a socket. */ + if (cygheap->fdtab.not_open (fds[i].fd) + || ((sock = cygheap->fdtab[fds[i].fd]->is_socket ()) + && sock->saw_shutdown_read ())) fds[i].revents = POLLHUP; else { - fhandler_socket *sock; - if (FD_ISSET(fds[i].fd, read_fds)) /* This should be sufficient for sockets, too. Using MSG_PEEK, as before, can be considered dangerous at