/* File: socket_xsb.c
** Author(s): juliana, davulcu, kifer, songmei yu
** Contact: xsb-contact@cs.sunysb.edu
**
** Copyright (C) The Research Foundation of SUNY, 1999
**
** XSB is free software; you can redistribute it and/or modify it under the
** terms of the GNU Library General Public License as published by the Free
** Software Foundation; either version 2 of the License, or (at your option)
** any later version.
**
** XSB is distributed in the hope that it will be useful, but WITHOUT ANY
** WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
** FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for
** more details.
**
** You should have received a copy of the GNU Library General Public License
** along with XSB; if not, write to the Free Software Foundation,
** Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
**
** $Id: socket_xsb.c,v 1.25 2002/08/07 15:42:13 lfcastro Exp $
**
*/
#undef __STRICT_ANSI__
#include "xsb_config.h"
#include "xsb_debug.h"
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <signal.h>
/* wind2unix.h must be included after sys/stat.h */
#include "wind2unix.h"
#include "xsb_time.h"
/* The socket material */
#ifdef WIN_NT
#include <windows.h>
#include <winuser.h>
#include <winbase.h>
#include <process.h>
#include <tchar.h>
#include <io.h>
#include <stdarg.h>
#include <winsock.h>
#include "wsipx.h"
#else /* UNIX */
#include <sys/socket.h>
#include <sys/uio.h>
#include <netdb.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#endif
#include "auxlry.h"
#include "cell_xsb.h"
#include "error_xsb.h"
#include "cinterf.h"
#include "basictypes.h"
#include "io_builtins_xsb.h"
#include "socket_xsb.h"
#include "timer_xsb.h"
#include "psc_xsb.h"
#include "register.h"
/* return error code handling */
static xsbBool set_error_code(int ErrCode, int ErrCodeArgNumber,char *Where);
/* define a macro to do memory free and errro handling for timeout related
socket calls */
#define FREE_TIMEOUT_AND_SET_ECODE(ecode, arg_num, Where) \
free_timeout_obj(pSock); \
return set_error_code(ecode, arg_num, Where);
/* In WIN_NT, this gets redefined into _fdopen by configs/special.h */
extern FILE *fdopen(int fildes, const char *type);
/* return code from socket operation without timeout control */
int retcode;
/* define the timeout structure for the socket calls that need
timeout control */
struct xsb_timeout {
long parent_thread; /* This field is mandatory; must be first! */
int return_code; /* return value from the socket call */
void *sockdata; /* used to pass socket specific data */
};
/* free xsbTimeout object */
static void free_timeout_obj(xsbTimeout * pSock);
static xsbTimeout *make_timeout_obj();
/* declare the utility functions for select calls */
static void init_connections();
static void set_sockfd(int count);
static xsbBool list_sockfd(prolog_term list, fd_set *fdset, int *max_fd,
int **fds, int * size);
static void test_ready(prolog_term *avail_sockfds, fd_set *fdset,
int *fds, int size);
static void select_destroy(char *connection_name);
static int getsize (prolog_term list);
static int checkslot (void);
/* define the structure for select call */
static struct connection_t {
char *connection_name;
int maximum_fd;
int empty_flag;
int sizer; /* size of read fds */
int sizew; /* size of write fds */
int sizee; /* size of exception fds */
fd_set readset;
fd_set writeset;
fd_set exceptionset;
int *read_fds; /* array of read fds */
int *write_fds; /* array of write fds */
int *exception_fds; /* array of exception fds */
} connections[MAXCONNECT];
static char *get_host_IP(char *host_name_or_IP) {
struct hostent *host_struct;
struct in_addr *ptr;
char **listptr;
/* if host_name_or_IP is an IP addr, then just return; else use
gethostbyname */
if (IS_IP_ADDR(host_name_or_IP))
return(host_name_or_IP);
host_struct = gethostbyname(host_name_or_IP);
listptr = host_struct->h_addr_list;
if ((ptr = (struct in_addr *) *listptr++) != NULL) {
xsb_mesg(" IP address: %s", inet_ntoa(*ptr));
return(inet_ntoa(*ptr));
}
return NULL;
}
/* Returns:
normal: SOCK_OK
EOF: SOCK_READMSG_EOF
error: SOCK_READMSG_FAILED
Read message header, then read the message itself.
*/
static int readmsg(SOCKET sock_handle, char **msg_buff)
{
int actual_len;
/* 4-char buf that keeps the length of the subsequent msg */
char lenbuf[XSB_MSG_HEADER_LENGTH];
unsigned int msglen, net_encoded_len;
actual_len
= (long)recvfrom(sock_handle,lenbuf,XSB_MSG_HEADER_LENGTH,0,NULL,0);
if (SOCKET_OP_FAILED(actual_len)) return SOCK_READMSG_FAILED;
if (actual_len == 0) {
*msg_buff = NULL;
return SOCK_READMSG_EOF;
}
memcpy((void *) &net_encoded_len, (void *) lenbuf, XSB_MSG_HEADER_LENGTH);
msglen = ntohl(net_encoded_len);
if ((*msg_buff = (char *)calloc(msglen+1, sizeof(char))) == NULL) {
xsb_abort("[SOCKET_RECV] Can't allocate memory for the message buffer");
}
actual_len = (long) recvfrom(sock_handle,*msg_buff,msglen,0,NULL,0);
if (SOCKET_OP_FAILED(actual_len)) return SOCK_READMSG_FAILED;
/* The following should never arise. Points to a bug: some kind of mismatch
between the communicating machines. */
if ((unsigned int)actual_len != msglen)
xsb_warn("[SOCKET_RECV] Message length %ld differs from the header value %ld",
msglen, actual_len);
return SOCK_OK;
}
/* socket calls which need timeout control:
socket_accept();
socket_connect();
socket_recv();
socket_send();
socket_get0();
socket_put();
The formal parameter is pointer to xsbTimeout.
*/
static void socket_accept(xsbTimeout *pptr) {
SOCKET sock_handle_in = (SOCKET) ptoc_int(2);
SOCKET sock_handle = accept(sock_handle_in, NULL, NULL);
pptr->return_code = (int) sock_handle;
NOTIFY_PARENT_THREAD(pptr);
}
static void socket_connect(xsbTimeout *pptr) {
SOCKET sock_handle;
int domain, portnum;
SOCKADDR_IN socket_addr;
domain = ptoc_int(2);
sock_handle = (SOCKET) ptoc_int(3);
portnum = ptoc_int(4);
if (domain == 0) domain = AF_INET;
else if (domain == 1) {
domain = AF_UNIX;
xsb_abort("[SOCKET_REQUEST] Domain AF_UNIX is not implemented");
} else {
xsb_abort("[SOCKET_REQUEST] Invalid domain. Valid domains are: 0(AF_INET), 1(AF_UNIX)");
}
/*** prepare to connect ***/
FillWithZeros(socket_addr);
socket_addr.sin_port = htons((unsigned short)portnum);
socket_addr.sin_family = AF_INET;
socket_addr.sin_addr.s_addr =
inet_addr((char*)get_host_IP(ptoc_string(5)));
pptr->return_code =
connect(sock_handle,(PSOCKADDR)&socket_addr,sizeof(socket_addr));
NOTIFY_PARENT_THREAD(pptr);
}
static void socket_recv(xsbTimeout *pptr) {
SOCKET sock_handle;
sock_handle = (SOCKET) ptoc_int(2);
pptr->return_code = readmsg(sock_handle,(char**)(&pptr->sockdata));
NOTIFY_PARENT_THREAD(pptr);
}
static void socket_send(xsbTimeout *pptr) {
SOCKET sock_handle = (SOCKET) ptoc_int(2);
char *send_msg_aux = ptoc_string(3);
unsigned int msg_body_len, network_encoded_len;
/* We use the first XSB_MSG_HEADER_LENGTH bytes for the message size.*/
if ((pptr->sockdata
= calloc(strlen(send_msg_aux)+XSB_MSG_HEADER_LENGTH+1,sizeof(char)))
== NULL) {
xsb_abort("[SOCKET_SEND] Can't allocate memory for the message buffer");
}
msg_body_len = strlen(send_msg_aux);
network_encoded_len = (unsigned int) htonl((unsigned long int)
msg_body_len);
memcpy((void *) (pptr->sockdata), (void *)
&network_encoded_len,XSB_MSG_HEADER_LENGTH);
strcpy((char*)(pptr->sockdata)+XSB_MSG_HEADER_LENGTH, send_msg_aux);
pptr->return_code = sendto(sock_handle,
(char*)(pptr->sockdata),
msg_body_len+XSB_MSG_HEADER_LENGTH,
0, NULL, 0);
NOTIFY_PARENT_THREAD(pptr);
}
static void socket_get0(xsbTimeout * pptr) {
SOCKET sock_handle;
pptr->sockdata = malloc(sizeof(char));
sock_handle = (SOCKET) ptoc_int(2);
pptr->return_code =
recvfrom(sock_handle,(char*)(pptr->sockdata),1,0,NULL,0);
NOTIFY_PARENT_THREAD(pptr);
}
static void socket_put(xsbTimeout *pptr) {
SOCKET sock_handle;
static char tmpch;
sock_handle = (SOCKET) ptoc_int(2);
tmpch = (char)ptoc_int(3);
pptr->return_code = sendto(sock_handle, &tmpch, 1, 0, NULL,0);
NOTIFY_PARENT_THREAD(pptr);
}
/* in order to save builtin numbers, create a single socket function with
* options socket_request(SockOperation,....) */
xsbBool xsb_socket_request(void)
{
static int ecode = 0; /* error code for socket ops */
static SOCKET sock_handle;
static int domain, portnum;
static SOCKADDR_IN socket_addr;
static struct linger sock_linger_opt;
xsbTimeout *pSock;
switch (ptoc_int(1)) {
case SOCKET_ROOT: /* this is the socket() request */
/* socket_request(SOCKET_ROOT,+domain,-socket_fd,-Error,_,_,_)
Currently only AF_INET domain */
domain = ptoc_int(2);
if (domain == 0) domain = AF_INET;
else if (domain == 1){
domain = AF_UNIX;
xsb_abort("[SOCKET_REQUEST] Domain AF_INET is not implemented");
} else {
xsb_abort("[SOCKET_REQUEST] Invalid domain. Valid domains are: 0 - AF_INET, 1 - AF_UNIX");
return FALSE;
}
sock_handle = socket(domain, SOCK_STREAM, IPPROTO_TCP);
/* error handling */
if (BAD_SOCKET(sock_handle)) {
ecode = XSB_SOCKET_ERRORCODE;
perror("SOCKET_REQUEST");
} else
ecode = SOCK_OK;
ctop_int(3, (SOCKET) sock_handle);
return set_error_code(ecode, 4, "SOCKET_REQUEST");
case SOCKET_BIND:
/* socket_request(SOCKET_BIND,+domain,+sock_handle,+port,-Error,_,_)
Currently only supports AF_INET */
sock_handle = (SOCKET) ptoc_int(3);
portnum = ptoc_int(4);
domain = ptoc_int(2);
if (domain == 0) domain = AF_INET;
else if (domain == 1){
domain = AF_UNIX;
xsb_abort("[SOCKET_REQUEST] domain AF_INET is not implemented");
} else {
xsb_abort("[SOCKET_REQUEST] Invalid domain. Valid domains are: 0 - AF_INET, 1 - AF_UNIX");
return FALSE;
}
/* Bind server to the agreed upon port number.
** See commdef.h for the actual port number. */
FillWithZeros(socket_addr);
socket_addr.sin_port = htons((unsigned short)portnum);
socket_addr.sin_family = AF_INET;
#ifndef WIN_NT
socket_addr.sin_addr.s_addr = htonl(INADDR_ANY);
#endif
retcode = bind(sock_handle, (PSOCKADDR) &socket_addr, sizeof(socket_addr));
/* error handling */
if (SOCKET_OP_FAILED(retcode)) {
ecode = XSB_SOCKET_ERRORCODE;
perror("SOCKET_BIND");
} else
ecode = SOCK_OK;
return set_error_code(ecode, 5, "SOCKET_BIND");
case SOCKET_LISTEN:
/* socket_request(SOCKET_LISTEN,+sock_handle,+length,-Error,_,_,_) */
sock_handle = (SOCKET) ptoc_int(2);
retcode = listen(sock_handle, ptoc_int(3));
/* error handling */
if (SOCKET_OP_FAILED(retcode)) {
ecode = XSB_SOCKET_ERRORCODE;
perror("SOCKET_LISTEN");
} else
ecode = SOCK_OK;
return set_error_code(ecode, 4, "SOCKET_LISTEN");
case SOCKET_ACCEPT:
/* socket_request(SOCKET_ACCEPT,+sock_handle_in,
-sock_handle_out, -Error,_,_,_) */
pSock = make_timeout_obj();
if (CHECK_TIMER_SET) {
int timeout_flag;
timeout_flag=make_timed_call(pSock, socket_accept);
if (timeout_flag == TIMER_SETUP_ERR) {
FREE_TIMEOUT_AND_SET_ECODE(TIMER_SETUP_ERR,4,"SOCKET_ACCEPT");
} else if(timeout_flag) { /* timed out and return */
FREE_TIMEOUT_AND_SET_ECODE(TIMEOUT_ERR, 4,"SOCKET_ACCEPT");
}
} else { /* timer not set */
socket_accept(pSock);
}
/* error handling */
if (BAD_SOCKET(pSock->return_code)) {
ecode = XSB_SOCKET_ERRORCODE;
perror("SOCKET_ACCEPT");
} else {
sock_handle = pSock->return_code; /* accept() returns sock_out */
ecode = SOCK_OK;
}
ctop_int(3, (SOCKET) sock_handle);
FREE_TIMEOUT_AND_SET_ECODE(ecode, 4, "SOCKET_ACCEPT");
case SOCKET_CONNECT: {
/* socket_request(SOCKET_CONNECT,+domain,+sock_handle,+port,
+hostname,-Error) */
pSock = make_timeout_obj();
/* control time out */
if (CHECK_TIMER_SET) {
int timeout_flag;
timeout_flag=make_timed_call(pSock, socket_connect);
if (timeout_flag == TIMER_SETUP_ERR) {
FREE_TIMEOUT_AND_SET_ECODE(TIMER_SETUP_ERR,6,"SOCKET_CONNECT");
} else if(timeout_flag) { /* timed out and return */
FREE_TIMEOUT_AND_SET_ECODE(TIMEOUT_ERR, 6,"SOCKET_CONNECT");
}
} else { /* timer not set */
socket_connect(pSock);
}
/* error handling */
if (SOCKET_OP_FAILED(pSock->return_code)) {
ecode = XSB_SOCKET_ERRORCODE;
perror("SOCKET_CONNECT");
/* close, because if connect() fails then socket becomes unusable */
closesocket(ptoc_int(3));
} else
ecode = SOCK_OK;
FREE_TIMEOUT_AND_SET_ECODE(ecode, 6, "SOCKET_CONNECT");
}
case SOCKET_CLOSE:
/* socket_request(SOCKET_CLOSE,+sock_handle,-Error,_,_,_,_) */
sock_handle = (SOCKET)ptoc_int(2);
/* error handling */
retcode = closesocket(sock_handle);
if (SOCKET_OP_FAILED(retcode)) {
ecode = XSB_SOCKET_ERRORCODE;
perror("SOCKET_CLOSE");
} else
ecode = SOCK_OK;
return set_error_code(ecode, 3, "SOCKET_CLOSE");
case SOCKET_RECV:
/* socket_request(SOCKET_RECV,+Sockfd, -Msg, -Error,_,_,_) */
pSock = make_timeout_obj();
/* control time out */
if (CHECK_TIMER_SET) {
int timeout_flag;
timeout_flag=make_timed_call(pSock,socket_recv);
if(timeout_flag == TIMER_SETUP_ERR) {
FREE_TIMEOUT_AND_SET_ECODE(TIMER_SETUP_ERR,4,"SOCKET_RECV");
} else if(timeout_flag) { /* timed out */
FREE_TIMEOUT_AND_SET_ECODE(TIMEOUT_ERR,4,"SOCKET_RECV");
}
} else /* timer not set */
socket_recv(pSock);
/* error handling */
switch (pSock->return_code) {
case SOCK_OK:
ecode = SOCK_OK;
break;
case SOCK_READMSG_FAILED:
ecode = XSB_SOCKET_ERRORCODE;
perror("SOCKET_RECV");
break;
case SOCK_READMSG_EOF:
ecode = SOCK_EOF;
break;
default:
xsb_abort("[SOCKET_RECV] XSB bug: invalid return code from readmsg");
}
if (pSock->sockdata != NULL)
ctop_string(3,(char*)string_find((char *)pSock->sockdata,1));
else /* this happens at end of file */
ctop_string(3,(char*)string_find("",1));
FREE_TIMEOUT_AND_SET_ECODE(ecode,4,"SOCKET_RECV");
case SOCKET_SEND:
/* socket_request(SOCKET_SEND,+Sockfd, +Msg, -Error,_,_,_) */
pSock = make_timeout_obj();
/* control time out */
if (CHECK_TIMER_SET) {
int timeout_flag;
timeout_flag=make_timed_call(pSock,socket_send);
if(timeout_flag == TIMER_SETUP_ERR ) {
FREE_TIMEOUT_AND_SET_ECODE(TIMER_SETUP_ERR, 4,"SOCKET_SEND");
} else if(timeout_flag) { /* timed out */
FREE_TIMEOUT_AND_SET_ECODE(TIMEOUT_ERR, 4, "SOCKET_SEND");
}
} else /* timer not set */
socket_send(pSock);
if (SOCKET_OP_FAILED(pSock->return_code)) {
ecode = XSB_SOCKET_ERRORCODE;
perror("SOCKET_SEND");
} else
ecode = SOCK_OK;
FREE_TIMEOUT_AND_SET_ECODE(ecode, 4, "SOCKET_SEND");
case SOCKET_GET0:
/* socket_request(SOCKET_GET0,+Sockfd,-C,-Error,_,_,_) */
pSock = make_timeout_obj();
/* control time out */
if (CHECK_TIMER_SET) {
int timeout_flag;
timeout_flag=make_timed_call(pSock,socket_get0);
if (timeout_flag == TIMER_SETUP_ERR) {
FREE_TIMEOUT_AND_SET_ECODE(TIMER_SETUP_ERR, 4,"SOCKET_GET0");
} else if(timeout_flag) /* timed out */ {
FREE_TIMEOUT_AND_SET_ECODE(TIMEOUT_ERR, 4, "SOCKET_GET0");
}
} else /* timer not set */
socket_get0(pSock);
/*error handling */
switch (pSock->return_code) {
case 1:
ctop_int(3,(unsigned char)(*(char*)(pSock->sockdata)));
ecode = SOCK_OK;
break;
case 0:
ecode = SOCK_EOF;
break;
default:
ctop_int(3,-1);
perror("SOCKET_GET0");
ecode = XSB_SOCKET_ERRORCODE;
}
FREE_TIMEOUT_AND_SET_ECODE(ecode, 4, "SOCKET_GET0");
case SOCKET_PUT:
/* socket_request(SOCKET_PUT,+Sockfd,+C,-Error_,_,_) */
pSock = make_timeout_obj();
/* control time out */
if (CHECK_TIMER_SET) {
int timeout_flag;
timeout_flag=make_timed_call(pSock,socket_put);
if(timeout_flag == TIMER_SETUP_ERR) {
FREE_TIMEOUT_AND_SET_ECODE(TIMER_SETUP_ERR, 4,"SOCKET_PUT");
} else if(timeout_flag) /* timed out */ {
FREE_TIMEOUT_AND_SET_ECODE(TIMEOUT_ERR, 4, "SOCKET_PUT");
}
} else /* timer not set */ {
socket_put(pSock);
}
/* error handling */
if (pSock->return_code == 1)
ecode = SOCK_OK;
else if (SOCKET_OP_FAILED(pSock->return_code)) {
ecode = XSB_SOCKET_ERRORCODE;
perror("SOCKET_PUT");
}
FREE_TIMEOUT_AND_SET_ECODE(ecode, 4, "SOCKET_PUT");
case SOCKET_SET_OPTION: {
/* socket_request(SOCKET_SET_OPTION,+Sockfd,+OptionName,+Value,_,_,_) */
char *option_name = ptoc_string(3);
sock_handle = (SOCKET)ptoc_int(2);
/* Set the "linger" parameter to a small number of seconds */
if (0==strcmp(option_name,"linger")) {
int linger_time=ptoc_int(4);
if (linger_time < 0) {
sock_linger_opt.l_onoff = FALSE;
sock_linger_opt.l_linger = 0;
} else {
sock_linger_opt.l_onoff = TRUE;
sock_linger_opt.l_linger = linger_time;
}
if (setsockopt(sock_handle, SOL_SOCKET, SO_LINGER,
(void *) &sock_linger_opt, sizeof(sock_linger_opt))
< 0) {
xsb_warn("[SOCKET_SET_OPTION] Cannot set socket linger time");
return FALSE;
}
}else {
xsb_warn("[SOCKET_SET_OPTION] Invalid option, `%s'", option_name);
return FALSE;
}
return TRUE;
}
case SOCKET_SET_SELECT: {
/*socket_request(SOCKET_SET_SELECT,+connection_name,
+R_sockfd,+W_sockfd,+E_sockfd) */
prolog_term R_sockfd, W_sockfd, E_sockfd;
int i, connection_count;
int rmax_fd=0, wmax_fd=0, emax_fd=0;
char *connection_name = ptoc_string(2);
/* bind fds to input arguments */
R_sockfd = reg_term(3);
W_sockfd = reg_term(4);
E_sockfd = reg_term(5);
/* initialize the array of connect_t structure for select call */
init_connections();
/* check whether the same connection name exists */
for (i=0;i<MAXCONNECT;i++) {
if ((connections[i].empty_flag==FALSE) &&
(strcmp(connection_name,connections[i].connection_name)==0))
xsb_abort("[SOCKET_SET_SELECT] Connection `%s' already exists!",
connection_name);
}
/* check whether there is empty slot left for connection */
if ((connection_count=checkslot())<MAXCONNECT) {
if (connections[connection_count].connection_name == NULL) {
connections[connection_count].connection_name = connection_name;
connections[connection_count].empty_flag = FALSE;
/* call the utility function separately to take the fds in */
list_sockfd(R_sockfd, &connections[connection_count].readset,
&rmax_fd, &connections[connection_count].read_fds,
&connections[connection_count].sizer);
list_sockfd(W_sockfd, &connections[connection_count].writeset,
&wmax_fd, &connections[connection_count].write_fds,
&connections[connection_count].sizew);
list_sockfd(E_sockfd, &connections[connection_count].exceptionset,
&emax_fd,&connections[connection_count].exception_fds,
&connections[connection_count].sizee);
connections[connection_count].maximum_fd =
max(max(rmax_fd,wmax_fd), emax_fd);
} else
/* if this one is reached, it is probably a bug */
xsb_abort("[SOCKET_SET_SELECT] All connections are busy!");
} else
xsb_abort("[SOCKET_SET_SELECT] Max number of collections exceeded!");
return TRUE;
}
case SOCKET_SELECT: {
/* socket_request(SOCKET_SELECT,+connection_name, +timeout
-avail_rsockfds,-avail_wsockfds,
-avail_esockfds,-ecode)
Returns 3 prolog_terms for available socket fds */
prolog_term Avail_rsockfds, Avail_wsockfds, Avail_esockfds;
prolog_term Avail_rsockfds_tail, Avail_wsockfds_tail, Avail_esockfds_tail;
int maxfd;
int i; /* index for connection_count */
char *connection_name = ptoc_string(2);
struct timeval *tv;
prolog_term timeout_term;
int timeout =0;
int connectname_found = FALSE;
int count=0;
/* specify the time out */
timeout_term = reg_term(3);
if (isinteger(timeout_term)|isboxedinteger(timeout_term)) {
timeout = int_val(timeout_term);
/* initialize tv */
tv = (struct timeval *)malloc(sizeof(struct timeval));
tv->tv_sec = timeout;
tv->tv_usec = 0;
} else
tv = NULL; /* no timeouts */
/* initialize the prolog term */
Avail_rsockfds = p2p_new();
Avail_wsockfds = p2p_new();
Avail_esockfds = p2p_new();
/* bind to output arguments */
Avail_rsockfds = reg_term(4);
Avail_wsockfds = reg_term(5);
Avail_esockfds = reg_term(6);
Avail_rsockfds_tail = Avail_rsockfds;
Avail_wsockfds_tail = Avail_wsockfds;
Avail_esockfds_tail = Avail_esockfds;
c2p_list(Avail_rsockfds_tail);
c2p_list(Avail_wsockfds_tail);
c2p_list(Avail_esockfds_tail);
for (i=0; i < MAXCONNECT; i++) {
/* find the matching connection_name to select */
if(connections[i].empty_flag==FALSE) {
if (strcmp(connection_name, connections[i].connection_name) == 0) {
connectname_found = TRUE;
count = i;
break;
}
}
}
if( i >= MAXCONNECT ) /* if no matching connection_name */
xsb_abort("[SOCKET_SELECT] connection `%s' doesn't exist",
connection_name);
/* compute maxfd for select call */
maxfd = connections[count].maximum_fd + 1;
/* FD_SET all sockets */
set_sockfd( count );
/* test whether the socket fd is available */
retcode = select(maxfd, &connections[count].readset,
&connections[count].writeset,
&connections[count].exceptionset, tv);
/* error handling */
if (retcode == 0) /* timed out */
ecode = TIMEOUT_ERR;
else if (SOCKET_OP_FAILED(retcode)) {
perror("SOCKET_SELECT");
ecode = XSB_SOCKET_ERRORCODE;
} else { /* no error */
ecode = SOCK_OK;
/* call the utility function to return the available socket fds */
test_ready(&Avail_rsockfds_tail, &connections[count].readset,
connections[count].read_fds,connections[count].sizer);
test_ready(&Avail_wsockfds_tail, &connections[count].writeset,
connections[count].write_fds,connections[count].sizew);
test_ready(&Avail_esockfds_tail,&connections[count].exceptionset,
connections[count].exception_fds,connections[count].sizee);
}
if (tv) free((struct timeval *)tv);
return set_error_code(ecode, 7, "SOCKET_SELECT");
}
case SOCKET_SELECT_DESTROY: {
/*socket_request(SOCKET_SELECT_DESTROY, +connection_name) */
char *connection_name = ptoc_string(2);
select_destroy(connection_name);
return TRUE;
}
default:
xsb_warn("[SOCKET_REQUEST] Invalid socket request %d", (int) ptoc_int(1));
return FALSE;
}
/* This trick would report a bug, if a newly added case
doesn't have a return clause */
xsb_bug("SOCKET_REQUEST case %d has no return clause", ptoc_int(1));
}
static xsbBool set_error_code(int ErrCode, int ErrCodeArgNumber, char *Where)
{
prolog_term ecode_value_term, ecode_arg_term = p2p_new();
ecode_value_term = reg_term(ErrCodeArgNumber);
if (!isref(ecode_value_term) &&
!(isinteger(ecode_value_term)|isboxedinteger(ecode_value_term)))
xsb_abort("[%s] Arg %d (the error code) must be a variable or an integer!",
Where, ErrCodeArgNumber);
c2p_int(ErrCode, ecode_arg_term);
return p2p_unify(ecode_arg_term, ecode_value_term);
}
/* free timeout object */
static void free_timeout_obj(xsbTimeout *pSock)
{
if(pSock->sockdata !=NULL) {
free(pSock->sockdata); /* free user specified data first */
}
free(pSock); /* free the whole structure */
return;
}
static xsbTimeout *make_timeout_obj()
{
xsbTimeout *pSock=NEW_TIMEOUT_OBJECT;
pSock->sockdata = NULL;
return pSock;
}
/* initialize the array of the structure */
static void init_connections()
{
int i;
static int initialized = FALSE;
if (!initialized) {
for (i=0; i<MAXCONNECT; i++) {
connections[i].connection_name = NULL;
connections[i].maximum_fd=0;
connections[i].empty_flag=TRUE;
/*clear all FD_SET */
FD_ZERO(&connections[i].readset);
FD_ZERO(&connections[i].writeset);
FD_ZERO(&connections[i].exceptionset);
connections[i].read_fds = 0;
connections[i].write_fds = 0 ;
connections[i].exception_fds = 0;
connections[i].sizer= 0;
connections[i].sizew = 0 ;
connections[i].sizee = 0 ;
}
initialized = TRUE;
}
}
/* FD_SET the socket fds */
static void set_sockfd(int count)
{
int i;
FD_ZERO(&connections[count].readset);
FD_ZERO(&connections[count].writeset);
FD_ZERO(&connections[count].exceptionset);
for (i=0; i< connections[count].sizer; i++) {
/* turn on the bit in the fd_set */
FD_SET(connections[count].read_fds[i], &connections[count].readset);
}
for (i=0; i< connections[count].sizew; i++) {
/* turn on the bit in the fd_set */
FD_SET(connections[count].write_fds[i], &connections[count].writeset);
}
for (i=0; i< connections[count].sizee; i++) {
/* turn on the bit in the fd_set */
FD_SET(connections[count].exception_fds[i],
&connections[count].exceptionset);
}
}
/* utility function to take the user specified fds in and prepare for
select call */
static xsbBool list_sockfd(prolog_term list, fd_set *fdset, int *max_fd,
int **fds, int * size)
{
int i=0;
prolog_term local=list;
prolog_term head;
*size = getsize(local);
*fds = (int*)malloc(sizeof(int)*(*size));
while (!isnil(list)) {
head = p2p_car(list);
(*fds)[i++] = p2c_int(head);
list = p2p_cdr(list);
}
for (i=0; i<(*size); i++) {
/* turn on the bit in the fd_set */
FD_SET((*fds)[i], fdset);
*max_fd = max(*max_fd, (*fds)[i]);
}
return TRUE;
}
/* utility function to return the available socket descriptors after testing */
static void test_ready(prolog_term *avail_sockfds, fd_set *fdset,
int *fds, int size)
{
prolog_term head;
int i=0;
for (i=0;i<size;i++) {
if (FD_ISSET(fds[i], fdset)) {
head = p2p_car(*avail_sockfds);
c2p_int(fds[i], head);
*avail_sockfds = p2p_cdr(*avail_sockfds);
c2p_list(*avail_sockfds);
}
}
c2p_nil(*avail_sockfds);
return;
}
/* utility function to destroy a select call */
static void select_destroy(char *connection_name)
{
int i;
int connectname_found = FALSE;
for (i=0; i < MAXCONNECT; i++) {
if(connections[i].empty_flag==FALSE) {
/* find the matching connection_name to destroy */
if (strcmp(connection_name, connections[i].connection_name) == 0) {
connectname_found = TRUE;
/* destroy the corresponding structure */
FD_ZERO(&connections[i].readset);
FD_ZERO(&connections[i].writeset);
FD_ZERO(&connections[i].exceptionset);
connections[i].connection_name = NULL;
connections[i].maximum_fd = 0;
/* free the fds obtained by malloc() */
free(connections[i].read_fds);
free(connections[i].write_fds);
free(connections[i].exception_fds);
connections[i].sizer = 0;
connections[i].sizew = 0 ;
connections[i].sizee = 0 ;
connections[i].empty_flag = TRUE; /* set the destroyed slot to empty */
break;
}
}
}
/* if no matching connection_name */
if (!connectname_found)
xsb_abort("[SOCKET_SELECT_DESTROY] connection `%s' doesn't exist",
connection_name);
}
/* utility function to check whether there is empty slot left to connect */
static int checkslot (void) {
int i;
for (i=0; i<MAXCONNECT;i++) {
if (connections[i].empty_flag == TRUE) break;
}
return i;
}
/* get the size of the list input from prolog side */
static int getsize (prolog_term list)
{
int size = 0;
prolog_term head;
while (!isnil(list)) {
head = p2p_car(list);
if(!(isinteger(head)|isboxedinteger(head)))
xsb_abort("A non-integer socket descriptor encountered in a socket operation");
list = p2p_cdr(list);
size++;
}
return size;
}
syntax highlighted by Code2HTML, v. 0.9.1