/*
|
/*
|
* socket.c -- Socket support module for UNIX
|
* socket.c -- Socket support module for UNIX
|
*
|
*
|
* Copyright (c) Go Ahead, 1995-1999
|
* Copyright (c) Go Ahead, 1995-1999
|
*/
|
*/
|
|
|
/******************************** Description *********************************/
|
/******************************** Description *********************************/
|
|
|
/*
|
/*
|
* SCO Unix Socket Module. This supports non-blocking buffered socket I/O.
|
* SCO Unix Socket Module. This supports non-blocking buffered socket I/O.
|
*/
|
*/
|
|
|
/********************************** Includes **********************************/
|
/********************************** Includes **********************************/
|
|
|
#include <errno.h>
|
#include <errno.h>
|
#include <fcntl.h>
|
#include <fcntl.h>
|
#include <string.h>
|
#include <string.h>
|
#include <stdlib.h>
|
#include <stdlib.h>
|
#include <unistd.h>
|
#include <unistd.h>
|
|
|
#if __rtems__
|
#if __rtems__
|
#include <sys/select.h>
|
#include <sys/select.h>
|
#endif
|
#endif
|
|
|
#include "uemf.h"
|
#include "uemf.h"
|
|
|
/*********************************** Defines **********************************/
|
/*********************************** Defines **********************************/
|
|
|
typedef struct {
|
typedef struct {
|
char host[64]; /* Host name */
|
char host[64]; /* Host name */
|
ringq_t inBuf; /* Input ring queue */
|
ringq_t inBuf; /* Input ring queue */
|
ringq_t outBuf; /* Output ring queue */
|
ringq_t outBuf; /* Output ring queue */
|
ringq_t lineBuf; /* Line ring queue */
|
ringq_t lineBuf; /* Line ring queue */
|
socketAccept_t accept; /* Accept handler */
|
socketAccept_t accept; /* Accept handler */
|
socketHandler_t handler; /* User I/O handler */
|
socketHandler_t handler; /* User I/O handler */
|
int handler_data; /* User handler data */
|
int handler_data; /* User handler data */
|
int sid; /* Index into socket[] */
|
int sid; /* Index into socket[] */
|
int port; /* Port to listen on */
|
int port; /* Port to listen on */
|
int flags; /* Current state flags */
|
int flags; /* Current state flags */
|
int readyMask; /* Events now ready */
|
int readyMask; /* Events now ready */
|
int interestMask; /* Events interest */
|
int interestMask; /* Events interest */
|
int error; /* Last error */
|
int error; /* Last error */
|
int sock; /* Actual socket handle */
|
int sock; /* Actual socket handle */
|
} socket_t;
|
} socket_t;
|
|
|
/************************************ Locals **********************************/
|
/************************************ Locals **********************************/
|
|
|
static socket_t** socketList; /* List of open sockets */
|
static socket_t** socketList; /* List of open sockets */
|
static int socketMax; /* Maximum size of socket */
|
static int socketMax; /* Maximum size of socket */
|
static int socketHighestFd = -1; /* Highest socket fd opened */
|
static int socketHighestFd = -1; /* Highest socket fd opened */
|
|
|
/***************************** Forward Declarations ***************************/
|
/***************************** Forward Declarations ***************************/
|
|
|
static int socketAlloc(char* host, int port, socketAccept_t accept, int flags);
|
static int socketAlloc(char* host, int port, socketAccept_t accept, int flags);
|
static void socketFree(int sid);
|
static void socketFree(int sid);
|
static void socketAccept(socket_t* sp);
|
static void socketAccept(socket_t* sp);
|
static int socketGetInput(int sid, char* buf, int toRead, int* errCode);
|
static int socketGetInput(int sid, char* buf, int toRead, int* errCode);
|
static int socketDoOutput(socket_t* sp, char* buf, int toWrite, int* errCode);
|
static int socketDoOutput(socket_t* sp, char* buf, int toWrite, int* errCode);
|
static int socketDoEvent(socket_t *sp);
|
static int socketDoEvent(socket_t *sp);
|
static int socketGetError();
|
static int socketGetError();
|
static int socketWaitForEvent(socket_t* sp, int events, int* errCode);
|
static int socketWaitForEvent(socket_t* sp, int events, int* errCode);
|
static int socketNonBlock(socket_t *sp);
|
static int socketNonBlock(socket_t *sp);
|
static socket_t* socketPtr(int sid);
|
static socket_t* socketPtr(int sid);
|
|
|
/*********************************** Code *************************************/
|
/*********************************** Code *************************************/
|
/*
|
/*
|
* Open socket module
|
* Open socket module
|
*/
|
*/
|
|
|
int socketOpen()
|
int socketOpen()
|
{
|
{
|
return 0;
|
return 0;
|
}
|
}
|
|
|
/******************************************************************************/
|
/******************************************************************************/
|
/*
|
/*
|
* Close the socket module, by closing all open connections
|
* Close the socket module, by closing all open connections
|
*/
|
*/
|
|
|
void socketClose()
|
void socketClose()
|
{
|
{
|
int i;
|
int i;
|
|
|
for (i = socketMax; i >= 0; i--) {
|
for (i = socketMax; i >= 0; i--) {
|
if (socketList && socketList[i]) {
|
if (socketList && socketList[i]) {
|
socketCloseConnection(i);
|
socketCloseConnection(i);
|
}
|
}
|
}
|
}
|
}
|
}
|
|
|
/******************************************************************************/
|
/******************************************************************************/
|
/*
|
/*
|
* Open a client or server socket. Host is NULL if we want server capability.
|
* Open a client or server socket. Host is NULL if we want server capability.
|
*/
|
*/
|
|
|
int socketOpenConnection(char* host, int port, socketAccept_t accept, int flags)
|
int socketOpenConnection(char* host, int port, socketAccept_t accept, int flags)
|
{
|
{
|
socket_t *sp;
|
socket_t *sp;
|
struct sockaddr_in sockaddr;
|
struct sockaddr_in sockaddr;
|
struct hostent *hostent; /* Host database entry */
|
struct hostent *hostent; /* Host database entry */
|
int sid, rc;
|
int sid, rc;
|
|
|
/*
|
/*
|
* Allocate a socket structure
|
* Allocate a socket structure
|
*/
|
*/
|
if ((sid = socketAlloc(host, port, accept, flags)) < 0) {
|
if ((sid = socketAlloc(host, port, accept, flags)) < 0) {
|
return -1;
|
return -1;
|
}
|
}
|
sp = socketList[sid];
|
sp = socketList[sid];
|
a_assert(sp);
|
a_assert(sp);
|
|
|
/*
|
/*
|
* Create the socket address structure
|
* Create the socket address structure
|
*/
|
*/
|
memset((char *) &sockaddr, '\0', sizeof(struct sockaddr_in));
|
memset((char *) &sockaddr, '\0', sizeof(struct sockaddr_in));
|
sockaddr.sin_family = AF_INET;
|
sockaddr.sin_family = AF_INET;
|
sockaddr.sin_port = htons((short) (port & 0xFFFF));
|
sockaddr.sin_port = htons((short) (port & 0xFFFF));
|
|
|
if (host == NULL) {
|
if (host == NULL) {
|
sockaddr.sin_addr.s_addr = INADDR_ANY;
|
sockaddr.sin_addr.s_addr = INADDR_ANY;
|
} else {
|
} else {
|
sockaddr.sin_addr.s_addr = inet_addr(host);
|
sockaddr.sin_addr.s_addr = inet_addr(host);
|
if (sockaddr.sin_addr.s_addr == INADDR_NONE) {
|
if (sockaddr.sin_addr.s_addr == INADDR_NONE) {
|
hostent = gethostbyname(host);
|
hostent = gethostbyname(host);
|
if (hostent != NULL) {
|
if (hostent != NULL) {
|
memcpy((char *) &sockaddr.sin_addr,
|
memcpy((char *) &sockaddr.sin_addr,
|
(char *) hostent->h_addr_list[0],
|
(char *) hostent->h_addr_list[0],
|
(size_t) hostent->h_length);
|
(size_t) hostent->h_length);
|
} else {
|
} else {
|
errno = ENXIO;
|
errno = ENXIO;
|
socketFree(sid);
|
socketFree(sid);
|
return -1;
|
return -1;
|
}
|
}
|
}
|
}
|
}
|
}
|
|
|
/*
|
/*
|
* Create the socket. Set the close on exec flag so children don't
|
* Create the socket. Set the close on exec flag so children don't
|
* inherit the socket.
|
* inherit the socket.
|
*/
|
*/
|
sp->sock = socket(AF_INET, SOCK_STREAM, 0);
|
sp->sock = socket(AF_INET, SOCK_STREAM, 0);
|
if (sp->sock < 0) {
|
if (sp->sock < 0) {
|
socketFree(sid);
|
socketFree(sid);
|
return -1;
|
return -1;
|
}
|
}
|
fcntl(sp->sock, F_SETFD, FD_CLOEXEC);
|
fcntl(sp->sock, F_SETFD, FD_CLOEXEC);
|
socketHighestFd = max(socketHighestFd, sp->sock);
|
socketHighestFd = max(socketHighestFd, sp->sock);
|
|
|
/*
|
/*
|
* Host is set if we are the client
|
* Host is set if we are the client
|
*/
|
*/
|
if (host) {
|
if (host) {
|
/*
|
/*
|
* Connect to the remote server
|
* Connect to the remote server
|
*/
|
*/
|
if (connect(sp->sock, (struct sockaddr *) &sockaddr,
|
if (connect(sp->sock, (struct sockaddr *) &sockaddr,
|
sizeof(sockaddr)) < 0) {
|
sizeof(sockaddr)) < 0) {
|
socketFree(sid);
|
socketFree(sid);
|
return -1;
|
return -1;
|
}
|
}
|
socketNonBlock(sp);
|
socketNonBlock(sp);
|
|
|
} else {
|
} else {
|
/*
|
/*
|
* Bind to the socket endpoint with resule and the call listen()
|
* Bind to the socket endpoint with resule and the call listen()
|
** to start listening
|
** to start listening
|
*/
|
*/
|
rc = 1;
|
rc = 1;
|
setsockopt(sp->sock, SOL_SOCKET, SO_REUSEADDR, (char *)&rc, sizeof(rc));
|
setsockopt(sp->sock, SOL_SOCKET, SO_REUSEADDR, (char *)&rc, sizeof(rc));
|
if (bind(sp->sock, (struct sockaddr *) &sockaddr, sizeof(sockaddr))
|
if (bind(sp->sock, (struct sockaddr *) &sockaddr, sizeof(sockaddr))
|
< 0) {
|
< 0) {
|
socketFree(sid);
|
socketFree(sid);
|
return -1;
|
return -1;
|
}
|
}
|
sp->flags |= SOCKET_LISTENING;
|
sp->flags |= SOCKET_LISTENING;
|
|
|
if (listen(sp->sock, SOMAXCONN) < 0) {
|
if (listen(sp->sock, SOMAXCONN) < 0) {
|
socketFree(sid);
|
socketFree(sid);
|
return -1;
|
return -1;
|
}
|
}
|
sp->interestMask = SOCKET_READABLE;
|
sp->interestMask = SOCKET_READABLE;
|
}
|
}
|
return sid;
|
return sid;
|
}
|
}
|
|
|
/******************************************************************************/
|
/******************************************************************************/
|
/*
|
/*
|
* Close a socket
|
* Close a socket
|
*/
|
*/
|
|
|
void socketCloseConnection(int sid)
|
void socketCloseConnection(int sid)
|
{
|
{
|
socket_t* sp;
|
socket_t* sp;
|
|
|
if ((sp = socketPtr(sid)) == NULL) {
|
if ((sp = socketPtr(sid)) == NULL) {
|
return;
|
return;
|
}
|
}
|
|
|
/*
|
/*
|
* We always flush all output before closing. Unlink from the emf event
|
* We always flush all output before closing. Unlink from the emf event
|
* mechanism and then free (and close) the connection
|
* mechanism and then free (and close) the connection
|
*/
|
*/
|
socketFlush(sid, 1);
|
socketFlush(sid, 1);
|
socketFree(sid);
|
socketFree(sid);
|
}
|
}
|
|
|
/******************************************************************************/
|
/******************************************************************************/
|
/*
|
/*
|
* Accept a connection. Called by socketDoEvent
|
* Accept a connection. Called by socketDoEvent
|
*/
|
*/
|
|
|
static void socketAccept(socket_t* sp)
|
static void socketAccept(socket_t* sp)
|
{
|
{
|
struct sockaddr_in addr;
|
struct sockaddr_in addr;
|
socket_t *nsp;
|
socket_t *nsp;
|
int len;
|
int len;
|
int newSock, nid;
|
int newSock, nid;
|
|
|
a_assert(sp);
|
a_assert(sp);
|
|
|
/*
|
/*
|
* Accept the connection and prevent inheriting by children (F_SETFD)
|
* Accept the connection and prevent inheriting by children (F_SETFD)
|
*/
|
*/
|
len = sizeof(struct sockaddr_in);
|
len = sizeof(struct sockaddr_in);
|
if ((newSock = accept(sp->sock, (struct sockaddr *) &addr, &len)) < 0) {
|
if ((newSock = accept(sp->sock, (struct sockaddr *) &addr, &len)) < 0) {
|
return;
|
return;
|
}
|
}
|
fcntl(newSock, F_SETFD, FD_CLOEXEC);
|
fcntl(newSock, F_SETFD, FD_CLOEXEC);
|
socketHighestFd = max(socketHighestFd, newSock);
|
socketHighestFd = max(socketHighestFd, newSock);
|
|
|
/*
|
/*
|
* Create a socket structure and insert into the socket list
|
* Create a socket structure and insert into the socket list
|
*/
|
*/
|
nid = socketAlloc(sp->host, sp->port, sp->accept, 0);
|
nid = socketAlloc(sp->host, sp->port, sp->accept, 0);
|
nsp = socketList[nid];
|
nsp = socketList[nid];
|
a_assert(nsp);
|
a_assert(nsp);
|
nsp->sock = newSock;
|
nsp->sock = newSock;
|
|
|
if (nsp == NULL) {
|
if (nsp == NULL) {
|
return;
|
return;
|
}
|
}
|
/*
|
/*
|
* Call the user accept callback, the user must call socketCreateHandler
|
* Call the user accept callback, the user must call socketCreateHandler
|
* to register for further events of interest.
|
* to register for further events of interest.
|
*/
|
*/
|
if (sp->accept != NULL) {
|
if (sp->accept != NULL) {
|
if ((sp->accept)(nid, inet_ntoa(addr.sin_addr),
|
if ((sp->accept)(nid, inet_ntoa(addr.sin_addr),
|
ntohs(addr.sin_port)) < 0) {
|
ntohs(addr.sin_port)) < 0) {
|
socketFree(nid);
|
socketFree(nid);
|
return;
|
return;
|
}
|
}
|
}
|
}
|
socketNonBlock(nsp);
|
socketNonBlock(nsp);
|
}
|
}
|
|
|
/******************************************************************************/
|
/******************************************************************************/
|
/*
|
/*
|
* Write to a socket. This may block if the underlying socket cannot
|
* Write to a socket. This may block if the underlying socket cannot
|
* absorb the data. Returns -1 on error, otherwise the number of bytes
|
* absorb the data. Returns -1 on error, otherwise the number of bytes
|
* written.
|
* written.
|
*/
|
*/
|
|
|
int socketWrite(int sid, char* buf, int bufsize)
|
int socketWrite(int sid, char* buf, int bufsize)
|
{
|
{
|
socket_t* sp;
|
socket_t* sp;
|
ringq_t* rq;
|
ringq_t* rq;
|
int len, bytesWritten, room;
|
int len, bytesWritten, room;
|
|
|
a_assert(buf);
|
a_assert(buf);
|
a_assert(bufsize >= 0);
|
a_assert(bufsize >= 0);
|
|
|
if ((sp = socketPtr(sid)) == NULL) {
|
if ((sp = socketPtr(sid)) == NULL) {
|
return -1;
|
return -1;
|
}
|
}
|
|
|
/*
|
/*
|
* Loop adding as much data to the output ringq as we can absorb
|
* Loop adding as much data to the output ringq as we can absorb
|
* Flush when the ringq is too full and continue.
|
* Flush when the ringq is too full and continue.
|
*/
|
*/
|
rq = &sp->outBuf;
|
rq = &sp->outBuf;
|
for (bytesWritten = 0; bufsize > 0; ) {
|
for (bytesWritten = 0; bufsize > 0; ) {
|
if ((room = ringqPutBlkMax(rq)) == 0) {
|
if ((room = ringqPutBlkMax(rq)) == 0) {
|
if (socketFlush(sid, 0) < 0) {
|
if (socketFlush(sid, 0) < 0) {
|
return -1;
|
return -1;
|
}
|
}
|
if ((room = ringqPutBlkMax(rq)) == 0) {
|
if ((room = ringqPutBlkMax(rq)) == 0) {
|
break;
|
break;
|
}
|
}
|
continue;
|
continue;
|
}
|
}
|
len = min(room, bufsize);
|
len = min(room, bufsize);
|
ringqPutBlk(rq, (unsigned char*) buf, len);
|
ringqPutBlk(rq, (unsigned char*) buf, len);
|
bytesWritten += len;
|
bytesWritten += len;
|
bufsize -= len;
|
bufsize -= len;
|
buf += len;
|
buf += len;
|
}
|
}
|
return bytesWritten;
|
return bytesWritten;
|
}
|
}
|
|
|
/******************************************************************************/
|
/******************************************************************************/
|
/*
|
/*
|
* Read from a socket. Return the number of bytes read if successful. This
|
* Read from a socket. Return the number of bytes read if successful. This
|
* may be less than the requested "bufsize" and may be zero. Return -1 for
|
* may be less than the requested "bufsize" and may be zero. Return -1 for
|
* errors. Return 0 for EOF. Otherwise return the number of bytes read. Since
|
* errors. Return 0 for EOF. Otherwise return the number of bytes read. Since
|
* this may be zero, callers should use socketEof() to distinguish between
|
* this may be zero, callers should use socketEof() to distinguish between
|
* this and EOF. Note: this ignores the line buffer, so a previous socketGets
|
* this and EOF. Note: this ignores the line buffer, so a previous socketGets
|
* which read a partial line may cause a subsequent socketRead to miss
|
* which read a partial line may cause a subsequent socketRead to miss
|
* some data.
|
* some data.
|
*/
|
*/
|
|
|
int socketRead(int sid, char* buf, int bufsize)
|
int socketRead(int sid, char* buf, int bufsize)
|
{
|
{
|
socket_t* sp;
|
socket_t* sp;
|
ringq_t* rq;
|
ringq_t* rq;
|
int len, room, errCode, bytesRead;
|
int len, room, errCode, bytesRead;
|
|
|
a_assert(buf);
|
a_assert(buf);
|
a_assert(bufsize > 0);
|
a_assert(bufsize > 0);
|
|
|
if ((sp = socketPtr(sid)) == NULL) {
|
if ((sp = socketPtr(sid)) == NULL) {
|
return -1;
|
return -1;
|
}
|
}
|
|
|
if (sp->flags & SOCKET_EOF) {
|
if (sp->flags & SOCKET_EOF) {
|
return 0;
|
return 0;
|
}
|
}
|
|
|
rq = &sp->inBuf;
|
rq = &sp->inBuf;
|
for (bytesRead = 0; bufsize > 0; ) {
|
for (bytesRead = 0; bufsize > 0; ) {
|
len = min(ringqLen(rq), bufsize);
|
len = min(ringqLen(rq), bufsize);
|
if (len <= 0) {
|
if (len <= 0) {
|
room = ringqPutBlkMax(rq);
|
room = ringqPutBlkMax(rq);
|
len = socketGetInput(sid, (char*) rq->endp, room, &errCode);
|
len = socketGetInput(sid, (char*) rq->endp, room, &errCode);
|
if (len < 0) {
|
if (len < 0) {
|
if (errCode == EWOULDBLOCK) {
|
if (errCode == EWOULDBLOCK) {
|
if (bytesRead >= 0) {
|
if (bytesRead >= 0) {
|
return bytesRead;
|
return bytesRead;
|
}
|
}
|
}
|
}
|
return -1;
|
return -1;
|
|
|
} else if (len == 0) {
|
} else if (len == 0) {
|
/*
|
/*
|
* This is EOF, but we may have already read some data so pass that
|
* This is EOF, but we may have already read some data so pass that
|
* back first before notifying EOF. The next read will return 0
|
* back first before notifying EOF. The next read will return 0
|
* to indicate EOF.
|
* to indicate EOF.
|
*/
|
*/
|
return bytesRead;
|
return bytesRead;
|
}
|
}
|
ringqPutBlkAdj(rq, len);
|
ringqPutBlkAdj(rq, len);
|
len = min(len, bufsize);
|
len = min(len, bufsize);
|
}
|
}
|
memcpy(&buf[bytesRead], rq->servp, len);
|
memcpy(&buf[bytesRead], rq->servp, len);
|
ringqGetBlkAdj(rq, len);
|
ringqGetBlkAdj(rq, len);
|
bufsize -= len;
|
bufsize -= len;
|
bytesRead += len;
|
bytesRead += len;
|
}
|
}
|
return bytesRead;
|
return bytesRead;
|
}
|
}
|
|
|
/******************************************************************************/
|
/******************************************************************************/
|
/*
|
/*
|
* Get a string from a socket. This returns data in *buf in a malloced string
|
* Get a string from a socket. This returns data in *buf in a malloced string
|
* after trimming the '\n'. If there is zero bytes returned, *buf will be set
|
* after trimming the '\n'. If there is zero bytes returned, *buf will be set
|
* to NULL. It returns -1 for error, EOF or when no complete line yet read.
|
* to NULL. It returns -1 for error, EOF or when no complete line yet read.
|
* Otherwise the length of the line is returned. If a partial line is read
|
* Otherwise the length of the line is returned. If a partial line is read
|
* socketInputBuffered or socketEof can be used to distinguish between EOF
|
* socketInputBuffered or socketEof can be used to distinguish between EOF
|
* and partial line still buffered. This routine eats and ignores carriage
|
* and partial line still buffered. This routine eats and ignores carriage
|
* returns.
|
* returns.
|
*/
|
*/
|
|
|
int socketGets(int sid, char** buf)
|
int socketGets(int sid, char** buf)
|
{
|
{
|
socket_t* sp;
|
socket_t* sp;
|
ringq_t* lq;
|
ringq_t* lq;
|
char c;
|
char c;
|
int rc, len;
|
int rc, len;
|
|
|
a_assert(buf);
|
a_assert(buf);
|
|
|
if ((sp = socketPtr(sid)) == NULL) {
|
if ((sp = socketPtr(sid)) == NULL) {
|
return -1;
|
return -1;
|
}
|
}
|
lq = &sp->lineBuf;
|
lq = &sp->lineBuf;
|
|
|
while (1) {
|
while (1) {
|
|
|
if ((rc = socketRead(sid, &c, 1)) < 0) {
|
if ((rc = socketRead(sid, &c, 1)) < 0) {
|
return rc;
|
return rc;
|
|
|
} else if (rc == 0) {
|
} else if (rc == 0) {
|
/*
|
/*
|
* If there is a partial line and we are at EOF, pretend we saw a '\n'
|
* If there is a partial line and we are at EOF, pretend we saw a '\n'
|
*/
|
*/
|
if (ringqLen(lq) > 0 && (sp->flags & SOCKET_EOF)) {
|
if (ringqLen(lq) > 0 && (sp->flags & SOCKET_EOF)) {
|
c = '\n';
|
c = '\n';
|
} else {
|
} else {
|
return -1;
|
return -1;
|
}
|
}
|
}
|
}
|
/*
|
/*
|
* If a newline is seen, return the data excluding the new line to the
|
* If a newline is seen, return the data excluding the new line to the
|
* caller. If carriage return is seen, just eat it.
|
* caller. If carriage return is seen, just eat it.
|
*/
|
*/
|
if (c == '\n') {
|
if (c == '\n') {
|
len = ringqLen(lq);
|
len = ringqLen(lq);
|
if (len > 0) {
|
if (len > 0) {
|
if ((*buf = balloc(B_L, len + 1)) == NULL) {
|
if ((*buf = balloc(B_L, len + 1)) == NULL) {
|
return -1;
|
return -1;
|
}
|
}
|
memset(*buf, 0, len + 1);
|
memset(*buf, 0, len + 1);
|
ringqGetBlk(lq, (unsigned char*) *buf, len);
|
ringqGetBlk(lq, (unsigned char*) *buf, len);
|
} else {
|
} else {
|
*buf = NULL;
|
*buf = NULL;
|
}
|
}
|
return len;
|
return len;
|
|
|
} else if (c == '\r') {
|
} else if (c == '\r') {
|
continue;
|
continue;
|
}
|
}
|
ringqPutc(lq, c);
|
ringqPutc(lq, c);
|
}
|
}
|
}
|
}
|
|
|
/******************************************************************************/
|
/******************************************************************************/
|
/*
|
/*
|
* Flush a socket. Do not wait, just initiate output and return.
|
* Flush a socket. Do not wait, just initiate output and return.
|
* This will return -1 on errors and 0 if successful.
|
* This will return -1 on errors and 0 if successful.
|
*/
|
*/
|
|
|
int socketFlush(int sid, int block)
|
int socketFlush(int sid, int block)
|
{
|
{
|
socket_t* sp;
|
socket_t* sp;
|
ringq_t* rq;
|
ringq_t* rq;
|
int len, bytesWritten, errCode;
|
int len, bytesWritten, errCode;
|
|
|
a_assert(block == 0 || block == 1);
|
a_assert(block == 0 || block == 1);
|
|
|
if ((sp = socketPtr(sid)) == NULL) {
|
if ((sp = socketPtr(sid)) == NULL) {
|
return -1;
|
return -1;
|
}
|
}
|
rq = &sp->outBuf;
|
rq = &sp->outBuf;
|
|
|
/*
|
/*
|
* Set the background flushing flag which socketDoEvent will check to
|
* Set the background flushing flag which socketDoEvent will check to
|
* continue the flush.
|
* continue the flush.
|
*/
|
*/
|
if (!block) {
|
if (!block) {
|
sp->flags |= SOCKET_FLUSHING;
|
sp->flags |= SOCKET_FLUSHING;
|
}
|
}
|
|
|
/*
|
/*
|
* Break from loop if not blocking after initiating output. If we are blocking
|
* Break from loop if not blocking after initiating output. If we are blocking
|
* we wait for a write event.
|
* we wait for a write event.
|
*/
|
*/
|
while (ringqLen(rq) > 0) {
|
while (ringqLen(rq) > 0) {
|
len = ringqGetBlkMax(&sp->outBuf);
|
len = ringqGetBlkMax(&sp->outBuf);
|
bytesWritten = socketDoOutput(sp, (char*) rq->servp, len, &errCode);
|
bytesWritten = socketDoOutput(sp, (char*) rq->servp, len, &errCode);
|
if (bytesWritten < 0) {
|
if (bytesWritten < 0) {
|
if (errCode == EINTR) {
|
if (errCode == EINTR) {
|
continue;
|
continue;
|
} else if (errCode == EWOULDBLOCK || errCode == EAGAIN) {
|
} else if (errCode == EWOULDBLOCK || errCode == EAGAIN) {
|
if (! block) {
|
if (! block) {
|
return 0;
|
return 0;
|
}
|
}
|
if (socketWaitForEvent(sp, SOCKET_WRITABLE | SOCKET_EXCEPTION,
|
if (socketWaitForEvent(sp, SOCKET_WRITABLE | SOCKET_EXCEPTION,
|
&errCode)) {
|
&errCode)) {
|
continue;
|
continue;
|
}
|
}
|
}
|
}
|
return -1;
|
return -1;
|
}
|
}
|
ringqGetBlkAdj(rq, bytesWritten);
|
ringqGetBlkAdj(rq, bytesWritten);
|
if (! block) {
|
if (! block) {
|
break;
|
break;
|
}
|
}
|
}
|
}
|
return 0;
|
return 0;
|
}
|
}
|
|
|
/******************************************************************************/
|
/******************************************************************************/
|
/*
|
/*
|
* Return the count of input characters buffered. We look at both the line
|
* Return the count of input characters buffered. We look at both the line
|
* buffer and the input (raw) buffer. Return -1 on error or EOF.
|
* buffer and the input (raw) buffer. Return -1 on error or EOF.
|
*/
|
*/
|
|
|
int socketInputBuffered(int sid)
|
int socketInputBuffered(int sid)
|
{
|
{
|
socket_t* sp;
|
socket_t* sp;
|
|
|
if ((sp = socketPtr(sid)) == NULL) {
|
if ((sp = socketPtr(sid)) == NULL) {
|
return -1;
|
return -1;
|
}
|
}
|
if (socketEof(sid)) {
|
if (socketEof(sid)) {
|
return -1;
|
return -1;
|
}
|
}
|
return ringqLen(&sp->lineBuf) + ringqLen(&sp->inBuf);
|
return ringqLen(&sp->lineBuf) + ringqLen(&sp->inBuf);
|
}
|
}
|
|
|
/******************************************************************************/
|
/******************************************************************************/
|
/*
|
/*
|
* Return true if EOF
|
* Return true if EOF
|
*/
|
*/
|
|
|
int socketEof(int sid)
|
int socketEof(int sid)
|
{
|
{
|
socket_t* sp;
|
socket_t* sp;
|
|
|
if ((sp = socketPtr(sid)) == NULL) {
|
if ((sp = socketPtr(sid)) == NULL) {
|
return -1;
|
return -1;
|
}
|
}
|
return sp->flags & SOCKET_EOF;
|
return sp->flags & SOCKET_EOF;
|
}
|
}
|
|
|
/******************************************************************************/
|
/******************************************************************************/
|
/*
|
/*
|
* Create a user handler for this socket. The handler called whenever there
|
* Create a user handler for this socket. The handler called whenever there
|
* is an event of interest as defined by interestMask (SOCKET_READABLE, ...)
|
* is an event of interest as defined by interestMask (SOCKET_READABLE, ...)
|
*/
|
*/
|
|
|
void socketCreateHandler(int sid, int interestMask, socketHandler_t handler,
|
void socketCreateHandler(int sid, int interestMask, socketHandler_t handler,
|
int data)
|
int data)
|
{
|
{
|
socket_t* sp;
|
socket_t* sp;
|
|
|
if ((sp = socketPtr(sid)) == NULL) {
|
if ((sp = socketPtr(sid)) == NULL) {
|
return;
|
return;
|
}
|
}
|
sp->handler = handler;
|
sp->handler = handler;
|
sp->handler_data = data;
|
sp->handler_data = data;
|
sp->interestMask = interestMask;
|
sp->interestMask = interestMask;
|
}
|
}
|
|
|
/******************************************************************************/
|
/******************************************************************************/
|
/*
|
/*
|
* Delete a handler
|
* Delete a handler
|
*/
|
*/
|
|
|
void socketDeleteHandler(int sid)
|
void socketDeleteHandler(int sid)
|
{
|
{
|
socket_t* sp;
|
socket_t* sp;
|
|
|
if ((sp = socketPtr(sid)) == NULL) {
|
if ((sp = socketPtr(sid)) == NULL) {
|
return;
|
return;
|
}
|
}
|
sp->handler = NULL;
|
sp->handler = NULL;
|
sp->interestMask = 0;
|
sp->interestMask = 0;
|
}
|
}
|
|
|
/******************************************************************************/
|
/******************************************************************************/
|
/*
|
/*
|
* Get more input from the socket and return in buf.
|
* Get more input from the socket and return in buf.
|
* Returns 0 for EOF, -1 for errors and otherwise the number of bytes read.
|
* Returns 0 for EOF, -1 for errors and otherwise the number of bytes read.
|
*/
|
*/
|
|
|
static int socketGetInput(int sid, char* buf, int toRead, int* errCode)
|
static int socketGetInput(int sid, char* buf, int toRead, int* errCode)
|
{
|
{
|
struct sockaddr_in server;
|
struct sockaddr_in server;
|
socket_t* sp;
|
socket_t* sp;
|
int len, bytesRead;
|
int len, bytesRead;
|
|
|
a_assert(buf);
|
a_assert(buf);
|
a_assert(errCode);
|
a_assert(errCode);
|
|
|
*errCode = 0;
|
*errCode = 0;
|
|
|
if ((sp = socketPtr(sid)) == NULL) {
|
if ((sp = socketPtr(sid)) == NULL) {
|
return -1;
|
return -1;
|
}
|
}
|
|
|
/*
|
/*
|
* If we have previously seen an EOF condition, then just return
|
* If we have previously seen an EOF condition, then just return
|
*/
|
*/
|
if (sp->flags & SOCKET_EOF) {
|
if (sp->flags & SOCKET_EOF) {
|
return 0;
|
return 0;
|
}
|
}
|
|
|
/*
|
/*
|
* Read the data
|
* Read the data
|
*/
|
*/
|
if (sp->flags & SOCKET_BROADCAST) {
|
if (sp->flags & SOCKET_BROADCAST) {
|
server.sin_family = AF_INET;
|
server.sin_family = AF_INET;
|
server.sin_addr.s_addr = INADDR_BROADCAST;
|
server.sin_addr.s_addr = INADDR_BROADCAST;
|
server.sin_port = htons((short)(sp->port & 0xFFFF));
|
server.sin_port = htons((short)(sp->port & 0xFFFF));
|
len = sizeof(server);
|
len = sizeof(server);
|
bytesRead = recvfrom(sp->sock, buf, toRead, 0,
|
bytesRead = recvfrom(sp->sock, buf, toRead, 0,
|
(struct sockaddr*) &server, &len);
|
(struct sockaddr*) &server, &len);
|
} else {
|
} else {
|
bytesRead = recv(sp->sock, buf, toRead, 0);
|
bytesRead = recv(sp->sock, buf, toRead, 0);
|
}
|
}
|
|
|
if (bytesRead < 0) {
|
if (bytesRead < 0) {
|
if (errno == ECONNRESET) {
|
if (errno == ECONNRESET) {
|
return 0;
|
return 0;
|
}
|
}
|
*errCode = socketGetError();
|
*errCode = socketGetError();
|
return -1;
|
return -1;
|
|
|
} else if (bytesRead == 0) {
|
} else if (bytesRead == 0) {
|
sp->flags |= SOCKET_EOF;
|
sp->flags |= SOCKET_EOF;
|
}
|
}
|
return bytesRead;
|
return bytesRead;
|
}
|
}
|
|
|
/******************************************************************************/
|
/******************************************************************************/
|
/*
|
/*
|
* Socket output procedure. Return -1 on errors otherwise return the number
|
* Socket output procedure. Return -1 on errors otherwise return the number
|
* of bytes written.
|
* of bytes written.
|
*/
|
*/
|
|
|
static int socketDoOutput(socket_t* sp, char* buf, int toWrite, int* errCode)
|
static int socketDoOutput(socket_t* sp, char* buf, int toWrite, int* errCode)
|
{
|
{
|
struct sockaddr_in server;
|
struct sockaddr_in server;
|
int bytes;
|
int bytes;
|
|
|
a_assert(sp);
|
a_assert(sp);
|
a_assert(buf);
|
a_assert(buf);
|
a_assert(toWrite > 0);
|
a_assert(toWrite > 0);
|
a_assert(errCode);
|
a_assert(errCode);
|
|
|
*errCode = 0;
|
*errCode = 0;
|
|
|
/*
|
/*
|
* Write the data
|
* Write the data
|
*/
|
*/
|
if (sp->flags & SOCKET_BROADCAST) {
|
if (sp->flags & SOCKET_BROADCAST) {
|
server.sin_family = AF_INET;
|
server.sin_family = AF_INET;
|
server.sin_addr.s_addr = INADDR_BROADCAST;
|
server.sin_addr.s_addr = INADDR_BROADCAST;
|
server.sin_port = htons((short)(sp->port & 0xFFFF));
|
server.sin_port = htons((short)(sp->port & 0xFFFF));
|
bytes = sendto(sp->sock, buf, toWrite, 0,
|
bytes = sendto(sp->sock, buf, toWrite, 0,
|
(struct sockaddr*) &server, sizeof(server));
|
(struct sockaddr*) &server, sizeof(server));
|
} else {
|
} else {
|
bytes = send(sp->sock, buf, toWrite, 0);
|
bytes = send(sp->sock, buf, toWrite, 0);
|
}
|
}
|
|
|
if (bytes == 0 && bytes != toWrite) {
|
if (bytes == 0 && bytes != toWrite) {
|
*errCode = EWOULDBLOCK;
|
*errCode = EWOULDBLOCK;
|
return -1;
|
return -1;
|
}
|
}
|
|
|
if (bytes < 0) {
|
if (bytes < 0) {
|
*errCode = socketGetError();
|
*errCode = socketGetError();
|
}
|
}
|
return bytes;
|
return bytes;
|
}
|
}
|
|
|
/******************************************************************************/
|
/******************************************************************************/
|
/*
|
/*
|
* Return TRUE if there is a socket with an event ready to process,
|
* Return TRUE if there is a socket with an event ready to process,
|
*/
|
*/
|
|
|
int socketReady()
|
int socketReady()
|
{
|
{
|
socket_t *sp;
|
socket_t *sp;
|
int i;
|
int i;
|
|
|
for (i = 0; i < socketMax; i++) {
|
for (i = 0; i < socketMax; i++) {
|
if ((sp = socketList[i]) == NULL) {
|
if ((sp = socketList[i]) == NULL) {
|
continue;
|
continue;
|
}
|
}
|
if (sp->readyMask & sp->interestMask) {
|
if (sp->readyMask & sp->interestMask) {
|
return 1;
|
return 1;
|
}
|
}
|
}
|
}
|
return 0;
|
return 0;
|
}
|
}
|
|
|
/******************************************************************************/
|
/******************************************************************************/
|
/*
|
/*
|
* Wait for a handle to become readable or writable and return a number of
|
* Wait for a handle to become readable or writable and return a number of
|
* noticed events.
|
* noticed events.
|
*/
|
*/
|
|
|
int socketSelect()
|
int socketSelect()
|
{
|
{
|
socket_t *sp;
|
socket_t *sp;
|
fd_mask *readFds, *writeFds, *exceptFds;
|
fd_mask *readFds, *writeFds, *exceptFds;
|
int sid, len, nwords, index, bit, nEvents;
|
int sid, len, nwords, index, bit, nEvents;
|
|
|
/*
|
/*
|
* Allocate and zero the select masks
|
* Allocate and zero the select masks
|
*/
|
*/
|
nwords = (socketHighestFd + NFDBITS - 1) / NFDBITS;
|
nwords = (socketHighestFd + NFDBITS - 1) / NFDBITS;
|
len = nwords * sizeof(int);
|
len = nwords * sizeof(int);
|
|
|
readFds = balloc(B_L, len);
|
readFds = balloc(B_L, len);
|
memset(readFds, 0, len);
|
memset(readFds, 0, len);
|
writeFds = balloc(B_L, len);
|
writeFds = balloc(B_L, len);
|
memset(writeFds, 0, len);
|
memset(writeFds, 0, len);
|
exceptFds = balloc(B_L, len);
|
exceptFds = balloc(B_L, len);
|
memset(exceptFds, 0, len);
|
memset(exceptFds, 0, len);
|
|
|
/*
|
/*
|
* Set the select event masks for events to watch
|
* Set the select event masks for events to watch
|
*/
|
*/
|
for (sid = 0; sid < socketMax; sid++) {
|
for (sid = 0; sid < socketMax; sid++) {
|
if ((sp = socketList[sid]) == NULL) {
|
if ((sp = socketList[sid]) == NULL) {
|
continue;
|
continue;
|
}
|
}
|
a_assert(sp);
|
a_assert(sp);
|
|
|
/*
|
/*
|
* Initialize the ready masks and compute the mask offsets.
|
* Initialize the ready masks and compute the mask offsets.
|
*/
|
*/
|
index = sp->sock / (NBBY * sizeof(fd_mask));
|
index = sp->sock / (NBBY * sizeof(fd_mask));
|
bit = 1 << (sp->sock % (NBBY * sizeof(fd_mask)));
|
bit = 1 << (sp->sock % (NBBY * sizeof(fd_mask)));
|
|
|
/*
|
/*
|
* Set the appropriate bit in the ready masks for the sp->sock.
|
* Set the appropriate bit in the ready masks for the sp->sock.
|
*/
|
*/
|
if (sp->interestMask & SOCKET_READABLE) {
|
if (sp->interestMask & SOCKET_READABLE) {
|
readFds[index] |= bit;
|
readFds[index] |= bit;
|
}
|
}
|
if (sp->interestMask & SOCKET_WRITABLE) {
|
if (sp->interestMask & SOCKET_WRITABLE) {
|
writeFds[index] |= bit;
|
writeFds[index] |= bit;
|
}
|
}
|
if (sp->interestMask & SOCKET_EXCEPTION) {
|
if (sp->interestMask & SOCKET_EXCEPTION) {
|
exceptFds[index] |= bit;
|
exceptFds[index] |= bit;
|
}
|
}
|
}
|
}
|
|
|
/*
|
/*
|
* Wait for the event or a timeout.
|
* Wait for the event or a timeout.
|
*/
|
*/
|
nEvents = select(socketHighestFd + 1, (fd_set *) readFds,
|
nEvents = select(socketHighestFd + 1, (fd_set *) readFds,
|
(fd_set *) writeFds, (fd_set *) exceptFds, NULL);
|
(fd_set *) writeFds, (fd_set *) exceptFds, NULL);
|
if (nEvents > 0) {
|
if (nEvents > 0) {
|
for (sid = 0; sid < socketMax; sid++) {
|
for (sid = 0; sid < socketMax; sid++) {
|
if ((sp = socketList[sid]) == NULL) {
|
if ((sp = socketList[sid]) == NULL) {
|
continue;
|
continue;
|
}
|
}
|
|
|
index = sp->sock / (NBBY * sizeof(fd_mask));
|
index = sp->sock / (NBBY * sizeof(fd_mask));
|
bit = 1 << (sp->sock % (NBBY * sizeof(fd_mask)));
|
bit = 1 << (sp->sock % (NBBY * sizeof(fd_mask)));
|
|
|
if (readFds[index] & bit) {
|
if (readFds[index] & bit) {
|
sp->readyMask |= SOCKET_READABLE;
|
sp->readyMask |= SOCKET_READABLE;
|
}
|
}
|
if (writeFds[index] & bit) {
|
if (writeFds[index] & bit) {
|
sp->readyMask |= SOCKET_WRITABLE;
|
sp->readyMask |= SOCKET_WRITABLE;
|
}
|
}
|
if (exceptFds[index] & bit) {
|
if (exceptFds[index] & bit) {
|
sp->readyMask |= SOCKET_EXCEPTION;
|
sp->readyMask |= SOCKET_EXCEPTION;
|
}
|
}
|
}
|
}
|
}
|
}
|
|
|
bfree(B_L, readFds);
|
bfree(B_L, readFds);
|
bfree(B_L, writeFds);
|
bfree(B_L, writeFds);
|
bfree(B_L, exceptFds);
|
bfree(B_L, exceptFds);
|
|
|
return nEvents;
|
return nEvents;
|
}
|
}
|
|
|
/******************************************************************************/
|
/******************************************************************************/
|
/*
|
/*
|
* Process socket events
|
* Process socket events
|
*/
|
*/
|
|
|
void socketProcess()
|
void socketProcess()
|
{
|
{
|
socket_t *sp;
|
socket_t *sp;
|
int sid;
|
int sid;
|
|
|
/*
|
/*
|
* Process each socket
|
* Process each socket
|
*/
|
*/
|
for (sid = 0; sid < socketMax; sid++) {
|
for (sid = 0; sid < socketMax; sid++) {
|
if ((sp = socketList[sid]) == NULL) {
|
if ((sp = socketList[sid]) == NULL) {
|
continue;
|
continue;
|
}
|
}
|
if ((sp->readyMask & sp->interestMask) ||
|
if ((sp->readyMask & sp->interestMask) ||
|
((sp->interestMask & SOCKET_READABLE) &&
|
((sp->interestMask & SOCKET_READABLE) &&
|
socketInputBuffered(sid))) {
|
socketInputBuffered(sid))) {
|
socketDoEvent(sp);
|
socketDoEvent(sp);
|
}
|
}
|
}
|
}
|
}
|
}
|
|
|
/******************************************************************************/
|
/******************************************************************************/
|
/*
|
/*
|
* Process and event on the event queue
|
* Process and event on the event queue
|
*/
|
*/
|
|
|
static int socketDoEvent(socket_t *sp)
|
static int socketDoEvent(socket_t *sp)
|
{
|
{
|
ringq_t* rq;
|
ringq_t* rq;
|
int sid;
|
int sid;
|
|
|
a_assert(sp);
|
a_assert(sp);
|
|
|
sid = sp->sid;
|
sid = sp->sid;
|
if (sp->readyMask & SOCKET_READABLE) {
|
if (sp->readyMask & SOCKET_READABLE) {
|
if (sp->flags & SOCKET_LISTENING) {
|
if (sp->flags & SOCKET_LISTENING) {
|
socketAccept(sp);
|
socketAccept(sp);
|
sp->readyMask = 0;
|
sp->readyMask = 0;
|
return 1;
|
return 1;
|
}
|
}
|
} else {
|
} else {
|
/*
|
/*
|
* If there is still read data in the buffers, trigger the read handler
|
* If there is still read data in the buffers, trigger the read handler
|
* NOTE: this may busy spin if the read handler doesn't read the data
|
* NOTE: this may busy spin if the read handler doesn't read the data
|
*/
|
*/
|
if (sp->interestMask & SOCKET_READABLE && socketInputBuffered(sid)) {
|
if (sp->interestMask & SOCKET_READABLE && socketInputBuffered(sid)) {
|
sp->readyMask |= SOCKET_READABLE;
|
sp->readyMask |= SOCKET_READABLE;
|
}
|
}
|
}
|
}
|
|
|
|
|
/*
|
/*
|
* If now writable and flushing in the background, continue flushing
|
* If now writable and flushing in the background, continue flushing
|
*/
|
*/
|
if (sp->readyMask & SOCKET_WRITABLE) {
|
if (sp->readyMask & SOCKET_WRITABLE) {
|
if (sp->flags & SOCKET_FLUSHING) {
|
if (sp->flags & SOCKET_FLUSHING) {
|
rq = &sp->outBuf;
|
rq = &sp->outBuf;
|
if (ringqLen(rq) > 0) {
|
if (ringqLen(rq) > 0) {
|
socketFlush(sp->sid, 0);
|
socketFlush(sp->sid, 0);
|
} else {
|
} else {
|
sp->flags &= ~SOCKET_FLUSHING;
|
sp->flags &= ~SOCKET_FLUSHING;
|
}
|
}
|
}
|
}
|
}
|
}
|
|
|
/*
|
/*
|
* Now invoke the users socket handler. NOTE: the handler may delete the
|
* Now invoke the users socket handler. NOTE: the handler may delete the
|
* socket, so we must be very careful after calling the handler.
|
* socket, so we must be very careful after calling the handler.
|
*/
|
*/
|
if (sp->handler && (sp->interestMask & sp->readyMask)) {
|
if (sp->handler && (sp->interestMask & sp->readyMask)) {
|
(sp->handler)(sid, sp->interestMask & sp->readyMask,
|
(sp->handler)(sid, sp->interestMask & sp->readyMask,
|
sp->handler_data);
|
sp->handler_data);
|
/*
|
/*
|
* Make sure socket pointer is still valid, then set the readyMask
|
* Make sure socket pointer is still valid, then set the readyMask
|
* to 0.
|
* to 0.
|
*/
|
*/
|
if (socketPtr(sid)) {
|
if (socketPtr(sid)) {
|
sp->readyMask = 0;
|
sp->readyMask = 0;
|
}
|
}
|
}
|
}
|
return 1;
|
return 1;
|
}
|
}
|
|
|
/******************************************************************************/
|
/******************************************************************************/
|
/*
|
/*
|
* Allocate a new socket structure
|
* Allocate a new socket structure
|
*/
|
*/
|
|
|
static int socketAlloc(char* host, int port, socketAccept_t accept, int flags)
|
static int socketAlloc(char* host, int port, socketAccept_t accept, int flags)
|
{
|
{
|
socket_t *sp;
|
socket_t *sp;
|
int sid;
|
int sid;
|
|
|
if ((sid = hAlloc((void***) &socketList)) < 0) {
|
if ((sid = hAlloc((void***) &socketList)) < 0) {
|
return -1;
|
return -1;
|
}
|
}
|
if ((sp = (socket_t*) balloc(B_L, sizeof(socket_t))) == NULL) {
|
if ((sp = (socket_t*) balloc(B_L, sizeof(socket_t))) == NULL) {
|
hFree((void***) &socket, sid);
|
hFree((void***) &socket, sid);
|
return -1;
|
return -1;
|
}
|
}
|
memset(sp, 0, sizeof(socket_t));
|
memset(sp, 0, sizeof(socket_t));
|
socketList[sid] = sp;
|
socketList[sid] = sp;
|
if (sid >= socketMax)
|
if (sid >= socketMax)
|
socketMax = sid + 1;
|
socketMax = sid + 1;
|
|
|
sp->sid = sid;
|
sp->sid = sid;
|
sp->accept = accept;
|
sp->accept = accept;
|
sp->port = port;
|
sp->port = port;
|
sp->flags = flags;
|
sp->flags = flags;
|
|
|
if (host) {
|
if (host) {
|
strncpy(sp->host, host, sizeof(sp->host));
|
strncpy(sp->host, host, sizeof(sp->host));
|
}
|
}
|
|
|
ringqOpen(&sp->inBuf, SOCKET_BUFSIZ, SOCKET_BUFSIZ);
|
ringqOpen(&sp->inBuf, SOCKET_BUFSIZ, SOCKET_BUFSIZ);
|
ringqOpen(&sp->outBuf, SOCKET_BUFSIZ, SOCKET_BUFSIZ);
|
ringqOpen(&sp->outBuf, SOCKET_BUFSIZ, SOCKET_BUFSIZ);
|
ringqOpen(&sp->lineBuf, SOCKET_BUFSIZ, -1);
|
ringqOpen(&sp->lineBuf, SOCKET_BUFSIZ, -1);
|
|
|
return sid;
|
return sid;
|
}
|
}
|
|
|
/******************************************************************************/
|
/******************************************************************************/
|
/*
|
/*
|
* Free a socket structure
|
* Free a socket structure
|
*/
|
*/
|
|
|
static void socketFree(int sid)
|
static void socketFree(int sid)
|
{
|
{
|
socket_t* sp;
|
socket_t* sp;
|
int i;
|
int i;
|
|
|
if ((sp = socketPtr(sid)) == NULL) {
|
if ((sp = socketPtr(sid)) == NULL) {
|
return;
|
return;
|
}
|
}
|
if (sp->sock >= 0) {
|
if (sp->sock >= 0) {
|
close(sp->sock);
|
close(sp->sock);
|
}
|
}
|
|
|
ringqClose(&sp->inBuf);
|
ringqClose(&sp->inBuf);
|
ringqClose(&sp->outBuf);
|
ringqClose(&sp->outBuf);
|
ringqClose(&sp->lineBuf);
|
ringqClose(&sp->lineBuf);
|
|
|
bfree(B_L, sp);
|
bfree(B_L, sp);
|
socketMax = hFree((void***) &socketList, sid);
|
socketMax = hFree((void***) &socketList, sid);
|
|
|
/*
|
/*
|
* Calculate the new highest socket number
|
* Calculate the new highest socket number
|
*/
|
*/
|
socketHighestFd = -1;
|
socketHighestFd = -1;
|
for (i = 0; i < socketMax; i++) {
|
for (i = 0; i < socketMax; i++) {
|
if ((sp = socketList[i]) == NULL) {
|
if ((sp = socketList[i]) == NULL) {
|
continue;
|
continue;
|
}
|
}
|
socketHighestFd = max(socketHighestFd, sp->sock);
|
socketHighestFd = max(socketHighestFd, sp->sock);
|
}
|
}
|
}
|
}
|
|
|
/******************************************************************************/
|
/******************************************************************************/
|
/*
|
/*
|
* Validate a socket handle
|
* Validate a socket handle
|
*/
|
*/
|
|
|
static socket_t* socketPtr(int sid)
|
static socket_t* socketPtr(int sid)
|
{
|
{
|
if (sid < 0 || sid >= socketMax || socketList[sid] == NULL) {
|
if (sid < 0 || sid >= socketMax || socketList[sid] == NULL) {
|
a_assert(NULL);
|
a_assert(NULL);
|
return NULL;
|
return NULL;
|
}
|
}
|
|
|
a_assert(socketList[sid]);
|
a_assert(socketList[sid]);
|
return socketList[sid];
|
return socketList[sid];
|
}
|
}
|
|
|
/******************************************************************************/
|
/******************************************************************************/
|
/*
|
/*
|
* Get the operating system error code
|
* Get the operating system error code
|
*/
|
*/
|
|
|
static int socketGetError()
|
static int socketGetError()
|
{
|
{
|
return errno;
|
return errno;
|
}
|
}
|
|
|
/******************************************************************************/
|
/******************************************************************************/
|
/*
|
/*
|
* Wait until an event occurs on a socket. Return 1 on success, 0 on failure.
|
* Wait until an event occurs on a socket. Return 1 on success, 0 on failure.
|
*/
|
*/
|
|
|
static int socketWaitForEvent(socket_t* sp, int interestMask, int* errCode)
|
static int socketWaitForEvent(socket_t* sp, int interestMask, int* errCode)
|
{
|
{
|
a_assert(sp);
|
a_assert(sp);
|
|
|
while (socketSelect()) {
|
while (socketSelect()) {
|
if (sp->readyMask & interestMask) {
|
if (sp->readyMask & interestMask) {
|
break;
|
break;
|
}
|
}
|
}
|
}
|
if (sp->readyMask & SOCKET_EXCEPTION) {
|
if (sp->readyMask & SOCKET_EXCEPTION) {
|
return -1;
|
return -1;
|
} else if (sp->readyMask & SOCKET_WRITABLE) {
|
} else if (sp->readyMask & SOCKET_WRITABLE) {
|
return 0;
|
return 0;
|
} else {
|
} else {
|
*errCode = errno = EWOULDBLOCK;
|
*errCode = errno = EWOULDBLOCK;
|
return -1;
|
return -1;
|
}
|
}
|
}
|
}
|
|
|
/******************************************************************************/
|
/******************************************************************************/
|
/*
|
/*
|
* Put the socket into non-blocking mode
|
* Put the socket into non-blocking mode
|
*/
|
*/
|
|
|
static int socketNonBlock(socket_t *sp)
|
static int socketNonBlock(socket_t *sp)
|
{
|
{
|
int flags;
|
int flags;
|
|
|
flags = fcntl(sp->sock, F_GETFL) | O_NONBLOCK;
|
flags = fcntl(sp->sock, F_GETFL) | O_NONBLOCK;
|
if (fcntl(sp->sock, F_SETFL, flags) < 0) {
|
if (fcntl(sp->sock, F_SETFL, flags) < 0) {
|
return -1;
|
return -1;
|
}
|
}
|
return 0;
|
return 0;
|
}
|
}
|
|
|
/******************************************************************************/
|
/******************************************************************************/
|
/*
|
/*
|
* Duplicate stdin and stdout
|
* Duplicate stdin and stdout
|
*/
|
*/
|
|
|
int DuplicateStdFile (int sid)
|
int DuplicateStdFile (int sid)
|
{
|
{
|
if (0 != dup2(socketList[sid]->sock, 0) || 1 != dup2(socketList[sid]->sock, 1))
|
if (0 != dup2(socketList[sid]->sock, 0) || 1 != dup2(socketList[sid]->sock, 1))
|
return -1;
|
return -1;
|
|
|
return 0;
|
return 0;
|
|
|
}
|
}
|
|
|