socket_xsb.c

00001 /* File:      socket_xsb.c
00002 ** Author(s): juliana, davulcu, kifer, songmei yu
00003 ** Contact:   xsb-contact@cs.sunysb.edu
00004 **
00005 ** Copyright (C) The Research Foundation of SUNY, 1999
00006 ** 
00007 ** XSB is free software; you can redistribute it and/or modify it under the
00008 ** terms of the GNU Library General Public License as published by the Free
00009 ** Software Foundation; either version 2 of the License, or (at your option)
00010 ** any later version.
00011 ** 
00012 ** XSB is distributed in the hope that it will be useful, but WITHOUT ANY
00013 ** WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
00014 ** FOR A PARTICULAR PURPOSE.  See the GNU Library General Public License for
00015 ** more details.
00016 ** 
00017 ** You should have received a copy of the GNU Library General Public License
00018 ** along with XSB; if not, write to the Free Software Foundation,
00019 ** Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
00020 **
00021 ** $Id: socket_xsb.c,v 1.35 2005/12/22 23:33:58 tswift Exp $
00022 ** 
00023 */
00024 
00025 #include <errno.h>
00026 #include <stdio.h>
00027 #include <stdlib.h>
00028 #include <string.h>
00029 #include <sys/types.h>
00030 #include <sys/stat.h>
00031 #include <signal.h>
00032 
00033 
00034 /* wind2unix.h must be included after sys/stat.h */
00035 #include "wind2unix.h"
00036 
00037 #include "xsb_time.h"
00038 
00039 /* The socket material */
00040 #ifdef WIN_NT
00041 #include <windows.h>
00042 #include <winuser.h>
00043 #include <winbase.h>
00044 #include <process.h>
00045 #include <tchar.h>
00046 #include <io.h>
00047 #include <stdarg.h>
00048 #include <winsock.h>
00049 #include "wsipx.h"
00050 #else /* UNIX */
00051 #include <sys/socket.h>
00052 #include <sys/uio.h>
00053 #include <netdb.h>
00054 #include <netinet/in.h>
00055 #include <arpa/inet.h>
00056 #include <unistd.h> 
00057 #include <fcntl.h> 
00058 #endif 
00059 
00060 #include "xsb_config.h"
00061 #include "xsb_debug.h"
00062 #include "socket_xsb.h"
00063 #include "flags_xsb.h"
00064 #include "thread_xsb.h"
00065 #include "thread_defs_xsb.h"
00066 #include "timer_xsb.h" 
00067 
00068 #include "auxlry.h"
00069 #include "cell_xsb.h"
00070 #include "error_xsb.h"
00071 #include "cinterf.h"
00072 #include "basictypes.h"
00073 
00074 #include "io_builtins_xsb.h"
00075 #include "socket_xsb.h"
00076 #include "psc_xsb.h"
00077 #include "register.h"
00078 #include "memory_xsb.h"
00079 
00080 #ifdef WIN_NT
00081 typedef int socklen_t;
00082 #elif defined(SOLARIS)
00083 typedef unsigned int socklen_t;
00084 #endif
00085 
00086 static u_long block_true = 1;
00087 static u_long block_false = 0;
00088 
00089 /* return error code handling */
00090 static xsbBool set_error_code(CTXTdeclc int ErrCode, int ErrCodeArgNumber,char *Where);
00091 
00092 /* In WIN_NT, this gets redefined into _fdopen by configs/special.h */
00093 extern FILE *fdopen(int fildes, const char *type);
00094 
00095 /* declare the utility functions for select calls (bodies of these functions are below) */
00096 static void init_connections(CTXTdecl);
00097 static void set_sockfd(int count);
00098 static xsbBool list_sockfd(prolog_term list, fd_set *fdset, int *max_fd,
00099                            int **fds, int * size);
00100 static void test_ready(CTXTdeclc prolog_term *avail_sockfds, fd_set *fdset,
00101                        int *fds, int size);
00102 static void select_destroy(char *connection_name);
00103 static int getsize (prolog_term list);
00104 static int checkslot (void);
00105 
00106 /* define the structure for select call */
00107 static struct connection_t {
00108      char *connection_name;
00109      int maximum_fd;
00110      int empty_flag;
00111      int sizer; /* size of read fds */
00112      int sizew; /* size of write fds */
00113      int sizee; /* size of exception fds */
00114      fd_set readset;    
00115      fd_set writeset;
00116      fd_set exceptionset;
00117      int *read_fds;     /* array of read fds */ 
00118      int *write_fds;    /* array of write fds */
00119      int *exception_fds;        /* array of exception fds */
00120 } connections[MAXCONNECT];
00121 
00122 static char *get_host_IP(char *host_name_or_IP) {
00123      struct hostent *host_struct;
00124      struct in_addr *ptr;
00125      char **listptr;
00126  
00127      /* if host_name_or_IP is an IP addr, then just return; else use 
00128         gethostbyname */
00129      if (IS_IP_ADDR(host_name_or_IP))
00130           return(host_name_or_IP);
00131      host_struct = gethostbyname(host_name_or_IP);
00132   
00133      listptr = host_struct->h_addr_list;
00134 
00135      if ((ptr = (struct in_addr *) *listptr++) != NULL) {
00136           xsb_mesg(" IP address: %s", inet_ntoa(*ptr));
00137           return(inet_ntoa(*ptr));
00138      }
00139      return NULL;
00140 }
00141 
00148 static int translate_domain(int xsb_domain, int *socket_domain) {
00149      if (xsb_domain == 0) {
00150           *socket_domain = AF_INET;
00151           return TRUE;
00152      } else if (xsb_domain == 1) {
00153           *socket_domain = AF_UNIX;
00154           xsb_abort("[SOCKET_REQUEST] domain AF_INET is not implemented");
00155           return TRUE;
00156      } else {
00157           xsb_abort("[SOCKET_REQUEST] Invalid domain (%d). Valid domains are: 0 - AF_INET, 1 - AF_UNIX", xsb_domain);
00158           return FALSE;
00159      }    
00160 }
00161 
00167 int read_select(SOCKET sock_handle, int timeout) {
00168      int rc = 0;
00169      fd_set fds;
00170      struct timeval tv;
00171 
00172      FD_ZERO(&fds);
00173      FD_SET(sock_handle, &fds);
00174      tv.tv_sec = timeout;
00175      tv.tv_usec = 0;
00176 
00177      if (tv.tv_sec > 0) {
00178           rc = select(sock_handle+1, &fds, NULL, NULL, &tv);
00179           if (rc < 0) {
00180                rc = 0;
00181           } else {
00182                rc = FD_ISSET(sock_handle, &fds);
00183           }
00184      } else {
00185           rc = 1;
00186      }
00187      return rc;
00188 }
00189   
00195 int write_select(SOCKET sock_handle, int timeout) {
00196      int rc = 0;
00197      fd_set fds;
00198      struct timeval tv;
00199 
00200      FD_ZERO(&fds);
00201      FD_SET(sock_handle, &fds);
00202      tv.tv_sec = timeout;
00203      tv.tv_usec = 0;
00204 
00205      if (tv.tv_sec > 0) {
00206           rc = select(sock_handle+1, NULL, &fds, NULL, &tv);
00207           if (rc < 0) {
00208                rc = 0;
00209           } else {
00210                rc = FD_ISSET(sock_handle, &fds);
00211           }
00212      } else {
00213           rc = 1;
00214      }
00215 
00216      return rc;
00217 }
00218   
00219 /* Returns:
00220    normal:  SOCK_OK
00221    EOF:     SOCK_READMSG_EOF
00222    error:   SOCK_READMSG_FAILED
00223    Read message header, then read the message itself.
00224 */
00225 static int readmsg(SOCKET sock_handle, char **msg_buff, unsigned long *msg_len) 
00226 {
00227      int actual_len;
00228      /* 4-char buf that keeps the length of the subsequent msg */
00229      char lenbuf[XSB_MSG_HEADER_LENGTH];
00230      unsigned int msglen, net_encoded_len;
00231 
00232      actual_len
00233           = (long)recvfrom(sock_handle,lenbuf,XSB_MSG_HEADER_LENGTH,0,NULL,0);
00234 
00235      if (SOCKET_OP_FAILED(actual_len)) return SOCK_READMSG_FAILED;
00236      if (actual_len == 0) {
00237           *msg_buff = NULL;
00238           return SOCK_READMSG_EOF;
00239      }
00240 
00241      memcpy((void *) &net_encoded_len, (void *) lenbuf, XSB_MSG_HEADER_LENGTH);
00242      msglen = ntohl(net_encoded_len);
00243      *msg_len = (msglen+1)*sizeof(char);
00244 
00245      if ((*msg_buff = (char *)mem_calloc(msglen+1, sizeof(char),OTHER_SPACE)) == NULL) {
00246           xsb_abort("[SOCKET_RECV] Can't allocate memory for the message buffer");
00247      }
00248 
00249      actual_len = (long) recvfrom(sock_handle,*msg_buff,msglen,0,NULL,0);
00250      if (SOCKET_OP_FAILED(actual_len)) return SOCK_READMSG_FAILED;
00251 
00252      /* The following should never arise. Points to a bug: some kind of mismatch
00253         between the communicating machines. */
00254      if ((unsigned int)actual_len != msglen)
00255           xsb_warn("[SOCKET_RECV] Message length %ld differs from the header value %ld",
00256                    msglen, actual_len);
00257 
00258      return SOCK_OK;
00259 }
00260 
00261 static int socket_accept(CTXTdeclc SOCKET *sock_handle, int timeout) {     
00262      SOCKET sock_handle_in = (SOCKET) ptoc_int(CTXTc 2);
00263      if (read_select(sock_handle_in, timeout)) {
00264           *sock_handle = accept(sock_handle_in, NULL, NULL);
00265           return NORMAL_TERMINATION;
00266      } else {
00267           return TIMED_OUT;
00268      }
00269 }
00270 
00271 static int socket_connect(CTXTdeclc int *rc, int timeout) {
00272      int error;
00273      socklen_t len;
00274      SOCKET sock_handle;
00275      int domain, portnum;
00276      SOCKADDR_IN socket_addr;
00277     
00278      domain = ptoc_int(CTXTc 2);
00279      sock_handle = (SOCKET) ptoc_int(CTXTc 3);
00280      portnum = ptoc_int(CTXTc 4);
00281 
00283      translate_domain(domain, &domain);
00284     
00285      /*** prepare to connect ***/
00286      FillWithZeros(socket_addr);
00287      socket_addr.sin_port = htons((unsigned short)portnum);
00288      socket_addr.sin_family = AF_INET;
00289      socket_addr.sin_addr.s_addr =
00290           inet_addr((char*)get_host_IP(ptoc_string(CTXTc 5)));
00291 
00292      
00293      if (timeout > 0) {
00294           /* Set up timeout */
00295           
00296 
00297           if(! SET_SOCKET_BLOCKING(sock_handle, block_false)) {
00298                xsb_error("Can't save options");
00299                return TIMER_SETUP_ERR;
00300           }
00301           
00302           /* This will return immediately */
00303           *rc = connect(sock_handle,(PSOCKADDR)&socket_addr,sizeof(socket_addr));
00304           error = XSB_SOCKET_ERRORCODE;
00305 
00306           /* restore flags */
00307           if(! SET_SOCKET_BLOCKING(sock_handle, block_true)) {
00308                xsb_error("Can't restore the flags: %d (0x%x)", XSB_SOCKET_ERRORCODE, XSB_SOCKET_ERRORCODE);
00309                return TIMER_SETUP_ERR;
00310           }
00311           
00312           /* return and indicate an error immediately unless the connection
00313            * was successful or the connect is still in progress. */
00314           if(*rc < 0 && error != EINPROGRESS && error != EWOULDBLOCK) {
00315                *rc = error;
00316                return NORMAL_TERMINATION; /* Since it didn't time out */
00317           }
00318           
00319           /* Wait until the connect is completed (or a timeout occurs) */
00320           error = write_select(sock_handle, timeout);
00321           
00322           if(error == 0) {
00323                closesocket(sock_handle);
00324                *rc = XSB_SOCKET_ERRORCODE;
00325                return TIMED_OUT;
00326           }
00327           
00328           /* Get the return code from the connect */
00329           len=sizeof(error);
00330           error = GETSOCKOPT(sock_handle, SOL_SOCKET, SO_ERROR, &error, &len);
00331           if(error < 0) {
00332                xsb_error("GETSOCKOPT failed");
00333                *rc = error;
00334                return NORMAL_TERMINATION; /* Since it didn't time out */
00335           }
00336           
00337           /* error=0 means success, otherwise it contains the errno */
00338           if(error) {
00339                *rc = error;
00340                return NORMAL_TERMINATION; /* Since it didn't time out */
00341           }
00342           
00343           *rc = sock_handle;
00344           return NORMAL_TERMINATION;
00345      } else {
00346           *rc = connect(sock_handle,(PSOCKADDR)&socket_addr,sizeof(socket_addr));
00347           return NORMAL_TERMINATION;
00348      }
00349 }
00350 
00351 static int socket_recv(CTXTdeclc int *rc, char** buffer, unsigned long *buffer_len, int timeout) {
00352      SOCKET sock_handle = (SOCKET) ptoc_int(CTXTc 2);
00353      if (read_select(sock_handle, timeout)) {
00354           *rc = readmsg(sock_handle, buffer, buffer_len);
00355           return NORMAL_TERMINATION;
00356      } else {
00357           return TIMED_OUT;
00358      }
00359 }
00360 
00361 static int socket_send(CTXTdeclc int *rc, int timeout) {
00362      SOCKET sock_handle = (SOCKET) ptoc_int(CTXTc 2);
00363      char *send_msg_aux = ptoc_string(CTXTc 3);
00364      unsigned int msg_body_len, network_encoded_len;
00365      char *message_buffer;
00366 
00367      if (!write_select(sock_handle, timeout)) {
00368           return TIMED_OUT;
00369      }
00370 
00371      msg_body_len = strlen(send_msg_aux);
00372 
00373      /* We use the first XSB_MSG_HEADER_LENGTH bytes for the message size.*/
00374      message_buffer = mem_calloc(msg_body_len + XSB_MSG_HEADER_LENGTH + 1, sizeof(char),LEAK_SPACE);
00375      if (message_buffer == NULL) {
00376           xsb_abort("[SOCKET_SEND] Can't allocate memory for the message buffer");
00377      }
00378 
00379      network_encoded_len = (unsigned int) htonl((unsigned long int) msg_body_len); 
00380      memcpy((void*) (message_buffer), (void *) &network_encoded_len, XSB_MSG_HEADER_LENGTH);
00381      strcpy(message_buffer + XSB_MSG_HEADER_LENGTH, send_msg_aux);
00382 
00383      *rc = sendto(sock_handle, message_buffer, msg_body_len+XSB_MSG_HEADER_LENGTH, 0, NULL, 0);
00384      mem_dealloc(message_buffer,(msg_body_len + XSB_MSG_HEADER_LENGTH + 1)*sizeof(char),LEAK_SPACE);
00385 
00386      return NORMAL_TERMINATION;
00387 }
00388 
00389 static int socket_get0(CTXTdeclc int *rc, char* message_read, int timeout) {
00390      SOCKET sock_handle;
00391      sock_handle = (SOCKET) ptoc_int(CTXTc 2);
00392      if (read_select(sock_handle, timeout)) {
00393           *rc = recvfrom(sock_handle, message_read, 1, 0, NULL, 0);
00394           return NORMAL_TERMINATION;
00395      } else {
00396           return TIMED_OUT;
00397      }
00398 }
00399   
00400 static int socket_put(CTXTdeclc int *rc, int timeout) {
00401      SOCKET sock_handle;
00402      char tmpch;        
00403     
00404      sock_handle = (SOCKET) ptoc_int(CTXTc 2);
00405      tmpch = (char)ptoc_int(CTXTc 3);
00406 
00407      if (write_select(sock_handle, timeout)) {
00408         *rc = sendto(sock_handle, &tmpch, 1, 0, NULL,0);
00409           return NORMAL_TERMINATION;
00410      } else {
00411           return TIMED_OUT;
00412      }
00413 }
00414 
00415 /* in order to save builtin numbers, create a single socket function with
00416  * options socket_request(SockOperation,....)  */
00417 xsbBool xsb_socket_request(CTXTdecl)
00418 {
00419      int ecode = 0;  /* error code for socket ops */
00420      int timeout_flag;
00421      SOCKET sock_handle;
00422      int domain, portnum;
00423      SOCKADDR_IN socket_addr;
00424      struct linger sock_linger_opt;
00425      int rc;
00426      char *message_buffer;
00427      unsigned long msg_len;
00428      char char_read;
00429 
00430      switch (ptoc_int(CTXTc 1)) {
00431      case SOCKET_ROOT: /* this is the socket() request */
00432           /* socket_request(SOCKET_ROOT,+domain,-socket_fd,-Error,_,_,_) 
00433              Currently only AF_INET domain */
00434           domain = ptoc_int(CTXTc 2); 
00435           if (!translate_domain(domain, &domain)) {
00436                return FALSE;
00437           }
00438     
00439           sock_handle = socket(domain, SOCK_STREAM, IPPROTO_TCP);
00440         
00441           /* error handling */
00442           if (BAD_SOCKET(sock_handle)) {
00443                ecode = XSB_SOCKET_ERRORCODE;
00444                perror("SOCKET_REQUEST");
00445           } else {
00446                ecode = SOCK_OK;
00447           }
00448 
00449           ctop_int(CTXTc 3, (SOCKET) sock_handle);
00450         
00451           return set_error_code(CTXTc ecode, 4, "SOCKET_REQUEST");
00452 
00453      case SOCKET_BIND:
00454           /* socket_request(SOCKET_BIND,+domain,+sock_handle,+port,-Error,_,_) 
00455              Currently only supports AF_INET */
00456           sock_handle = (SOCKET) ptoc_int(CTXTc 3);
00457           portnum = ptoc_int(CTXTc 4);
00458           domain = ptoc_int(CTXTc 2);
00459 
00460           if (!translate_domain(domain, &domain)) {
00461                return FALSE;
00462           }
00463     
00464           /* Bind server to the agreed upon port number.
00465           ** See commdef.h for the actual port number. */
00466           FillWithZeros(socket_addr);
00467           socket_addr.sin_port = htons((unsigned short)portnum);
00468           socket_addr.sin_family = AF_INET;
00469 #ifndef WIN_NT
00470           socket_addr.sin_addr.s_addr = htonl(INADDR_ANY);
00471 #endif
00472     
00473           rc = bind(sock_handle, (PSOCKADDR) &socket_addr, sizeof(socket_addr));
00474         
00475           /* error handling */
00476           if (SOCKET_OP_FAILED(rc)) {
00477                ecode = XSB_SOCKET_ERRORCODE;
00478                perror("SOCKET_BIND");
00479           } else
00480                ecode = SOCK_OK;
00481 
00482           return set_error_code(CTXTc ecode, 5, "SOCKET_BIND");
00483 
00484      case SOCKET_LISTEN: 
00485           /* socket_request(SOCKET_LISTEN,+sock_handle,+length,-Error,_,_,_) */
00486           sock_handle = (SOCKET) ptoc_int(CTXTc 2);
00487           rc = listen(sock_handle, ptoc_int(CTXTc 3));
00488 
00489           /* error handling */
00490           if (SOCKET_OP_FAILED(rc)) {
00491                ecode = XSB_SOCKET_ERRORCODE;
00492                perror("SOCKET_LISTEN");
00493           } else
00494                ecode = SOCK_OK;
00495 
00496           return set_error_code(CTXTc ecode, 4, "SOCKET_LISTEN");
00497 
00498      case SOCKET_ACCEPT:
00499           timeout_flag = socket_accept(CTXTc &rc, (int)pflags[SYS_TIMER]);
00500           
00501           if (timeout_flag == TIMED_OUT) {
00502                return set_error_code(CTXTc TIMEOUT_ERR, 4, "SOCKET_SEND");
00503           } else {
00504                /* error handling */ 
00505                if (BAD_SOCKET(rc)) {
00506                     ecode = XSB_SOCKET_ERRORCODE;
00507                     perror("SOCKET_ACCEPT");
00508                     sock_handle = rc; /* shut up warning */
00509                } else {
00510                     sock_handle = rc; /* accept() returns sock_out */
00511                     ecode = SOCK_OK;
00512                }
00513                
00514                ctop_int(CTXTc 3, (SOCKET) sock_handle);
00515                
00516                return set_error_code(CTXTc ecode,  4,  "SOCKET_ACCEPT");          
00517           }
00518      case SOCKET_CONNECT: {
00519           /* socket_request(SOCKET_CONNECT,+domain,+sock_handle,+port,
00520              +hostname,-Error) */
00521           timeout_flag = socket_connect(CTXTc &rc, (int)pflags[SYS_TIMER]);
00522 
00523           if (timeout_flag == TIMED_OUT) {
00524                return set_error_code(CTXTc TIMEOUT_ERR, 6, "SOCKET_CONNECT");
00525           } else if (timeout_flag == TIMER_SETUP_ERR) {
00526                return set_error_code(CTXTc TIMER_SETUP_ERR, 6, "SOCKET_CONNECT");
00527           } else {
00528                /* error handling */
00529                if (SOCKET_OP_FAILED(rc)) {
00530                     ecode = XSB_SOCKET_ERRORCODE;
00531                     perror("SOCKET_CONNECT");
00532                     /* close, because if connect() fails then socket becomes unusable */
00533                     closesocket(ptoc_int(CTXTc 3));
00534                } else {
00535                     ecode = SOCK_OK;
00536                }
00537                return set_error_code(CTXTc ecode,  6,  "SOCKET_CONNECT");
00538           }
00539      }
00540 
00541      case SOCKET_CLOSE: 
00542           /* socket_request(SOCKET_CLOSE,+sock_handle,-Error,_,_,_,_) */
00543     
00544           sock_handle = (SOCKET)ptoc_int(CTXTc 2);
00545     
00546           /* error handling */
00547           rc = closesocket(sock_handle);
00548           if (SOCKET_OP_FAILED(rc)) {
00549                ecode = XSB_SOCKET_ERRORCODE;
00550                perror("SOCKET_CLOSE");
00551           } else
00552                ecode = SOCK_OK;
00553     
00554           return set_error_code(CTXTc ecode, 3, "SOCKET_CLOSE");
00555     
00556      case SOCKET_RECV:
00557           /* socket_request(SOCKET_RECV,+Sockfd, -Msg, -Error,_,_,_) */
00558           timeout_flag = socket_recv(CTXTc &rc, &message_buffer, &msg_len, (int)pflags[SYS_TIMER]);
00559           
00560           if (timeout_flag == TIMED_OUT) {
00561                return set_error_code(CTXTc TIMEOUT_ERR, 4, "SOCKET_SEND");
00562           } else {
00563                /* error handling */
00564                switch (rc) {
00565                case SOCK_OK:
00566                     ecode = SOCK_OK;
00567                     break;
00568                case SOCK_READMSG_FAILED:
00569                     ecode = XSB_SOCKET_ERRORCODE;
00570                     perror("SOCKET_RECV");
00571                     break;
00572                case SOCK_READMSG_EOF:
00573                     ecode = SOCK_EOF;
00574                     break;
00575                default:
00576                     xsb_abort("[SOCKET_RECV] XSB bug: invalid return code from readmsg");
00577                }
00578                
00579                if (message_buffer != NULL) {
00580                     ctop_string(CTXTc 3, (char*)string_find(message_buffer, 1));
00581                     mem_dealloc(message_buffer,msg_len,OTHER_SPACE);
00582                } else {  /* this happens at end of file */
00583                     ctop_string(CTXTc 3, (char*)string_find("", 1));
00584                }
00585                
00586                return set_error_code(CTXTc ecode, 4, "SOCKET_RECV");  
00587           }
00588                
00589      case SOCKET_SEND:
00590           /* socket_request(SOCKET_SEND,+Sockfd, +Msg, -Error,_,_,_) */
00591           timeout_flag = socket_send(CTXTc &rc, (int)pflags[SYS_TIMER]);
00592     
00593           if (timeout_flag == TIMED_OUT) {
00594                return set_error_code(CTXTc TIMEOUT_ERR, 4, "SOCKET_SEND");
00595           } else {
00596                /* error handling */
00597                if (SOCKET_OP_FAILED(rc)) {
00598                     ecode = XSB_SOCKET_ERRORCODE;
00599                     perror("SOCKET_SEND");
00600                } else {
00601                     ecode = SOCK_OK;
00602                }
00603                return set_error_code(CTXTc ecode,  4,  "SOCKET_SEND"); 
00604           }
00605 
00606      case SOCKET_GET0:
00607           /* socket_request(SOCKET_GET0,+Sockfd,-C,-Error,_,_,_) */
00608           message_buffer = &char_read;
00609           timeout_flag = socket_get0(CTXTc &rc, message_buffer, (int)pflags[SYS_TIMER]);
00610           
00611           if (timeout_flag == TIMED_OUT) {
00612                return set_error_code(CTXTc TIMEOUT_ERR, 4, "SOCKET_SEND");
00613           } else {
00614                /*error handling */ 
00615                switch (rc) {
00616                case 1:
00617                     ctop_int(CTXTc 3,(unsigned char)message_buffer[0]);
00618                     ecode = SOCK_OK;
00619                     break;
00620                case 0:
00621                     ecode = SOCK_EOF;
00622                     break;
00623                default:
00624                     ctop_int(CTXTc 3,-1);
00625                     perror("SOCKET_GET0");
00626                     ecode = XSB_SOCKET_ERRORCODE;
00627                }
00628                
00629                return set_error_code(CTXTc ecode,  4,  "SOCKET_GET0");
00630           }    
00631      case SOCKET_PUT:
00632           /* socket_request(SOCKET_PUT,+Sockfd,+C,-Error_,_,_) */
00633           timeout_flag = socket_put(CTXTc &rc, (int)pflags[SYS_TIMER]);
00634                
00635           if (timeout_flag == TIMED_OUT) {
00636                return set_error_code(CTXTc TIMEOUT_ERR, 4, "SOCKET_SEND");
00637           } else {
00638                /* error handling */
00639                if (rc == 1) {
00640                     ecode = SOCK_OK;
00641                } else if (SOCKET_OP_FAILED(rc)) {
00642                     ecode = XSB_SOCKET_ERRORCODE;
00643                     perror("SOCKET_PUT");
00644                }
00645                
00646                return set_error_code(CTXTc ecode,  4,  "SOCKET_PUT");
00647           }
00648      case SOCKET_SET_OPTION: {
00649           /* socket_request(SOCKET_SET_OPTION,+Sockfd,+OptionName,+Value,_,_,_) */
00650     
00651           char *option_name = ptoc_string(CTXTc 3);
00652     
00653           sock_handle = (SOCKET)ptoc_int(CTXTc 2);
00654 
00655           /* Set the "linger" parameter to a small number of seconds */
00656           if (0==strcmp(option_name,"linger")) {
00657                int  linger_time=ptoc_int(CTXTc 4);
00658       
00659                if (linger_time < 0) {
00660                     sock_linger_opt.l_onoff = FALSE;
00661                     sock_linger_opt.l_linger = 0;
00662                } else {
00663                     sock_linger_opt.l_onoff = TRUE;
00664                     sock_linger_opt.l_linger = linger_time;
00665                }
00666       
00667                if (SETSOCKOPT(sock_handle, SOL_SOCKET, SO_LINGER,
00668                               &sock_linger_opt, sizeof(sock_linger_opt))
00669                    < 0) {
00670                     xsb_warn("[SOCKET_SET_OPTION] Cannot set socket linger time");
00671                     return FALSE;
00672                } 
00673           }else {
00674                xsb_warn("[SOCKET_SET_OPTION] Invalid option, `%s'", option_name);
00675                return FALSE;
00676           }
00677     
00678           return TRUE;
00679      }
00680 
00681      case SOCKET_SET_SELECT:  {  
00682           /*socket_request(SOCKET_SET_SELECT,+connection_name,
00683             +R_sockfd,+W_sockfd,+E_sockfd) */
00684           prolog_term R_sockfd, W_sockfd, E_sockfd;
00685           int i, connection_count;
00686           int rmax_fd=0, wmax_fd=0, emax_fd=0; 
00687           char *connection_name = ptoc_string(CTXTc 2);
00688     
00689           /* bind fds to input arguments */
00690           R_sockfd = reg_term(CTXTc 3);
00691           W_sockfd = reg_term(CTXTc 4);
00692           E_sockfd = reg_term(CTXTc 5); 
00693     
00694           /* initialize the array of connect_t structure for select call */     
00695           init_connections(CTXT); 
00696     
00697           /* check whether the same connection name exists */
00698           for (i=0;i<MAXCONNECT;i++) {
00699                if ((connections[i].empty_flag==FALSE) &&
00700                    (strcmp(connection_name,connections[i].connection_name)==0))         
00701                     xsb_abort("[SOCKET_SET_SELECT] Connection `%s' already exists!",
00702                               connection_name);
00703           }
00704     
00705           /* check whether there is empty slot left for connection */   
00706           if ((connection_count=checkslot())<MAXCONNECT) {
00707                if (connections[connection_count].connection_name == NULL) {
00708                     connections[connection_count].connection_name = connection_name;
00709                     connections[connection_count].empty_flag = FALSE;
00710         
00711                     /* call the utility function separately to take the fds in */
00712                     list_sockfd(R_sockfd, &connections[connection_count].readset,
00713                                 &rmax_fd, &connections[connection_count].read_fds,
00714                                 &connections[connection_count].sizer);
00715                     list_sockfd(W_sockfd, &connections[connection_count].writeset,
00716                                 &wmax_fd, &connections[connection_count].write_fds,
00717                                 &connections[connection_count].sizew);
00718                     list_sockfd(E_sockfd, &connections[connection_count].exceptionset, 
00719                                 &emax_fd,&connections[connection_count].exception_fds,
00720                                 &connections[connection_count].sizee);
00721         
00722                     connections[connection_count].maximum_fd =
00723                          xsb_max(xsb_max(rmax_fd,wmax_fd), emax_fd);
00724                } else 
00725                     /* if this one is reached, it is probably a bug */
00726                     xsb_abort("[SOCKET_SET_SELECT] All connections are busy!");
00727           } else
00728                xsb_abort("[SOCKET_SET_SELECT] Max number of collections exceeded!");
00729     
00730           return TRUE;
00731      }
00732   
00733      case SOCKET_SELECT: {
00734           /* socket_request(SOCKET_SELECT,+connection_name, +timeout
00735              -avail_rsockfds,-avail_wsockfds,
00736              -avail_esockfds,-ecode)
00737              Returns 3 prolog_terms for available socket fds */
00738 
00739           prolog_term Avail_rsockfds, Avail_wsockfds, Avail_esockfds;
00740           prolog_term Avail_rsockfds_tail, Avail_wsockfds_tail, Avail_esockfds_tail;
00741 
00742           int maxfd;
00743           int i;       /* index for connection_count */
00744           char *connection_name = ptoc_string(CTXTc 2);
00745           struct timeval *tv;
00746           prolog_term timeout_term;
00747           int timeout =0;
00748           int connectname_found = FALSE;
00749           int count=0;                  
00750 
00751           /* specify the time out */
00752           timeout_term = reg_term(CTXTc 3);
00753           if (isinteger(timeout_term)|isboxedinteger(timeout_term)) {
00754                timeout = oint_val(timeout_term);
00755                /* initialize tv */
00756                tv = (struct timeval *)mem_alloc(sizeof(struct timeval),LEAK_SPACE);
00757                tv->tv_sec = timeout;
00758                tv->tv_usec = 0;
00759           } else
00760                tv = NULL; /* no timeouts */
00761 
00762           /* initialize the prolog term */ 
00763           Avail_rsockfds = p2p_new(CTXT);
00764           Avail_wsockfds = p2p_new(CTXT);
00765           Avail_esockfds = p2p_new(CTXT); 
00766 
00767           /* bind to output arguments */
00768           Avail_rsockfds = reg_term(CTXTc 4);
00769           Avail_wsockfds = reg_term(CTXTc 5);
00770           Avail_esockfds = reg_term(CTXTc 6);
00771 
00772           Avail_rsockfds_tail = Avail_rsockfds;
00773           Avail_wsockfds_tail = Avail_wsockfds;
00774           Avail_esockfds_tail = Avail_esockfds;
00775 
00776           c2p_list(CTXTc Avail_rsockfds_tail);
00777           c2p_list(CTXTc Avail_wsockfds_tail);  
00778           c2p_list(CTXTc Avail_esockfds_tail); 
00779     
00780           for (i=0; i < MAXCONNECT; i++) {
00781                /* find the matching connection_name to select */
00782                if(connections[i].empty_flag==FALSE) {
00783                     if (strcmp(connection_name, connections[i].connection_name) == 0) {
00784                          connectname_found = TRUE;
00785                          count = i;
00786                          break;
00787                     } 
00788                }
00789           }
00790           if( i >= MAXCONNECT )  /* if no matching connection_name */
00791                xsb_abort("[SOCKET_SELECT] connection `%s' doesn't exist",
00792                          connection_name); 
00793     
00794           /* compute maxfd for select call */
00795           maxfd = connections[count].maximum_fd + 1;
00796 
00797           /* FD_SET all sockets */
00798           set_sockfd( count );
00799 
00800           /* test whether the socket fd is available */
00801           rc = select(maxfd, &connections[count].readset, 
00802                       &connections[count].writeset,
00803                       &connections[count].exceptionset, tv);
00804     
00805           /* error handling */  
00806           if (rc == 0)     /* timed out */
00807                ecode = TIMEOUT_ERR;
00808           else if (SOCKET_OP_FAILED(rc)) {
00809                perror("SOCKET_SELECT");
00810                ecode = XSB_SOCKET_ERRORCODE;
00811           } else {      /* no error */
00812                ecode = SOCK_OK;
00813          
00814                /* call the utility function to return the available socket fds */
00815                test_ready(CTXTc &Avail_rsockfds_tail, &connections[count].readset,
00816                           connections[count].read_fds,connections[count].sizer);
00817 
00818                test_ready(CTXTc &Avail_wsockfds_tail, &connections[count].writeset,
00819                           connections[count].write_fds,connections[count].sizew);
00820 
00821                test_ready(CTXTc &Avail_esockfds_tail,&connections[count].exceptionset,
00822                           connections[count].exception_fds,connections[count].sizee);
00823           }
00824 
00825           if (tv) mem_dealloc((struct timeval *)tv,sizeof(struct timeval),LEAK_SPACE);
00826           return set_error_code(CTXTc ecode, 7, "SOCKET_SELECT");
00827      }
00828 
00829      case SOCKET_SELECT_DESTROY:  { 
00830           /*socket_request(SOCKET_SELECT_DESTROY, +connection_name) */
00831           char *connection_name = ptoc_string(CTXTc 2);
00832           select_destroy(connection_name);
00833           return TRUE;
00834      }
00835 
00836      default:
00837           xsb_warn("[SOCKET_REQUEST] Invalid socket request %d", (int) ptoc_int(CTXTc 1));
00838           return FALSE;
00839      }
00840 
00841      /* This trick would report a bug, if a newly added case
00842         doesn't have a return clause */
00843      xsb_bug("SOCKET_REQUEST case %d has no return clause", ptoc_int(CTXTc 1));
00844 }
00845 
00846 
00847 static xsbBool set_error_code(CTXTdeclc int ErrCode, int ErrCodeArgNumber, char *Where)
00848 {
00849      prolog_term ecode_value_term, ecode_arg_term = p2p_new(CTXT);
00850   
00851      ecode_value_term = reg_term(CTXTc ErrCodeArgNumber);
00852      if (!isref(ecode_value_term) && 
00853          !(isinteger(ecode_value_term)|isboxedinteger(ecode_value_term)))
00854           xsb_abort("[%s] Arg %d (the error code) must be a variable or an integer!",
00855                     Where, ErrCodeArgNumber);
00856 
00857      c2p_int(CTXTc ErrCode, ecode_arg_term);
00858      return p2p_unify(CTXTc ecode_arg_term, ecode_value_term);
00859 }
00860 
00861 /* initialize the array of the structure */
00862 static void init_connections(CTXTdecl) 
00863 {
00864      int i;  
00865      static int initialized = FALSE; /* This is only initialized once. */
00866 
00867      SYS_MUTEX_LOCK(MUTEX_SOCKETS);
00868 
00869      if (!initialized) {
00870           for (i=0; i<MAXCONNECT; i++) {
00871                connections[i].connection_name = NULL; 
00872                connections[i].maximum_fd=0;
00873                connections[i].empty_flag=TRUE;
00874                /*clear all FD_SET */
00875                FD_ZERO(&connections[i].readset);
00876                FD_ZERO(&connections[i].writeset);
00877                FD_ZERO(&connections[i].exceptionset);
00878       
00879                connections[i].read_fds = 0;
00880                connections[i].write_fds = 0 ;
00881                connections[i].exception_fds = 0; 
00882                connections[i].sizer= 0;
00883                connections[i].sizew = 0 ;
00884                connections[i].sizee = 0 ;
00885           }
00886           initialized = TRUE;
00887      }
00888 
00889      SYS_MUTEX_UNLOCK(MUTEX_SOCKETS);
00890 }
00891 
00892 /* FD_SET the socket fds */
00893 static void set_sockfd(int count)
00894 {
00895      int i;
00896   
00897      FD_ZERO(&connections[count].readset);
00898      FD_ZERO(&connections[count].writeset);
00899      FD_ZERO(&connections[count].exceptionset);
00900   
00901      for (i=0; i< connections[count].sizer; i++) {
00902           /* turn on the bit in the fd_set */
00903           FD_SET(connections[count].read_fds[i], &connections[count].readset);
00904      }
00905 
00906      for (i=0; i< connections[count].sizew; i++) {
00907           /* turn on the bit in the fd_set */
00908           FD_SET(connections[count].write_fds[i], &connections[count].writeset);
00909      }
00910 
00911      for (i=0; i< connections[count].sizee; i++) {
00912           /* turn on the bit in the fd_set */
00913           FD_SET(connections[count].exception_fds[i],
00914                  &connections[count].exceptionset);
00915      }
00916 }
00917 
00918 /* utility function to take the user specified fds in and prepare for
00919    select call */
00920 
00921 static xsbBool list_sockfd(prolog_term list, fd_set *fdset, int *max_fd,
00922                            int **fds, int * size)
00923 {
00924      int i=0;
00925      prolog_term local=list;
00926      prolog_term head;
00927 
00928      *size = getsize(local);
00929      *fds = (int*)mem_alloc(sizeof(int)*(*size),OTHER_SPACE);
00930 
00931      while (!isnil(list)) {
00932           head = p2p_car(list);
00933           (*fds)[i++] = p2c_int(head);
00934           list = p2p_cdr(list);
00935      }
00936 
00937      for (i=0; i<(*size); i++) {
00938           /* turn on the bit in the fd_set */
00939           FD_SET((*fds)[i], fdset);
00940           *max_fd = xsb_max(*max_fd, (*fds)[i]);
00941      }
00942 
00943      return TRUE;
00944 }
00945 
00946 /* utility function to return the available socket descriptors after testing */
00947 static void test_ready(CTXTdeclc prolog_term *avail_sockfds, fd_set *fdset,
00948                        int *fds, int size) 
00949 {
00950      prolog_term head;
00951      int i=0;
00952 
00953      for (i=0;i<size;i++) {
00954           if (FD_ISSET(fds[i], fdset)) {
00955                head = p2p_car(*avail_sockfds);
00956                c2p_int(CTXTc fds[i], head);
00957                *avail_sockfds = p2p_cdr(*avail_sockfds);
00958                c2p_list(CTXTc *avail_sockfds);
00959           } 
00960      }
00961      c2p_nil(CTXTc *avail_sockfds);
00962      return;
00963 }
00964 
00965 /* utility function to destroy a select call */
00966 static void select_destroy(char *connection_name)
00967 {
00968      int i;
00969      int connectname_found = FALSE;
00970 
00971      for (i=0; i < MAXCONNECT; i++) {
00972           if(connections[i].empty_flag==FALSE) {
00973                /* find the matching connection_name to destroy */
00974                if (strcmp(connection_name, connections[i].connection_name) == 0) {
00975                     connectname_found = TRUE;
00976               
00977                     /* destroy the corresponding structure */
00978                     FD_ZERO(&connections[i].readset);
00979                     FD_ZERO(&connections[i].writeset);
00980                     FD_ZERO(&connections[i].exceptionset);
00981 
00982                     connections[i].connection_name = NULL;
00983                     connections[i].maximum_fd = 0;
00984 
00985                     /* free the fds obtained by mem_alloc() */
00986                     mem_dealloc(connections[i].read_fds,connections[i].sizer,OTHER_SPACE);
00987                     mem_dealloc(connections[i].write_fds,connections[i].sizew,OTHER_SPACE);
00988                     mem_dealloc(connections[i].exception_fds,connections[i].sizee,OTHER_SPACE); 
00989       
00990                     connections[i].sizer = 0;
00991                     connections[i].sizew = 0 ;
00992                     connections[i].sizee = 0 ;
00993 
00994                     connections[i].empty_flag = TRUE; /* set the destroyed slot to empty */
00995                     break;
00996                }
00997           }
00998      }
00999   
01000      /* if no matching connection_name */
01001      if (!connectname_found)
01002           xsb_abort("[SOCKET_SELECT_DESTROY] connection `%s' doesn't exist", 
01003                     connection_name);
01004   
01005 }
01006 
01007 /* utility function to check whether there is empty slot left to connect */
01008 static int checkslot (void) {
01009      int i;
01010      for (i=0; i<MAXCONNECT;i++) {
01011           if (connections[i].empty_flag == TRUE) break;
01012      }
01013      return i;
01014 }
01015 
01016 /* get the size of the list input from prolog side */
01017 static int getsize (prolog_term list)
01018 {
01019      int size = 0;
01020      prolog_term head;
01021 
01022      while (!isnil(list)) {
01023           head = p2p_car(list);
01024           if(!(isinteger(head)|isboxedinteger(head))) 
01025                xsb_abort("A non-integer socket descriptor encountered in a socket operation");
01026           list = p2p_cdr(list);
01027           size++;
01028      }
01029 
01030      return size;
01031 }

Generated on Wed Jul 26 13:30:42 2006 for XSB by  doxygen 1.4.5