Skip to content

Instantly share code, notes, and snippets.

@mdevaev
Created March 3, 2019 01:05
Show Gist options
  • Save mdevaev/d4bcdc467b4cfb973a84be64c7ba3597 to your computer and use it in GitHub Desktop.
Save mdevaev/d4bcdc467b4cfb973a84be64c7ba3597 to your computer and use it in GitHub Desktop.
#!mkcmd
# $Mkcmd(*): ${mkcmd-mkcmd} %f
#
from '<sys/types.h>'
from '<sys/time.h>'
from '<sys/socket.h>'
from '<sys/ioctl.h>'
from '<netinet/in.h>'
from '<fcntl.h>'
from '<signal.h>'
from '<stdio.h>'
from '<netdb.h>'
from '<pwd.h>'
from '<errno.h>'
from '<ctype.h>'
from '<pwd.h>'
from '<unistd.h>'
from '"cons.h"'
from '"machine.h"'
require "std_help.m" "std_version.m" "std_noargs.m"
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 '<stdio.h>'
list {
param ""
update "if (argc - %K<getopt_switches>1v > 0) {fprintf(stderr, \"%%s: too many arguments (%%s%%s)\\n\", %b, argv[%K<getopt_switches>1v], argc - %K<getopt_switches>1v > 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[32] variable "acPass" {
local
help "hold the console server password for quit"
}
after 'if (%U) {(void)strcpy(acPass, 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 <strings.h>
#else
#include <string.h>
#endif
#if USE_TERMIOS
#include <termios.h>
#include <unistd.h>
#else
#if USE_TERMIO
#include <termio.h>
#else /* use ioctl stuff */
#include <sgtty.h>
#include <sys/ioctl.h>
#endif
#endif
#if HAVE_MALLOC_H
#include <malloc.h>
#else
#if NEED_MALLOC_EXTERN
extern char *calloc(), *realloc();
#endif
#endif
#if HAVE_STDLIB_H
#include <stdlib.h>
#else
extern char *getenv(), *getpass();
#endif
extern int errno;
#if !HAVE_STRERROR
extern char *sys_errlist[];
#define strerror(Me) (sys_errlist[Me])
#endif
#if HAVE_SELECT_H
#include <sys/select.h>
#endif
static char rcsid[] =
"$Id: console.m,v 8.3 2000/08/02 18:34:03 ksb Exp $";
int chAttn = -1, chEsc = -1;
%%
%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 {
extern int h_errno;
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 pass[32];
c2cooked();
(void)strcpy(pass, getpass(" need password:"));
c2raw();
(void)sprintf(acMesg, "%s\n", pass);
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;
static int Indir();
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 pass[32];
(void)strcpy(pass, getpass("Enter password:"));
(void)sprintf(acMesg, "%s\n", pass);
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:<none>\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 '<pwd.h>'
%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);
}
%%
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment