mirror of
https://github.com/bstansell/conserver.git
synced 2025-02-04 09:40:37 +00:00
1327 lines
31 KiB
C
1327 lines
31 KiB
C
/*
|
|
* $Id: console.c,v 5.22 1999-01-13 11:48:38-08 bryan Exp $
|
|
*
|
|
* GNAC, Inc., 1998
|
|
*
|
|
* Maintainer/Enhancer: Bryan Stansell (bryan@gnac.com)
|
|
*/
|
|
|
|
/*
|
|
* 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 <sys/types.h>
|
|
#include <sys/time.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/ioctl.h>
|
|
#include <netinet/in.h>
|
|
#include <fcntl.h>
|
|
#include <signal.h>
|
|
#include <stdio.h>
|
|
#include <netdb.h>
|
|
#include <pwd.h>
|
|
#include <errno.h>
|
|
#include <ctype.h>
|
|
|
|
#include "cons.h"
|
|
#include "port.h"
|
|
#include "version.h"
|
|
|
|
#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 !defined IBMR2
|
|
extern char *strrchr(), *strchr();
|
|
#endif
|
|
extern char *getenv();
|
|
extern char *getpass();
|
|
extern char *calloc(), *realloc();
|
|
/* extern short htons(); */
|
|
extern int errno;
|
|
|
|
#if !HAVE_STRERROR
|
|
extern char *sys_errlist[];
|
|
#define strerror(Me) (sys_errlist[Me])
|
|
#endif
|
|
|
|
static char rcsid[] =
|
|
"$Id: console.c,v 5.22 1999-01-13 11:48:38-08 bryan Exp $";
|
|
static char *progname =
|
|
rcsid;
|
|
int fVerbose = 0, fReplay = 0, fRaw = 0;
|
|
int chAttn = -1, chEsc = -1;
|
|
char *pcInMaster = /* which machine is current */
|
|
HOST;
|
|
|
|
/* 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 */
|
|
acLocalhost[] = /* the loopback device */
|
|
"localhost",
|
|
acThisHost[256], /* what the remote host would call us */
|
|
acMyName[256]; /* what we call ourselves */
|
|
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);
|
|
}
|
|
|
|
static char *apcLong[] = {
|
|
"a(A) attach politelty (and replay last 20 lines)",
|
|
"d(D) display (local) daemon version",
|
|
"e esc set the initial escape characters",
|
|
"f(F) force read/write connection (and replay)",
|
|
"h output this message",
|
|
"l user use username instead of current username",
|
|
"M mach master server to poll first",
|
|
"q(Q) send a quit command to the (local) server",
|
|
"r connect to the console group only",
|
|
"s(S) spy on a console (and replay)",
|
|
"u show users on the various consoles",
|
|
"v be more verbose",
|
|
"V show version information",
|
|
"w show who is on which console",
|
|
"x examine ports and baud rates",
|
|
(char *)0
|
|
};
|
|
|
|
/* output a long message to the user
|
|
*/
|
|
static void
|
|
Usage(fp, ppc)
|
|
FILE *fp;
|
|
char **ppc;
|
|
{
|
|
for (/* passed */; (char *)0 != *ppc; ++ppc)
|
|
(void)fprintf(fp, "%s\n", *ppc);
|
|
}
|
|
|
|
/* expain who we are and which revision we are (ksb)
|
|
*/
|
|
static void
|
|
Version()
|
|
{
|
|
register unsigned char *puc;
|
|
|
|
printf("%s: %s\n", progname, GNAC_VERSION);
|
|
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]);
|
|
}
|
|
|
|
|
|
/* 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));
|
|
exit(9);
|
|
}
|
|
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));
|
|
exit(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));
|
|
exit(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 and 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 defined(SERVICE)
|
|
static struct servent *pSE;
|
|
#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"
|
|
* and send the given command to the group leader at 1782
|
|
* and ask the machine master at mentor for more group leaders
|
|
* and ask the machine master at pop.stat for more group leaders
|
|
*/
|
|
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';
|
|
|
|
(void)strcpy(acExcg, pcMaster);
|
|
if ((char *)0 != (pcServer = strchr(pcPorts, '@'))) {
|
|
*pcServer++ = '\000';
|
|
if ('\000' != *pcServer) {
|
|
(void)strcpy(acExcg, pcServer);
|
|
}
|
|
}
|
|
|
|
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])) {
|
|
fprintf(stderr, "%s: %s: %s\n", progname, pcMaster, pcPorts);
|
|
exit(2);
|
|
} else {
|
|
j = htons((short)atoi(pcPorts));
|
|
}
|
|
|
|
s = GetPort(acExcg, & client_port, j);
|
|
|
|
if (0 != ReadReply(s, acMesg, sizeof(acMesg), "ok\r\n")) {
|
|
fprintf(stderr, "%s: %s: %s", progname, acExcg, acMesg);
|
|
exit(4);
|
|
}
|
|
|
|
iRet += (*pfi)(s, acExcg, pcTo, pcCmd, pcWho);
|
|
(void)close(s);
|
|
if ((char *)0 != pcServer) {
|
|
*pcServer = '@';
|
|
}
|
|
}
|
|
return iRet;
|
|
}
|
|
|
|
|
|
static int SawUrg = 0;
|
|
|
|
/* 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
|
|
*/
|
|
SIGRETS
|
|
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 (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 if 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
|
|
|
|
/* 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 on 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)sprintf(acMesg, "Enter %s's password:", pcUser);
|
|
(void)strcpy(pass, getpass(acMesg));
|
|
(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);
|
|
exit(5);
|
|
}
|
|
}
|
|
|
|
printf("[Enter `");
|
|
putCtlc(chAttn, stdout);
|
|
putCtlc(chEsc, stdout);
|
|
printf("?\' for help]\n");
|
|
|
|
/* 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*/
|
|
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: master forward broken\n", progname);
|
|
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);
|
|
}
|
|
/* to 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, 9);
|
|
|
|
/* 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)SendOut(1, pcBuf, i); */
|
|
(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];
|
|
|
|
/* send request for master list
|
|
*/
|
|
(void)sprintf(acPorts, "groups\r\n", pcCmd);
|
|
SendOut(s, acPorts, strlen(acPorts));
|
|
|
|
/* get the ports number */
|
|
if (0 >= ReadReply(s, acPorts, sizeof(acPorts), (char *)0)) {
|
|
fprintf(stderr, "%s: master forward broken\n", progname);
|
|
exit(1);
|
|
}
|
|
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];
|
|
|
|
/* 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: master forward broken\n", progname);
|
|
exit(1);
|
|
}
|
|
/* 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: master forward broken\n", progname);
|
|
exit(1);
|
|
}
|
|
/* to the command to each master
|
|
*/
|
|
return Gather(Ctl, acPorts, pcMaster, pcMach, pcCmd, pcWho);
|
|
}
|
|
|
|
|
|
/* mainline for console client program (ksb)
|
|
* setup who we are, and what our loopback addr is
|
|
* parse the cmd line,
|
|
* (optionally) get a shutdown passwd
|
|
* Gather results
|
|
* exit happy or sad
|
|
*/
|
|
int
|
|
main(argc, argv)
|
|
int argc;
|
|
char **argv;
|
|
{
|
|
register struct hostent *hp;
|
|
register char *ptr, *pcCmd, *pcTo;
|
|
register struct passwd *pwdMe;
|
|
auto int opt;
|
|
auto int fLocal;
|
|
auto char acPorts[1024];
|
|
auto char *pcUser;
|
|
auto int (*pfiCall)();
|
|
static char acOpts[] = "aAdDsSfFe:hl:M:pvVwWUqQrux";
|
|
extern long atol();
|
|
extern int optind;
|
|
extern char *optarg;
|
|
|
|
if ((char *)0 == (progname = strrchr(argv[0], '/'))) {
|
|
progname = argv[0];
|
|
} else {
|
|
++progname;
|
|
}
|
|
|
|
#if defined(SERVICE)
|
|
if ((struct servent *)0 == (pSE = getservbyname(SERVICE, "tcp"))) {
|
|
fprintf(stderr, "%s: getservbyname: %s: %s\n", progname, SERVICE, strerror(errno));
|
|
exit(1);
|
|
}
|
|
#endif
|
|
|
|
if (((char *)0 != (ptr = getenv("USER")) || (char *)0 != (ptr = getenv("LOGNAME"))) &&
|
|
(struct passwd *)0 != (pwdMe = getpwnam(ptr)) &&
|
|
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);
|
|
}
|
|
pcUser = pwdMe->pw_name;
|
|
|
|
/* get the out hostname and the loopback devices IP address
|
|
* (for trusted connections mostly)
|
|
*/
|
|
if (-1 == gethostname(acMyName, sizeof(acMyName)-1)) {
|
|
fprintf(stderr, "%s: gethostname: %s\n", progname, strerror(errno));
|
|
exit(2);
|
|
}
|
|
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';
|
|
}
|
|
|
|
/* command line parsing
|
|
*/
|
|
pcCmd = (char *)0;
|
|
fLocal = 0;
|
|
while (EOF != (opt = getopt(argc, argv, acOpts))) {
|
|
switch(opt) {
|
|
case 'A': /* attach with log replay */
|
|
fReplay = 1;
|
|
/* fall through */
|
|
case 'a': /* attach */
|
|
pcCmd = "attach";
|
|
break;
|
|
|
|
case 'D':
|
|
fLocal = 1;
|
|
/*fallthrough*/
|
|
case 'd': /* display daemon version */
|
|
pcCmd = "version";
|
|
break;
|
|
|
|
case 'e': /* set escape chars */
|
|
ParseEsc(optarg);
|
|
break;
|
|
|
|
case 'F': /* force attach with log replay */
|
|
fReplay = 1;
|
|
/* fall through */
|
|
case 'f': /* force attach */
|
|
pcCmd = "force";
|
|
break;
|
|
|
|
case 'M':
|
|
pcInMaster = optarg;
|
|
break;
|
|
|
|
case 'l':
|
|
pcUser = optarg;
|
|
break;
|
|
|
|
case 'r':
|
|
fRaw = 1;
|
|
if ((char *)0 == pcCmd) {
|
|
pcCmd = "spy";
|
|
}
|
|
break;
|
|
|
|
case 'S': /* spy with log replay */
|
|
fReplay = 1;
|
|
/* fall through */
|
|
case 's': /* spy */
|
|
pcCmd = "spy";
|
|
break;
|
|
|
|
case 'u':
|
|
case 'U':
|
|
pcCmd = "users";
|
|
break;
|
|
|
|
case 'w': /* who */
|
|
case 'W' :
|
|
pcCmd = "groups";
|
|
break;
|
|
|
|
case 'x':
|
|
pcCmd = "xamine";
|
|
break;
|
|
|
|
case 'p': /* send a pid command to the server */
|
|
pcCmd = "pid";
|
|
break;
|
|
|
|
case 'Q': /* only quit this host */
|
|
fLocal = 1;
|
|
/*fallthough*/
|
|
case 'q': /* send quit command to server */
|
|
pcCmd = "quit";
|
|
break;
|
|
|
|
case 'h':
|
|
printf("%s: usage [-aAfFsS] [-v] [-e esc] [-M mach] [-l username] machine\n", progname);
|
|
printf("%s: usage [-hpuVw]\n", progname);
|
|
printf("%s: usage [-qQ] [-v] [-M mach]\n", progname);
|
|
Usage(stdout, apcLong);
|
|
exit(0);
|
|
/*NOTREACHED*/
|
|
|
|
case 'v':
|
|
fVerbose = 1;
|
|
break;
|
|
|
|
case 'V':
|
|
Version();
|
|
exit(0);
|
|
|
|
default: /* huh? */
|
|
fprintf(stderr, "%s: unknown option `%c\', try -h\n", progname, opt);
|
|
exit(1);
|
|
/*NOTREACHED*/
|
|
}
|
|
}
|
|
|
|
/* finish resolving the command to do, call Gather
|
|
*/
|
|
if ((char *)0 == pcCmd) {
|
|
pcCmd = "attach";
|
|
}
|
|
if ('a' == *pcCmd || 'f' == *pcCmd || 's' == *pcCmd) {
|
|
if (optind >= argc) {
|
|
fprintf(stderr, "%s: missing machine name\n", progname);
|
|
exit(1);
|
|
}
|
|
pcTo = argv[optind++];
|
|
} else {
|
|
pcTo = "*";
|
|
}
|
|
if (optind < argc) {
|
|
fprintf(stderr, "%s: extra garbage on command line? (%s...)\n", progname, argv[optind]);
|
|
exit(1);
|
|
}
|
|
(void)sprintf(acPorts, "@%s", pcInMaster);
|
|
if ('q' == *pcCmd) {
|
|
auto char acPass[32];
|
|
(void)strcpy(acPass, getpass("Enter root password:"));
|
|
pfiCall = fLocal ? Ctl : CtlMaster;
|
|
pcTo = acPass;
|
|
} else if ('v' == *pcCmd) {
|
|
pfiCall = fLocal ? Ctl : CtlMaster;
|
|
} else if ('p' == *pcCmd) {
|
|
pfiCall = CtlMaster;
|
|
} else if ('a' == *pcCmd || 'f' == *pcCmd || 's' == *pcCmd) {
|
|
pfiCall = Indir;
|
|
} else {
|
|
pfiCall = CmdMaster;
|
|
}
|
|
exit(Gather(pfiCall, acPorts, pcInMaster, pcTo, pcCmd, pcUser));
|
|
}
|