/* 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);
}