/* interface.c */ #include <sys/types.h> #include <sys/time.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <fcntl.h> #include <sys/uio.h> #include <signal.h> #include "config.h" #include "object.h" #include "globals.h" #include "interp.h" #include "dbhandle.h" #include "construct.h" #include "interface.h" #include "clearq.h" #include "file.h" #include "cache.h" #include "edit.h" struct connlist_s { int fd; struct sockaddr_in address; char inbuf[MAX_STR_LEN]; int inbuf_count; int outbuf_count; char *outbuf; struct object *obj; }; struct connlist_s *connlist; int num_conns,sockfd; void saniflush(int fd,int count,char *buf) { struct timeval timeout; fd_set writefds,exceptfds; int num_written,num_to_write,num_actually_wrote; num_written=0; while (num_written<count) { FD_ZERO(&writefds); FD_ZERO(&exceptfds); FD_SET(fd,&writefds); FD_SET(fd,&exceptfds); timeout.tv_sec=0; timeout.tv_usec=0; select(getdtablesize(),NULL,&writefds,&exceptfds,&timeout); if (FD_ISSET(fd,&exceptfds)) return; if (!(FD_ISSET(fd,&writefds))) return; if ((count-num_written)<WRITE_BURST) num_to_write=count-num_written; else num_to_write=WRITE_BURST; num_actually_wrote=write(fd,buf+num_written,num_to_write); if (num_actually_wrote<=0) return; num_written+=num_actually_wrote; } } struct object *next_who(struct object *obj) { long loop; if (obj) loop=obj->devnum; else loop=(-1); while (++loop<num_conns) if (connlist[loop].fd!=(-1)) return connlist[loop].obj; return NULL; } void unbuf_output(int devnum) { int num_written; char *tmp; num_written=write(connlist[devnum].fd,connlist[devnum].outbuf, ((connlist[devnum].outbuf_count>WRITE_BURST) ? WRITE_BURST:connlist[devnum].outbuf_count)); if (num_written<=0) return; if (num_written<connlist[devnum].outbuf_count) { tmp=MALLOC(connlist[devnum].outbuf_count-num_written+1); strcpy(tmp,&((connlist[devnum].outbuf)[num_written])); connlist[devnum].outbuf_count-=num_written; FREE(connlist[devnum].outbuf); connlist[devnum].outbuf=tmp; } else { connlist[devnum].outbuf_count=0; FREE(connlist[devnum].outbuf); connlist[devnum].outbuf=NULL; } } void set_now_time() { now_time=time2int(time(NULL)); } void immediate_disconnect(int devnum) { if (devnum==(-1)) return; if (connlist[devnum].obj->flags & IN_EDITOR) remove_from_edit(connlist[devnum].obj); connlist[devnum].obj->flags&=~CONNECTED; if (connlist[devnum].outbuf_count>0) saniflush(connlist[devnum].fd,connlist[devnum].outbuf_count, connlist[devnum].outbuf); shutdown(connlist[devnum].fd,2); close(connlist[devnum].fd); connlist[devnum].fd=(-1); connlist[devnum].obj->devnum=(-1); connlist[devnum].outbuf_count=0; if (connlist[devnum].outbuf) FREE(connlist[devnum].outbuf); connlist[devnum].outbuf=NULL; } void make_new_conn() { int loop; int devnum,new_fd; struct sockaddr_in tmpaddr; struct object *boot_obj; struct var_stack *rts; struct var tmp; struct fns *func; boot_obj=ref_to_obj(0); devnum=-1; loop=sizeof(struct sockaddr); new_fd=accept(sockfd,(struct sockaddr *) &tmpaddr,&loop); if (new_fd<0) return; if (fcntl(new_fd,F_SETFL,O_NDELAY)<0) { shutdown(new_fd,2); close(new_fd); return; } loop=0; while (loop<num_conns) { if (connlist[loop].fd==(-1)) { devnum=loop; break; } loop++; } if (devnum==(-1) || (boot_obj->devnum!=(-1))) { shutdown(new_fd,2); close(new_fd); return; } connlist[devnum].fd=new_fd; connlist[devnum].inbuf_count=0; connlist[devnum].address=tmpaddr; connlist[devnum].obj=boot_obj; connlist[devnum].outbuf_count=0; connlist[devnum].outbuf=NULL; boot_obj->devnum=devnum; boot_obj->flags|=CONNECTED; #ifdef CYCLE_HARD_MAX hard_cycles=0; #endif /* CYCLE_HARD_MAX */ #ifdef CYCLE_SOFT_MAX soft_cycles=0; #endif /* CYCLE_SOFT_MAX */ func=find_fns("connect",boot_obj); if (func) { tmp.type=NUM_ARGS; tmp.value.num=0; rts=NULL; push(&tmp,&rts); interp(NULL,boot_obj,NULL,&rts,func); free_stack(&rts); } handle_destruct(); } void buffer_input(int conn_num) { static char buf[MAX_STR_LEN]; struct var_stack *rts; struct var tmp; struct fns *func; int retlen; struct object *obj; int loop; retlen=read(connlist[conn_num].fd,buf,MAX_STR_LEN-2); if (retlen<=0) { obj=connlist[conn_num].obj; immediate_disconnect(conn_num); func=find_fns("disconnect",obj); #ifdef CYCLE_HARD_MAX hard_cycles=0; #endif /* CYCLE_HARD_MAX */ #ifdef CYCLE_SOFT_MAX soft_cycles=0; #endif /* CYCLE_SOFT_MAX */ if (func) { rts=NULL; tmp.type=NUM_ARGS; tmp.value.num=0; push(&tmp,&rts); interp(NULL,obj,NULL,&rts,func); free_stack(&rts); } handle_destruct(); return; } loop=0; while (loop<retlen) { if (buf[loop]=='\n') { connlist[conn_num].inbuf[connlist[conn_num].inbuf_count]='\0'; if (connlist[conn_num].obj->flags & IN_EDITOR) do_edit_command(connlist[conn_num].obj,connlist[conn_num].inbuf); else queue_command(connlist[conn_num].obj,connlist[conn_num].inbuf); connlist[conn_num].inbuf_count=0; } else if (buf[loop]!='\r' && (isgraph(buf[loop]) || buf[loop]==' ')) { if (connlist[conn_num].inbuf_count<MAX_STR_LEN-2) { connlist[conn_num].inbuf[connlist[conn_num].inbuf_count]=buf[loop]; ++(connlist[conn_num].inbuf_count); } } loop++; } } void handle_input() { fd_set input_set,output_set,exception_set; int loop; struct timeval delay_s; struct timeval *delay_p; struct var tmp; struct var_stack *rts; struct fns *func; struct object *obj; set_now_time(); while (1) { if (cache_top>MAX_CACHEFILE_SIZE) { log_sysmsg(" cache: auto-saving"); if (save_db(NULL)) log_sysmsg(" cache: auto-save failed"); else log_sysmsg(" cache: auto-save completed"); } FD_ZERO(&input_set); FD_ZERO(&output_set); FD_ZERO(&exception_set); loop=0; while (loop<num_conns) { if (connlist[loop].fd!=(-1)) { FD_SET(connlist[loop].fd,&input_set); FD_SET(connlist[loop].fd,&exception_set); if (connlist[loop].outbuf_count) { FD_SET(connlist[loop].fd,&output_set); } } loop++; } FD_SET(sockfd,&input_set); if (alarm_list) { if (alarm_list->delay>=now_time) delay_s.tv_sec=alarm_list->delay-now_time; else delay_s.tv_sec=0; delay_s.tv_usec=0; delay_p=&delay_s; } else delay_p=NULL; select(getdtablesize(),&input_set,&output_set,&exception_set,delay_p); set_now_time(); if (FD_ISSET(sockfd,&input_set)) make_new_conn(); loop=0; while (loop<num_conns) { if (connlist[loop].fd!=(-1)) if (FD_ISSET(connlist[loop].fd,&exception_set)) { obj=connlist[loop].obj; immediate_disconnect(loop); func=find_fns("disconnect",obj); #ifdef CYCLE_HARD_MAX hard_cycles=0; #endif /* CYCLE_HARD_MAX */ #ifdef CYCLE_SOFT_MAX soft_cycles=0; #endif /* CYCLE_SOFT_MAX */ if (func) { rts=NULL; tmp.type=NUM_ARGS; tmp.value.num=0; push(&tmp,&rts); interp(NULL,obj,NULL,&rts,func); free_stack(&rts); } handle_destruct(); } loop++; } loop=0; while (loop<num_conns) { if (connlist[loop].fd!=(-1)) if (FD_ISSET(connlist[loop].fd,&input_set)) buffer_input(loop); loop++; } loop=0; while (loop<num_conns) { if (connlist[loop].fd!=(-1)) if (FD_ISSET(connlist[loop].fd,&output_set)) unbuf_output(loop); loop++; } #ifdef CYCLE_HARD_MAX hard_cycles=0; #endif /* CYCLE_HARD_MAX */ do { handle_destruct(); handle_alarm(); handle_destruct(); handle_command(); handle_destruct(); handle_alarm(); handle_destruct(); } while (cmd_head || dest_list); unload_data(); } } int init_interface(int port, int do_single) { int loop=0; struct sockaddr_in server; if (do_single) return NOSINGLE; sockfd=socket(AF_INET,SOCK_STREAM,0); if (sockfd<0) return NOSOCKET; server.sin_family=AF_INET; server.sin_addr.s_addr=INADDR_ANY; server.sin_port=htons(port); if (bind(sockfd,(struct sockaddr *) &server, sizeof(server))) return PORTINUSE; listen(sockfd,5); num_conns=getdtablesize()-(7+MIN_FREE_FILES); if (num_conns<1) num_conns=1; if (num_conns>MAX_CONNS) num_conns=MAX_CONNS; connlist=MALLOC(sizeof(struct connlist_s)*num_conns); loop=0; while (loop<num_conns) { connlist[loop].fd=(-1); loop++; } signal(SIGPIPE,SIG_IGN); return 0; } void shutdown_interface() { int loop=0; while (loop<num_conns) { if (connlist[loop].fd!=(-1)) immediate_disconnect(loop); loop++; } close(sockfd); FREE(connlist); } char *get_devconn(struct object *obj) { if (!obj) return NULL; if (obj->devnum==-1) return NULL; return inet_ntoa(connlist[obj->devnum].address.sin_addr); } void send_device(struct object *obj, char *msg) { int len; char *tmp; if (!obj) return; if (obj->devnum==-1) return; if (!msg) return; len=strlen(msg); if (!len) return; if (connlist[obj->devnum].outbuf_count>MAX_OUTBUF_LEN) return; if (connlist[obj->devnum].outbuf_count+len>MAX_OUTBUF_LEN) len+=24; if (connlist[obj->devnum].outbuf) { tmp=MALLOC(connlist[obj->devnum].outbuf_count+len+1); strcpy(tmp,connlist[obj->devnum].outbuf); strcat(tmp,msg); if (connlist[obj->devnum].outbuf_count+len-24>MAX_OUTBUF_LEN) strcat(tmp,"\n*** Output Flushed ***\n"); FREE(connlist[obj->devnum].outbuf); connlist[obj->devnum].outbuf=tmp; connlist[obj->devnum].outbuf_count+=len; } else { tmp=MALLOC(len+1); strcpy(tmp,msg); if (len-24>MAX_OUTBUF_LEN) strcat(tmp,"\n*** Output Flushed ***\n"); connlist[obj->devnum].outbuf=tmp; connlist[obj->devnum].outbuf_count=len; } } int reconnect_device(struct object *src, struct object *dest) { if (dest->devnum!=-1) return 1; if (src->devnum==-1) return 1; dest->devnum=src->devnum; src->flags&=~CONNECTED; src->devnum=(-1); dest->flags|=CONNECTED; connlist[dest->devnum].obj=dest; return 0; } void disconnect_device(struct object *obj) { if (obj->devnum==-1) return; if (!(obj->flags & CONNECTED)) return; immediate_disconnect(obj->devnum); obj->flags&=~CONNECTED; obj->devnum=(-1); }