/* 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 */ #if HAVE_CONFIG_H #include #endif #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" 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; 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 int expmem_proc() { int tot; struct proc_t *p; tot=0; p=proc; while (p!=NULL) { tot+=sizeof(struct proc_t); p=p->next; } return tot; } void 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; } 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; } /* 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,"tandem-bot"); break; case DCC_RELAY: strcpy(s1,"relay"); break; case DCC_SEND: strcpy(s1,"file-xfer"); break; case DCC_EDIT: strcpy(s1,"edit"); 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; int i,ok; time_t now=time(NULL); char t[20]; p=proc; while (p!=NULL) { if ((now - p->ts > 600) && (!(p->dead))) { /* 10 mins */ i=get_forked_idx(p->pid); if (i<0) { log(LOG_MISC,"Expired fork: process #%d (NO DCC ENTRY!)",p->pid); } else if ((dcc[i].u.fork->type!=DCC_EDIT) && (dcc[i].u.fork->type!=DCC_FORK)) { /* ^ external editor and exec'd processes don't expire */ 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; } log(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); shutdown(p->sock,2); 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); shutdown(p->sock,2); kill(p->pid,9); } } } 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; i=0) exit(0); else exit(errno); } else if (x<0) { log(LOG_MISC,"Forking error!"); return; } else { /* parent */ dcc[idx].u.fork->pid=x; new_proc(x,P_DCC,sk,0); } } void fork_link(addr,port,idx,stport) char *addr; int port,idx,stport; { int sk,x,z; struct proc_t *p; char s[21]; context; 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) { log(LOG_MISC,"Forking error!"); return; } else { /* parent */ dcc[idx].u.fork->pid=x; new_proc(x,P_LINK,sk,stport); } } void fork_relay(addr,port,idx,stport,uidx) char *addr; int port,idx,stport,uidx; { int sk,x,z; struct proc_t *p; char s[21]; 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) { log(LOG_MISC,"Forking error!"); 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); } } void fork_exec(cmd,idx) char *cmd; int idx; { int x,z; struct proc_t *p; 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) { log(LOG_MISC,"Forking error!"); 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 j,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); if (p->dead) es=230; /* just some big number */ if ((es>0) && (p->type!=P_EXEC)) { close(p->sock); shutdown(p->sock,2); } 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) { chanout(0,"*** %s joined the party line.\n",dcc[idx].nick); tandout("chan %s %d %s joined the party line.\n",origbotname,0, dcc[idx].nick); } else if (dcc[idx].u.chat->channel>0) { chanout(dcc[idx].u.chat->channel,"*** %s joined the channel.\n", dcc[idx].nick); tandout("chan %s %d %s joined the channel.\n",origbotname, dcc[idx].u.chat->channel,dcc[idx].nick); } rem_proc(i); } else { log(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); }