/* * ...and: * Copyright (C) 1998 Dave Cridland * Copyright (C) 1998 Stanley J. Brooks */ /* * This file implements a special-purpose streams-like file interface * for use with S-Lang programs. It supports normal files, and TCP * and UDP sockets, at the moment. Will hopefully be extended to handle * serial devices in the near/distant future. * It adds some Select-like calls, adjusted to be more slangy. * It adds some generic networking stuff... Like Service and DNS * lookups, etc. * Note that the hton stuff is done here, instead of in the SLang files * themselves... Much easier, but could be a bit of a pain. * hton* and ntoh* are both interfaced here, too, indirectly via the * array_get_u32() and array_put_u32() intrinsics. These let us * read/write an integer in network-order (MSB 1st) from/to some * offset in a Char_Type array. Well, we do got a dependancy on * int being 32 bit here :( * * The ideas herein are heavily borrowed from John E. Davis's slfile.c, * from slang-1.0.3. Many header declarations are copied from J.E.D.'s * _slang.h file, since we need some of the hidden intrinsics in order * to deal with: * (1) Char_Type arrays -- vf_read(file,array,n) vf_write(file,array,n) * (2) reference types -- vf_read(file,&buf,n) * (3) SLang_Object_Type -- set_action(file,i,"function",cookie) * where cookie is an arbitrary object to pass. * * Why do we need Char_Type arrays? Because strings can't handle * embedded nulls, whereas binary files and sockets surely can and do. * ^^^ somewhat outdated remark since slang-1.3.* now has BString_Type... * so that vfile-module now needs some serious re-working and * simplification. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* we need stripped-down version of SLang_Object_Type for cookies: */ typedef struct { unsigned char data_type; /* SLANG_INT_TYPE, ... */ union { void * p_val; double f_val; }v; } SLang_Object_Type; /* * some extra internals we need, ripped out of _slang.h */ /* From: _slang.h */ extern int SLang_pop(SLang_Object_Type *); extern void SLang_free_object (SLang_Object_Type *); #if 0 extern int SLang_push(SLang_Object_Type *); /* doesn't handle ref-cts, etc */ #endif extern int _SLpush_slang_obj (SLang_Object_Type *); extern int _SLang_deref_assign (SLang_Ref_Type *); extern int _SLstack_depth(void); /* End: _slang.h */ /* * * Begin vfile file interface code * */ static int VFerrno = 0; static int char_array_data(SLang_Array_Type *at, void **data, int len) { int ct; if (at->data_type != SLANG_CHAR_TYPE) { SLang_doerror("Operation requires character array"); return -1; } if (len<0) ct = at->num_elements; else if (len > at->num_elements) { SLang_doerror("Too much data for array size"); return -1; }else ct = len; *data = at->data; return ct; } #ifndef AR_GETPUT static void ar_get_string(void) { int ix,len = 0; char *p,*s = NULL; void *data; SLang_Array_Type *at = NULL; if (SLang_pop_integer(&ix)) goto free_fail; if (-1 == SLang_pop_array (&at, 0)) goto free_fail; len = char_array_data(at,&data,-1); /* len is size of array */ if (ix < 0 || ix > len) { len = 0; goto free_fail; } if (!(len -= ix)) goto free_fail; s = (char*)data+ix; p = memchr(s,0,len); if (p) len = p - s; free_fail: if (at) SLang_free_array (at); p = SLmake_nstring(s,len+1); if (p) { SLang_push_string(p); SLfree(p); } return; } /* ar_get_u32() soon to be replaced by pack()/unpack() */ static int ar_get_u32(void) { int val=0,ix,len; void *data; SLang_Array_Type *at = NULL; if (SLang_pop_integer(&ix)) goto free_fail; if (-1 == SLang_pop_array (&at, 0)) goto free_fail; len = char_array_data(at,&data,-1); if (ix < 0 || len < (ix+4)) goto free_fail; /* ct = -1 */ val = *(int*)((char*)data+ix); val = ntohl((unsigned long int)val); free_fail: if (at) SLang_free_array (at); return val; } static void ar_put_u32(void) { int val,ix,len; void *data; SLang_Array_Type *at = NULL; if (SLang_pop_integer(&val)) goto free_fail; if (SLang_pop_integer(&ix)) goto free_fail; if (-1 == SLang_pop_array (&at, 0)) goto free_fail; len = char_array_data(at,&data,-1); if (ix < 0 || len < (ix+4)) goto free_fail; val = htonl((unsigned long int)val); *(int*)((char*)data+ix) = val; free_fail: if (at) SLang_free_array (at); return; } #endif #define SJ_VFILE_TYPE 128 typedef struct _VFILE { struct _VFILE *next; int fd; /* file# (0,1,2 are stdin,stdout,stderr) */ char *file; /* file name associated with pointer */ unsigned int flags; /* read,write,socket,etc */ SLang_MMT_Type *mmt; /* itself as an mmt */ char *rbuf0; /* address of alloc'd buff, or NULL */ char *rbufp; /* points 1st byte of read data */ char *rbufq; /* [bufp,bufq) has no rtermn char */ char *rbufr; /* [bufp,bufr) is data already read */ char *rbuft; /* above top-of-buffer (rbuf0+len(rbuf)) */ int rthrsh; /* event when this many bytes in rbuf */ int rtermn; /* event when this char is received (if>=0) */ int rerror; /* last error reported upon read */ int werror; /* last error reported upon write */ pid_t pid; struct _VFILE *wrvf; /* if non-NULL, data read pipes to this VFILE */ struct _VFILE *rdvf; /* if non-NULL, data written as it arrives here */ struct sockaddr_in sin; /* remote sockaddr_in, when relevant */ SLang_Name_Type *fns[4]; /* the functions to execute */ SLang_Object_Type cookies[4]; /* cookie structs for functions */ } VFILE; static char* VFerrmsg = ""; /* thoughts on linked pair rdvf,wrvf: restrict to rtermn=-1, thrsh>0 will read into rdvf while r=thrsh SO: (1) buffer refill is as normal, (2) write-trigger is dependant on rdata>=thrsh */ static VFILE *vfile_last = NULL; static int vfile_list_dirty = 0; #define SJ_READ 0x01 #define SJ_WRITE 0x02 #define SJ_BINARY 0x04 #define SJ_TCP 0x08 #define SJ_UDPu 0x10 #define SJ_UDPc 0x20 #define SJ_SIN 0x80 #define SJ_PROC 0x100 #define SJ_POPEN 0x200 #define SJ_EOF 0x2000 #define SJ_ERROR 0x4000 /* UDPc is 'connected' (use send/recv) */ /* UDPu is not connected (use sendto/recvfrom) */ #define SJ_UDP 0x30 #define SJ_SOCKET 0x38 /* String_Type ftp_hostport(host,port) * converts (int) host,port to the ascii h,h,h,h,p,p format * which ftp PORT command wants */ static char *VF_ftp_hostport(int *host,int *port) { static char psz[32]; /* must be static! */ char *cp; unsigned int v,i; v = *host; for (cp=psz,i = 4; i-- ; ) { sprintf(cp,"%d,", v >> 24); cp += strlen(cp); v = v << 8; } v = *port & 0xffff; sprintf(cp,"%d,%d", v >> 8, v); return psz; } static int VF_getservbyname(char *service) { struct servent *sp; if (NULL == (sp = getservbyname(service, "tcp"))) return -1; return (ntohs(sp->s_port)); } static int VF_inet_addr(char *DottedQuad) { return (int) ntohl(inet_addr(DottedQuad)); } static char *VF_inet_ntoa(int *ipnum) { struct in_addr in; in.s_addr = htonl(*ipnum); return inet_ntoa(in); } static int VF_gethostbyname(char *hostname) { struct hostent *hp; if (NULL == (hp = gethostbyname(hostname))) return (-1); return (ntohl(*(unsigned long *) hp->h_addr)); } static char *VF_gethostbyaddr(int *host) { struct hostent *hp; char *dummy; int h; h = htonl(*host); if (NULL == (hp = gethostbyaddr((char *) &h, 4, AF_INET))) return ""; dummy = SLmake_string(hp->h_name); return dummy; } static int set_O_NONBLOCK(int fd) { int flags,r = -1; if (-1 == (flags=fcntl(fd,F_GETFL))) VFerrmsg="Couldn't fcntl(fd,F_GETFL) on fd."; else if (-1 == fcntl(fd,F_SETFL,flags|O_NONBLOCK)) VFerrmsg="Couldn't set O_NONBLOCK on fd"; else r = 0; return r; } static int udp_bind(unsigned long ip, int port, struct sockaddr_in *From) { int s; if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { VFerrno=errno; VFerrmsg ="Unable to create socket"; return -3; } bzero((char *) From, sizeof(struct sockaddr_in)); From->sin_addr.s_addr = htonl(ip); From->sin_family = AF_INET; From->sin_port = htons(port); if (bind(s, (struct sockaddr *) From, sizeof(struct sockaddr_in)) < 0) { VFerrno=errno; VFerrmsg="Couldn't bind socket."; return -5; } if (set_O_NONBLOCK(s)) return -4; return s; } static int sock_connect(int type, unsigned long ip, int port, struct sockaddr_in *Rem) { int s; bzero((char *) Rem, sizeof(struct sockaddr_in)); Rem->sin_addr.s_addr = htonl(ip); Rem->sin_family = AF_INET; Rem->sin_port = htons(port); if ((s = socket(AF_INET, type, 0)) < 0) { VFerrno=errno; VFerrmsg ="Unable to create socket"; return -3; } if (connect(s, (struct sockaddr *) Rem, sizeof(struct sockaddr_in)) < 0) { VFerrno=errno; VFerrmsg="Unable to connect."; return -5; } if (set_O_NONBLOCK(s)) return -4; return s; } static int tcp_listen(void) { struct sockaddr_in Loc; int s; bzero((char *) &Loc, sizeof(Loc)); Loc.sin_addr.s_addr = INADDR_ANY; Loc.sin_family = AF_INET; Loc.sin_port = 0; if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) { VFerrno=errno; VFerrmsg="Unable to create socket."; return -3; } if (set_O_NONBLOCK(s)) return -4; if (bind(s, (struct sockaddr *) &Loc, sizeof(Loc)) < 0) { VFerrno=errno; VFerrmsg="Couldn't bind socket."; return -5; } if (listen(s, 5) < 0) { VFerrno=errno; VFerrmsg = "Couldn't listen socket."; return -6; } return s; } static unsigned int convert_flags(int mode) { unsigned int flags; switch (mode & O_ACCMODE) { case O_RDONLY: flags = SJ_READ; break; case O_WRONLY: flags = SJ_WRITE; break; case O_RDWR: flags = SJ_READ|SJ_WRITE; break; default: SLang_verror(SL_INVALID_PARM, "invalid open mode %08x", mode); flags=0; } return flags; } /* returns pointer to file entry if it is open and consistent with flags. Returns NULL otherwise */ static SLang_MMT_Type *pop_vfd(unsigned int flags, VFILE ** t_ptr) { VFILE *t; SLang_MMT_Type *mmt; if (!(mmt = SLang_pop_mmt(SJ_VFILE_TYPE))) { VFerrno = EBADF; return NULL; } t = (VFILE *) SLang_object_from_mmt(mmt); if ((*t_ptr = t)) { if (t->flags & flags) return mmt; VFerrno = EACCES; }else VFerrno = EBADF; SLang_free_mmt(mmt); return NULL; } static int close_vfile_type(SLang_MMT_Type *mmt, VFILE *t) { int i, r = -1, free_ct = 0; VFerrno = 0; if (t->fd < 0) { /* SLang_doerror("file already closed"); */ r = 0; goto return_r; } if (t->rdvf || t->wrvf) { SLang_doerror("You must set_wrdep(*,*,0) before close"); goto return_r; } r = close(t->fd); if (r != -1) { vfile_list_dirty = 1; /* only that this 1 file is closed */ t->fd = -1; if (t->pid) { /* SJ_PROC this probably needs improvement: */ (void) kill(t->pid,SIGHUP); (void) waitpid(t->pid,NULL,0); t->pid = 0; } if (t->rbuf0) { SLfree(t->rbuf0); t->rbuf0 = NULL; t->rthrsh = 0; /* just 2b safe */ } for (i = 0; i < 4; i++) { if (t->fns[i]) { free_ct++; /* need more frees */ t->fns[i] = NULL; } if(t->cookies[i].data_type) { SLang_free_object(t->cookies + i); t->cookies[i].data_type = 0; } } while(free_ct--) SLang_free_mmt(mmt); /* need more thought -- really only the vfile_list action-stuff */ /* should guard files even when explicit ref's to them have vanished */ }else VFerrno = errno; return_r: return r; } static int destroy_vfile_type0(VFILE * t) { VFILE *p,*pl; int r = -1; if (t == NULL) return 0; /* fprintf(stderr,"\ndestroy fd=%d\n",t->fd); fflush(stderr); */ if (close_vfile_type(t->mmt,t) < 0) goto return_r; if (t->file) SLang_free_slstring(t->file); /* now we find it, cut it out of list, and free its mem */ p = pl = vfile_last; do { if (p->next == t) break; p = p->next; }while (p != pl); if (p->next == t) { if (p == t) vfile_last = NULL; else { p->next = t->next; if (pl == t) vfile_last = p; } r = 0; }else fprintf(stderr,"\nNot in list: destroy fd=%d\n",t->fd); fflush(stderr); (void) SLfree((void*)t); return_r: return r; } static void destroy_vfile_type(unsigned char type, VOID_STAR ptr) { (void) type; (void) destroy_vfile_type0((VFILE *) ptr); } /* VF_reset should be called before main process exits * in order that all the child processes be killed */ static int VF_reset(void) { VFILE *pl,*p,*pn; int r = 0; if ((pl = vfile_last)) { for (p = pl->next; ;p = pn) { pn = p->next; if (destroy_vfile_type0(p)) r++; if (p == pl) break; /* just did last one. */ } } return r; } static int VF_close(void) { SLang_MMT_Type *mmt; VFILE *t; int r = -1; if ((mmt = pop_vfd(0xffff, &t))) { r = close_vfile_type(mmt,t); SLang_free_mmt(mmt); } return r; } static int VF_unlink(char * file) { int r = unlink(file); VFerrno = errno; return r; } static int VF_fileno(void) { SLang_MMT_Type *mmt; VFILE *t; int fd; if (!(mmt=pop_vfd(0xFFFF, &t))) return -1; fd = t->fd; SLang_free_mmt (mmt); return fd; } static int VF_size(void) { SLang_MMT_Type *mmt; VFILE *t; int fd,current,size,r=-1; VFerrno = 0; if (!(mmt=pop_vfd(0xFFFF, &t))) return -1; fd = t->fd; r = lseek(fd,0,SEEK_CUR); if (r == -1) goto return_r; current = r; r = lseek(fd,0,SEEK_END); if (r == -1) goto return_r; size = r; r = lseek(fd,current,SEEK_SET); if (r == -1) goto return_r; r = size; return_r: if (r == -1) VFerrno = errno; SLang_free_mmt (mmt); return r; } static int VF_seek(int *disp, int *whence) { SLang_MMT_Type *mmt; VFILE *t; int r=-1; VFerrno = 0; if ((mmt=pop_vfd(0xFFFF, &t))) { r = lseek(t->fd,*disp,*whence); if (r == -1) VFerrno = errno; SLang_free_mmt (mmt); } return r; } static int set_TCP_NODELAY(void) { int fd,flag,r; if (SLang_pop_integer(&flag)) return -2; if ((fd=VF_fileno()) < 0) return -2; if (flag) flag = 1; r = setsockopt(fd,IPPROTO_TCP,TCP_NODELAY,(char*)&flag,sizeof(flag)); if (r < 0) VFerrno = errno; return r; } static void VF_clearerr(void) { SLang_MMT_Type *mmt; VFILE *t; if ((mmt = pop_vfd(0xffff, &t))) { t->flags &= ~(SJ_ERROR|SJ_EOF); SLang_free_mmt (mmt); } } /* vf_error(VFile_Type) * -1 is unknown error, * 0 is no error, * >0 is errno. (error will not be reported till all data read) */ static int VF_errno(void) { SLang_MMT_Type *mmt; VFILE *t; int r=0; if (!(mmt = pop_vfd(0xffff, &t))) return -1; if (t->flags & SJ_ERROR) if (!t->rthrsh || t->rbufr == t->rbufp) if (!(r = t->rerror)) r = -1; SLang_free_mmt(mmt); return r; } static char *VF_strerr(int *err) { return strerror(*err); } /* vf_eof(VFile_Type) * -1 is invalid file, * 0 is not EOF ( EOF doesn't appear till there's no more data to read) * 1 is EOF */ static int VF_eof(void) { SLang_MMT_Type *mmt; VFILE *t; int r = 0; if (!(mmt = pop_vfd(0xffff, &t))) return -1; if (t->flags & SJ_EOF) if (!t->rthrsh || t->rbufr == t->rbufp) r = 1; SLang_free_mmt(mmt); return r; } /* vf_rbuffct(VFile_Type) * -1 is invalid file or not readable, * >=0 is #bytes currently in r-buffer */ static int VF_rbuffct(void) { SLang_MMT_Type *mmt; VFILE *t; int r = -1; if ((mmt = pop_vfd(SJ_READ, &t))) { r = (t->rthrsh)? (t->rbufr == t->rbufp):0; SLang_free_mmt(mmt); } return r; } /* * failure returns NULL, * success also pushes the mmt, if push != 0 */ static SLang_MMT_Type* add_to_vf_list(int fd,int flags,char* name,struct sockaddr_in *sin,pid_t pid,int push) { SLang_MMT_Type *mmt; VFILE *t; if (!(t = (VFILE*)SLmalloc(sizeof(VFILE)))) goto free0; bzero((char *) t, sizeof(VFILE)); t->fd = fd; if (sin) { flags |= SJ_SIN; memcpy(&t->sin,sin,sizeof(struct sockaddr_in)); } if (pid) { flags |= SJ_PROC; t->pid = pid; } t->flags = flags; t->rtermn = -1; if (name && !(t->file = SLang_create_slstring(name))) goto free1; t->mmt = mmt = SLang_create_mmt(SJ_VFILE_TYPE, (VOID_STAR) t); if (!mmt) goto free1; if (push && SLang_push_mmt(mmt)) goto free2; # if 0 SLang_inc_mmt(mmt); /* because the list ref's it */ /* above needs more thought -- probably only wanted * when there is an action set */ # endif t->next = t; if (vfile_last) { t->next = vfile_last->next; vfile_last->next = t; } vfile_last = t; vfile_list_dirty = 1; /* an insert into list */ return (void*) mmt; free2: SLang_free_mmt(mmt); free1: SLfree((void*)t); free0: return NULL; } /* returns -1 upon failure or returns a handle to file */ static void VF_open(char *file, int *uflags, int *mode) { unsigned int flags,fd; VFerrno=0; if (0 == (flags = convert_flags(*uflags))) goto null_fail; fd = open(file, *uflags, *mode); # if 0 fprintf(stderr,"open(\"%s\",%x,%o) returned %d\n",file,*uflags,*mode,fd);fflush(stderr); # endif if (fd==-1) {VFerrno=errno; goto null_fail;} if (add_to_vf_list(fd,flags,file,NULL,0,1)) return;/* marks dirty if success */ /* failure */ close(fd); null_fail: (void) SLang_push_null(); } /* extern int h_errno; * HOST_NOT_FOUND */ static int child_HUPed = 0; static void child_HUP(int signum) { child_HUPed = 1; signal(SIGHUP,child_HUP); } /* * this is a child process attached thru AF_UNIX socketpair * to do host lookups without blocking main process. * talk with it via a UDPc type VFile in main process * * requests/responses are structure: * int ip; (net u32 IPnum or 0) * int error; (net u32 input 0, output may be nonzero) * char host[]; (hostname string w/0 any terminator... len is inferred) * * (1) Reverse DNS lookup: * ----ip--- --error-- host--- * IN: request XXXX NONE (packet length is 8) * Failure OUT: same h_errno NONE (packet length is 8) * Success OUT: same 0 host (packet length > 8) * * (2) Forward DNS lookup: * ----ip--- --error-- host--- * IN: XXXX XXXX host (packet length > 8) * Failure OUT: -1 h_errno same (packet length same) * Success OUT: valid ip 0 same (packet length same) * * Note: 'XXXX' means 'don't care' */ static void host_looker(int fd) { int r,w; unsigned long ip; static struct { int ip; int herror; char host[1024]; } rb; struct hostent *hp; child_HUPed = 0; /* unnecessary */ (void) signal(SIGHUP,child_HUP); (void) siginterrupt(SIGHUP,1); /* * we really want HUP to interrupt read() and write() * which is default for POSIX, but i'm not positive how to * do this for BSD. Is siginterrupt() the best way? */ for (r = 0; r<256; r++) if (r != fd) close(r); if (-1 == setsid()) goto my_exit; while (1) { do{ if (child_HUPed) goto my_exit; r = recv(fd,(char*)&rb,sizeof(rb)-1,0); }while (r == -1 && errno == EINTR); if (r < 8) { if (r <= 0) goto my_exit; continue; /* hmm. what would be best in this odd event? */ } rb.herror = 0; if (r > 8) { /* gethostbyname */ ((char*)&rb)[r] = 0; if (-1 != (ip = inet_addr(rb.host))) rb.ip = ip; else if (!(hp = gethostbyname(rb.host))){ rb.ip = -1; rb.herror = htonl(h_errno); }else rb.ip = *(unsigned long *) hp->h_addr; }else{ /* gethostbyaddr */ if ((hp = gethostbyaddr((char*)&rb.ip, 4, AF_INET))) { strcpy(rb.host,hp->h_name); r += strlen(rb.host); }else{ rb.herror = htonl(h_errno); } } do{ if (child_HUPed) goto my_exit; w = send(fd,(char*)&rb,r,0); }while (r == -1 && errno == EINTR); } my_exit: close(fd); _exit(-1); } /* returns NULL upon failure or returns a VFile_Type */ static void VFopen_resolvr(void) { int r,fds[2]; pid_t pid; VFerrno=0; if ((r = socketpair(AF_UNIX,SOCK_DGRAM,0,fds))) { VFerrno = errno; goto null_fail; } pid = fork(); if (pid == 0) { /* child */ host_looker(fds[1]); }else if (pid == -1) { VFerrno = errno; goto null_fail; } close(fds[1]); if (add_to_vf_list(fds[0], SJ_UDPc|SJ_READ|SJ_WRITE, "UDPc:(Resolver)", NULL ,pid,1)) return; close(fds[0]); /* need to kill pid also, fix me! */ null_fail: (void) SLang_push_null(); return; } /* returns NULL upon failure or returns a VFile_Type */ static void VFudp_connect(int *ip, int *port) { int fd; struct sockaddr_in Rem; char name[64]; VFerrno=0; if ((fd = sock_connect(SOCK_DGRAM, *ip, *port, &Rem)) < 0) goto null_fail; sprintf(name, "UDPc:%s:%d", inet_ntoa(Rem.sin_addr), *port); if (add_to_vf_list(fd, SJ_UDPc|SJ_READ|SJ_WRITE, name, &Rem ,0,1)) return; close(fd); null_fail: (void) SLang_push_null(); return; } static void VFudp_open(int *ip, int *port) { int fd; struct sockaddr_in Loc; char name[64]; VFerrno=0; if ((fd = udp_bind(*ip, *port, &Loc)) < 0) goto null_fail; sprintf(name, "UDPu:%s:%d", inet_ntoa(Loc.sin_addr), ntohs(Loc.sin_port)); if (add_to_vf_list(fd, SJ_UDPu|SJ_READ|SJ_WRITE, name,NULL,0,1)) return; close(fd); null_fail: (void) SLang_push_null(); return; } /* returns NULL upon failure or returns a VFile_Type */ static void VFtcp_connect(int *ip, int *port) { int fd; struct sockaddr_in Rem; char name[64]; /* security: watch for buffer overflow! */ VFerrno=0; if ((fd = sock_connect(SOCK_STREAM, *ip, *port, &Rem)) < 0) goto null_fail; sprintf(name, "TCPc:%s:%d", inet_ntoa(Rem.sin_addr), *port); if (add_to_vf_list(fd, SJ_TCP|SJ_READ|SJ_WRITE, name, &Rem,0,1)) return; close(fd); null_fail: (void) SLang_push_null(); return; } /* returns NULL upon failure or returns a VFile_Type */ static void VFtcp_open(char *host, int *port) { struct hostent *hp; int ip; VFerrno=0; if (NULL == (hp = gethostbyname(host))) { VFerrmsg = "Unknown host"; (void) SLang_push_null(); }else{ ip = ntohl(*(unsigned long *) hp->h_addr); VFtcp_connect(&ip,port); } return; } /* hmm... this opens a weird kinda read-socket */ static void VFtcp_listen(void) { int len,fd; struct sockaddr_in Loc; char name[64]; VFerrno=0; if ((fd = tcp_listen()) < 0) goto null_fail; len = sizeof(Loc); if (getsockname(fd, (struct sockaddr *) &Loc, &len) < 0) { VFerrmsg="Couldn't getsockname."; VFerrno=errno; }else{ int port = ntohs(Loc.sin_port); SLang_push_integer(port); sprintf(name, "TCPu:%s:%d", inet_ntoa(Loc.sin_addr), port); if (add_to_vf_list(fd,SJ_TCP|SJ_READ,name,NULL,0,1)) return; /* success */ SLdo_pop(); /* the port pushed above */ } close(fd); null_fail: (void) SLang_push_null(); return; } /* * VFtcp_Type tcp_accept(VFile_Type listen_socket) * failure: returns NULL * success: returns VFile_Type new_socket * AND (Integer_Type) remote port * AND (String_Type) remote hostname */ static void VFtcp_accept(void) { struct sockaddr_in Rem; struct hostent *hp; char *remname; int oldfd,fd; int len = sizeof(Rem); char name[64]; VFerrno=0; if ((oldfd=VF_fileno()) < 0) goto null_fail; fd = accept(oldfd, (struct sockaddr *) &Rem, &len); if (fd < 0) { VFerrno=errno; goto null_fail; } (void)set_O_NONBLOCK(fd); /* Note: It returns extra arguments IF successful. */ SLang_push_integer(ntohs(Rem.sin_port)); if ((hp = gethostbyaddr( (char *) &Rem.sin_addr.s_addr, sizeof(Rem.sin_addr.s_addr), Rem.sin_family ))) { remname = hp->h_name; } else { remname = inet_ntoa(Rem.sin_addr); } SLang_push_string(remname); sprintf(name, "TCPc:%s:%d", inet_ntoa(Rem.sin_addr), ntohs(Rem.sin_port)); if (add_to_vf_list(fd, SJ_TCP|SJ_READ|SJ_WRITE, name, &Rem,0,1)) return; /* failure */ SLdo_pop_n(2); /* the (host,port) pushed above */ close(fd); null_fail: (void) SLang_push_null(); return; } /* get_local_ipp(VFile_Type) */ /* returns (s_addr,s_port) of the socket (in host-order) */ static void VF_get_local_ipp(void) { int fd; struct sockaddr_in sin; int s = sizeof(sin); unsigned long ip = -1; unsigned short port = -1; fd = VF_fileno(); /* get fileno by popping VFile_Type from stack */ if (fd >= 0 && !getsockname(fd, (struct sockaddr *) &sin, &s)) { ip = ntohl(sin.sin_addr.s_addr); port = ntohs(sin.sin_port); } SLang_push_integer(ip); SLang_push_integer(port); } /* (IPnum,port) = get_remote_ipp(VFile_Type) */ /* returns (s_addr,s_port) of remote connection of socket (in host-order) */ /* or -1,-1 if error */ static void VF_get_remote_ipp(void) { SLang_MMT_Type *mmt; VFILE *t; unsigned long ip = -1; unsigned short port = -1; if ((mmt=pop_vfd(SJ_SOCKET, &t))) { if (t->flags & SJ_SIN) { port = ntohs(t->sin.sin_port); ip = ntohl(t->sin.sin_addr.s_addr); } SLang_free_mmt(mmt); } SLang_push_integer((int) ip); SLang_push_integer((int) port); } /* this is the ONLY place where data is pulled out of a rbuffer */ /* if data == NULL, just updates pointers, etc, like for reading */ static int copy_from_buf(VFILE *t,void * data,int ct) { char *p0,*p; int m; if (ct<=0) return 0; p0 = t->rbuf0; p = t->rbufp; m = t->rbufr - p; if (m < ct) return -1; if (data) memcpy(data,p,ct); p += ct; t->rbufp = p; t->rbufq = p; if (p >= p0 + (t->rbuft - p0)/2) { /* copydown */ int l = t->rbufr - p; /* buff'd data */ memcpy(p0,p,l); t->rbufp = p0; t->rbufq = p0; t->rbufr = p0 + l; } return ct; } /* this where unbuffered read() is done (rthrsh=0) */ static int read_to_mem(VFILE* t,void* p,int ct) { int r; if (t->fd < 0 || t->flags & SJ_EOF || ct <= 0) return 0; do { if (t->flags & SJ_UDPc) r = recv(t->fd,p,ct,0); else r = read(t->fd,p,ct); }while(r<0 && errno==EINTR); if (r<=0) { if (!r) t->flags |= SJ_EOF; else { t->flags |= SJ_ERROR; t->rerror = errno; } } return r; } /* this where read() is done for rthrsh>0 (buffered) */ static int read_into_buf(VFILE* t) { int l; int fd = t->fd; l = t->rbuft - t->rbufr; if (l <= 0) { /* shouldn't happen */ SLang_doerror("read_into_buf() called with window=0"); return -1; } if (t->fd < 0 || t->flags & SJ_EOF) return 0; /* also shouldn't happen */ do { l = read(fd,t->rbufr,l); }while (l<0 && errno==EINTR); /*fprintf(stderr,"fd=%d read %d bytes\n",fd,l); */ if (l>0) t->rbufr += l; else if (l==0) t->flags |= SJ_EOF; else { t->rerror = errno; t->flags |= SJ_ERROR; } return l; } /* this checks data already in rbuf to see if it would satisfy * the 'readable' criteria, or has hit EOF or ERROR * if not, returns -1 * if yes, returns the #bytes (>=0) which would be read */ static int check_buf_data(VFILE* t) { int l,m; char *cp; /* following might happen in the SLexecute loop of do_actions(): */ if (!t->rbuf0 || t->fd < 0) return -1; l = t->rbufr - t->rbufp; /* buffer has l bytes of data */ if (!l || t->rtermn < 0 || !(m = t->rbufr - t->rbufq)) goto dflt_ret; if (t->rbufq < t->rbufp) { SLang_doerror("qrbufq = t->rbufp; } if ( (cp = (char*)memchr(t->rbufq,t->rtermn,m)) ||(cp = (char*)memchr(t->rbufq,0,m))) { t->rbufq = cp; return (++cp - t->rbufp); } t->rbufq = t->rbufr; dflt_ret: if (!(t->flags & (SJ_ERROR|SJ_EOF)) && (l < t->rthrsh)) l = -1; return l; } /* vf_read(vfile,&stringbuf|char_array[,ct]) * Note: * (1) vf_read(*,&buf) equiv to vf_read(*,&buf,4096) * (2) vf_read(*,char_array) equiv to vf_read(*,char_array,sizeof(char_array)) */ static int VF_read(void) { int fromlen,type,ct=-1,m,r=-1; void *data; char *s = NULL; SLang_Array_Type *at = NULL; SLang_MMT_Type *mmt = NULL; VFILE *t; SLang_Ref_Type *ref = NULL; type = SLang_peek_at_stack(); if (type == SLANG_INT_TYPE) { if (SLang_pop_integer(&ct)) return -1; if (ct<0) ct = 0; type = SLang_peek_at_stack(); } if (type == SLANG_ARRAY_TYPE) { if (-1 == SLang_pop_array (&at, 0)) return -1; ct = char_array_data(at,&data,ct); if (ct < 0) goto return_r; /* r = -1 */ }else{ if (-1 == SLang_pop_ref(&ref)) return -1; if (ct<0) ct = 4*1024; /* dest is string w/o maxlen spec'd */ } if (!(mmt = pop_vfd(SJ_READ, &t))) goto return_r; /*fprintf(stderr,"fd=%d, rthrsh=%d, bytes=%d\n",t->fd,t->rthrsh,t->rbufr-t->rbufp); */ if (t->fd < 0) { SLang_doerror("Read on closed VFile"); goto return_r; } if (t->flags & SJ_UDPu) { /* an 'un-connected' UDP socket, so we recvfrom anywhere, */ /* and push the source ip#,port# back on stack. */ fromlen = sizeof(struct sockaddr_in); t->sin.sin_addr.s_addr = INADDR_ANY; t->sin.sin_family = AF_INET; t->sin.sin_port = 0; /* means any */ if (!at) { if(!(s = SLmalloc(ct+1))) goto return_r; data = s; } do { r = recvfrom(t->fd,data,ct,0,(struct sockaddr*)&t->sin,&fromlen); }while(r<0 && errno==EINTR); if (r < 0) goto return_r; SLang_push_integer(ntohl(t->sin.sin_addr.s_addr)); SLang_push_integer(ntohs(t->sin.sin_port)); if (s) goto fin_s; goto return_r; } if (!t->rthrsh) { /* unbuffered reads */ if (!at) { if(!(s = SLmalloc(ct+1))) goto return_r; data = s; } r = read_to_mem(t,data,ct); if (s && r >= 0) goto fin_s; goto return_r; } if (t->rtermn < 0) m = t->rbufr - t->rbufp; else m = check_buf_data(t); if (m < 0) m = 0; else if (m > ct) m = ct; if (at) { r = copy_from_buf(t,data,m); }else{ if(!(s = SLmalloc(ct+1))) goto return_r; r = copy_from_buf(t,s,m); fin_s: s[r] = 0; if (SLang_push_string(s) || _SLang_deref_assign(ref)) r = -1; } return_r: if (mmt) SLang_free_mmt(mmt); if (at) SLang_free_array(at); if (ref) SLang_free_ref(ref); SLfree(s); /* NULL ok */ return r; } /* vf_write(vfile,string|bstring|char_array[,n]) (n is max bytes to write) */ /* vf_sendto(ip,port,vfile,string|char_array[,n]) (n is max bytes to write) */ /* Note: if n not specified, taken to be sizeof the (b)string or char_array */ static int VF_write(void) { int port,type,ct=-1,len=-1; unsigned long ip; void *data; char *s = NULL; SLang_BString_Type *bs = NULL; SLang_Array_Type *at = NULL; SLang_MMT_Type *mmt = NULL; VFILE *t; VFerrno = 0; type = SLang_peek_at_stack(); if (type == SLANG_INT_TYPE) { if (SLang_pop_integer(&len) || (len<0)) return -1; type = SLang_peek_at_stack(); } switch (type) { case SLANG_ARRAY_TYPE: if (-1 == SLang_pop_array (&at, 0)) return -1; len = char_array_data(at,&data,len); if (len < 0) goto free_fail; /* ct = -1 */ break; case SLANG_STRING_TYPE: if (SLang_pop_slstring(&s)) return -1; type = strlen(s); if (len < 0 || len > type) len = type; data = s; break; case SLANG_BSTRING_TYPE: if (SLang_pop_bstring(&bs)) return -1; data = SLbstring_get_pointer(bs,&type); if (len < 0 || len > type) len = type; break; default: return -1; } if (!(mmt = pop_vfd(SJ_WRITE, &t))) goto free_fail; /* ct = -1 */ if (t->fd < 0) { SLang_doerror("Write on closed VFile"); goto free_fail; } if (t->flags & SJ_UDPu) { if (SLang_pop_integer(&port)) goto free_fail; if (SLang_pop_integer((int*)&ip)) goto free_fail; t->sin.sin_addr.s_addr = htonl(ip); t->sin.sin_family = AF_INET; t->sin.sin_port = htons(port); do { ct = sendto(t->fd,data,len,0,(struct sockaddr*) &t->sin,sizeof(struct sockaddr_in)); }while(ct<0 && errno==EINTR); }else{ do { if (t->flags & SJ_UDPc) ct = send(t->fd,data,len,0); else ct = write(t->fd,data,len); }while(ct<0 && errno==EINTR); } if (-1 == ct) VFerrno = errno; free_fail: if (at) SLang_free_array (at); if (bs) SLbstring_free(bs); SLang_free_slstring(s); /* NULL is ok */ if (mmt) SLang_free_mmt(mmt); return ct; } /* int VF_copybytes(VFile_Type dest, VFile_Type src, int count) */ /* copy up to count bytes from src to dest files -- */ /* [ they must have been linked by a prior set_wrdep(dest,src,1) ] */ /* return the number of bytes copied, */ /* or -1 in case of error */ /* or 0 maybe in case EOF or error on input file */ static int VF_copybytes(void) { int ct,m,r=-1; SLang_MMT_Type *rd_mmt, *wr_mmt; VFILE *rt,*wt; VFerrno = 0; if (SLang_pop_integer(&ct)) goto free0; if (!(rd_mmt = pop_vfd(SJ_READ, &rt))) goto free0; if (!(wr_mmt = pop_vfd(SJ_WRITE, &wt))) goto free1; if (wt != rt->wrvf || rt != wt->rdvf) { SLang_doerror("copybytes filepair not linked by set_wrdep()"); goto free2; } if (rt->rtermn < 0) m = rt->rbufr - rt->rbufp; else m = check_buf_data(rt); if (m <= 0) { r=m; goto free2; } if (m > ct) m = ct; do { r = write(wt->fd,rt->rbufp,m); }while(r < 0 && errno == EINTR); if (r > 0) (void) copy_from_buf(rt,NULL,r); /* this does copy-down & pointer updates */ else if (r < 0) { wt->werror = errno; VFerrno = errno; } free2: SLang_free_mmt(wr_mmt); free1: SLang_free_mmt(rd_mmt); free0: return r; } /* * int tcp_is_readable(VFile_Type,int timeout) * returns 1 if is readable, * 0 if not, -- ah, inconsistency with check_buf_data * -1 if error * probably broken ATM */ static int VF_is_readable(void) { int fd,r; struct timeval tv; fd_set rfds; SLang_MMT_Type *mmt = NULL; VFILE *t; VFerrno = 0; tv.tv_usec = 0; if (SLang_pop_integer((int *)&tv.tv_sec)) return -1; if (!(mmt = pop_vfd(SJ_READ, &t))) return -1; /* ct = -1 */ if (t->fd < 0) { SLang_doerror("Read on closed VFile"); r = -1; goto return_r; } r = check_buf_data(t); if (r >= 0) goto return_r; fd=t->fd; FD_ZERO(&rfds); FD_SET(fd, &rfds); r = select(fd + 1, &rfds, NULL, NULL, &tv); if (r < 0) { if (errno!=EINTR) VFerrno = errno; goto return_r; } if (r>0 && t->rthrsh) if (read_into_buf(t) > 0) if (check_buf_data(t) < 0) r = 0; return_r: SLang_free_mmt(mmt); return r; } /* tcp_is_writeable() Like tcp_is_readable() */ static int VF_is_writeable(void) { int fd,r; struct timeval tv; fd_set rfds; VFerrno = 0; tv.tv_usec = 0; if (SLang_pop_integer((int *)&tv.tv_sec)) return -1; if ((fd=VF_fileno()) < 0) return -1; FD_ZERO(&rfds); FD_SET(fd, &rfds); #ifdef __linux__ do { #endif r = select(fd + 1, NULL, &rfds, NULL, &tv); #ifdef __linux__ }while (r == -1 && errno == EINTR); #endif if (r < 0) VFerrno = errno; /* could be EINTR */ return r; } /* the next is a select on all read */ /* pushes a null, then all readable vfiles */ static void VF_select_on_all(int *secs) { fd_set rfds; int fd,r,maxfd = -1; VFILE *t,*tf; struct timeval tv; FD_ZERO(&rfds); tv.tv_sec = *secs; tv.tv_usec = 0; (void) SLang_push_null(); /* mark top of list */ tf = NULL; t = vfile_last; do { t = t->next; if ((fd = t->fd) >= 0 && (t->flags & SJ_READ)) { if(!t->rthrsh || t->rbufr < t->rbuft) { if (maxfd < fd) maxfd = fd; FD_SET(fd, &rfds); } if (check_buf_data(t) >= 0) { SLang_push_mmt(t->mmt); tf = t; } } }while (t != vfile_last); if (tf || maxfd < 0) return; #ifdef __linux__ do { #endif r = select(maxfd + 1, &rfds, NULL, NULL, &tv); #ifdef __linux__ }while(r == -1 && errno == EINTR); #endif if(r > 0) { tf = NULL; t = vfile_last; do { t = t->next; if ((fd = t->fd) >= 0 && (t->flags & SJ_READ) && (FD_ISSET(fd,&rfds))){ if (t->rthrsh) if (read_into_buf(t) > 0) if (check_buf_data(t) < 0) continue; SLang_push_mmt(t->mmt); } }while (t != vfile_last); } return; } /* * int set_wrdep(VFile wr,VFile rd,int y) * y=0 clears, y!=0 sets. * -1 is failure, 0 is success * the read-file must be buffered (rthrsh>0) * you must break the link before closing/destroying either file */ static int VF_set_wrdep(void) { SLang_MMT_Type *rd_mmt,*wr_mmt; VFILE *r,*w; int i,ret=-1; if (SLang_pop_integer(&i)) goto free0; if (!(rd_mmt = pop_vfd(SJ_READ, &r))) goto free0; if (!(wr_mmt = pop_vfd(SJ_WRITE, &w))) goto free1; if (r->fd < 0 || w->fd < 0) goto free2; if (i) { if (!r->rthrsh || r->fns[0] || r->wrvf || w->rdvf) goto free2; r->wrvf = w; w->rdvf = r; vfile_list_dirty = 1; /* just the r/w pair */ return 0; } if (w != r->wrvf || r != w->rdvf) goto free2; r->wrvf = NULL; w->rdvf = NULL; vfile_list_dirty = 1; ret = 0; SLang_free_mmt(wr_mmt); SLang_free_mmt(rd_mmt); free2: SLang_free_mmt(wr_mmt); free1: SLang_free_mmt(rd_mmt); free0: return ret; } static int VF_clr_action(void) { SLang_MMT_Type *mmt; VFILE *t; int i,rfch=1; if (!(mmt = pop_vfd(0xffff, &t))) return -1; /* go thru fns... */ for (i = 0; i < 4; i++) { if(t->fns[i]) rfch++; t->fns[i]=NULL; if(t->cookies[i].data_type) { SLang_free_object(t->cookies + i); t->cookies[i].data_type = 0; } } vfile_list_dirty = 1; /* just t */ while(rfch--) SLang_free_mmt(mmt); return 0; } /* * int set_action(VFile_Type,int which,NULL) clears action.which * int set_action(VFile_Type,int which,String_Type func [,cookie] ) * like r = set_action(fserver,0,"doit") * the 'which' is 0,1,2 corresponding to read,write,exception in select() * returns <0 if error * returns 0 on success */ static int VF_set_action(void) { char *func_name = NULL; SLang_Name_Type *fnt = NULL; int r=-1, vrfch=0, which; SLang_MMT_Type *mmt = NULL; VFILE *t; SLang_Object_Type cookie; cookie.data_type = 0; /* SLANG_UNDEFINED_TYPE */ if (SLang_Num_Function_Args > 3) { if(SLang_pop(&cookie)) goto return_r; /*fail leaves 0 */ if (!cookie.data_type) { SLang_verror(SL_INVALID_PARM,"Can't pass undefined parm as cookie"); SLang_free_object(&cookie); cookie.data_type = SLANG_NULL_TYPE; } } if (!cookie.data_type && SLang_peek_at_stack() == SLANG_NULL_TYPE) SLdo_pop(); else if (SLang_pop_slstring(&func_name)) goto return_r; if (SLang_pop_integer(&which) || !(mmt = pop_vfd(0xffff, &t))) goto return_r; vrfch++; if(func_name) { fnt = SLang_get_function(func_name); if (!fnt) {r = -2; goto return_r;} if (t->fd < 0) { SLang_doerror("Can't set_action() on closed VFile"); r = -3; goto return_r; } } if (which < 0 || which > 4) goto return_r; if(fnt && !which && t->wrvf) { r = -4; goto return_r;} r = 0; if(t->fns[which] != fnt) { if ((fnt && !t->fns[which]) || (!fnt && t->fns[which])) { vfile_list_dirty = 1; /* just t */ if(fnt) vrfch--;else vrfch++; /* need less/more free_mmt's */ } t->fns[which] = fnt; } if (cookie.data_type || !fnt) { /* changing cookie doesn't need to set vfile_list_dirty */ if (t->cookies[which].data_type) SLang_free_object(t->cookies + which); memcpy(t->cookies+which, &cookie, sizeof(SLang_Object_Type)); cookie.data_type = 0; /* so it's NOT free'd below */ } return_r: while(vrfch-->0) SLang_free_mmt(mmt); SLang_free_slstring(func_name); /* NULL is ok */ if (cookie.data_type) SLang_free_object(&cookie); /* only when error */ return r; } /* * int vf_set_rtype(vfile,ev_ch,threshold) * failure: returns -1, * success: returns #bytes in rbuf */ static int VF_set_rmode(void) { int ch,ct,r=0,bufsize=1024; SLang_MMT_Type *mmt; VFILE *t; if (SLang_pop_integer(&ct) || SLang_pop_integer(&ch) || !(mmt = pop_vfd(SJ_READ, &t)) || ct < 0 || ct > 0x4000 ) return -1; if (t->flags & SJ_UDP) { SLang_doerror("only default (unbuffered) rmode makes sense for UDP"); r = -1; goto return_r; } if (ch>255 || ch<0) ch=-1; if (t->fd < 0) goto return_r; if (!ct) { if (t->rthrsh) { /* changing from buffered to unbuffered */ if (t->rbufr > t->rbufp) { SLang_doerror("must read data before switching to unbuffered mode"); r = -1; goto return_r; } SLfree(t->rbuf0); t->rbuf0 = NULL; vfile_list_dirty = 1; /* just t */ } if (ch>=0) { SLang_doerror("termination char ignored in unbuffered mode"); ch = -1; } } if (t->rtermn != ch) { vfile_list_dirty = 1; /* just t */ t->rtermn = ch; t->rbufq = t->rbufp; } if (t->rthrsh == ct) goto return_r; if (!(t->rthrsh = ct)) goto return_r; vfile_list_dirty = 1; /* just t */ if (bufsize<4*ct) bufsize = 4*ct; /* 2*ct is sufficient */ if (t->rbuf0 && bufsize <= (t->rbuft-t->rbuf0)) { /* the old buffer is big enough */ r = t->rbufr - t->rbufp; }else{ int q = 0; char* newbuf = SLmalloc(bufsize); if (!newbuf) {r = -1; goto return_r;} if (t->rbuf0) { /* there was old buff */ if ((r = t->rbufr - t->rbufp) > 0) { memcpy(newbuf,t->rbufp,r); q = t->rbufq - t->rbufp; } SLfree(t->rbuf0); /* free old one */ } t->rbuf0 = newbuf; t->rbufp = newbuf; t->rbufq = newbuf+q; t->rbufr = newbuf+r; t->rbuft = newbuf+bufsize; } return_r: SLang_free_mmt(mmt); return r; } /*extern void SLirc_UpdateDisplay(void); */ /*void (*VF_do_actions_Hook)(void) = NULL; */ static int do_action_i(VFILE *t,int i) { int r,stkdep; stkdep = _SLstack_depth(); SLang_start_arg_list(); if (i == 1 && t->rdvf) /* extra param (read's VFile) for copybytes */ SLang_push_mmt(t->rdvf->mmt); SLang_push_mmt(t->mmt); SLang_push_integer(i); if (t->cookies[i].data_type) _SLpush_slang_obj(t->cookies + i); SLang_end_arg_list(); /* WARNING: this may change the list: * in particular: * it could close this (any) file, * it could change one or more of actions for this (any) file */ r = SLexecute_function(t->fns[i]); /*fprintf(stderr,"Did %s(%d,%d), r=%d\n",t->fns[i]->name,t->fd,i,r); */ stkdep = _SLstack_depth() - stkdep; /* % items left on stack. */ /* for some reason, this next seems to cause segfault when */ /* used from ./vf and do_actions() is called in SLang ?! */ if (stkdep && SLang_Error != USER_BREAK) { SLang_verror(SL_APPLICATION_ERROR, "Eeek... %s() left %d units on stack",t->fns[i]->name,stkdep); if (stkdep>0) { SLdo_pop_n(stkdep); SLang_Error = 0; /* probably safe to keep going */ } } /* if (SLang_Error) { // tends to cause me to go nuts */ /* SLang_Error = 0; */ /* //SLirc_UpdateDisplay(); */ /* //if (VF_do_actions_Hook) (*VF_do_actions_Hook)(); */ /* } */ return r; } static int VF_rb_actions(void) { VFILE *t,*tn; int ct0,ct=0; char* rbufp; /*int vf0 = vfile_list_dirty; */ do { if(!(tn = vfile_last)) break; vfile_list_dirty = 0; ct0 = ct; t = tn; do { t = t->next; if (t->fd < 0 || !t->fns[0] || !t->rthrsh) continue; if (check_buf_data(t) < 0) continue; vfile_last = t; rbufp = t->rbufp; ct++; do_action_i(t,0); if (t->fd >= 0 && t->fns[0] && rbufp == t->rbufp && check_buf_data(t) >= 0) { SLang_doerror("read-action did not handle data"); t->fns[0] = NULL; /* this to radically kill it */ } }while (!vfile_list_dirty && t != tn); }while (ct > ct0 || vfile_list_dirty); /*if (ct&!vf0) fprintf(stderr,"Leaving rb_actions() ct=%d\n",ct); */ return ct; } /* int r = tcp_do_actions(int timeout) */ /* returns the number of actions actually executed */ static int VF_do_actions(int *timeout) { VFILE *t,*tn; int i,r,rb,ct=0; int max = -1; /*int tim; */ struct timeval tv; #ifndef HAVE_SELECT_HACK struct timeval stv, ttv, rtv; #endif static int do_actions_dep = 0; /* static! init'd = 0 only once. */ static fd_set fds[3]; /* a big one, we could malloc */ if (do_actions_dep++) { /* we are NOT recursive! */ SLang_doerror("Can't call do_actions() recursively"); goto return_ct; } /*tim = (int)time(NULL); */ /*fprintf(stderr,"\n%d Enter do_actions, dirty=%d\n",tim,vfile_list_dirty); */ if (vfile_list_dirty) /* the outside world screwed around with vfile_list */ ct = VF_rb_actions(); if(!(tn = vfile_last)) goto return_ct; if (ct) goto return_ct; /* * better exit & update display before long wait * or we could do this way: update screen before the long wait! * if (ct && VF_do_actions_Hook) (*VF_do_actions_Hook)(); */ for (i = 0; i < 3; i++) FD_ZERO(fds+i); /* now figure out which fds bits to select on */ t = tn; do { int i; t = t->next; if (t->fd < 0) continue; /* closed file */ for (i = 0; i < 3; i++) { switch(i) { case 0: if (!(t->flags & SJ_READ)) continue; if (t->flags & (SJ_EOF|SJ_ERROR)) continue; if (t->rthrsh && t->rbufr >= t->rbuft) continue; break; case 1: if (!t->fns[1]) continue; if (t->rdvf && check_buf_data(t->rdvf) < 0) continue; break; case 2: if (!t->fns[2]) continue; } FD_SET(t->fd, fds + i); if (max < t->fd) max = t->fd; } }while(t != tn); if (max<0) goto return_ct; /* none to watch for */ tv.tv_sec = *timeout; tv.tv_usec = 0; stv = tv; #ifndef HAVE_SELECT_HACK gettimeofday(&ttv, NULL); #endif do { #ifndef HAVE_SELECT_HACK stv = tv; gettimeofday(&ttv, NULL); #endif r = select(max + 1, fds, fds+1, fds+2, &stv); /* linux updates tv, so we can loop here - Only in v2.0 */ #ifndef HAVE_SELECT_HACK gettimeofday(&rtv, NULL); ttv.tv_sec-=rtv.tv_sec; if(ttv.tv_usec < rtv.tv_usec) ttv.tv_sec--; tv.tv_usec-=ttv.tv_usec; ttv.tv_sec-=ttv.tv_sec; if(tv.tv_usec < ttv.tv_usec) tv.tv_sec--; tv.tv_usec-=ttv.tv_usec; #endif }while (r == -1 && errno == EINTR); if (r<=0) goto return_ct; rb = 0; /* will count # of read_into_buf's */ /* do all buffered reads 1st, since they can't mess up vfile_list */ /* t = tn; */ do { t = t->next; if (t->fd < 0) continue; /* this IS necessary */ if (FD_ISSET(t->fd, fds) && t->rthrsh) { read_into_buf(t); rb++; } }while (t != tn); /*fprintf(stderr,"Post-select, r=%d, rb=%d\n",r,rb); */ if (r <= rb) goto return_ct_a; /* there was nothing BUT rb's */ if (vfile_list_dirty) fprintf(stderr,"Huh? dirty already!\n"); vfile_list_dirty = 0; /* it already is = 0, anyway */ /*t = tn; */ do { int i; t = t->next; if (t->fd < 0) continue; /* necessary, as above */ for (i = 0; i < 3; i++) { if (FD_ISSET(t->fd, fds+i) && t->fns[i]) { /* above t->fns[i] is NOT redundant -- SLexecute below could change it */ if (!i && t->rthrsh) continue; /* buffer'd reads already handled */ ct++; do_action_i(t,i); /* see warnings in this func! */ if (vfile_list_dirty) goto return_ct_a; } } }while (t != tn); return_ct_a: if (rb) { /*vfile_list_dirty = 1; // just for debug */ ct += VF_rb_actions(); } return_ct: do_actions_dep--; /*tim = (int)time(NULL)-tim; */ /*fprintf(stderr,"Leave do_actions(%d), dirty=%d, ct=%d\n",tim,vfile_list_dirty,ct); */ return ct; } #define I SLANG_INT_TYPE #define S SLANG_STRING_TYPE #define V SLANG_VOID_TYPE static SLang_Intrin_Fun_Type VFile_Fun_Table[] = { MAKE_INTRINSIC_S("getservbyname", VF_getservbyname, I), MAKE_INTRINSIC_S("inet_addr", VF_inet_addr, I), MAKE_INTRINSIC_I("inet_ntoa", VF_inet_ntoa, S), MAKE_INTRINSIC_S("gethostbyname", VF_gethostbyname, I), MAKE_INTRINSIC_I("gethostbyaddr", VF_gethostbyaddr, S), # ifndef AR_GETPUT MAKE_INTRINSIC_0("array_get_string", ar_get_string, V), MAKE_INTRINSIC_0("array_get_u32", ar_get_u32, I), MAKE_INTRINSIC_0("array_put_u32", ar_put_u32, V), # endif MAKE_INTRINSIC_S("unlink", VF_unlink, I), MAKE_INTRINSIC_0("vf_fileno", VF_fileno, I), MAKE_INTRINSIC_0("vf_size", VF_size, I), MAKE_INTRINSIC_II("vf_seek", VF_seek, I), MAKE_INTRINSIC_0("vf_error", VF_errno, I), MAKE_INTRINSIC_I("vf_strerr", VF_strerr, S), MAKE_INTRINSIC_0("vf_eof", VF_eof, I), MAKE_INTRINSIC_0("vf_rbuffct", VF_rbuffct, I), MAKE_INTRINSIC_0("vf_clearerr", VF_clearerr, V), MAKE_INTRINSIC_0("set_nodelay", set_TCP_NODELAY, I), MAKE_INTRINSIC_0("open_resolver", VFopen_resolvr, V), MAKE_INTRINSIC_0("vf_reset", VF_reset, I), MAKE_INTRINSIC_SII("vf_open", VF_open, V), MAKE_INTRINSIC_0("vf_close", VF_close, I), MAKE_INTRINSIC_0("vf_read", VF_read, I), MAKE_INTRINSIC_0("vf_write", VF_write, I), MAKE_INTRINSIC_II("udp_connect", VFudp_connect, V), MAKE_INTRINSIC_II("udp_open", VFudp_open, V), MAKE_INTRINSIC_II("tcp_connect", VFtcp_connect, V), MAKE_INTRINSIC_SI("tcp_open", VFtcp_open, V), MAKE_INTRINSIC_0("tcp_listen", VFtcp_listen, V), MAKE_INTRINSIC_0("tcp_accept", VFtcp_accept, V), MAKE_INTRINSIC_I("wait_next_readable", VF_select_on_all, V), MAKE_INTRINSIC_0("is_readable", VF_is_readable, I), MAKE_INTRINSIC_0("is_writeable", VF_is_writeable, I), MAKE_INTRINSIC_0("set_rmode", VF_set_rmode, I), MAKE_INTRINSIC_0("set_action", VF_set_action, I), MAKE_INTRINSIC_0("clr_action", VF_clr_action, I), MAKE_INTRINSIC_I("do_actions", VF_do_actions, I), MAKE_INTRINSIC_0("set_wrdep", VF_set_wrdep, I), MAKE_INTRINSIC_0("copy_bytes", VF_copybytes, I), MAKE_INTRINSIC_0("get_local_ipp", VF_get_local_ipp, V), MAKE_INTRINSIC_0("get_remote_ipp", VF_get_remote_ipp, V), MAKE_INTRINSIC_II("ftp_hostport", VF_ftp_hostport, S), SLANG_END_TABLE }; static SLang_Intrin_Var_Type VFile_Var_Table[] = { MAKE_VARIABLE("vf_errno", &VFerrno, I, 1), MAKE_VARIABLE("vf_errmsg", &VFerrmsg, S, 1), SLANG_END_TABLE }; #undef I #undef S #undef V static SLang_IConstant_Type VFile_Constants [] = { #if 1 /* these are also in slang's slposio.c */ MAKE_ICONSTANT("O_RDONLY", O_RDONLY), MAKE_ICONSTANT("O_WRONLY", O_WRONLY), MAKE_ICONSTANT("O_RDWR", O_RDWR), MAKE_ICONSTANT("O_CREAT", O_CREAT), MAKE_ICONSTANT("O_EXCL", O_EXCL), MAKE_ICONSTANT("O_NOCTTY", O_NOCTTY), MAKE_ICONSTANT("O_TRUNC", O_TRUNC), MAKE_ICONSTANT("O_APPEND", O_APPEND), MAKE_ICONSTANT("O_NONBLOCK", O_NONBLOCK), #endif MAKE_ICONSTANT("O_NDELAY", O_NDELAY), MAKE_ICONSTANT("O_FSYNC", O_FSYNC), MAKE_ICONSTANT("SEEK_SET", SEEK_SET), MAKE_ICONSTANT("SEEK_CUR", SEEK_CUR), MAKE_ICONSTANT("SEEK_END", SEEK_END), SLANG_END_TABLE }; static void* add_std_to_vf_list(int fd,int flags,char* name) { SLang_MMT_Type *mmt; VFILE *s; mmt=add_to_vf_list(fd,flags,name,NULL,0,0); if (mmt) { SLang_inc_mmt(mmt); /* because the following ref's it */ s = (VFILE *) SLang_object_from_mmt(mmt); if(-1 != SLadd_intrinsic_variable(s->file, (VOID_STAR) &s->mmt, SJ_VFILE_TYPE, 1)) return mmt; SLang_free_mmt(mmt); } return NULL; } static char* vfile_string(unsigned char type, void *v) { SLang_MMT_Type *mmt; VFILE *t; char *name; mmt = *(SLang_MMT_Type **)v; t = (VFILE*) SLang_object_from_mmt(mmt); name = t->file; if (name) name = SLmake_string(name); return name; } static int char_to_int(unsigned char a_type, VOID_STAR ap, unsigned int na, unsigned char b_type, VOID_STAR bp) { int *b; unsigned char *s,*st; (void) b_type; b = (int *) bp; (void) a_type; s = (unsigned char *) ap; st = s + na; while(sINT)\n"); return -1; } if (SLdefine_for_ifdef("VFILE")) { fprintf(stderr,"VFile: fail define_for_isdef(VFILE)\n"); return -1; } if (SLadd_intrin_fun_table(VFile_Fun_Table, "_VFILE") || SLadd_intrin_var_table(VFile_Var_Table, NULL) || SLadd_iconstant_table (VFile_Constants, NULL)) return -1; if (NULL == (cl = SLclass_allocate_class("VFile_Type"))) return -1; cl->cl_destroy = destroy_vfile_type; cl->cl_string = vfile_string; if (-1 == SLclass_register_class(cl, SJ_VFILE_TYPE, sizeof(VFILE), SLANG_CLASS_TYPE_MMT)) return -1; if ( !add_std_to_vf_list(0,SJ_READ,"StdIn") || !add_std_to_vf_list(1,SJ_WRITE,"StdOut") || !add_std_to_vf_list(2,SJ_WRITE,"StdErr") ) return -1; return 1; }