/* * 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 #include #include #include #include #include #include #include #include #include #include #include #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 ; zyxpw_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 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 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 [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; ih_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); }