#include "wx/private/fd.h"
#include "wx/private/socket.h"
#include "wx/unix/private/sockunix.h"
-#include "wx/private/gsocketiohandler.h"
#if defined(__VISAGECPP__)
#define BSD_SELECT /* use Berkeley Sockets select */
#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"
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)
}
}
-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)
{
}
}
-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;
}
}
+void wxSocketImplUnix::OnExceptionWaiting()
+{
+ wxFAIL_MSG( "not supposed to be called" );
+}
+
/*
* -------------------------------------------------------------------------
* GAddress
return wxSOCKET_NOERROR;
}
#endif /* !defined(__VISAGECPP__) */
+
#endif /* wxUSE_SOCKETS */