X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/86c5b12b21333e9817bd2d0c21458308390e1524..41d0b41d6873cb50b7b025beb0cf9476d680004c:/src/unix/sockunix.cpp diff --git a/src/unix/sockunix.cpp b/src/unix/sockunix.cpp index 7fa43200ce..d0020dfbdb 100644 --- a/src/unix/sockunix.cpp +++ b/src/unix/sockunix.cpp @@ -146,33 +146,9 @@ int _System soclose(int); #define INADDR_NONE INADDR_BROADCAST #endif -#if defined(__VISAGECPP__) || defined(__WATCOMC__) - - #define MASK_SIGNAL() { - #define UNMASK_SIGNAL() } - -#else - extern "C" { typedef void (*wxSigHandler)(int); } - - #define MASK_SIGNAL() \ - { \ - wxSigHandler old_handler = signal(SIGPIPE, SIG_IGN); - - #define UNMASK_SIGNAL() \ - signal(SIGPIPE, old_handler); \ - } - -#endif - -/* If a SIGPIPE is issued by a socket call on a remotely closed socket, - the program will "crash" unless it explicitly handles the SIGPIPE. - By using MSG_NOSIGNAL, the SIGPIPE is suppressed. Later, we will - use SO_NOSIGPIPE (if available), the BSD equivalent. */ -#ifdef MSG_NOSIGNAL -# define GSOCKET_MSG_NOSIGNAL MSG_NOSIGNAL -#else /* MSG_NOSIGNAL not available (FreeBSD including OS X) */ -# define GSOCKET_MSG_NOSIGNAL 0 -#endif /* MSG_NOSIGNAL */ +// ---------------------------------------------------------------------------- +// implementation of thread-safe/reentrant functions if they're missing +// ---------------------------------------------------------------------------- #if wxUSE_THREADS && (defined(HAVE_GETHOSTBYNAME) || defined(HAVE_GETSERVBYNAME)) # include "wx/thread.h" @@ -424,134 +400,46 @@ struct servent *wxGetservbyname_r(const char *port, const char *protocol, return se; } -/* debugging helpers */ -#ifdef __GSOCKET_DEBUG__ -# define SOCKET_DEBUG(args) printf args -#else -# define SOCKET_DEBUG(args) -#endif /* __GSOCKET_DEBUG__ */ - -/* Constructors / Destructors for wxSocketImplUnix */ +// ============================================================================ +// wxSocketImpl implementation +// ============================================================================ -wxSocketImplUnix::wxSocketImplUnix(wxSocketBase& wxsocket) - : wxSocketImpl(wxsocket) +/* static */ +wxSocketImpl *wxSocketImpl::Create(wxSocketBase& wxsocket) { - m_fds[0] = - m_fds[1] = -1; - - m_use_events = false; + return new wxSocketImplUnix(wxsocket); } -/* - * Disallow further read/write operations on this socket, close - * the fd and disable all callbacks. - */ -void wxSocketImplUnix::Shutdown() -{ - /* Don't allow events to fire after socket has been closed */ - DisableEvents(); - wxSocketImpl::Shutdown(); -} - -/* - * Waits for an incoming client connection. Returns a pointer to - * a wxSocketImplUnix object, or NULL if there was an error, in which case - * the last error field will be updated for the calling wxSocketImplUnix. - * - * Error codes (set in the calling wxSocketImplUnix) - * wxSOCKET_INVSOCK - the socket is not valid or not a server. - * wxSOCKET_TIMEDOUT - timeout, no incoming connections. - * wxSOCKET_WOULDBLOCK - the call would block and the socket is nonblocking. - * wxSOCKET_MEMERR - couldn't allocate memory. - * wxSOCKET_IOERR - low-level error. - */ -wxSocketImpl *wxSocketImplUnix::WaitConnection(wxSocketBase& wxsocket) +wxSocketError wxSocketImplUnix::GetLastError() const { - wxSockAddr from; - WX_SOCKLEN_T fromlen = sizeof(from); - wxSocketImpl *connection; - wxSocketError err; - int arg = 1; - - /* If the socket has already been created, we exit immediately */ - if (m_fd == INVALID_SOCKET || !m_server) - { - m_error = wxSOCKET_INVSOCK; - return NULL; - } - - /* Create a wxSocketImplUnix object for the new connection */ - connection = wxSocketImplUnix::Create(wxsocket); - - if (!connection) - { - m_error = wxSOCKET_MEMERR; - return NULL; - } - - /* Wait for a connection (with timeout) */ - if (Input_Timeout() == wxSOCKET_TIMEDOUT) - { - delete connection; - /* m_error set by Input_Timeout */ - return NULL; - } - - connection->m_fd = accept(m_fd, (sockaddr*)&from, (WX_SOCKLEN_T *) &fromlen); - - /* Reenable CONNECTION events */ - EnableEvent(wxSOCKET_CONNECTION); - - if (connection->m_fd == INVALID_SOCKET) - { - if (errno == EWOULDBLOCK) - m_error = wxSOCKET_WOULDBLOCK; - else - m_error = wxSOCKET_IOERR; - - delete connection; - return NULL; - } - - /* Initialize all fields */ - connection->m_server = false; - connection->m_stream = true; - - /* Setup the peer address field */ - connection->m_peer = GAddress_new(); - if (!connection->m_peer) - { - delete connection; - m_error = wxSOCKET_MEMERR; - return NULL; - } - - err = _GAddress_translate_from(connection->m_peer, (sockaddr*)&from, fromlen); - if (err != wxSOCKET_NOERROR) - { - delete connection; - m_error = err; - return NULL; - } - -#if defined(__EMX__) || defined(__VISAGECPP__) - ioctl(connection->m_fd, FIONBIO, (char*)&arg, sizeof(arg)); -#else - ioctl(connection->m_fd, FIONBIO, &arg); -#endif - if (m_use_events) - connection->Notify(true); - - return connection; -} - -void wxSocketImplUnix::Notify(bool flag) -{ - if (flag == m_use_events) - return; - m_use_events = flag; - DoEnableEvents(flag); + switch ( errno ) + { + case 0: + return wxSOCKET_NOERROR; + + case ENOTSOCK: + return wxSOCKET_INVSOCK; + + // unfortunately EAGAIN only has the "would block" meaning for read(), + // not for connect() for which it means something rather different but + // we can't distinguish between these two situations currently... + // + // also notice that EWOULDBLOCK can be different from EAGAIN on some + // systems (HP-UX being the only known example) while it's defined as + // EAGAIN on most others (e.g. Linux) + case EAGAIN: +#ifdef EWOULDBLOCK + #if EWOULDBLOCK != EAGAIN + case EWOULDBLOCK: + #endif +#endif // EWOULDBLOCK + case EINPROGRESS: + return wxSOCKET_WOULDBLOCK; + + default: + return wxSOCKET_IOERR; + } } void wxSocketImplUnix::DoEnableEvents(bool flag) @@ -569,443 +457,25 @@ void wxSocketImplUnix::DoEnableEvents(bool flag) } } -wxSocketError wxSocketImplUnix::DoHandleConnect(int ret) -{ - /* We only call EnableEvents() if we know we aren't shutting down the socket. - * NB: EnableEvents() needs to be called whether the socket is blocking or - * non-blocking, it just shouldn't be called prior to knowing there is a - * connection _if_ blocking sockets are being used. - * If connect above returns 0, we are already connected and need to make the - * call to EnableEvents() now. - */ - if ( m_non_blocking || (ret == 0) ) - EnableEvents(); - - if (ret == -1) - { - const int err = errno; - - /* If connect failed with EINPROGRESS and the wxSocketImplUnix object - * is in blocking mode, we select() for the specified timeout - * checking for writability to see if the connection request - * completes. - */ - if ((err == EINPROGRESS) && (!m_non_blocking)) - { - if (Output_Timeout() == wxSOCKET_TIMEDOUT) - { - Close(); - /* m_error is set in Output_Timeout */ - return wxSOCKET_TIMEDOUT; - } - else - { - int error; - SOCKOPTLEN_T len = sizeof(error); - - getsockopt(m_fd, SOL_SOCKET, SO_ERROR, (char*) &error, &len); - EnableEvents(); - - if (!error) - return wxSOCKET_NOERROR; - } - } - - /* If connect failed with EINPROGRESS and the wxSocketImplUnix object - * is set to nonblocking, we set m_error to wxSOCKET_WOULDBLOCK - * (and return wxSOCKET_WOULDBLOCK) but we don't close the socket; - * this way if the connection completes, a wxSOCKET_CONNECTION - * event will be generated, if enabled. - */ - if ((err == EINPROGRESS) && (m_non_blocking)) - { - m_establishing = true; - m_error = wxSOCKET_WOULDBLOCK; - return wxSOCKET_WOULDBLOCK; - } - - /* If connect failed with an error other than EINPROGRESS, - * then the call to Connect has failed. - */ - Close(); - m_error = wxSOCKET_IOERR; - - return wxSOCKET_IOERR; - } - - return wxSOCKET_NOERROR; -} - -/* Generic IO */ - -/* Like recv(), send(), ... */ -int wxSocketImplUnix::Read(char *buffer, int size) -{ - int ret; - - if (m_fd == INVALID_SOCKET || m_server) - { - m_error = wxSOCKET_INVSOCK; - return -1; - } - - /* Disable events during query of socket status */ - DisableEvent(wxSOCKET_INPUT); - - /* If the socket is blocking, wait for data (with a timeout) */ - if (Input_Timeout() == wxSOCKET_TIMEDOUT) { - m_error = wxSOCKET_TIMEDOUT; - /* Don't return here immediately, otherwise socket events would not be - * re-enabled! */ - ret = -1; - } - else - { - /* Read the data */ - if (m_stream) - ret = Recv_Stream(buffer, size); - else - ret = Recv_Dgram(buffer, size); - - /* - * If recv returned zero for a TCP socket (if m_stream == NULL, it's an UDP - * socket and empty datagrams are possible), then the connection has been - * gracefully closed. - * - * Otherwise, recv has returned an error (-1), in which case we have lost - * the socket only if errno does _not_ indicate that there may be more data - * to read. - */ - if ((ret == 0) && m_stream) - { - /* Make sure wxSOCKET_LOST event gets sent and shut down the socket */ - if (m_use_events) - { - m_detected = wxSOCKET_LOST_FLAG; - Detected_Read(); - return 0; - } - } - else if (ret == -1) - { - if ((errno == EWOULDBLOCK) || (errno == EAGAIN)) - m_error = wxSOCKET_WOULDBLOCK; - else - m_error = wxSOCKET_IOERR; - } - } - - /* Enable events again now that we are done processing */ - EnableEvent(wxSOCKET_INPUT); - - return ret; -} - -int wxSocketImplUnix::Write(const char *buffer, int size) -{ - int ret; - - SOCKET_DEBUG(( "Write #1, size %d\n", size )); - - if (m_fd == INVALID_SOCKET || m_server) - { - m_error = wxSOCKET_INVSOCK; - return -1; - } - - SOCKET_DEBUG(( "Write #2, size %d\n", size )); - - /* If the socket is blocking, wait for writability (with a timeout) */ - if (Output_Timeout() == wxSOCKET_TIMEDOUT) - return -1; - - SOCKET_DEBUG(( "Write #3, size %d\n", size )); - - /* Write the data */ - if (m_stream) - ret = Send_Stream(buffer, size); - else - ret = Send_Dgram(buffer, size); - - SOCKET_DEBUG(( "Write #4, size %d\n", size )); - - if (ret == -1) - { - if ((errno == EWOULDBLOCK) || (errno == EAGAIN)) - { - m_error = wxSOCKET_WOULDBLOCK; - SOCKET_DEBUG(( "Write error WOULDBLOCK\n" )); - } - else - { - m_error = wxSOCKET_IOERR; - SOCKET_DEBUG(( "Write error IOERR\n" )); - } - - /* Only reenable OUTPUT events after an error (just like WSAAsyncSelect - * in MSW). Once the first OUTPUT event is received, users can assume - * that the socket is writable until a read operation fails. Only then - * will further OUTPUT events be posted. - */ - EnableEvent(wxSOCKET_OUTPUT); - - return -1; - } - - SOCKET_DEBUG(( "Write #5, size %d ret %d\n", size, ret )); - - return ret; -} - -/* Flags */ - -void wxSocketImplUnix::EnableEvent(wxSocketNotify event) -{ - if (m_use_events) - { - m_detected &= ~(1 << event); - wxSocketManager::Get()->Install_Callback(this, event); - } -} - -void wxSocketImplUnix::DisableEvent(wxSocketNotify event) -{ - if (m_use_events) - { - m_detected |= (1 << event); - wxSocketManager::Get()->Uninstall_Callback(this, event); - } -} - -/* - * For blocking sockets, wait until data is available or - * until timeout ellapses. - */ -wxSocketError wxSocketImplUnix::Input_Timeout() -{ - fd_set readfds; - int ret; - - // Linux select() will overwrite the struct on return so make a copy - struct timeval tv = m_timeout; - - if (!m_non_blocking) - { - wxFD_ZERO(&readfds); - wxFD_SET(m_fd, &readfds); - ret = select(m_fd + 1, &readfds, NULL, NULL, &tv); - if (ret == 0) - { - SOCKET_DEBUG(( "Input_Timeout, select returned 0\n" )); - m_error = wxSOCKET_TIMEDOUT; - return wxSOCKET_TIMEDOUT; - } - - if (ret == -1) - { - SOCKET_DEBUG(( "Input_Timeout, select returned -1\n" )); - if (errno == EBADF) { SOCKET_DEBUG(( "Invalid file descriptor\n" )); } - if (errno == EINTR) { SOCKET_DEBUG(( "A non blocked signal was caught\n" )); } - if (errno == EINVAL) { SOCKET_DEBUG(( "The highest number descriptor is negative\n" )); } - if (errno == ENOMEM) { SOCKET_DEBUG(( "Not enough memory\n" )); } - m_error = wxSOCKET_TIMEDOUT; - return wxSOCKET_TIMEDOUT; - } - } - - return wxSOCKET_NOERROR; -} - -/* - * For blocking sockets, wait until data can be sent without - * blocking or until timeout ellapses. - */ -wxSocketError wxSocketImplUnix::Output_Timeout() -{ - fd_set writefds; - int ret; - - // Linux select() will overwrite the struct on return so make a copy - struct timeval tv = m_timeout; - - SOCKET_DEBUG( ("m_non_blocking has: %d\n", (int)m_non_blocking) ); - - if (!m_non_blocking) - { - wxFD_ZERO(&writefds); - wxFD_SET(m_fd, &writefds); - ret = select(m_fd + 1, NULL, &writefds, NULL, &tv); - if (ret == 0) - { - SOCKET_DEBUG(( "Output_Timeout, select returned 0\n" )); - m_error = wxSOCKET_TIMEDOUT; - return wxSOCKET_TIMEDOUT; - } - - if (ret == -1) - { - SOCKET_DEBUG(( "Output_Timeout, select returned -1\n" )); - if (errno == EBADF) { SOCKET_DEBUG(( "Invalid file descriptor\n" )); } - if (errno == EINTR) { SOCKET_DEBUG(( "A non blocked signal was caught\n" )); } - if (errno == EINVAL) { SOCKET_DEBUG(( "The highest number descriptor is negative\n" )); } - if (errno == ENOMEM) { SOCKET_DEBUG(( "Not enough memory\n" )); } - m_error = wxSOCKET_TIMEDOUT; - return wxSOCKET_TIMEDOUT; - } - - if ( ! wxFD_ISSET(m_fd, &writefds) ) - { - SOCKET_DEBUG(( "Output_Timeout is buggy!\n" )); - } - else - { - SOCKET_DEBUG(( "Output_Timeout seems correct\n" )); - } - } - else - { - SOCKET_DEBUG(( "Output_Timeout, didn't try select!\n" )); - } - - return wxSOCKET_NOERROR; -} - -int wxSocketImplUnix::Recv_Stream(char *buffer, int size) -{ - int ret; - do - { - ret = recv(m_fd, buffer, size, GSOCKET_MSG_NOSIGNAL); - } - while (ret == -1 && errno == EINTR); /* Loop until not interrupted */ - - return ret; -} - -int wxSocketImplUnix::Recv_Dgram(char *buffer, int size) -{ - wxSockAddr from; - WX_SOCKLEN_T fromlen = sizeof(from); - int ret; - wxSocketError err; - - fromlen = sizeof(from); - - do - { - ret = recvfrom(m_fd, buffer, size, 0, (sockaddr*)&from, (WX_SOCKLEN_T *) &fromlen); - } - while (ret == -1 && errno == EINTR); /* Loop until not interrupted */ - - if (ret == -1) - return -1; - - /* Translate a system address into a wxSocketImplUnix address */ - if (!m_peer) - { - m_peer = GAddress_new(); - if (!m_peer) - { - m_error = wxSOCKET_MEMERR; - return -1; - } - } - - err = _GAddress_translate_from(m_peer, (sockaddr*)&from, fromlen); - if (err != wxSOCKET_NOERROR) - { - GAddress_destroy(m_peer); - m_peer = NULL; - m_error = err; - return -1; - } - - return ret; -} - -int wxSocketImplUnix::Send_Stream(const char *buffer, int size) -{ - int ret; - - MASK_SIGNAL(); - - do - { - ret = send(m_fd, (char *)buffer, size, GSOCKET_MSG_NOSIGNAL); - } - while (ret == -1 && errno == EINTR); /* Loop until not interrupted */ - - UNMASK_SIGNAL(); - - return ret; -} - -int wxSocketImplUnix::Send_Dgram(const char *buffer, int size) -{ - struct sockaddr *addr; - int len, ret; - wxSocketError err; - - if (!m_peer) - { - m_error = wxSOCKET_INVADDR; - return -1; - } - - err = _GAddress_translate_to(m_peer, &addr, &len); - if (err != wxSOCKET_NOERROR) - { - m_error = err; - return -1; - } - - MASK_SIGNAL(); - - do - { - ret = sendto(m_fd, (char *)buffer, size, 0, addr, len); - } - while (ret == -1 && errno == EINTR); /* Loop until not interrupted */ - - UNMASK_SIGNAL(); - - /* Frees memory allocated from _GAddress_translate_to */ - free(addr); - - return ret; -} void wxSocketImplUnix::OnStateChange(wxSocketNotify event) { - DisableEvent(event); NotifyOnStateChange(event); if ( event == wxSOCKET_LOST ) Shutdown(); } -void wxSocketImplUnix::Detected_Read() +void wxSocketImplUnix::OnReadWaiting() { char c; - /* Safeguard against straggling call to Detected_Read */ if (m_fd == INVALID_SOCKET) { return; } - /* If we have already detected a LOST event, then don't try - * to do any further processing. - */ - if ((m_detected & wxSOCKET_LOST_FLAG) != 0) - { - m_establishing = false; - - OnStateChange(wxSOCKET_LOST); - return; - } - - int num = recv(m_fd, &c, 1, MSG_PEEK | GSOCKET_MSG_NOSIGNAL); + int num = recv(m_fd, &c, 1, MSG_PEEK); if (num > 0) { @@ -1045,19 +515,8 @@ void wxSocketImplUnix::Detected_Read() } } -void wxSocketImplUnix::Detected_Write() +void wxSocketImplUnix::OnWriteWaiting() { - /* If we have already detected a LOST event, then don't try - * to do any further processing. - */ - if ((m_detected & wxSOCKET_LOST_FLAG) != 0) - { - m_establishing = false; - - OnStateChange(wxSOCKET_LOST); - return; - } - if (m_establishing && !m_server) { int error; @@ -1087,6 +546,11 @@ void wxSocketImplUnix::Detected_Write() } } +void wxSocketImplUnix::OnExceptionWaiting() +{ + wxFAIL_MSG( "not supposed to be called" ); +} + /* * ------------------------------------------------------------------------- * GAddress @@ -1686,4 +1150,5 @@ wxSocketError GAddress_UNIX_GetPath(GAddress *address, char *path, size_t sbuf) return wxSOCKET_NOERROR; } #endif /* !defined(__VISAGECPP__) */ + #endif /* wxUSE_SOCKETS */