/* process handling for eggdrop 13jun94 children are split off to: accept dcc connections (which is like telnetting) telnet (link) to other bots telnet (relay) users to other bots execute system shell commands dprintf'ized, 18nov95 */ #if HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #if HAVE_SYS_WAIT_H # include #endif #ifndef WEXITSTATUS # define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8) #endif #ifndef WIFEXITED # define WIFEXITED(stat_val) (((stat_val) & 255) == 0) #endif #include "eggdrop.h" #include "proto.h" /* if they can thread, do so instead of making full-fledged processes */ #ifdef LINUX_THREADS #include "thread.c" #endif extern struct dcc_t dcc[]; extern int dcc_total; extern char origbotname[]; extern char cx_file[]; extern int cx_line; struct proc_t { unsigned int pid; unsigned char type; unsigned int sock; unsigned char dead; /* process had to be killed */ long num; /* misc */ time_t ts; #ifdef LINUX_THREADS char host[121]; char portstr[11]; void *stack; int port; #endif struct proc_t *next; } *proc=NULL; /* valid process types: */ #define P_DCC 1 #define P_LINK 2 #define P_RELAY 3 #define P_EXEC 4 /* for threads */ #define STKSIZE 1024 int expmem_proc() { int tot; struct proc_t *p; tot=0; p=proc; while (p!=NULL) { tot+=sizeof(struct proc_t); #ifdef LINUX_THREADS tot+=STKSIZE; #endif p=p->next; } return tot; } struct proc_t *new_proc(pid,type,sock,num) int pid,type,sock,num; { struct proc_t *p; p=proc; proc=(struct proc_t *)nmalloc(sizeof(struct proc_t)); proc->next=p; proc->pid=pid; proc->type=type; proc->num=num; proc->ts=time(NULL); proc->sock=sock; proc->dead=0; return proc; } void rem_proc(pid) int pid; { struct proc_t *p,*pr; if (proc==NULL) return; if (proc->pid==pid) { p=proc; proc=proc->next; nfree(p); return; } p=proc; pr=proc; while ((p!=NULL) && (p->pid!=pid)) { pr=p; p=p->next; } if (p==NULL) return; pr->next=p->next; nfree(p); } struct proc_t *find_proc(pid) int pid; { struct proc_t *p; if (proc==NULL) return proc; p=proc; while (p!=NULL) { if (p->pid==pid) return p; p=p->next; } return NULL; } int get_forked_idx(pid) int pid; { int i; for (i=0; ipid==pid)) return i; return -1; } void kill_proc(idx) int idx; { struct proc_t *p; p=find_proc(dcc[idx].u.fork->pid); p->dead=1; close(p->sock); kill(p->pid,9); } /* like tell_dcc, zidx is negative if it's a z (sock), positive if idx */ void tell_dcc_proc(zidx,idx) int zidx,idx; { struct proc_t *p; time_t tt; int x; char s[20],s1[20]; p=find_proc(dcc[idx].u.fork->pid); if (p==NULL) { if (zidx<0) tprintf(-zidx," (NO PROCESS RECORD!)\n"); else dprintf(zidx," (NO PROCESS RECORD!)\n"); return; } tt=time(NULL); x=(tt-(p->ts)); if (x<60) sprintf(s,"%ds",x); else sprintf(s,"%dm%ds",(x/60),(x%60)); switch (dcc[idx].u.fork->type) { case DCC_CHAT: strcpy(s1,"chat"); break; case DCC_FILES: strcpy(s1,"chat/files"); break; case DCC_TANDEM: strcpy(s1,"bot-link"); break; case DCC_RELAY: strcpy(s1,"relay"); break; case DCC_SEND: strcpy(s1,"file-xfer"); break; case DCC_FORK: strcpy(s1,"exec"); break; } if (zidx<0) tprintf(-zidx," (attempting %s: process id #%d, socket %d, active %s%s)\n", s1,p->pid,p->sock,s,p->dead?" [DEAD]":""); else dprintf(zidx," (attempting %s: process id #%d, socket %d, active %s%s)\n", s1,p->pid,p->sock,s,p->dead?" [DEAD]":""); } void check_expired_forks() { struct proc_t *p=proc; int i; time_t now=time(NULL); while (p!=NULL) { if ((now - p->ts > WAIT_CONNECT) && (!(p->dead))) { /* expired */ i=get_forked_idx(p->pid); if (i<0) { putlog(LOG_MISC,"*","Expired fork: process #%d (NO DCC ENTRY!)", p->pid); } else if (dcc[i].u.fork->type!=DCC_FORK) { /* ^ external editor and exec'd processes don't expire */ /* char t[20]; switch(dcc[i].u.fork->type) { case DCC_CHAT: strcpy(t,"chat"); break; case DCC_FILES: strcpy(t,"files"); break; case DCC_TANDEM: strcpy(t,"tandem"); break; case DCC_RELAY: strcpy(t,"relay"); break; case DCC_SEND: strcpy(t,"send"); break; default: sprintf(t,"UNKNOWN %d",dcc[i].u.fork->type); break; } putlog(LOG_MISC,"*","Expired fork: process #%d (type %s to %s)",p->pid, t,dcc[i].nick); */ /* if (dcc[i].u.fork->type==DCC_TANDEM) tandout("*trying %s %s\n",origbotname,dcc[i].nick); */ p->dead=1; close(p->sock); kill(p->pid,9); /* don't remove process record: wait for dead child to do that */ } } p=p->next; } } /* stop any auto-connect processes to this bot */ void stop_auto(who) char *who; { struct proc_t *p; int i; p=proc; while (p!=NULL) { i=get_forked_idx(p->pid); if (i>=0) { if (strcasecmp(dcc[i].nick,who)==0) { if (dcc[i].u.fork->type==DCC_TANDEM) { p->dead=1; close(p->sock); kill(p->pid,9); dcc[i].port+=10; /* don't try again */ } } } p=p->next; } } int procs() { struct proc_t *p; int i=0; if (proc==NULL) return 0; p=proc; while (p!=NULL) { i++; p=p->next; } return i; } /* remove all signal handlers from a child process */ void iloveyou(sock) int sock; { struct sigaction sv; int i; for (i=0; itype) { case P_DCC: z=open_telnet_dcc(p->sock,p->host,p->portstr); break; default: z=open_telnet_raw(p->sock,p->host,p->port); break; } if (z>=0) exit(0); else exit(errno); } #endif void fork_telnet_dcc(idx,host,port) int idx; char *host,*port; { #ifdef LINUX_THREADS struct proc_t *p; int x; p=new_proc(-1,P_DCC,getsock(),0); strcpy(p->host,host); strcpy(p->portstr,port); p->stack=nmalloc(STKSIZE); x=new_thread(thr_link,(void *)p,CLONE_MEM|CLONE_FS|CLONE_FILES,p->stack, STKSIZE); if (x<0) { putlog(LOG_MISC,"*","Forking error #%d",errno); rem_proc(-1); return; } if (p!=NULL) { p->pid=x; dcc[idx].u.fork->pid=x; } #else int sk,x,z; sk=getsock(); x=fork(); if (x==0) { /* child */ iloveyou(sk); z=open_telnet_dcc(sk,host,port); if (z>=0) exit(0); else exit(errno); } else if (x<0) { putlog(LOG_MISC,"*","Forking error #%d!",errno); return; } else { /* parent */ dcc[idx].u.fork->pid=x; new_proc(x,P_DCC,sk,0); } #endif } void fork_link(addr,port,idx,stport) char *addr; int port,idx,stport; { #ifdef LINUX_THREADS struct proc_t *p; int x; p=new_proc(-1,P_LINK,getsock(),stport); strcpy(p->host,addr); p->port=port; p->stack=nmalloc(STKSIZE); x=new_thread(thr_link,(void *)p,CLONE_MEM|CLONE_FS|CLONE_FILES,p->stack, STKSIZE); if (x<0) { putlog(LOG_MISC,"*","Forking error #%d",errno); rem_proc(-1); return; } if (p!=NULL) { p->pid=x; dcc[idx].u.fork->pid=x; } #else int sk,x,z; sk=getsock(); x=fork(); if (x==0) { /* child */ iloveyou(sk); z=open_telnet_raw(sk,addr,port); if (z>=0) exit(0); else exit(errno); } else if (x<0) { putlog(LOG_MISC,"*","Forking error #%d!",errno); return; } else { /* parent */ dcc[idx].u.fork->pid=x; new_proc(x,P_LINK,sk,stport); } #endif } void fork_relay(addr,port,idx,stport,uidx) char *addr; int port,idx,stport,uidx; { #ifdef LINUX_THREADS struct proc_t *p; int x; p=new_proc(-1,P_RELAY,getsock(),stport); strcpy(p->host,addr); p->port=port; p->stack=nmalloc(STKSIZE); x=new_thread(thr_link,(void *)p,CLONE_MEM|CLONE_FS|CLONE_FILES,p->stack, STKSIZE); if (x<0) { putlog(LOG_MISC,"*","Forking error #%d",errno); rem_proc(-1); return; } if (p!=NULL) { p->pid=x; dcc[idx].u.fork->pid=x; dcc[uidx].u.fork->pid=x; dcc[idx].u.fork->x=0; dcc[uidx].u.fork->x=1; } #else int sk,x,z; sk=getsock(); x=fork(); if (x==0) { /* child */ iloveyou(sk); z=open_telnet_raw(sk,addr,port); if (z>=0) exit(0); else exit(errno); } else if (x<0) { putlog(LOG_MISC,"*","Forking error #%d!",errno); return; } else { /* parent */ dcc[idx].u.fork->pid=x; dcc[uidx].u.fork->pid=x; dcc[idx].u.fork->x=0; dcc[uidx].u.fork->x=1; new_proc(x,P_RELAY,sk,stport); } #endif } /* can't really thread for this... */ void fork_exec(cmd,idx) char *cmd; int idx; { int x,z; z=dcc[idx].sock; x=fork(); if (x==0) { /* child */ iloveyou(z); /* point stdin/stdout/stderr to this socket: */ dup2(z,0); dup2(z,1); dup2(z,2); system(cmd); exit(0); } else if (x<0) { putlog(LOG_MISC,"*","Forking error #%d!",errno); return; } else { /* parent */ dcc[idx].u.fork->pid=x; new_proc(x,P_EXEC,z,0); } } /* i=pid, st=status */ void got_dead_child2(i,st) int i; wait_t st; { int idx,idx2,es; struct proc_t *p; context; p=find_proc(i); if (p==NULL) { /* log(LOG_MISC,"* Child #%d terminated without a child record!",i); */ /* don't log an error, cos it's probably a Tcl thing */ return; } es=WEXITSTATUS(st); #ifdef LINUX_THREADS nfree(p->stack); #endif if (p->dead) es=FORK_KILLED; /* just some big number */ if ((es>0) && (p->type!=P_EXEC)) close(p->sock); if (p->type==P_DCC) { idx=get_forked_idx(i); if (es==0) dcc[idx].sock=p->sock; cont_got_dcc(es,idx); rem_proc(i); } else if (p->type==P_LINK) { idx=get_forked_idx(i); if (es==0) dcc[idx].sock=p->sock; cont_tandem_link(es,idx,p->num); rem_proc(i); } else if (p->type==P_RELAY) { idx=get_forked_idx(i); if (dcc[idx].u.fork->x==1) { idx2=idx; dcc[idx2].u.fork->pid=0; idx=get_forked_idx(i); } else { dcc[idx].u.fork->pid=0; idx2=get_forked_idx(i); } if (es==0) dcc[idx].sock=p->sock; cont_tandem_relay(es,idx,p->num,idx2); rem_proc(i); } else if (p->type==P_EXEC) { struct chat_info *ci; idx=get_forked_idx(i); ci=dcc[idx].u.fork->u.chat; nfree(dcc[idx].u.fork); dcc[idx].u.chat=ci; dcc[idx].type=DCC_CHAT; if (dcc[idx].u.chat->channel==0) chanout2(0,"%s joined the party line.\n",dcc[idx].nick); else if (dcc[idx].u.chat->channel>0) chanout2(dcc[idx].u.chat->channel,"%s joined the channel.\n", dcc[idx].nick); rem_proc(i); } else { putlog(LOG_MISC,"*","* Child #%d terminated and was not trapped! (type=%d)", i,p->type); rem_proc(i); } } /* sigchild received */ void got_dead_child() { wait_t st; int i; i=(-1); while (i<0) { i=wait(&st); /* keep calling if interrupted by signal */ if ((i==-1) && (errno==ECHILD)) return; } got_dead_child2(i,st); }