#!mkcmd # $Mkcmd(*): ${mkcmd-mkcmd} %f # from '' from '' from '' from '' from '' from '' from '' from '' from '' from '' from '' from '' from '' from '' require "std_help.m" "std_version.m" "std_noargs.m" from '"cons.h"' from '"machine.h"' getenv "CONSOLE" basename "console" "" usage "Usage" # buffers string[32] variable "acMe" { before "FindUser(acMe);" help "holds the login name of the client user" } char* variable "pcCmd" { static help "internal command string decoded from options" after 'if ((char *)0 == %n) {%n = "attach";}' } boolean variable "fLocal" { static init "0" } boolean variable "fReplay" { static init "0" } char* variable "pcTo" { param "machine" init '"*"' help "connect to the slaved console of this machine" } string[256] variable "acMyName" { help "what we call ourselves" } string[1024] variable "acPorts" { local after '(void)sprintf(%n, "@%%s", pcInMaster);' } string variable "acLocalhost" { init '"localhost"' help "the loopback device" } init "RunTime();" # options function 'e' { named "ParseEsc" param "attn" update '%n(%N);' help "negotiate a different attention character sequence" } char* 'M' { named "pcInMaster" param "master" init 'HOST' help "master console server host for this cluster" } boolean 'r' { named "fRaw" user 'if ((char *)0 == pcCmd) {pcCmd = "spy";}' help "connection to host should be raw" } boolean 'v' { named "fVerbose" help "be more verbose" } exclude "asfduqY" action 'aA' { once update 'pcCmd = "attach";' user 'fReplay = isupper(%w);' help "attach to the named machine (with replay)" } action 'fF' { once update 'pcCmd = "force";' user 'fReplay = isupper(%w);' help "force connection to the named machine (with replay)" } action 'sS' { once update 'pcCmd = "spy";' user 'fReplay = isupper(%w);' help "spy connection to the named machine (with replay)" } left "pcTo" { } action 'Y' { exclude "reM" update "%rMn = acLocalhost;" from '' list { param "" update "if (argc - %K1v > 0) {fprintf(stderr, \"%%s: too many arguments (%%s%%s)\\n\", %b, argv[%K1v], argc - %K1v > 1 ? \"...\" : \"\");exit(1);}" abort 'exit(Gather(CtlSignal, acPorts, pcInMaster, "*", "pid", acMe));' } help "send a restart signal to the local console server" } %c /* replace a very long ?: expession with something we can read (ksb) */ static char * ActMap(cKey) char cKey; { switch (cKey) { default: break; case 'D': case 'd': return "version"; case 'x': case 'X': return "xamine"; case 'P': case 'p': return "pid"; case 'U': case 'u': return "users"; case 'W': case 'w': return "groups"; } fprintf(stderr, "%s: unknown key `%c'\n", progname, cKey); exit(1); } %% action 'dDpP' { update 'pcCmd = ActMap(%w);' user 'fLocal = isupper(%w);' help "poll the (primary) console server for version information" exclude "re" exit { update "" named "Gather" aborts "exit(%n(fLocal ? Ctl : CtlMaster, acPorts, %rMn, pcTo, pcCmd, acMe));" } } augment action "pP" { help "poll the (primary) console server its process id" } action 'uUwWxX' { update 'pcCmd = ActMap(%w);' user 'fLocal = isupper(%w);' help "poll the (primary) console groups for console access lists" exclude "re" exit { update "" named "Gather" aborts "exit(%n(fLocal ? CmdGroup : CmdMaster, acPorts, %rMn, pcTo, pcCmd, acMe));" } } augment action 'wW' { help "poll the (primary) console groups for connected clients lists" } augment action 'xX' { help "poll the (primary) console groups for console baud rates" } action 'qQ' { track exclude "re" update 'pcCmd = "quit";' user 'fLocal = isupper(%w);' help "send a quit request to the (primary) console server" string["CONSOLE_WIRE_PASS+1"] variable "acPass" { local help "hold the console server password for quit" } after 'if (%U) {(void)strcpy(acPass, cons_getpass("Enter password:"));}' exit { update "" named "Gather" aborts "exit(%n(fLocal ? Ctl : CtlMaster, acPorts, %rMn, acPass, pcCmd, acMe));" } } augment action 'V' { user "Version();" } exit { update "" named "Gather" aborts "if (0 == %n(Indir, acPorts, %rMn, pcTo, pcCmd, acMe)) {exit(0);}(void)sprintf(acPorts, \"@%%s\", pcTo);exit(%n(Indir, acPorts, %rMn, pcTo, pcCmd, acMe));" } %c /* * 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. */ %% %i #if USE_STRINGS #include #else #include #endif #if USE_TERMIOS #include #else #if USE_TERMIO #include #else /* use ioctl stuff */ #include #include #endif #endif #if HAVE_MALLOC_H #include #else #if NEED_MALLOC_EXTERN extern char *calloc(), *realloc(); #endif #endif #if HAVE_STDLIB_H #include #else extern char *getenv(); #endif extern int errno; #if !HAVE_STRERROR extern char *sys_errlist[]; #define strerror(Me) (sys_errlist[Me]) #endif #if HAVE_SELECT_H #include #endif static char rcsid[] = "$Id: console.m,v 8.6 2007/10/22 19:12:51 ksb Exp $"; int chAttn = -1, chEsc = -1; /* we don't know what type of server we connect to, but pass (ksb) * a whole pass phrase if we can */ #if !defined(CONSOLE_WIRE_PASS) #define CONSOLE_WIRE_PASS 256 #endif #if USE_GETPASSPHRASE #define cons_getpass getpassphrase #else #define cons_getpass getpass #endif static int Indir(); %% %c /* panic -- we have no more momory */ static void OutOfMem() { static char acNoMem[] = ": out of memory\n"; write(2, progname, strlen(progname)); write(2, acNoMem, sizeof(acNoMem)-1); exit(1); } /* * remove from "host1" those domains common to "host1" and "host2" */ static char * whittle(host1, host2) char *host1, *host2; { char *p1, *p2; p1 = strchr(host1, '.'); p2 = strchr(host2, '.'); while (p1 != (char*)0 && p2 != (char*)0) { if (strcmp(p1+1, p2+1) == 0) { *p1 = '\000'; break; } p1 = strchr(p1+1, '.'); p2 = strchr(p2+1, '.'); } return host1; } static char acMesg[8192+2], /* the buffer for startup negotiation */ acThisHost[256]; /* what the remote host would call us */ static struct sockaddr_in local_port; /* the looback address, if local use it */ /* output a control (or plain) character as a UNIX user would expect it (ksb) */ static void putCtlc(c, fp) int c; FILE *fp; { if (0 != (0200 & c)) { (void)putc('M', fp); (void)putc('-', fp); c &= ~0200; } if (isprint(c)) { (void)putc(c, fp); return; } (void)putc('^', fp); if (c == 0177) { (void)putc('?', fp); return; } (void)putc(c+0100, fp); } #if defined(SERVICE) static struct servent *pSE; static char acService[] = SERVICE; #endif /* expain who we are and which revision we are (ksb) */ static void Version() { register unsigned char *puc; printf("%s: initial master server `%s\'\n", progname, pcInMaster); printf("%s: default escape sequence `", progname); putCtlc(DEFATTN, stdout); putCtlc(DEFESC, stdout); printf("\'\n"); puc = (unsigned char *)&local_port.sin_addr; printf("%s: loopback address for %s is %d.%d.%d.%d\n", progname, acMyName, puc[0], puc[1], puc[2], puc[3]); #if defined(SERVICE) if ((struct servent *)0 == (pSE = getservbyname(acService, "tcp"))) { fprintf(stderr, "%s: getservbyname: %s: %s\n", progname, acService, strerror(errno)); return; } printf("%s: service name `%s\'", progname, pSE->s_name); if (0 != strcmp(pSE->s_name, acService)) { printf(" (which we call `%s\')", acService); } printf(" on port %d\n", ntohs((u_short)pSE->s_port)); #else #if defined(PORT) printf("%s: on port %d\n", progname, PORT); #else printf("%s: no service or port compiled in\n", progname); exit(1); #endif #endif } /* convert text to control chars, we take `cat -v' style (ksb) * ^X (or ^x) contro-x * M-x x plus 8th bit * c a plain character */ static int ParseChar(ppcSrc, pcOut) char **ppcSrc, *pcOut; { register int cvt, n; register char *pcScan = *ppcSrc; if ('M' == pcScan[0] && '-' == pcScan[1] && '\000' != pcScan[2]) { cvt = 0x80; pcScan += 2; } else { cvt = 0; } if ('\000' == *pcScan) { return 1; } if ('^' == (n = *pcScan++)) { if ('\000' == (n = *pcScan++)) { return 1; } if (islower(n)) { n = toupper(n); } if ('@' <= n && n <= '_') { cvt |= n - '@'; } else if ('?' == *pcScan) { cvt |= '\177'; } else { return 1; } } else { cvt |= n; } if ((char *)0 != pcOut) { *pcOut = cvt; } *ppcSrc = pcScan; return 0; } /* find the two characters that makeup the users escape sequence (ksb) */ static void ParseEsc(pcText) char *pcText; { auto char *pcTemp; auto char c1, c2; pcTemp = pcText; if (ParseChar(&pcTemp, &c1) || ParseChar(&pcTemp, &c2)) { fprintf(stderr, "%s: poorly formed escape sequence `%s\'\n", progname, pcText); exit(3); } if ('\000' != *pcTemp) { fprintf(stderr, "%s: too many characters in new escape sequence at ...`%s\'\n", progname, pcTemp); exit(3); } chAttn = c1; chEsc = c2; } /* set the port for socket connection (ksb) * return the fd for the new connection; if we can use the loopback, do * as a side effect we set ThisHost to a short name for this host */ int GetPort(pcToHost, pPort, sPort) char *pcToHost; struct sockaddr_in *pPort; short sPort; { register int s; register struct hostent *hp; #if USE_STRINGS (void)bzero((char *)pPort, sizeof(*pPort)); #else memset((void *)pPort, '\000', sizeof(*pPort)); #endif if (0 == strcmp(pcToHost, strcpy(acThisHost, acMyName))) { (void)strcpy(pcToHost, acLocalhost); #if USE_STRINGS (void)bcopy((char *)&local_port.sin_addr, (char *)&pPort->sin_addr, sizeof(local_port.sin_addr)); #else memcpy((char *)&pPort->sin_addr, (char *)&local_port.sin_addr, sizeof(local_port.sin_addr)); #endif } else if ((struct hostent *)0 != (hp = gethostbyname(pcToHost))) { #if USE_STRINGS (void)bcopy((char *)hp->h_addr, (char *)&pPort->sin_addr, hp->h_length); #else memcpy((char *)&pPort->sin_addr, (char *)hp->h_addr, hp->h_length); #endif } else { fprintf(stderr, "%s: gethostbyname: %s: %s\n", progname, pcToHost, hstrerror(h_errno)); return -1; } pPort->sin_port = sPort; pPort->sin_family = AF_INET; /* make hostname short, if we are calling ourself, chop at first dot */ if (0 == strcmp(pcToHost, acLocalhost)) { register char *pcChop; if ((char *)0 != (pcChop = strchr(acThisHost, '.'))) { *pcChop = '\000'; } } else { (void)whittle(acThisHost, pcToHost); } /* set up the socket to talk to the server for all consoles * (it will tell us who to talk to to get a real connection) */ if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) { fprintf(stderr, "%s: socket: %s\n", progname, strerror(errno)); return -1; } if (connect(s, (struct sockaddr *)pPort, sizeof(*pPort)) < 0) { fprintf(stderr, "%s: connect: %d@%s: %s\n", progname, ntohs(pPort->sin_port), pcToHost, strerror(errno)); close(s); return -1; } return s; } /* the next two routines assure that the users tty is in the * correct mode for us to do our thing */ static int screwy = 0; #if USE_TERMIOS static struct termios o_tios; #else #if USE_TERMIO static struct termio o_tio; #else static struct sgttyb o_sty; static struct tchars o_tchars; static struct ltchars o_ltchars; #endif #endif /* * show characters that are already tty processed, * and read characters before cononical processing * we really use cbreak at PUCC because we need even parity... */ static void c2raw() { #if USE_TERMIOS auto struct termios n_tios; #else #if USE_TERMIO auto struct termio n_tio; #else auto struct sgttyb n_sty; auto struct tchars n_tchars; auto struct ltchars n_ltchars; #endif #endif if (!isatty(0) || 0 != screwy) return; #if USE_TERMIOS if (0 != ioctl(0, TCGETS, & o_tios)) { fprintf(stderr, "%s: iotcl: getsw: %s\n", progname, strerror(errno)); exit(10); } n_tios = o_tios; n_tios.c_iflag &= ~(INLCR|IGNCR|ICRNL|IUCLC|IXON); n_tios.c_oflag &= ~OPOST; n_tios.c_lflag &= ~(ICANON|ISIG|ECHO); n_tios.c_cc[VMIN] = 1; n_tios.c_cc[VTIME] = 0; if (0 != ioctl(0, TCSETS, & n_tios)) { fprintf(stderr, "%s: getarrt: %s\n", progname, strerror(errno)); exit(10); } #else #if USE_TERMIO if (0 != ioctl(0, TCGETA, & o_tio)) { fprintf(stderr, "%s: iotcl: geta: %s\n", progname, strerror(errno)); exit(10); } n_tio = o_tio; n_tio.c_iflag &= ~(INLCR|IGNCR|ICRNL|IUCLC|IXON); n_tio.c_oflag &= ~OPOST; n_tio.c_lflag &= ~(ICANON|ISIG|ECHO|ECHOE|ECHOK|ECHONL); n_tio.c_cc[VMIN] = 1; n_tio.c_cc[VTIME] = 0; if (0 != ioctl(0, TCSETAF, & n_tio)) { fprintf(stderr, "%s: iotcl: seta: %s\n", progname, strerror(errno)); exit(10); } #else if (0 != ioctl(0, TIOCGETP, (char *)&o_sty)) { fprintf(stderr, "%s: iotcl: getp: %s\n", progname, strerror(errno)); exit(10); } n_sty = o_sty; n_sty.sg_flags |= CBREAK; n_sty.sg_flags &= ~(CRMOD|ECHO); n_sty.sg_kill = -1; n_sty.sg_erase = -1; if (0 != ioctl(0, TIOCSETP, (char *)&n_sty)) { fprintf(stderr, "%s: iotcl: setp: %s\n", progname, strerror(errno)); exit(10); } /* stty undef all tty chars */ if (-1 == ioctl(0, TIOCGETC, (char *)&n_tchars)) { fprintf(stderr, "%s: ioctl: getc: %s\n", progname, strerror(errno)); return; } o_tchars = n_tchars; n_tchars.t_intrc = -1; n_tchars.t_quitc = -1; if (-1 == ioctl(0, TIOCSETC, (char *)&n_tchars)) { fprintf(stderr, "%s: ioctl: setc: %s\n", progname, strerror(errno)); return; } if (-1 == ioctl(0, TIOCGLTC, (char *)&n_ltchars)) { fprintf(stderr, "%s: ioctl: gltc: %s\n", progname, strerror(errno)); return; } o_ltchars = n_ltchars; n_ltchars.t_suspc = -1; n_ltchars.t_dsuspc = -1; n_ltchars.t_flushc = -1; n_ltchars.t_lnextc = -1; if (-1 == ioctl(0, TIOCSLTC, (char *)&n_ltchars)) { fprintf(stderr, "%s: ioctl: sltc: %s\n", progname, strerror(errno)); return; } #endif #endif screwy = 1; } /* * put the tty back as it was, however that was */ static void c2cooked() { if (!screwy) return; #if USE_TERMIOS (void)ioctl(0, TCSETS, (char *)&o_tios); #else #if USE_TERMIO (void)ioctl(0, TCSETA, (char *)&o_tio); #else (void)ioctl(0, TIOCSETP, (char *)&o_sty); (void)ioctl(0, TIOCSETC, (char *)&o_tchars); (void)ioctl(0, TIOCSLTC, (char *)&o_ltchars); #endif #endif screwy = 0; } /* send out some data along the connection (ksb) */ static void SendOut(fd, pcBuf, iLen) int fd, iLen; char *pcBuf; { register int nr; while (0 != iLen) { if (-1 == (nr = write(fd, pcBuf, iLen))) { c2cooked(); fprintf(stderr, "%s: lost connection\n", progname); exit(3); } iLen -= nr; pcBuf += nr; } } /* read a reply from the console server (ksb) * if pcWnat == (char *)0 we strip \r\n from the end and return strlen */ static int ReadReply(fd, pcBuf, iLen, pcWant) int fd, iLen; char *pcBuf, *pcWant; { register int nr, j, iKeep; iKeep = iLen; for (j = 0; j < iLen; /* j+=nr */) { switch (nr = read(fd, &pcBuf[j], iLen-1)) { case 0: if (iKeep != iLen) { break; } /* fall through */ case -1: c2cooked(); fprintf(stderr, "%s: lost connection\n", progname); exit(3); default: j += nr; iLen -= nr; if ('\n' == pcBuf[j-1]) { pcBuf[j] = '\000'; break; } if (0 == iLen) { c2cooked(); fprintf(stderr, "%s: reply too long\n", progname); exit(3); } continue; } break; } /* in this case the called wants a line of text * remove the cr/lf sequence and any trtailing spaces * (s/[ \t\r\n]*$//) */ if ((char *)0 == pcWant) { while (0 != j && isspace(pcBuf[j-1])) { pcBuf[--j] = '\000'; } return j; } return strcmp(pcBuf, pcWant); } #if DO_POWER /* we are on a control port, shift to power controller (ksb) * and send commands to it, then hang up to get back on the console */ static int PowerCtl(s, pcKey, pcController, pcMaster) int s; char *pcKey, *pcController, *pcMaster; { register int nc; register char **ppc; auto fd_set rmask, rinit; static char *apcHelp[] = { " help", " . quit power mode", " 0 power down the unit", " 1 power up the unit", " # describe power status of unit", " ? output this help message", " w which key and controller line infomation", " (other commands might be accepted by some units)", (char *)0 }; (void)sprintf(acMesg, "%c%c;", DEFATTN, DEFESC); SendOut(s, acMesg, 3); if (0 != ReadReply(s, acMesg, sizeof(acMesg), "[login:\r\n") && 0 != strcmp(acMesg, "\r\n[login:\r\n")) { CSTROUT(1," login "); SendOut(1, acMesg, strlen(acMesg)); return 1; } (void)sprintf(acMesg, "%s\n", pcKey); SendOut(s, acMesg, strlen(acMesg)); if (0 != ReadReply(s, acMesg, sizeof(acMesg), "host:\r\n")) { CSTROUT(1, " "); SendOut(1, pcKey, strlen(pcKey)); CSTROUT(1, " "); SendOut(1, acMesg, strlen(acMesg)); return 2; } /* which host we want, and a passwd if asked for one */ (void)sprintf(acMesg, "%s\n", pcController); SendOut(s, acMesg, strlen(acMesg)); (void)ReadReply(s, acMesg, sizeof(acMesg), (char *)0); if (0 == strcmp(acMesg, "passwd:")) { auto char acCtlPass[CONSOLE_WIRE_PASS+1]; c2cooked(); (void)strcpy(acCtlPass, cons_getpass(" need password:")); c2raw(); (void)sprintf(acMesg, "%s\n", acCtlPass); SendOut(s, acMesg, strlen(acMesg)); (void)ReadReply(s, acMesg, sizeof(acMesg), (char *)0); } /* how did we do, did we get a read-only or read-write? */ if (0 != strcmp(acMesg, "attached]")) { register char *pcClose; if ((char *)0 != (pcClose = strchr(acMesg, ']'))) *pcClose = '\000'; CSTROUT(1, " aborted "); write(1, acMesg, strlen(acMesg)); return 3; } FD_ZERO(& rinit); FD_SET(s, &rinit); FD_SET(0, &rinit); /* reset read mask and select on it * ZZZ set a timeout on power line attach of 10 minutes? -- ksb */ while (rmask = rinit, -1 != select(sizeof(rmask)*8, &rmask, (fd_set *)0, (fd_set *)0, (struct timeval *)0)) { /* anything from socket? */ if (FD_ISSET(s, &rmask)) { if ((nc = read(s, acMesg, sizeof(acMesg))) == 0) { break; } #if STRIP8 /* clear parity? */ for (i = 0; i < nc; ++i) acMesg[i] &= 127; #endif SendOut(1, acMesg, nc); } /* anything from stdin? */ if (!FD_ISSET(0, &rmask)) { continue; } if ((nc = read(0, acMesg, 1)) == 0) { break; } if ('q' == acMesg[0] || 'Q' == acMesg[0] || '.' == acMesg[0]) { break; } /* must be a controller command, send any of: * \n status of all lines? [not on the POW-R-Switch] * key # power status * key 1 power on * key 0 power off * key _ [where _ is something else] unknown pass through */ switch (acMesg[0]) { case '?': /* meta help */ for (ppc = apcHelp; (char *)0 != *ppc; ++ppc) { (void)write(1, *ppc, strlen(*ppc)); (void)write(1, "\r\n", 2); } /* sprintf(acMesg, "?\n"); and break? XXX */ continue; case 'w': /* which line */ sprintf(acMesg, " %s line %s on %s", pcKey, pcController, pcMaster); (void)write(1, acMesg, strlen(acMesg)); continue; case '\r': /* power might want newlines passed */ case '\n': sprintf(acMesg, "\n"); break; case '#': sprintf(acMesg, "%s#\n", pcKey); break; case '0': case '-': case 'd': sprintf(acMesg, "%s0\n", pcKey); break; case '1': case '+': case 'u': sprintf(acMesg, "%s1\n", pcKey); break; default: /* try anything else as a command */ sprintf(acMesg, "%s%c\n", pcKey, acMesg[0]); break; } /* The power controller might not say anything if the * key is wrong, show the User that we sent something -- ksb */ CSTROUT(1, " "); SendOut(s, acMesg, strlen(acMesg)); } close(s); CSTROUT(1, " done"); return 0; } #endif /* call a machine master for group master ports and machine master ports * take a list like "1782@localhost:mentor.cc.purdue.edu:@pop.stat.purdue.edu" */ static int Gather(pfi, pcPorts, pcMaster, pcTo, pcCmd, pcWho) int (*pfi)(); char *pcPorts, *pcMaster, *pcTo, *pcCmd, *pcWho; { register int s; register short j; register char *pcNext, *pcServer; auto char acExcg[256]; auto struct sockaddr_in client_port; auto int iRet = 0; for (/* param */; '\000' != *pcPorts; pcPorts = pcNext) { if ((char *)0 == (pcNext = strchr(pcPorts, ':'))) pcNext = ""; else *pcNext++ = '\000'; if ((char *)0 != (pcServer = strchr(pcPorts, '@'))) { *pcServer++ = '\000'; if ('\000' != *pcServer) { (void)strcpy(acExcg, pcServer); } } else if (isdigit(pcPorts[0])) { (void)strcpy(acExcg, pcMaster); } else { (void)strcpy(acExcg, pcPorts); pcPorts = ""; } if ('\000' == *pcPorts) { #if defined(SERVICE) /* in net order -- ksb */ j = pSE->s_port; #else #if defined(PORT) j = htons(PORT); #else fprintf(stderr, "%s: no port or service compiled in?\n", progname); exit(8); #endif #endif } else if (!isdigit(pcPorts[0])) { /* XXX we should look up the service here */ fprintf(stderr, "%s: %s: %s\n", progname, pcMaster, pcPorts); iRet += 32; continue; } else { j = htons((short)atoi(pcPorts)); } if (-1 == (s = GetPort(acExcg, & client_port, j))) { iRet += 8; } else { if (0 != ReadReply(s, acMesg, sizeof(acMesg), "ok\r\n")) { fprintf(stderr, "%s: %s: %s", progname, acExcg, acMesg); iRet += 16; } else { iRet += (*pfi)(s, acExcg, pcTo, pcCmd, pcWho); } (void)close(s); } if ((char *)0 != pcServer) { *pcServer = '@'; } } return iRet; } static int SawUrg = 0; #if DO_POWER static char acPowCtl[128]; /* We take a moment to call the power controller and ask (ksb) * them about the console line we were chatting with. We'll be right * back after this little diversion. * For your enjoyment: note we can power a power controller as well. */ int Power(pcControl, pcLine, pcCurrent) char *pcControl, *pcLine, *pcCurrent; { register char *pcAt; if ((char *)0 != (pcAt = strchr(pcControl, '@'))) { *pcAt++ = '\000'; } else { pcAt = pcCurrent; } return Gather(Indir, pcAt, pcAt, pcControl, acPowCtl, pcLine); } #endif /* power controller */ /* when the conserver program gets the suspend sequence it will send us * an out of band command to suspend ourself. We just tell the reader * routine we saw one */ SIGRET_T oob(sig) int sig; { ++SawUrg; } /* interact with a group server (ksb) */ static int CallUp(s, pcMaster, pcMach, pcHow, pcUser) int s; char *pcMaster, *pcMach, *pcHow, *pcUser; { register int nc; register int fIn; auto fd_set rmask, rinit; extern int atoi(); #if DO_POWER auto char acCtlr[4096-128]; #endif if (fVerbose) { printf("%s: %s to %s (%son %s)\n", progname, pcHow, pcMach, fRaw ? "raw " : "", pcMaster); } #if defined(F_SETOWN) if (-1 == fcntl(s, F_SETOWN, getpid())) { fprintf(stderr, "%s: fcntl: %d: %s\n", progname, s, strerror(errno)); } #else #if defined(SIOCSPGRP) { auto int iTemp; /* on the HP-UX systems it is different */ iTemp = -getpid(); if (-1 == ioctl(s, SIOCSPGRP, & iTemp)) { fprintf(stderr, "%s: ioctl: %d: %s\n", progname, s, strerror(errno)); } } #endif #endif #if defined(SIGURG) (void)signal(SIGURG, oob); #endif #if DO_POWER if (acPowCtl == pcHow) { return PowerCtl(s, acPowCtl, pcMach, pcMaster); } #endif /* change escape sequence (if set on the command line) * and replay the log for the user, if asked */ if (chAttn == -1 || chEsc == -1) { chAttn = DEFATTN; chEsc = DEFESC; } else { /* tell the conserver to change escape sequences, assmue OK * (we'll find out soon enough) */ (void)sprintf(acMesg, "%c%ce%c%c", DEFATTN, DEFESC, chAttn, chEsc); SendOut(s, acMesg, 5); if (0 == ReadReply(s, acMesg, sizeof(acMesg), (char *)0)) { fprintf(stderr, "protocol botch on redef of escape sequence\n"); exit(8); } } if (fVerbose) { printf("Enter `"); putCtlc(chAttn, stdout); putCtlc(chEsc, stdout); printf("?\' for help.\n"); } /* if we are going for a particular console * send sign-on stuff, then wait for some indication of what mode * we got from the server (if we are the only people on we get write * access by default, which is fine for most people). */ if (!fRaw) { /* begin connect with who we are */ (void)sprintf(acMesg, "%c%c;", chAttn, chEsc); SendOut(s, acMesg, 3); if (0 != ReadReply(s, acMesg, sizeof(acMesg), "[login:\r\n") && 0 != strcmp(acMesg, "\r\n[login:\r\n")) { fprintf(stderr, "%s: call: %s\n", progname, acMesg); exit(2); } (void)sprintf(acMesg, "%s@%s\n", pcUser, acThisHost); SendOut(s, acMesg, strlen(acMesg)); if (0 != ReadReply(s, acMesg, sizeof(acMesg), "host:\r\n")) { fprintf(stderr, "%s: %s\n", progname, acMesg); exit(2); } /* which host we want, and a passwd if asked for one */ (void)sprintf(acMesg, "%s\n", pcMach); SendOut(s, acMesg, strlen(acMesg)); (void)ReadReply(s, acMesg, sizeof(acMesg), (char *)0); if (0 == strcmp(acMesg, "passwd:")) { auto char acHostPass[CONSOLE_WIRE_PASS+1]; (void)strcpy(acHostPass, cons_getpass("Enter password:")); (void)sprintf(acMesg, "%s\n", acHostPass); SendOut(s, acMesg, strlen(acMesg)); (void)ReadReply(s, acMesg, sizeof(acMesg), (char *)0); } /* how did we do, did we get a read-only or read-write? */ if (0 == strcmp(acMesg, "attached]")) { /* OK -- we are good as gold */ fIn = 'a'; } else if (0 == strcmp(acMesg, "spy]") || 0 == strcmp(acMesg, "ok]")) { /* Humph, someone else is on * or we have an old version of the server (4.X) */ fIn = 's'; } else if (0 == strcmp(acMesg, "host is read-only]")) { fIn = 'r'; } else if (0 == strcmp(acMesg, "line to host is down]")) { /* ouch, the machine is down on the server */ fIn = '-'; fprintf(stderr, "%s: %s is down\n", progname, pcMach); if (fVerbose) { printf("[use `"); putCtlc(chAttn, stdout); putCtlc(chEsc, stdout); printf("o\' to open console line]\n"); } } else if (0 == strcmp(acMesg, "no -- on ctl]")) { fIn = '-'; fprintf(stderr, "%s: %s is a control port\n", progname, pcMach); if (fVerbose) { printf("[use `"); putCtlc(chAttn, stdout); putCtlc(chEsc, stdout); printf(";\' to open a console line]\n"); } } else { fprintf(stderr, "%s: %s: %s\n", progname, pcMach, acMesg); c2cooked(); exit(5); } } /* if the host is not down, finish the connection, and force * the correct attachment for the user */ if ('-' != fIn) { if (fIn == 'r') { if ('s' != *pcHow) { fprintf(stderr, "%s: %s is read-only\n", progname, pcMach); } } else if (fIn != ('f' == *pcHow ? 'a' : *pcHow)) { (void)sprintf(acMesg, "%c%c%c", chAttn, chEsc, *pcHow); SendOut(s, acMesg, 3); } if (fReplay) { (void)sprintf(acMesg, "%c%cr", chAttn, chEsc); SendOut(s, acMesg, 3); } else if (fVerbose) { (void)sprintf(acMesg, "%c%c\022", chAttn, chEsc); SendOut(s, acMesg, 3); } } (void)fflush(stdout); (void)fflush(stderr); c2raw(); /* read from stdin and the socket (non-blocking!). * rmask indicates which descriptors to read from, * the others are not used, nor is the result from * select, read, or write. */ FD_ZERO(& rinit); FD_SET(s, &rinit); FD_SET(0, &rinit); for (;;) { /* reset read mask and select on it */ rmask = rinit; while (-1 == select(sizeof(rmask)*8, &rmask, (fd_set *)0, (fd_set *)0, (struct timeval *)0)) { static char acCmd[64]; rmask = rinit; /* if we were suspened, try again */ if (EINTR != errno || !SawUrg) { continue; } SawUrg = 0; #if defined(SIGURG) (void)signal(SIGURG, oob); #endif /* get the pending urgent message */ while (recv(s, acCmd, 1, MSG_OOB) < 0) { switch (errno) { case EWOULDBLOCK: /* clear any pending input to make room */ (void)read(s, acCmd, sizeof(acCmd)); CSTROUT(1, "."); continue; case EINVAL: default: fprintf(stderr, "%s: recv: %d: %s\r\n", progname, s, strerror(errno)); sleep(1); continue; } } switch (acCmd[0]) { case OB_SUSP: #if defined(SIGSTOP) CSTROUT(1, "stop]"); c2cooked(); (void)kill(getpid(), SIGSTOP); c2raw(); CSTROUT(1, "[press any character to continue"); #else CSTROUT(1, "stop not supported -- press any character to continue"); #endif break; case OB_DROP: CSTROUT(1, "dropped by server]\r\n"); c2cooked(); exit(1); /*NOTREACHED*/ case OB_POWER: #if DO_POWER CSTROUT(1, "power"); /* get address to call and prefix */ SendOut(s, "k", 1); if (0 == ReadReply(s, acPowCtl, sizeof(acPowCtl), (char *)0)) { CSTROUT(1, "no key"); SendOut(s, "b", 1); break; } SendOut(s, "s", 1); if (0 == ReadReply(s, acCtlr, sizeof(acCtlr), (char *)0)) { CSTROUT(1, "no controller"); SendOut(s, "b", 1); break; } Power(acCtlr, pcMach, pcMaster); #else CSTROUT(1, "no client support"); #endif SendOut(s, "b", 1); break; default: fprintf(stderr, "%s: unknown out of band command `%c\'\r\n", progname, acCmd[0]); (void)fflush(stderr); break; } } /* anything from socket? */ if (FD_ISSET(s, &rmask)) { if ((nc = read(s, acMesg, sizeof(acMesg))) == 0) { break; } #if STRIP8 /* clear parity? */ for (i = 0; i < nc; ++i) acMesg[i] &= 127; #endif SendOut(1, acMesg, nc); } /* anything from stdin? */ if (FD_ISSET(0, &rmask)) { if ((nc = read(0, acMesg, sizeof(acMesg))) == 0) break; SendOut(s, acMesg, nc); } } c2cooked(); if (fVerbose) printf("Console %s closed.\n", pcMach); return 0; } /* the group leader tells is the server to connect to (ksb) * we use CallUp to start a session with each target, or forward it */ static int Indir(s, pcMaster, pcMach, pcCmd, pcWho) int s; char *pcMaster, *pcMach, *pcCmd, *pcWho; { auto char acPorts[4097]; /* send request for master list */ (void)sprintf(acPorts, "call:%s\r\n", pcMach); SendOut(s, acPorts, strlen(acPorts)); /* get the ports number */ if (0 >= ReadReply(s, acPorts, sizeof(acPorts), (char *)0)) { fprintf(stderr, "%s: %s: call forward broken\n", progname, pcMaster); exit(1); } if ('@' == acPorts[0]) { static int iLimit = 0; if (iLimit++ > 10) { fprintf(stderr, "%s: forwarding level too deep!\n", progname); return 1; } return Gather(Indir, acPorts, pcMaster, pcMach, pcCmd, pcWho); } /* send the command to each master */ return Gather(CallUp, acPorts, pcMaster, pcMach, pcCmd, pcWho); } #define BUF_G1 (MAXGRP*80) #define BUF_MIN 80 #define BUF_CHUNK (2*132) /* Cmd is implemented seperately from above because of the need buffer (ksb) * the ports' output. It's about the same as what's above otherwise. * We trick lint because we have to be call compatible (prototype'd) * the same as all the other Gather functions. */ /*ARGSUSED*/ static int Cmd(s, pcMaster, pcMach, pcCmd, pcWho) int s; char *pcMaster, *pcMach, *pcCmd, *pcWho; { static int iMax = 0; static char *pcBuf = (char *)0; register int nr, iRem, i, fBrace; /* setup the big buffer for the server output */ if ((char *)0 == pcBuf) { iMax = BUF_G1; if ((char *)0 == (pcBuf = calloc(BUF_G1, sizeof(char)))) { OutOfMem(); } } /* send sign-on stuff, then wait for a reply, like "ok\r\n" * before allowing a write */ (void)sprintf(acMesg, "%c%c%c%c%c.", DEFATTN, DEFESC, *pcCmd, DEFATTN, DEFESC); SendOut(s, acMesg, 6); /* read the server's reply, * We buffer until we close the connection because it * wouldn't be fair to ask the server to keep up with * itself :-) {if we are inside a console connection}. */ iRem = iMax; i = 0; while (0 < (nr = read(s, pcBuf+i, iRem))) { i += nr; iRem -= nr; if (iRem >= BUF_MIN) { continue; } iMax += BUF_CHUNK; if ((char *)0 == (pcBuf = realloc(pcBuf, iMax))) { OutOfMem(); } iRem += BUF_CHUNK; } /* edit out the command lines [...] */ iRem = fBrace = 0; for (nr = 0; nr < i; ++nr) { if (0 != fBrace) { if (']' == pcBuf[nr]) { fBrace = 0; } continue; } switch (pcBuf[nr]) { case '\r': if (0 == iRem) continue; break; case '\n': if (0 == iRem) continue; (void)putchar('\n'); iRem = 0; continue; case '[': fBrace = 1; continue; } (void)putchar(pcBuf[nr]); iRem = 1; } (void)fflush(stdout); return 0; } /* the masters tell us the group masters with a "groups" command (ksb) */ static int CmdGroup(s, pcMaster, pcMach, pcCmd, pcWho) int s; char *pcMaster, *pcMach, *pcCmd, *pcWho; { auto char acPorts[4097]; register int iNull; /* send request for master list */ (void)sprintf(acPorts, "groups\r\n", pcCmd); SendOut(s, acPorts, strlen(acPorts)); /* get the port numbers for the groups on this master */ if (0 > (iNull = ReadReply(s, acPorts, sizeof(acPorts), (char *)0))) { fprintf(stderr, "%s: %s: group forward broken\n", progname, pcMaster); exit(1); } /* an empty list means this console server node _just_ forwards */ if (0 == iNull) { if (fVerbose) { printf("%s:\r\n", pcMaster); } return 0; } if (fVerbose) { printf("%s:\r\n", pcMaster); } /* to the command to each master */ return Gather(Cmd, acPorts, pcMaster, pcMach, pcCmd, pcWho); } /* the master tells us the machine masters with a "master" command (ksb) * we ask each of those for the group members */ static int CmdMaster(s, pcMaster, pcMach, pcCmd, pcWho) int s; char *pcMaster, *pcMach, *pcCmd, *pcWho; { auto char acPorts[4097]; register int iNull; /* send request for master list */ CSTROUT(s, "master\r\n"); /* get the ports number */ if (0 > (iNull = ReadReply(s, acPorts, sizeof(acPorts), (char *)0))) { fprintf(stderr, "%s: %s: master forward broken\n", progname, pcMaster); exit(1); } if (0 == iNull) { if (fVerbose) { printf("%s: master list empyt\r\n", pcMaster); } return 0; } if (fVerbose) { printf("%s: %s\r\n", pcMaster, acPorts); } /* to the command to each master */ return Gather(CmdGroup, acPorts, pcMaster, pcMach, pcCmd, pcWho); } /* The masters tell us the group masters with a "groups" command. (ksb) * We trick lint because we have to be call compatible (prototype'd) * the same as all the other Gather functions. */ /*ARGSUSED*/ static int Ctl(s, pcMaster, pcMach, pcCmd, pcWho) int s; char *pcMaster, *pcMach, *pcCmd, *pcWho; { auto char acPorts[4097]; /* send request for master list */ (void)sprintf(acPorts, "%s:%s\r\n", pcCmd, pcMach); SendOut(s, acPorts, strlen(acPorts)); /* get the ports number */ if (0 >= ReadReply(s, acPorts, sizeof(acPorts), (char *)0)) { fprintf(stderr, "%s: group leader died?\n", progname); return 1; } if (fVerbose) { printf("%s:\r\n", pcMaster); } printf("%s: %s\r\n", pcMaster, acPorts); /* to the command to each master */ return 0; } /* the master tells us the machine masters with a "master" command (ksb) * we tell each of those the command we want them to do */ static int CtlMaster(s, pcMaster, pcMach, pcCmd, pcWho) int s; char *pcMaster, *pcMach, *pcCmd, *pcWho; { auto char acPorts[4097]; /* send request for master list */ CSTROUT(s, "master\r\n"); /* get the ports number */ if (0 >= ReadReply(s, acPorts, sizeof(acPorts), (char *)0)) { fprintf(stderr, "%s: %s: master forward broken\n", progname, pcMaster); exit(1); } /* to the command to each master */ return Gather(Ctl, acPorts, pcMaster, pcMach, pcCmd, pcWho); } %% from '' %c /* get the user's password entry name, believe $USER or $LOGNAME (ksb) * if they have the correct uid */ char * FindUser(pcOut) char *pcOut; { register char *pcEnv; register struct passwd *pwdMe; if (((char *)0 != (pcEnv = getenv("USER")) || (char *)0 != (pcEnv = getenv("LOGNAME"))) && (struct passwd *)0 != (pwdMe = getpwnam(pcEnv)) && getuid() == pwdMe->pw_uid) { /* use the login $USER is set to, if it is our (real) uid */; } else if ((struct passwd *)0 == (pwdMe = getpwuid(getuid()))) { fprintf(stderr, "%s: getpwuid: %d: %s\n", progname, getuid(), strerror(errno)); exit(1); } return strcpy(pcOut, pwdMe->pw_name); } /* Lookup runtime information, like the loopback interface address (ksb) * to use (should be 127.0.0.1, but let's look) and the service if * we don't know the port. */ static void RunTime() { register struct hostent *hp; if ((struct hostent *)0 != (hp = gethostbyname(acLocalhost))) { #if USE_STRINGS (void)bcopy((char *)hp->h_addr, (char *)&local_port.sin_addr, hp->h_length); #else memcpy((char *)&local_port.sin_addr, (char *)hp->h_addr, hp->h_length); #endif } else { acLocalhost[0] = '\000'; } if (-1 == gethostname(acMyName, sizeof(acMyName))) { fprintf(stderr, "%s: gethostname: %s\n", progname, strerror(errno)); exit(1); } #if defined(SERVICE) if ((struct servent *)0 == (pSE = getservbyname(acService, "tcp"))) { fprintf(stderr, "%s: getservbyname: %s: %s\n", progname, acService, strerror(errno)); exit(1); } #endif } /* like the above, but we signal the console server with a USR2 (ksb) */ /*ARGSUSED*/ static int CtlSignal(s, pcMaster, pcMach, pcCmd, pcWho) int s; char *pcMaster, *pcMach, *pcCmd, *pcWho; { register int iPid; auto char acPorts[4097]; /* send request for master list */ (void)sprintf(acPorts, "%s:%s\r\n", pcCmd, pcMach); SendOut(s, acPorts, strlen(acPorts)); /* get the ports number */ if (0 >= ReadReply(s, acPorts, sizeof(acPorts), (char *)0)) { fprintf(stderr, "%s: group leader died?\n", progname); return 1; } iPid = atoi(acPorts); if (fVerbose) { printf("%s: kill -USR2 %d\r\n", pcMaster, iPid); } return -1 == kill(iPid, SIGUSR2); } %%