/* * Copyright (c) 1990 The Ohio State University. * All rights reserved. * * Redistribution and use in source and binary forms are permitted * provided that: (1) source distributions retain this entire copyright * notice and comment, and (2) distributions including binaries display * the following acknowledgement: ``This product includes software * developed by The Ohio State University and its contributors'' * in the documentation or other materials provided with the distribution * and in all advertising materials mentioning features or use of this * software. Neither the name of the University nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ #include "cons.h" #include <sys/file.h> #include <stdio.h> #include <fcntl.h> #include <termio.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <netdb.h> #include <sys/ioctl.h> #include <errno.h> #include <signal.h> #include <pwd.h> #define puke(msg) { perror(msg); exit(-1); } #define drop(q) {tyme=time(NULL);\ fprintf(clog,"%-9s%-14.14s session ended %-14s %s",\ ctbl[q].name,ctbl[q].from,\ console[group][ctbl[q].mmb].server,ctime(&tyme));\ fflush(clog);\ close(ctbl[q].fd);\ for(zyx= q ; zyx<nc; ++zyx){\ ctbl[zyx]=ctbl[zyx+1];cnct_port[zyx]=cnct_port[zyx+1];\ } --nc; --i; goto next_conn;} /* flags bit values */ #define IC1 0001 /* first interrupt character received */ #define IC2 0002 /* second interrupt character received */ #define HC1 0010 /* first break character received */ #define HC2 0020 /* second break character received */ #define CES 0100 /* change escape sequence command received */ #define CC1 0200 /* first char of new escape sequence received */ #ifndef FD_SET #define FD_ZERO(a) {*(a)=0;} #define FD_SET(d,a) {*(a) |= (1 << (d));} #define FD_ISSET(d,a) (*(a) & (1 << (d))) #define OLDSEL #endif struct sockaddr_in master_port, lstn_port, response_port; struct sockaddr_in cnct_port[MAXMEMB*3], in_port; struct hostent *hp; int sfd, sin, m[MAXGRP], procnum, zyx; char host[64], rootpass[32], conspass[32]; int glargc; char **glargv; FILE *fp, *clog; struct consent { /* console information */ char server[32]; /* server name */ char dfile[32]; /* device file */ char lfile[32]; /* log file */ int port; int pid; } console[MAXGRP][MAXMEMB]; main(argc,argv) int argc; char *argv[]; { char ch[BUFSIZ], serv[32], dev[32], log[32]; int group; long tyme; glargc=argc; glargv=argv; /* get the root pw entry */ if (PASSENTRY != NULL) strcpy(conspass,getpwnam(PASSENTRY)->pw_passwd); else conspass[0]='\0'; strcpy(rootpass,getpwnam("root")->pw_passwd); /* read the config file */ if ((fp=fopen(CONFIG,"r")) == NULL) puke ("fopen"); group=0; procnum=0; while(fgets(ch,127,fp) != NULL) { if (ch[0] != '#') { sscanf(ch,"%[^:]:%[^:]:%[^:]:%d\n",serv,dev,log,&group); if (group > procnum) { procnum = group; m[group]=0; } strcpy(console[group][m[group]].server,serv); strcpy(console[group][m[group]].dfile,dev); strcpy(console[group][m[group]].lfile,log); ++m[group]; fprintf(stderr,"%s %s %s %d\n",serv,dev,log,group); } } fclose(fp); fprintf(stderr,"done reading config file\n"); /* open the console usage log file */ if ((clog=fopen(CLOG,"a")) == NULL) puke ("fopen"); tyme=time(NULL); fprintf(clog,"Starting Console Server at %s",ctime(&tyme)); fflush(clog); gethostname(host,63); hp=gethostbyname(host); if (hp == NULL) puke ("gethostbyname"); /* spawn all the children */ for (group=1; group<=procnum; ++group) { spawn(group); /* child never returns */ } master(); exit(1); /* should never get here */ } /* this routine is used by the master console server process */ master() { char *ptr, serv[32], ch[BUFSIZ]; int msfd, so, tmpntr, i, j, prnum, found; long tyme; void fixkids(), killkids(); /* set up signal handler */ signal(SIGCHLD,fixkids); signal(SIGTERM,killkids); /* set up port for master to listen on */ bzero((char *)&master_port, sizeof(master_port)); /*socket for listening*/ bcopy(hp->h_addr, (char *)&master_port.sin_addr, hp->h_length); master_port.sin_port = PORT; master_port.sin_family = hp->h_addrtype; if ((msfd=socket(AF_INET,SOCK_STREAM,0)) < 0) puke("socket"); if (setsockopt(msfd,SOL_SOCKET,SO_REUSEADDR,0,0)<0) puke("setsockopt"); if (bind(msfd, &master_port, sizeof(master_port))<0) puke("bind"); if (listen(msfd,SOMAXCONN)<0) puke("listen"); so=sizeof(master_port); while(1) { sin=accept(msfd,&response_port,&so); /* handle the connection (port lookup) */ ptr=ch; i=sizeof(ch); do { /* thanks to John P. Linderman <jpl@allegra.att.com> for buffer check */ if (--i <= 0) { fprintf(stderr,"Master port lookup - buffer overrun.\n"); goto done; } if (read(sin,ptr,1) == 0) { fprintf(stderr,"Master port lookup - connection lost.\n"); goto done; } } while (*ptr++ != '\n'); *ptr='\0'; sscanf(ch,"%s",serv); if (strcmp(serv,"who") == 0) { for (i=1; i<=procnum; ++i) { sprintf(ch,"%d:",console[i][0].port); write(sin,ch,strlen(ch)); } sprintf(ch,"\n"); write(sin,ch,strlen(ch)); goto done; } found=0; for (i=1; i<=procnum; ++i) { for (j=0; j<m[i]; ++j) { if (strncmp(serv,console[i][j].server,strlen(serv)) == 0) { prnum=console[i][0].port; ++found; } } } if (found == 1) { sprintf(ch,"%d\n",prnum); write(sin,ch,strlen(ch)); goto done; } else if (found > 1) { sprintf(ch,"Ambigous server abbreviation\r\n"); write(sin,ch,strlen(ch)); } else { sprintf(ch,"Server not found\r\n"); write(sin,ch,strlen(ch)); } done: close(sin); } } /* routine used by the child processes. Most of it is UGLY escape sequence parsing. All of it is squirrely code, for which I most humbly apologize */ kiddie(group) int group; { int i, j, k; char *ptr; u_char rem_addr[4]; char ich1='', ich2='', hch1='l', hch2='1'; struct sgttyb sty; int s, nf, nr, so, tmpntr, softcar=1; #ifdef OLDSEL long rmask, wmask; #else fd_set rmask, wmask; #endif int lfd[MAXMEMB], tfd[MAXMEMB], nc=0; char ch[BUFSIZ], och[256], serv[32], comm[32], user[32]; char pass[32], args[128], *argp, *end; int argl; long tyme; struct cnctbl { /* Connection Information: */ int fd; /* file descriptor */ int mmb; /* which member in group */ char wren; /* (client) write enable flag */ char name[9]; /* name of user */ char from[32]; /* location of user */ long tym; /* time of connect */ char flags; /* flags for interrupt control chars and halt chars */ char ic[2]; /* two character escape sequence */ } ctbl[MAXMEMB*3]; /* turn off signals that master() turned on (only matters if respawned) */ signal(SIGCHLD,SIG_DFL); signal(SIGTERM,SIG_DFL); so=sizeof(lstn_port); sprintf(args,"conserver"); /* open all the files we need for the consoles in our group */ for (i=0; i < m[group]; ++i) { lfd[i] = open (console[group][i].lfile,O_RDWR|O_CREAT|O_APPEND,0666); tfd[i] = open (console[group][i].dfile,O_RDWR|O_NDELAY); ioctl(tfd[i],TIOCSSOFTCAR,&softcar); ioctl(tfd[i],TIOCGETP,&sty); sty.sg_flags = (sty.sg_flags | RAW); sty.sg_flags = (sty.sg_flags & ~ECHO); sty.sg_flags = (sty.sg_flags & ~CRMOD); sty.sg_flags = (sty.sg_flags | TANDEM); sty.sg_ispeed=B9600; sty.sg_ospeed=B9600; ioctl(tfd[i],TIOCSETP,&sty); strcat(args," "); strcat(args,console[group][i].server); } /* reset the arg list - This is not a safe thing to do. It works on our UNIX. It may not work elsewhere. If the provided args for conserver aren't long enough, the new args will be truncated */ if (glargc > 1) { argp=glargv[0]; /* *argp++='-'; This would make it append the real name at end in ps */ end = glargv[glargc-1]+strlen(glargv[glargc-1]); argl=strlen(args); if (argl > end-argp-2) { argl=end-argp-2; args[argl]='\0'; } strcpy(argp,args); argp += argl; while (argp < end) *argp++ = ' '; } /* the MAIN loop of the console server */ FD_ZERO(&wmask); while (1) { /* set up stuff for the select() call */ FD_ZERO(&rmask); FD_SET(sfd,&rmask); for (i=0; i<m[group]; ++i) FD_SET(tfd[i],&rmask); for (i=0; i<nc; ++i) FD_SET(ctbl[i].fd,&rmask); nf = select(getdtablesize(),&rmask,&wmask,&wmask,NULL); /* anything from console? */ for (i=0; i<m[group]; ++i) { if (FD_ISSET(tfd[i],&rmask)) { if ((nr=read(tfd[i],ch,BUFSIZ)) == 0) { /* read terminal line */ /* carrier lost */ fprintf(stderr,"Carrier Lost!\n"); } else { for (j=0; j<nr; ++j) ch[j]&=127; /* clear parity */ write(lfd[i],ch,nr); /* write to log file */ for (j=0; j<nc; ++j) /* write to all connections on this server */ if (ctbl[j].mmb == i) write(ctbl[j].fd,ch,nr); } } } /* anything from a connection? */ for (i=0; i<nc; ++i) { if (FD_ISSET(ctbl[i].fd,&rmask)) { if ((nr=read(ctbl[i].fd,ch,BUFSIZ)) == 0) { /* read connection */ /* reached EOF - close connection */ drop(i); } else { tmpntr=0; for (j=0; j<nr; ++j) { /* are we in middle of halt sequence? */ if (ctbl[i].flags & HC1) { if (ch[j] == hch2) { ctbl[i].flags=0; tmpntr=j+1; if (ctbl[i].wren) { ioctl(tfd[ctbl[i].mmb],TCSBRK,NULL); /* THE OLD WAY // ioctl(tfd[ctbl[i].mmb],TIOCSBRK,NULL); // sleep(3); // ioctl(tfd[ctbl[i].mmb],TIOCCBRK,NULL); */ } else { sprintf(och,"\r\nYou must be fully attached to halt machine.\r\n"); write(ctbl[i].fd,och,strlen(och)); } } else { ctbl[i].flags = 0; if (ctbl[i].wren) { write(tfd[ctbl[i].mmb],ctbl[i].ic,2); /* write intrpt chars */ write(tfd[ctbl[i].mmb],&hch1,1); /* write halt char 1 */ tmpntr=j; } } } /* are we in middle of changing escape sequence? */ else if (ctbl[i].flags & CES) { if (ctbl[i].flags & CC1) { /* already have first one */ tmpntr=j+1; ctbl[i].ic[1]=ch[j]; ctbl[i].flags=0; } else { ctbl[i].ic[0]=ch[j]; ctbl[i].flags |= CC1; tmpntr=j+1; } } /* do we have any of the escape sequence? */ else if (ctbl[i].flags & IC1) { /* already have first char */ if (ctbl[i].flags & IC2) { /* already have both chars */ switch (ch[j]) { case 'a' : /* attach */ for (k=0; k<nc; ++k) { if (ctbl[k].mmb == ctbl[i].mmb && ctbl[k].wren && i!=k) { sprintf(och,"\r\nSomeone is already attached.\r\n"); write(ctbl[i].fd,och,strlen(och)); break; } } if (k == nc) { tyme=time(NULL); fprintf(clog,"%-9s%-14.14s change attach %-14s %s", ctbl[i].name,ctbl[i].from, console[group][ctbl[i].mmb].server,ctime(&tyme)); fflush(clog); ctbl[i].wren=1; sprintf(och,"\r\n[attached]\r\n"); write(ctbl[i].fd,och,strlen(och)); } break; case 'f' : /* force attach */ for (k=0; k<nc; ++k) { if (ctbl[k].mmb == ctbl[i].mmb && ctbl[k].wren && i!=k) { ctbl[k].wren=0; sprintf(och,"\r\nYour attach has been forced off by %s@%s.\r\n",ctbl[i].name,ctbl[i].from); write(ctbl[k].fd,och,strlen(och)); sprintf(och,"You are now in 'spy' mode.\r\n"); write(ctbl[k].fd,och,strlen(och)); tyme=time(NULL); fprintf(clog,"%-9s%-14.14s forced to spy %-14s %s", ctbl[i].name,ctbl[i].from, console[group][ctbl[i].mmb].server,ctime(&tyme)); fflush(clog); } } tyme=time(NULL); fprintf(clog,"%-9s%-14.14s change force %-14s %s", ctbl[i].name,ctbl[i].from, console[group][ctbl[i].mmb].server,ctime(&tyme)); fflush(clog); ctbl[i].wren=1; sprintf(och,"\r\n[attached]\r\n"); write(ctbl[i].fd,och,strlen(och)); break; case 's': /* spy mode */ tyme=time(NULL); fprintf(clog,"%-9s%-14.14s change spying %-14s %s", ctbl[i].name,ctbl[i].from, console[group][ctbl[i].mmb].server,ctime(&tyme)); fflush(clog); ctbl[i].wren=0; sprintf(och,"\r\n[spying]\r\n"); write(ctbl[i].fd,och,strlen(och)); break; case '.': /* disconnect */ case '': case '': ioctl(tfd[ctbl[i].mmb],TIOCGETP,&sty); /* turn flow */ sty.sg_flags = (sty.sg_flags | TANDEM); /* control */ ioctl(tfd[ctbl[i].mmb],TIOCSETP,&sty); /* (back) on */ nr=0; tmpntr=0; drop(i); break; case '': /* background */ case 'b': sprintf(och,"\r\nNot implemented.\r\n"); write(ctbl[i].fd,och,strlen(och)); break; case 'c': case 19 : /* ctrl-S */ ioctl(tfd[ctbl[i].mmb],TIOCGETP,&sty); if (sty.sg_flags & TANDEM) { /* if TANDEM is set */ sty.sg_flags = (sty.sg_flags & ~TANDEM); sprintf(och,"\r\n[Control flow OFF]\r\n"); write(ctbl[i].fd,och,strlen(och)); } else { sty.sg_flags = (sty.sg_flags | TANDEM); sprintf(och,"\r\n[Control flow ON]\r\n"); write(ctbl[i].fd,och,strlen(och)); } ioctl(tfd[ctbl[i].mmb],TIOCSETP,&sty); break; case 'k': /* redefine escape keys */ case 'e': case 'r': ctbl[i].flags |= CES; break; case 'w': /* who */ sprintf(och,"\r\nok\r\n"); write(ctbl[i].fd,och,strlen(och)); for (k=0; k<nc; ++k) { if (ctbl[k].mmb == ctbl[i].mmb) { sprintf(och,"%-10s%-16.16s",ctbl[k].name,ctbl[k].from); write(ctbl[i].fd,och,strlen(och)); if (ctbl[k].wren) sprintf(och,"attached since %s\r",ctime(&(ctbl[k].tym))); else sprintf(och,"spying since %s\r",ctime(&(ctbl[k].tym))); write(ctbl[i].fd,och,strlen(och)); } } break; case 'l': /* halt character 1 */ ctbl[i].flags |= HC1; break; case 'h': /* help */ case '?': sprintf(och,"\r\nCommand summary:\r\n"); write(ctbl[i].fd,och,strlen(och)); sprintf(och,"a attach\r\n"); write(ctbl[i].fd,och,strlen(och)); sprintf(och,"f force attach\r\n"); write(ctbl[i].fd,och,strlen(och)); sprintf(och,"s unattach and spy\r\n"); write(ctbl[i].fd,och,strlen(och)); sprintf(och,". [^D][^C] disconnect\r\n"); write(ctbl[i].fd,och,strlen(och)); sprintf(och,"c [^S] toggle control flow (on by default)\r\n"); write(ctbl[i].fd,och,strlen(och)); sprintf(och,"l1 halt server\r\n"); write(ctbl[i].fd,och,strlen(och)); sprintf(och,"w who else is connected\r\n"); write(ctbl[i].fd,och,strlen(och)); sprintf(och,"e [r][k] redefine escape sequence\r\n"); write(ctbl[i].fd,och,strlen(och)); sprintf(och,"h [?] help\r\n"); write(ctbl[i].fd,och,strlen(och)); sprintf(och,"<CR> [spc] continue\r\n"); write(ctbl[i].fd,och,strlen(och)); break; case ' ': /* abort escape sequence */ case '\n': break; default: /* false alarm, send escape sequence */ ctbl[i].flags = 0; if (ctbl[i].wren) { write(tfd[ctbl[i].mmb],ctbl[i].ic,2); /* interupt chars */ write(tfd[ctbl[i].mmb],&ch[j],1); /*write current char*/ } break; } tmpntr=j+1; if (!(ctbl[i].flags & (HC1|CES) )) ctbl[i].flags=0; } else { /* only have first char */ if (ch[j] == ctbl[i].ic[1]) { ctbl[i].flags |= IC2; tmpntr=j+1; } else { ctbl[i].flags = 0; tmpntr=j; if (ctbl[i].wren) write(tfd[ctbl[i].mmb],ctbl[i].ic,1); /*write intrpt char*/ } } } /* is current char the first escape char? */ /* the fact that this is an 'else' means we can NOT interrupt*/ /* the escape sequence with the escape sequence */ /* this has to be this way, because the first character */ /* of the seqnc may be the same as the end of a command */ else if (ch[j] == ctbl[i].ic[0]) { ctbl[i].flags |= IC1; tmpntr=j+1; if (ctbl[i].wren) write(tfd[ctbl[i].mmb] ,ch,j); /* write up to interupt char */ } } if (ctbl[i].wren && !ctbl[i].flags) /* if writable & no interrupt */ write(tfd[ctbl[i].mmb],ch+tmpntr,nr-tmpntr); /*write to terminal*/ } } next_conn: continue; /* re-entry point after a connection is dropped */ } /* anything to accept? */ if (FD_ISSET(sfd,&rmask)) { /* accept new connections and deal with them */ ctbl[nc].fd=accept(sfd,&(cnct_port[nc]),&so); if (ctbl[nc].fd < 0) { if (errno == EMFILE) /* too many open descriptors */ continue; /* continue the while (1) */ else puke("accept"); } ctbl[nc].flags=0; so=sizeof(lstn_port); getpeername(ctbl[nc].fd,&in_port,&so); bcopy(&in_port.sin_addr, rem_addr, hp->h_length); /* I meant to use this information to verify the source machine as being local. Never got around to it. */ /* figure out command and server */ ptr=ch; i=sizeof(ch); do { if (--i <= 0) { fprintf(stderr,"Connection request - buffer overrun.\n"); ch[0]='\n'; *ptr='\n'; } else if (read(ctbl[nc].fd,ptr,1) == 0) { fprintf(stderr,"Connection request - connection lost.\n"); ch[0]='\n'; *ptr='\n'; } } while (*ptr++ != '\n'); *ptr='\0'; if (ch[0] == '\n') continue; /* if connection was lost */ pass[0]=NULL; sscanf(ch,"%[^:]:%[^:]:%[^:]:%[^:]:%s\n",serv,comm,ctbl[nc].name,ctbl[nc].from,pass); if (comm[0] != 'w') if ((strcmp(rootpass,crypt(pass,rootpass)) != 0) && (strcmp(conspass,crypt(pass,conspass)) != 0)) { fprintf(stderr,"Bad password attempt\r\n"); sprintf(och,"Sorry.\r\n"); write(ctbl[nc].fd,och,strlen(och)); close(ctbl[nc].fd); continue; } ctbl[nc].ic[0]=ich1; ctbl[nc].ic[1]=ich2; for (i=0; i<m[group]; ++i) if (strncmp(serv,console[group][i].server,strlen(serv)) == 0) { ctbl[nc].mmb=i; break; } switch(comm[0]) { case 'a' : case 'A' : ctbl[nc].wren=1; sprintf(och,"ok\r\n"); for (i=0; i<nc; ++i) if (ctbl[i].mmb == ctbl[nc].mmb && ctbl[i].wren) { sprintf(och,"ok\r\n[Someone is already attached - spying]\r\n"); ctbl[nc].wren=0; break; } ctbl[nc].tym=time(NULL); write(ctbl[nc].fd,och,strlen(och)); if (comm[0] == 'A') replay(lfd[ctbl[nc].mmb],ctbl[nc].fd); fprintf(clog,"%-9s%-14.14s connect %s %-14s %s", ctbl[nc].name,ctbl[nc].from,((ctbl[nc].wren)?"attach":"spying"), console[group][ctbl[nc].mmb].server,ctime(&(ctbl[nc].tym))); fflush(clog); ++nc; break; case 'f' : case 'F' : for (i=0; i<nc; ++i) if (ctbl[i].mmb == ctbl[nc].mmb && ctbl[i].wren) { ctbl[i].wren=0; sprintf(och,"Your attach has been forced off by %s@%s.\r\n",ctbl[nc].name,ctbl[nc].from); write(ctbl[i].fd,och,strlen(och)); sprintf(och,"You are now in 'spy' mode.\r\n"); write(ctbl[i].fd,och,strlen(och)); tyme=time(NULL); fprintf(clog,"%-9s%-14.14s forced to spy %-14s %s", ctbl[i].name,ctbl[i].from, console[group][ctbl[i].mmb].server,ctime(&tyme)); fflush(clog); } ctbl[nc].wren=1; ctbl[nc].tym=time(NULL); sprintf(och,"ok\r\n"); write(ctbl[nc].fd,och,strlen(och)); if (comm[0] == 'F') replay(lfd[ctbl[nc].mmb],ctbl[nc].fd); fprintf(clog,"%-9s%-14.14s connect forced %-14s %s", ctbl[nc].name,ctbl[nc].from, console[group][ctbl[nc].mmb].server,ctime(&(ctbl[nc].tym))); fflush(clog); ++nc; break; case 's' : case 'S' : ctbl[nc].tym=time(NULL); ctbl[nc].wren=0; sprintf(och,"ok\r\n"); write(ctbl[nc].fd,och,strlen(och)); if (comm[0] == 'S') replay(lfd[ctbl[nc].mmb],ctbl[nc].fd); fprintf(clog,"%-9s%-14.14s connect spying %-14s %s", ctbl[nc].name,ctbl[nc].from, console[group][ctbl[nc].mmb].server,ctime(&(ctbl[nc].tym))); fflush(clog); ++nc; break; case 'w' : sprintf(och,"ok\r\n"); write(ctbl[nc].fd,och,strlen(och)); for (i=0; i<nc; ++i) { if (strcmp(serv,"who") == 0 || ctbl[i].mmb == ctbl[nc].mmb) { sprintf(och,"%-9s%-14.14s",ctbl[i].name,ctbl[i].from); write(ctbl[nc].fd,och,strlen(och)); if (ctbl[i].wren) sprintf(och,"attached to %-14s %s\r",console[group][ctbl[i].mmb].server,ctime(&(ctbl[i].tym))); else sprintf(och,"spying on %-14s %s\r",console[group][ctbl[i].mmb].server,ctime(&(ctbl[i].tym))); write(ctbl[nc].fd,och,strlen(och)); } } close(ctbl[nc].fd); break; default: sprintf(och,"Command not understood.\n"); write(ctbl[nc].fd,och,strlen(och)); break; } } } } /* create a child process */ spawn(gr) int gr; { int pid, so; /* fork off a process for each group with an open socket for connections */ bzero((char *)&lstn_port, sizeof(lstn_port)); /*socket for listening*/ bcopy(hp->h_addr, (char *)&lstn_port.sin_addr, hp->h_length); lstn_port.sin_port = 0; lstn_port.sin_family = hp->h_addrtype; if ((sfd=socket(AF_INET,SOCK_STREAM,0)) < 0) puke("socket"); if (setsockopt(sfd,SOL_SOCKET,SO_REUSEADDR,0,0)<0) puke("setsockopt"); if (bind(sfd, &lstn_port, sizeof(lstn_port))<0) puke("bind"); /* listen on socket prepared by master */ if (listen(sfd,SOMAXCONN)<0) puke("listen"); so=sizeof(lstn_port); getsockname(sfd,&lstn_port, &so); console[gr][0].port = lstn_port.sin_port; fprintf(stderr,"group %d port number %d\n",gr,lstn_port.sin_port); if ((pid=fork()) != 0) { /* if not child */ close(sfd); console[gr][0].pid=pid; return(pid); } else kiddie(gr); exit(1); /* should never get here */ } /* check all the kids and respawn as needed. Called when master process receives SIGCHLD */ void fixkids() { int gr; long tyme; for (gr=0; gr <= procnum; ++gr) { if (kill(console[gr][0].pid,0) == -1) if (errno == ESRCH) { spawn(gr); /* if kid is dead, start another */ tyme=time(NULL); fprintf(clog,"Restarting group %d at %s",gr,ctime(&tyme)); fflush(clog); } } return; } /* kill all the kids and exit. Called when master process receives SIGTERM */ void killkids() { int gr; long tyme; signal(SIGCHLD,SIG_DFL); for (gr=0; gr <= procnum; ++gr) { kill(console[gr][0].pid,SIGTERM); } tyme=time(NULL); fprintf(clog,"Killed at %s",ctime(&tyme)); fflush(clog); exit(0); } /* replay last 20 lines of the log file upon connect to kiddie */ /* (blocks server while running) */ replay(ld,cd) int ld, cd; { int m, n, i, cr=0, tot=0; char c, dun=0, bf[BUFSIZ]; n=lseek(ld,-1,L_XTND); /* go to last character */ if (n <= 0) return(0); while (!dun) { read(ld,&c,1); if (c == '\n') ++cr; if (cr >= 21) dun=1; else if((m=lseek(ld,-2,L_INCR)) == 0) dun=1; else if (++tot > 2000) dun=1; /* guard against lack of cr's */ } while (read(ld,&c,1)>0) write(cd,&c,1); }