conserver/console/console.c

1765 lines
40 KiB
C
Raw Permalink Normal View History

/*
2003-04-09 08:30:48 -07:00
* $Id: console.c,v 5.117 2003-04-06 05:29:24-07 bryan Exp $
*
2002-01-21 02:58:05 -08:00
* Copyright conserver.com, 2000
2000-12-14 16:32:51 -08:00
*
* Maintainer/Enhancer: Bryan Stansell (bryan@conserver.com)
*
2000-12-14 16:32:51 -08:00
* Copyright GNAC, Inc., 1998
*/
/*
* 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.
*/
2001-02-18 22:50:29 -08:00
#include <config.h>
2001-05-03 06:44:08 -07:00
#include <sys/param.h>
#include <sys/socket.h>
#include <netinet/in.h>
2001-07-26 17:05:04 -07:00
#include <arpa/inet.h>
#include <fcntl.h>
#include <signal.h>
#include <stdio.h>
#include <netdb.h>
#include <pwd.h>
#include <ctype.h>
2002-03-12 01:12:20 -08:00
#include <sys/stat.h>
2001-02-18 22:50:29 -08:00
#include <compat.h>
2001-07-26 17:05:04 -07:00
#include <util.h>
2001-02-18 22:50:29 -08:00
#include <version.h>
2003-04-09 08:30:48 -07:00
#if HAVE_OPENSSL
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <openssl/opensslv.h>
#endif
2003-03-10 18:08:07 -08:00
int fReplay = 0, fRaw = 0, fVersion = 0, fStrip = 0;
2002-10-14 14:03:35 -07:00
#if HAVE_OPENSSL
int fReqEncryption = 1;
char *pcCredFile = (char *)0;
#endif
int chAttn = -1, chEsc = -1;
2001-07-26 17:05:04 -07:00
char *pcInMaster = /* which machine is current */
MASTERHOST;
2001-07-05 09:18:19 -07:00
char *pcPort = DEFPORT;
2001-07-26 17:05:04 -07:00
unsigned short bindPort;
2002-10-14 14:03:35 -07:00
CONSFILE *cfstdout;
2002-03-12 01:12:20 -08:00
static char acMesg[8192]; /* the buffer for startup negotiation */
2002-10-14 14:03:35 -07:00
#if HAVE_OPENSSL
SSL_CTX *ctx = (SSL_CTX *) 0;
void
2003-03-10 18:08:07 -08:00
#if PROTOTYPES
SetupSSL(void)
2002-10-14 14:03:35 -07:00
#else
2003-03-10 18:08:07 -08:00
SetupSSL()
2002-10-14 14:03:35 -07:00
#endif
{
if (ctx == (SSL_CTX *) 0) {
SSL_load_error_strings();
if (!SSL_library_init()) {
Error("SSL library initialization failed");
exit(EX_UNAVAILABLE);
}
if ((ctx = SSL_CTX_new(SSLv23_method())) == (SSL_CTX *) 0) {
Error("Creating SSL context failed");
exit(EX_UNAVAILABLE);
}
if (SSL_CTX_set_default_verify_paths(ctx) != 1) {
Error("Could not load SSL default CA file and/or directory");
exit(EX_UNAVAILABLE);
}
if (pcCredFile != (char *)0) {
if (SSL_CTX_use_certificate_chain_file(ctx, pcCredFile) != 1) {
Error("Could not load SSL certificate from '%s'",
pcCredFile);
exit(EX_UNAVAILABLE);
}
if (SSL_CTX_use_PrivateKey_file
(ctx, pcCredFile, SSL_FILETYPE_PEM) != 1) {
Error("Could not SSL private key from '%s'", pcCredFile);
exit(EX_UNAVAILABLE);
}
}
2003-03-10 18:08:07 -08:00
SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, SSLVerifyCallback);
2002-10-14 14:03:35 -07:00
SSL_CTX_set_options(ctx,
SSL_OP_ALL | SSL_OP_NO_SSLv2 |
SSL_OP_SINGLE_DH_USE);
SSL_CTX_set_mode(ctx,
SSL_MODE_ENABLE_PARTIAL_WRITE |
SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER |
SSL_MODE_AUTO_RETRY);
if (SSL_CTX_set_cipher_list(ctx, "ALL:!LOW:!EXP:!MD5:@STRENGTH") !=
1) {
Error("Setting SSL cipher list failed");
exit(EX_UNAVAILABLE);
}
}
}
void
2003-03-10 18:08:07 -08:00
#if PROTOTYPES
AttemptSSL(CONSFILE * pcf)
2002-10-14 14:03:35 -07:00
#else
2003-03-10 18:08:07 -08:00
AttemptSSL(pcf)
2002-10-14 14:03:35 -07:00
CONSFILE *pcf;
#endif
{
SSL *ssl;
if (ctx == (SSL_CTX *) 0) {
Error("WTF? The SSL context disappeared?!?!?");
exit(EX_UNAVAILABLE);
}
if (!(ssl = SSL_new(ctx))) {
Error("Couldn't create new SSL context");
exit(EX_UNAVAILABLE);
}
2003-03-10 18:08:07 -08:00
FileSetSSL(pcf, ssl);
SSL_set_fd(ssl, FileFDNum(pcf));
Debug(1, "About to SSL_connect() on fd %d", FileFDNum(pcf));
2002-10-14 14:03:35 -07:00
if (SSL_connect(ssl) <= 0) {
Error("SSL negotiation failed");
ERR_print_errors_fp(stderr);
exit(EX_UNAVAILABLE);
}
2003-03-10 18:08:07 -08:00
FileSetType(pcf, SSLSocket);
2002-10-14 14:03:35 -07:00
if (fDebug)
Debug(1, "SSL Connection: %s :: %s", SSL_get_cipher_version(ssl),
SSL_get_cipher_name(ssl));
}
#endif
2003-03-10 18:08:07 -08:00
void
#if PROTOTYPES
DestroyDataStructures(void)
#else
DestroyDataStructures()
#endif
{
}
/* output a control (or plain) character as a UNIX user would expect it (ksb)
*/
static void
2003-03-10 18:08:07 -08:00
#if PROTOTYPES
PutCtlc(int c, FILE * fp)
2002-03-12 01:12:20 -08:00
#else
2003-03-10 18:08:07 -08:00
PutCtlc(c, fp)
2001-07-26 17:05:04 -07:00
int c;
FILE *fp;
2002-03-12 01:12:20 -08:00
#endif
{
2001-07-26 17:05:04 -07:00
if (0 != (0200 & c)) {
2003-03-10 18:08:07 -08:00
putc('M', fp);
putc('-', fp);
2001-07-26 17:05:04 -07:00
c &= ~0200;
}
if (isprint(c)) {
2003-03-10 18:08:07 -08:00
putc(c, fp);
2001-07-26 17:05:04 -07:00
return;
}
2003-03-10 18:08:07 -08:00
putc('^', fp);
2001-07-26 17:05:04 -07:00
if (c == 0177) {
2003-03-10 18:08:07 -08:00
putc('?', fp);
2001-07-26 17:05:04 -07:00
return;
}
2003-03-10 18:08:07 -08:00
putc(c + 0100, fp);
}
2003-03-10 18:08:07 -08:00
/* output a long message to the user
*/
static void
#if PROTOTYPES
Usage(int wantfull)
2002-10-14 14:03:35 -07:00
#else
2003-03-10 18:08:07 -08:00
Usage(wantfull)
int wantfull;
2002-10-14 14:03:35 -07:00
#endif
2003-03-10 18:08:07 -08:00
{
static char *full[] = {
"7 strip the high bit of all console data",
"a(A) attach politely (and replay last 20 lines)",
"b(B) send broadcast message to all users (on master)",
2002-10-14 14:03:35 -07:00
#if HAVE_OPENSSL
2003-03-10 18:08:07 -08:00
"c cred load an SSL certificate and key from the PEM encoded file",
2002-10-14 14:03:35 -07:00
#else
2003-03-10 18:08:07 -08:00
"c cred ignored - encryption not compiled into code",
2002-10-14 14:03:35 -07:00
#endif
2003-03-10 18:08:07 -08:00
"D enable debug output, sent to stderr",
"e esc set the initial escape characters",
#if HAVE_OPENSSL
"E don't require encrypted connections",
2002-03-12 01:12:20 -08:00
#else
2003-03-10 18:08:07 -08:00
"E ignored - encryption not compiled into code",
2002-03-12 01:12:20 -08:00
#endif
2003-03-10 18:08:07 -08:00
"f(F) force read/write connection (and replay)",
"G connect to the console group only",
"i(I) display information in machine-parseable form (on master)",
"h output this message",
"l user use username instead of current username",
"M mach master server to poll first",
"p port port to connect to",
"P display pids of daemon(s)",
"q(Q) send a quit command to the (master) server",
"r(R) display (master) daemon version (think 'r'emote version)",
"s(S) spy on a console (and replay)",
"u show users on the various consoles",
"v be more verbose",
"V show version information",
"w(W) show who is on which console (on master)",
"x examine ports and baud rates",
(char *)0
};
fprintf(stderr,
"%s: usage [-aAEfFGsS] [-7Dv] [-c cred] [-M mach] [-p port] [-e esc] [-l username] console\n",
progname);
fprintf(stderr,
"%s: usage [-hiIPrRuVwWx] [-7Dv] [-M mach] [-p port] [-[bB] message]\n",
progname);
fprintf(stderr, "%s: usage [-qQ] [-7Dv] [-M mach] [-p port]\n",
progname);
if (wantfull) {
int i;
for (i = 0; full[i] != (char *)0; i++)
fprintf(stderr, "\t%s\n", full[i]);
}
}
/* expain who we are and which revision we are (ksb)
*/
static void
2003-03-10 18:08:07 -08:00
#if PROTOTYPES
Version()
2002-03-12 01:12:20 -08:00
#else
Version()
#endif
{
2001-07-26 17:05:04 -07:00
int i;
2003-03-10 18:08:07 -08:00
static STRING *acA1 = (STRING *) 0;
static STRING *acA2 = (STRING *) 0;
2002-10-14 14:03:35 -07:00
char *optionlist[] = {
2003-04-09 08:30:48 -07:00
#if HAVE_DMALLOC
"dmalloc",
#endif
2002-10-14 14:03:35 -07:00
#if USE_LIBWRAP
"libwrap",
#endif
#if HAVE_OPENSSL
"openssl",
#endif
#if HAVE_PAM
"pam",
#endif
#if HAVE_POSIX_REGCOMP
"regex",
#endif
(char *)0
};
2001-07-26 17:05:04 -07:00
2003-03-10 18:08:07 -08:00
if (acA1 == (STRING *) 0)
acA1 = AllocString();
if (acA2 == (STRING *) 0)
acA2 = AllocString();
Msg("%s", THIS_VERSION);
Msg("initial master server `%s\'", pcInMaster);
Msg("default escape sequence `%s%s\'", FmtCtl(DEFATTN, acA1),
FmtCtl(DEFESC, acA2));
2001-07-26 17:05:04 -07:00
/* Look for non-numeric characters */
for (i = 0; pcPort[i] != '\000'; i++)
if (!isdigit((int)pcPort[i]))
break;
if (pcPort[i] == '\000') {
/* numeric only */
bindPort = atoi(pcPort);
2003-03-10 18:08:07 -08:00
Msg("on port %hu (referenced as `%s')", bindPort, pcPort);
2001-07-26 17:05:04 -07:00
} else {
/* non-numeric only */
struct servent *pSE;
if ((struct servent *)0 == (pSE = getservbyname(pcPort, "tcp"))) {
2003-03-10 18:08:07 -08:00
Error("getservbyname(%s): %s", pcPort, strerror(errno));
2001-07-05 09:18:19 -07:00
} else {
2001-07-26 17:05:04 -07:00
bindPort = ntohs((u_short) pSE->s_port);
2003-03-10 18:08:07 -08:00
Msg("on port %hu (referenced as `%s')", bindPort, pcPort);
2001-07-05 09:18:19 -07:00
}
2001-07-26 17:05:04 -07:00
}
2003-03-10 18:08:07 -08:00
BuildString((char *)0, acA1);
2002-10-14 14:03:35 -07:00
if (optionlist[0] == (char *)0)
2003-03-10 18:08:07 -08:00
BuildString("none", acA1);
2002-10-14 14:03:35 -07:00
for (i = 0; optionlist[i] != (char *)0; i++) {
if (i == 0)
2003-03-10 18:08:07 -08:00
BuildString(optionlist[i], acA1);
2002-10-14 14:03:35 -07:00
else {
2003-03-10 18:08:07 -08:00
BuildString(", ", acA1);
BuildString(optionlist[i], acA1);
2002-10-14 14:03:35 -07:00
}
}
2003-03-10 18:08:07 -08:00
Msg("options: %s", acA1->string);
2003-04-09 08:30:48 -07:00
#if HAVE_DMALLOC
BuildString((char *)0, acA1);
BuildStringChar('0' + DMALLOC_VERSION_MAJOR, acA1);
BuildStringChar('.', acA1);
BuildStringChar('0' + DMALLOC_VERSION_MINOR, acA1);
BuildStringChar('.', acA1);
BuildStringChar('0' + DMALLOC_VERSION_PATCH, acA1);
if (DMALLOC_VERSION_BETA != 0) {
BuildString("-b", acA1);
BuildStringChar('0' + DMALLOC_VERSION_BETA, acA1);
}
Msg("dmalloc version: %s", acA1->string);
#endif
#if HAVE_OPENSSL
Msg("openssl version: %s", OPENSSL_VERSION_TEXT);
#endif
2003-03-10 18:08:07 -08:00
Msg("built with `%s'", CONFIGINVOCATION);
2001-07-26 17:05:04 -07:00
if (fVerbose)
printf(COPYRIGHT);
}
/* 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
2003-03-10 18:08:07 -08:00
#if PROTOTYPES
2002-03-12 01:12:20 -08:00
ParseChar(char **ppcSrc, char *pcOut)
#else
ParseChar(ppcSrc, pcOut)
2001-07-26 17:05:04 -07:00
char **ppcSrc, *pcOut;
2002-03-12 01:12:20 -08:00
#endif
{
2001-07-26 17:05:04 -07:00
int cvt, n;
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 {
2001-07-26 17:05:04 -07:00
return 1;
}
2001-07-26 17:05:04 -07:00
} else {
cvt |= n;
}
2001-07-26 17:05:04 -07:00
if ((char *)0 != pcOut) {
*pcOut = cvt;
}
*ppcSrc = pcScan;
return 0;
}
2001-07-26 17:05:04 -07:00
/*
*/
static void
2003-03-10 18:08:07 -08:00
#if PROTOTYPES
2002-03-12 01:12:20 -08:00
ValidateEsc()
#else
2001-07-26 17:05:04 -07:00
ValidateEsc()
2002-03-12 01:12:20 -08:00
#endif
2001-07-26 17:05:04 -07:00
{
unsigned char c1, c2;
if (!fStrip)
return;
if (chAttn == -1 || chEsc == -1) {
c1 = DEFATTN;
c2 = DEFESC;
} else {
c1 = chAttn;
c2 = chEsc;
}
if (c1 > 127 || c2 > 127) {
Error("High-bit set in escape sequence: not allowed with -7");
exit(EX_UNAVAILABLE);
}
}
/* find the two characters that makeup the users escape sequence (ksb)
*/
static void
2003-03-10 18:08:07 -08:00
#if PROTOTYPES
2002-03-12 01:12:20 -08:00
ParseEsc(char *pcText)
#else
ParseEsc(pcText)
2001-07-26 17:05:04 -07:00
char *pcText;
2002-03-12 01:12:20 -08:00
#endif
{
2001-07-26 17:05:04 -07:00
char *pcTemp;
char c1, c2;
pcTemp = pcText;
if (ParseChar(&pcTemp, &c1) || ParseChar(&pcTemp, &c2)) {
Error("poorly formed escape sequence `%s\'", pcText);
exit(EX_UNAVAILABLE);
}
if ('\000' != *pcTemp) {
Error("too many characters in new escape sequence at ...`%s\'",
pcTemp);
exit(EX_UNAVAILABLE);
}
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
*/
2002-10-14 14:03:35 -07:00
CONSFILE *
2003-03-10 18:08:07 -08:00
#if PROTOTYPES
2002-03-12 01:12:20 -08:00
GetPort(char *pcToHost, struct sockaddr_in *pPort, unsigned short sPort)
#else
GetPort(pcToHost, pPort, sPort)
2001-07-26 17:05:04 -07:00
char *pcToHost;
struct sockaddr_in *pPort;
unsigned short sPort;
2002-03-12 01:12:20 -08:00
#endif
{
2001-07-26 17:05:04 -07:00
int s;
struct hostent *hp = (struct hostent *)0;
2001-02-18 22:50:29 -08:00
#if HAVE_MEMSET
2001-07-26 17:05:04 -07:00
memset((void *)pPort, '\000', sizeof(*pPort));
#else
2003-03-10 18:08:07 -08:00
bzero((char *)pPort, sizeof(*pPort));
2001-02-18 22:50:29 -08:00
#endif
2001-07-26 17:05:04 -07:00
2002-03-12 01:12:20 -08:00
pPort->sin_addr.s_addr = inet_addr(pcToHost);
if ((in_addr_t) (-1) == pPort->sin_addr.s_addr) {
2001-02-18 22:50:29 -08:00
if ((struct hostent *)0 != (hp = gethostbyname(pcToHost))) {
#if HAVE_MEMCPY
2001-07-26 17:05:04 -07:00
memcpy((char *)&pPort->sin_addr.s_addr, (char *)hp->h_addr,
hp->h_length);
2001-02-18 22:50:29 -08:00
#else
2003-03-10 18:08:07 -08:00
bcopy((char *)hp->h_addr, (char *)&pPort->sin_addr.s_addr,
hp->h_length);
#endif
} else {
2003-03-10 18:08:07 -08:00
Error("gethostbyname(%s): %s", pcToHost, hstrerror(h_errno));
2001-07-26 17:05:04 -07:00
exit(EX_UNAVAILABLE);
}
}
pPort->sin_port = sPort;
pPort->sin_family = AF_INET;
if (fDebug) {
if ((struct hostent *)0 != hp && (char *)0 != hp->h_name)
2003-03-10 18:08:07 -08:00
Debug(1, "GetPort: hostname=%s (%s), ip=%s, port=%hu",
2002-03-12 01:12:20 -08:00
hp->h_name, pcToHost, inet_ntoa(pPort->sin_addr),
ntohs(sPort));
2001-07-26 17:05:04 -07:00
else
2003-03-10 18:08:07 -08:00
Debug(1,
"GetPort: hostname=<unresolved> (%s), ip=%s, port=%hu",
2002-03-12 01:12:20 -08:00
pcToHost, inet_ntoa(pPort->sin_addr), ntohs(sPort));
2001-07-26 17:05:04 -07:00
}
/* 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) {
2003-03-10 18:08:07 -08:00
Error("socket(AF_INET,SOCK_STREAM): %s", strerror(errno));
2001-07-26 17:05:04 -07:00
exit(EX_UNAVAILABLE);
}
if (connect(s, (struct sockaddr *)pPort, sizeof(*pPort)) < 0) {
2003-03-10 18:08:07 -08:00
Error("connect(): %hu@%s: %s", ntohs(pPort->sin_port), pcToHost,
2001-07-26 17:05:04 -07:00
strerror(errno));
exit(EX_UNAVAILABLE);
}
2003-03-10 18:08:07 -08:00
return FileOpenFD(s, simpleSocket);
}
/* the next two routines assure that the users tty is in the
* correct mode for us to do our thing
*/
static int screwy = 0;
2001-02-18 22:50:29 -08:00
#if HAVE_TERMIOS_H
static struct termios o_tios;
#else
2001-02-18 22:50:29 -08:00
# if HAVE_TERMIO_H
static struct termio o_tio;
2001-02-18 22:50:29 -08:00
# else
static struct sgttyb o_sty;
static struct tchars o_tchars;
static struct ltchars o_ltchars;
2001-02-18 22:50:29 -08:00
# 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
2003-03-10 18:08:07 -08:00
#if PROTOTYPES
C2Raw()
2002-03-12 01:12:20 -08:00
#else
2003-03-10 18:08:07 -08:00
C2Raw()
2002-03-12 01:12:20 -08:00
#endif
{
2001-02-18 22:50:29 -08:00
#if HAVE_TERMIOS_H
2001-07-26 17:05:04 -07:00
struct termios n_tios;
#else
2001-02-18 22:50:29 -08:00
# if HAVE_TERMIO_H
2001-07-26 17:05:04 -07:00
struct termio n_tio;
2001-02-18 22:50:29 -08:00
# else
2001-07-26 17:05:04 -07:00
struct sgttyb n_sty;
struct tchars n_tchars;
struct ltchars n_ltchars;
2001-02-18 22:50:29 -08:00
# endif
#endif
2001-07-26 17:05:04 -07:00
if (!isatty(0) || 0 != screwy)
return;
2001-07-26 17:05:04 -07:00
#if HAVE_TERMIOS_H
# if HAVE_TCGETATTR
2003-03-10 18:08:07 -08:00
if (0 != tcgetattr(0, &o_tios)) {
Error("tcgetattr(0): %s", strerror(errno));
exit(EX_UNAVAILABLE);
}
2001-02-18 22:50:29 -08:00
# else
2003-03-10 18:08:07 -08:00
if (0 != ioctl(0, TCGETS, &o_tios)) {
Error("iotcl(0, TCGETS): %s", strerror(errno));
2001-07-26 17:05:04 -07:00
exit(EX_UNAVAILABLE);
}
2003-03-10 18:08:07 -08:00
# endif
2001-07-26 17:05:04 -07:00
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 | IEXTEN);
n_tios.c_cc[VMIN] = 1;
n_tios.c_cc[VTIME] = 0;
# if HAVE_TCSETATTR
2003-03-10 18:08:07 -08:00
if (0 != tcsetattr(0, TCSANOW, &n_tios)) {
Error("tcsetattr(0, TCSANOW): %s", strerror(errno));
exit(EX_UNAVAILABLE);
}
2001-02-18 22:50:29 -08:00
# else
2003-03-10 18:08:07 -08:00
if (0 != ioctl(0, TCSETS, &n_tios)) {
Error("ioctl(0, TCSETS): %s", strerror(errno));
2001-07-26 17:05:04 -07:00
exit(EX_UNAVAILABLE);
}
2003-03-10 18:08:07 -08:00
# endif
#else
2001-07-26 17:05:04 -07:00
# if HAVE_TERMIO_H
if (0 != ioctl(0, TCGETA, &o_tio)) {
2003-03-10 18:08:07 -08:00
Error("iotcl(0, TCGETA): %s", strerror(errno));
2001-07-26 17:05:04 -07:00
exit(EX_UNAVAILABLE);
}
n_tio = o_tio;
n_tio.c_iflag &= ~(INLCR | IGNCR | ICRNL | IUCLC | IXON);
n_tio.c_oflag &= ~OPOST;
2001-10-15 22:49:17 -07:00
n_tio.c_lflag &=
~(ICANON | ISIG | ECHO | ECHOE | ECHOK | ECHONL | IEXTEN);
2001-07-26 17:05:04 -07:00
n_tio.c_cc[VMIN] = 1;
n_tio.c_cc[VTIME] = 0;
if (0 != ioctl(0, TCSETAF, &n_tio)) {
2003-03-10 18:08:07 -08:00
Error("iotcl(0, TCSETAF): %s", strerror(errno));
2001-07-26 17:05:04 -07:00
exit(EX_UNAVAILABLE);
}
2001-02-18 22:50:29 -08:00
# else
2001-07-26 17:05:04 -07:00
if (0 != ioctl(0, TIOCGETP, (char *)&o_sty)) {
2003-03-10 18:08:07 -08:00
Error("iotcl(0, TIOCGETP): %s", strerror(errno));
2001-07-26 17:05:04 -07:00
exit(EX_UNAVAILABLE);
}
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)) {
2003-03-10 18:08:07 -08:00
Error("iotcl(0, TIOCSETP): %s", strerror(errno));
2001-07-26 17:05:04 -07:00
exit(EX_UNAVAILABLE);
}
/* stty undef all tty chars
*/
if (-1 == ioctl(0, TIOCGETC, (char *)&n_tchars)) {
2003-03-10 18:08:07 -08:00
Error("ioctl(0, TIOCGETC): %s", strerror(errno));
2001-07-26 17:05:04 -07:00
return;
}
o_tchars = n_tchars;
n_tchars.t_intrc = -1;
n_tchars.t_quitc = -1;
if (-1 == ioctl(0, TIOCSETC, (char *)&n_tchars)) {
2003-03-10 18:08:07 -08:00
Error("ioctl(0, TIOCSETC): %s", strerror(errno));
2001-07-26 17:05:04 -07:00
return;
}
if (-1 == ioctl(0, TIOCGLTC, (char *)&n_ltchars)) {
2003-03-10 18:08:07 -08:00
Error("ioctl(0, TIOCGLTC): %s", strerror(errno));
2001-07-26 17:05:04 -07:00
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)) {
2003-03-10 18:08:07 -08:00
Error("ioctl(0, TIOCSLTC): %s", strerror(errno));
2001-07-26 17:05:04 -07:00
return;
}
2001-02-18 22:50:29 -08:00
# endif
#endif
2001-07-26 17:05:04 -07:00
screwy = 1;
}
/*
* put the tty back as it was, however that was
*/
static void
2003-03-10 18:08:07 -08:00
#if PROTOTYPES
C2Cooked()
2002-03-12 01:12:20 -08:00
#else
2003-03-10 18:08:07 -08:00
C2Cooked()
2002-03-12 01:12:20 -08:00
#endif
{
2001-07-26 17:05:04 -07:00
if (!screwy)
return;
#if HAVE_TERMIOS_H
# if HAVE_TCSETATTR
tcsetattr(0, TCSANOW, &o_tios);
2001-02-18 22:50:29 -08:00
# else
2003-03-10 18:08:07 -08:00
ioctl(0, TCSETS, (char *)&o_tios);
2001-02-18 22:50:29 -08:00
# endif
#else
2001-07-26 17:05:04 -07:00
# if HAVE_TERMIO_H
2003-03-10 18:08:07 -08:00
ioctl(0, TCSETA, (char *)&o_tio);
2001-02-18 22:50:29 -08:00
# else
2003-03-10 18:08:07 -08:00
ioctl(0, TIOCSETP, (char *)&o_sty);
ioctl(0, TIOCSETC, (char *)&o_tchars);
ioctl(0, TIOCSLTC, (char *)&o_ltchars);
2001-02-18 22:50:29 -08:00
# endif
#endif
2001-07-26 17:05:04 -07:00
screwy = 0;
}
/* send out some data along the connection (ksb)
*/
static void
2003-03-10 18:08:07 -08:00
#if PROTOTYPES
2002-10-14 14:03:35 -07:00
SendOut(CONSFILE * fd, char *pcBuf, int iLen)
2002-03-12 01:12:20 -08:00
#else
SendOut(fd, pcBuf, iLen)
2002-10-14 14:03:35 -07:00
CONSFILE *fd;
int iLen;
2001-07-26 17:05:04 -07:00
char *pcBuf;
2002-03-12 01:12:20 -08:00
#endif
{
2001-07-26 17:05:04 -07:00
int nr;
if (fDebug) {
2003-03-10 18:08:07 -08:00
static STRING *tmpString = (STRING *) 0;
if (tmpString == (STRING *) 0)
tmpString = AllocString();
BuildString((char *)0, tmpString);
FmtCtlStr(pcBuf, iLen, tmpString);
Debug(1, "SendOut: `%s'", tmpString->string);
2001-07-26 17:05:04 -07:00
}
while (0 != iLen) {
2003-03-10 18:08:07 -08:00
if (-1 == (nr = FileWrite(fd, pcBuf, iLen))) {
C2Cooked();
2001-07-26 17:05:04 -07:00
Error("lost connection");
exit(EX_UNAVAILABLE);
}
iLen -= nr;
pcBuf += nr;
}
}
/* read a reply from the console server (ksb)
2001-07-26 17:05:04 -07:00
* if pcWnat == (char *)0 we strip \r\n from the end and return strlen
*/
static int
2003-03-10 18:08:07 -08:00
#if PROTOTYPES
2002-10-14 14:03:35 -07:00
ReadReply(CONSFILE * fd, char *pcBuf, int iLen, char *pcWant)
2002-03-12 01:12:20 -08:00
#else
ReadReply(fd, pcBuf, iLen, pcWant)
2002-10-14 14:03:35 -07:00
CONSFILE *fd;
int iLen;
2001-07-26 17:05:04 -07:00
char *pcBuf, *pcWant;
2002-03-12 01:12:20 -08:00
#endif
{
2001-07-26 17:05:04 -07:00
int nr, j, iKeep;
iKeep = iLen;
for (j = 0; j < iLen; /* j+=nr */ ) {
2003-03-10 18:08:07 -08:00
switch (nr = FileRead(fd, &pcBuf[j], iLen - 1)) {
2001-07-26 17:05:04 -07:00
case 0:
if (iKeep != iLen) {
break;
}
2001-07-26 17:05:04 -07:00
/* fall through */
case -1:
2003-03-10 18:08:07 -08:00
C2Cooked();
2001-07-26 17:05:04 -07:00
Error("lost connection");
exit(EX_UNAVAILABLE);
default:
j += nr;
iLen -= nr;
if ('\n' == pcBuf[j - 1]) {
pcBuf[j] = '\000';
break;
}
2001-07-26 17:05:04 -07:00
if (0 == iLen) {
2003-03-10 18:08:07 -08:00
C2Cooked();
2001-07-26 17:05:04 -07:00
Error("reply too long");
exit(EX_UNAVAILABLE);
}
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((int)(pcBuf[j - 1]))) {
pcBuf[--j] = '\000';
}
2002-03-12 01:12:20 -08:00
Debug(1, "ReadReply: %s", pcBuf);
2001-07-26 17:05:04 -07:00
return j;
}
if (fDebug) {
2003-03-10 18:08:07 -08:00
static STRING *tmpString = (STRING *) 0;
if (tmpString == (STRING *) 0)
tmpString = AllocString();
BuildString((char *)0, tmpString);
FmtCtlStr(pcWant, -1, tmpString);
2001-07-26 17:05:04 -07:00
if (strcmp(pcBuf, pcWant))
2003-03-10 18:08:07 -08:00
Debug(1, "ReadReply: didn't match `%s'", tmpString->string);
2001-07-26 17:05:04 -07:00
else
2003-03-10 18:08:07 -08:00
Debug(1, "ReadReply: matched `%s'", tmpString->string);
2001-07-26 17:05:04 -07:00
}
return strcmp(pcBuf, pcWant);
}
/* 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
2003-03-10 18:08:07 -08:00
#if PROTOTYPES
2002-03-12 01:12:20 -08:00
Gather(int (*pfi) (), char *pcPorts, char *pcMaster, char *pcTo,
char *pcCmd, char *pcWho)
#else
Gather(pfi, pcPorts, pcMaster, pcTo, pcCmd, pcWho)
2001-07-26 17:05:04 -07:00
int (*pfi) ();
char *pcPorts, *pcMaster, *pcTo, *pcCmd, *pcWho;
2002-03-12 01:12:20 -08:00
#endif
{
2002-10-14 14:03:35 -07:00
CONSFILE *pcf;
2001-07-26 17:05:04 -07:00
unsigned short j;
char *pcNext, *pcServer;
2003-03-10 18:08:07 -08:00
STRING *acExcg = (STRING *) 0;
2001-07-26 17:05:04 -07:00
struct sockaddr_in client_port;
int iRet = 0;
2003-03-10 18:08:07 -08:00
if (acExcg == (STRING *) 0)
acExcg = AllocString();
2001-07-26 17:05:04 -07:00
for ( /* param */ ; '\000' != *pcPorts; pcPorts = pcNext) {
if ((char *)0 == (pcNext = strchr(pcPorts, ':')))
pcNext = "";
else
*pcNext++ = '\000';
2003-03-10 18:08:07 -08:00
BuildString((char *)0, acExcg);
BuildString(pcMaster, acExcg);
2001-07-26 17:05:04 -07:00
if ((char *)0 != (pcServer = strchr(pcPorts, '@'))) {
*pcServer++ = '\000';
if ('\000' != *pcServer) {
2003-03-10 18:08:07 -08:00
BuildString((char *)0, acExcg);
BuildString(pcServer, acExcg);
2001-07-26 17:05:04 -07:00
}
}
if ('\000' == *pcPorts) {
j = htons(bindPort);
} else if (!isdigit((int)(pcPorts[0]))) {
Error("%s: %s", pcMaster, pcPorts);
exit(EX_UNAVAILABLE);
} else {
j = htons((short)atoi(pcPorts));
}
2003-03-10 18:08:07 -08:00
pcf = GetPort(acExcg->string, &client_port, j);
2002-10-14 14:03:35 -07:00
if (0 != ReadReply(pcf, acMesg, sizeof(acMesg), "ok\r\n")) {
2001-07-26 17:05:04 -07:00
int s = strlen(acMesg);
if ((s > 0) && ('\n' == acMesg[s - 1]))
acMesg[s - 1] = '\000';
2003-03-10 18:08:07 -08:00
Error("%s: %s", acExcg->string, acMesg);
2001-07-26 17:05:04 -07:00
exit(EX_UNAVAILABLE);
}
2003-03-10 18:08:07 -08:00
iRet += (*pfi) (pcf, acExcg->string, pcTo, pcCmd, pcWho);
2001-07-26 17:05:04 -07:00
2003-03-10 18:08:07 -08:00
FileClose(&pcf);
2001-07-26 17:05:04 -07:00
if ((char *)0 != pcServer) {
*pcServer = '@';
}
2001-07-26 17:05:04 -07:00
}
2003-03-10 18:08:07 -08:00
DestroyString(acExcg);
2001-07-26 17:05:04 -07:00
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
*/
2001-02-18 22:50:29 -08:00
RETSIGTYPE
2003-03-10 18:08:07 -08:00
#if PROTOTYPES
OOB(int sig)
2002-03-12 01:12:20 -08:00
#else
2003-03-10 18:08:07 -08:00
OOB(sig)
2001-07-26 17:05:04 -07:00
int sig;
2002-03-12 01:12:20 -08:00
#endif
{
2001-07-26 17:05:04 -07:00
++SawUrg;
#if !HAVE_SIGACTION
#if defined(SIGURG)
2003-03-10 18:08:07 -08:00
SimpleSignal(SIGURG, OOB);
2001-07-26 17:05:04 -07:00
#endif
#endif
}
void
2003-03-10 18:08:07 -08:00
#if PROTOTYPES
ProcessUrgentData(int s)
2002-03-12 01:12:20 -08:00
#else
2003-03-10 18:08:07 -08:00
ProcessUrgentData(s)
2001-07-26 17:05:04 -07:00
int s;
2002-03-12 01:12:20 -08:00
#endif
{
2002-03-12 01:12:20 -08:00
static char acCmd;
2001-07-26 17:05:04 -07:00
SawUrg = 0;
/* get the pending urgent message
*/
2002-03-12 01:12:20 -08:00
while (recv(s, &acCmd, 1, MSG_OOB) < 0) {
2001-07-26 17:05:04 -07:00
switch (errno) {
case EWOULDBLOCK:
/* clear any pending input to make room */
2003-03-10 18:08:07 -08:00
read(s, &acCmd, 1);
2001-07-26 17:05:04 -07:00
write(1, ".", 1);
continue;
case EINVAL:
default:
2003-03-10 18:08:07 -08:00
Error("recv(%d): %s\r", s, strerror(errno));
2001-07-26 17:05:04 -07:00
sleep(1);
continue;
}
}
2002-03-12 01:12:20 -08:00
switch (acCmd) {
case OB_SUSP:
#if defined(SIGSTOP)
2001-07-26 17:05:04 -07:00
write(1, "stop]", 5);
2003-03-10 18:08:07 -08:00
C2Cooked();
kill(getpid(), SIGSTOP);
C2Raw();
2001-07-26 17:05:04 -07:00
write(1, "[press any character to continue", 32);
#else
2001-07-26 17:05:04 -07:00
write(1,
"stop not supported -- press any character to continue",
2002-03-12 01:12:20 -08:00
53);
#endif
2001-07-26 17:05:04 -07:00
break;
case OB_DROP:
2001-07-26 17:05:04 -07:00
write(1, "dropped by server]\r\n", 20);
2003-03-10 18:08:07 -08:00
C2Cooked();
2001-07-26 17:05:04 -07:00
exit(EX_UNAVAILABLE);
/*NOTREACHED*/ default:
2002-03-12 01:12:20 -08:00
Error("unknown out of band command `%c\'\r", acCmd);
2003-03-10 18:08:07 -08:00
fflush(stderr);
2001-07-26 17:05:04 -07:00
break;
}
}
/* interact with a group server (ksb)
*/
static int
2003-03-10 18:08:07 -08:00
#if PROTOTYPES
2002-10-14 14:03:35 -07:00
CallUp(CONSFILE * pcf, char *pcMaster, char *pcMach, char *pcHow,
char *pcUser)
2002-03-12 01:12:20 -08:00
#else
2002-10-14 14:03:35 -07:00
CallUp(pcf, pcMaster, pcMach, pcHow, pcUser)
CONSFILE *pcf;
2001-07-26 17:05:04 -07:00
char *pcMaster, *pcMach, *pcHow, *pcUser;
2002-03-12 01:12:20 -08:00
#endif
{
2001-07-26 17:05:04 -07:00
int nc;
int fIn = '-';
fd_set rmask, rinit;
int i;
2002-03-12 01:12:20 -08:00
int justProcessedUrg = 0;
2001-07-26 17:05:04 -07:00
if (fVerbose) {
2003-03-10 18:08:07 -08:00
Msg("%s to %s (%son %s)", pcHow, pcMach, fRaw ? "raw " : "",
pcMaster);
2001-07-26 17:05:04 -07:00
}
#if !defined(__CYGWIN__)
# if defined(F_SETOWN)
2003-03-10 18:08:07 -08:00
if (-1 == fcntl(FileFDNum(pcf), F_SETOWN, getpid())) {
Error("fcntl(F_SETOWN,%d): %d: %s", getpid(), FileFDNum(pcf),
2002-10-14 14:03:35 -07:00
strerror(errno));
2001-07-26 17:05:04 -07:00
}
# else
# if defined(SIOCSPGRP)
{
int iTemp;
/* on the HP-UX systems if different
*/
iTemp = -getpid();
2003-03-10 18:08:07 -08:00
if (-1 == ioctl(FileFDNum(pcf), SIOCSPGRP, &iTemp)) {
Error("ioctl(%d,SIOCSPGRP): %s", FileFDNum(pcf),
strerror(errno));
}
2001-07-26 17:05:04 -07:00
}
# endif
2001-02-18 22:50:29 -08:00
# endif
#endif
#if defined(SIGURG)
2003-03-10 18:08:07 -08:00
SimpleSignal(SIGURG, OOB);
#endif
2001-07-26 17:05:04 -07:00
/* 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 {
2002-01-21 02:58:05 -08:00
/* tell the conserver to change escape sequences, assume OK
2001-07-26 17:05:04 -07:00
* (we'll find out soon enough)
*/
2003-03-10 18:08:07 -08:00
sprintf(acMesg, "%c%ce%c%c", DEFATTN, DEFESC, chAttn, chEsc);
2002-10-14 14:03:35 -07:00
SendOut(pcf, acMesg, 5);
if (0 == ReadReply(pcf, acMesg, sizeof(acMesg), (char *)0)) {
2002-03-12 01:12:20 -08:00
Error("protocol botch on redef of escape sequence");
2001-07-26 17:05:04 -07:00
exit(EX_UNAVAILABLE);
}
}
if (fVerbose) {
printf("Enter `");
2003-03-10 18:08:07 -08:00
PutCtlc(chAttn, stdout);
PutCtlc(chEsc, stdout);
2001-07-26 17:05:04 -07:00
printf("?\' for help.\n");
}
2001-07-26 17:05:04 -07:00
/* 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) {
2002-10-14 14:03:35 -07:00
#if HAVE_OPENSSL
2003-03-10 18:08:07 -08:00
sprintf(acMesg, "%c%c*", chAttn, chEsc);
2002-10-14 14:03:35 -07:00
SendOut(pcf, acMesg, 3);
if (0 == ReadReply(pcf, acMesg, sizeof(acMesg), "[ssl:\r\n")) {
2003-03-10 18:08:07 -08:00
AttemptSSL(pcf);
2002-10-14 14:03:35 -07:00
}
2003-03-10 18:08:07 -08:00
if (fReqEncryption && FileGetType(pcf) != SSLSocket) {
2002-10-14 14:03:35 -07:00
Error("Encryption not supported by server");
exit(EX_UNAVAILABLE);
}
#endif
2001-07-26 17:05:04 -07:00
/* begin connect with who we are
*/
2003-03-10 18:08:07 -08:00
sprintf(acMesg, "%c%c;", chAttn, chEsc);
2002-10-14 14:03:35 -07:00
SendOut(pcf, acMesg, 3);
if (0 != ReadReply(pcf, acMesg, sizeof(acMesg), "[login:\r\n") &&
2001-07-26 17:05:04 -07:00
0 != strcmp(acMesg, "\r\n[login:\r\n")) {
int s = strlen(acMesg);
2002-10-14 14:03:35 -07:00
if (0 != strcmp(acMesg, "[Encryption required\r\n")) {
if ((s > 0) && ('\n' == acMesg[s - 1]))
acMesg[s - 1] = '\000';
Error("call: %s", acMesg);
} else {
Error("Encryption required by server for login");
}
2001-07-26 17:05:04 -07:00
exit(EX_UNAVAILABLE);
}
2003-03-10 18:08:07 -08:00
sprintf(acMesg, "%s\r\n", pcUser);
2002-10-14 14:03:35 -07:00
SendOut(pcf, acMesg, strlen(acMesg));
if (0 != ReadReply(pcf, acMesg, sizeof(acMesg), "host:\r\n")) {
2001-07-26 17:05:04 -07:00
int s = strlen(acMesg);
if ((s > 0) && ('\n' == acMesg[s - 1]))
acMesg[s - 1] = '\000';
Error("%s", acMesg);
exit(EX_UNAVAILABLE);
}
/* which host we want, and a passwd if asked for one
*/
2003-03-10 18:08:07 -08:00
sprintf(acMesg, "%s\r\n", pcMach);
2002-10-14 14:03:35 -07:00
SendOut(pcf, acMesg, strlen(acMesg));
2003-03-10 18:08:07 -08:00
ReadReply(pcf, acMesg, sizeof(acMesg), (char *)0);
2001-07-26 17:05:04 -07:00
if (0 == strcmp(acMesg, "passwd:")) {
2003-03-10 18:08:07 -08:00
static STRING *tmpString = (STRING *) 0;
if (tmpString == (STRING *) 0)
tmpString = AllocString();
BuildString((char *)0, tmpString);
sprintf(acMesg, "Enter %s@%s's password:", pcUser, pcMaster);
2001-07-26 17:05:04 -07:00
#if defined(HAVE_GETPASSPHRASE)
2003-03-10 18:08:07 -08:00
BuildString(getpassphrase(acMesg), tmpString);
2001-07-26 17:05:04 -07:00
#else
2003-03-10 18:08:07 -08:00
BuildString(getpass(acMesg), tmpString);
#endif
2003-03-10 18:08:07 -08:00
BuildString("\r\n", tmpString);
SendOut(pcf, tmpString->string, strlen(tmpString->string));
ReadReply(pcf, acMesg, sizeof(acMesg), (char *)0);
}
2001-07-26 17:05:04 -07:00
/* 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 = '-';
Error("%s is down", pcMach);
if (fVerbose) {
printf("[use `");
2003-03-10 18:08:07 -08:00
PutCtlc(chAttn, stdout);
PutCtlc(chEsc, stdout);
2001-07-26 17:05:04 -07:00
printf("o\' to open console line]\n");
}
} else if (0 == strcmp(acMesg, "no -- on ctl]")) {
fIn = '-';
Error("%s is a control port", pcMach);
if (fVerbose) {
printf("[use `");
2003-03-10 18:08:07 -08:00
PutCtlc(chAttn, stdout);
PutCtlc(chEsc, stdout);
2001-07-26 17:05:04 -07:00
printf(";\' to open a console line]\n");
}
} else {
Error("%s: %s", pcMach, acMesg);
exit(EX_UNAVAILABLE);
}
}
printf("[Enter `");
2003-03-10 18:08:07 -08:00
PutCtlc(chAttn, stdout);
PutCtlc(chEsc, stdout);
2001-07-26 17:05:04 -07:00
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) {
Error("%s is read-only", pcMach);
}
} else if (fIn != ('f' == *pcHow ? 'a' : *pcHow)) {
2003-03-10 18:08:07 -08:00
sprintf(acMesg, "%c%c%c", chAttn, chEsc, *pcHow);
2002-10-14 14:03:35 -07:00
SendOut(pcf, acMesg, 3);
2001-07-26 17:05:04 -07:00
}
if (fReplay) {
2003-03-10 18:08:07 -08:00
sprintf(acMesg, "%c%cr", chAttn, chEsc);
2002-10-14 14:03:35 -07:00
SendOut(pcf, acMesg, 3);
2001-07-26 17:05:04 -07:00
} else if (fVerbose) {
2003-03-10 18:08:07 -08:00
sprintf(acMesg, "%c%c\022", chAttn, chEsc);
2002-10-14 14:03:35 -07:00
SendOut(pcf, acMesg, 3);
2001-07-26 17:05:04 -07:00
}
}
2003-03-10 18:08:07 -08:00
fflush(stdout);
fflush(stderr);
2001-07-26 17:05:04 -07:00
2003-03-10 18:08:07 -08:00
C2Raw();
2001-07-26 17:05:04 -07:00
/* 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);
2003-03-10 18:08:07 -08:00
FD_SET(FileFDNum(pcf), &rinit);
2001-07-26 17:05:04 -07:00
FD_SET(0, &rinit);
for (;;) {
2002-03-12 01:12:20 -08:00
justProcessedUrg = 0;
2001-07-26 17:05:04 -07:00
if (SawUrg) {
2003-03-10 18:08:07 -08:00
ProcessUrgentData(FileFDNum(pcf));
2002-03-12 01:12:20 -08:00
justProcessedUrg = 1;
2001-07-26 17:05:04 -07:00
}
/* 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)) {
rmask = rinit;
if (SawUrg) {
2003-03-10 18:08:07 -08:00
ProcessUrgentData(FileFDNum(pcf));
2002-03-12 01:12:20 -08:00
justProcessedUrg = 1;
2001-07-26 17:05:04 -07:00
}
}
/* anything from socket? */
2003-03-10 18:08:07 -08:00
if (FD_ISSET(FileFDNum(pcf), &rmask)) {
if ((nc = FileRead(pcf, acMesg, sizeof(acMesg))) == 0) {
2002-03-12 01:12:20 -08:00
if (justProcessedUrg) {
printf("\n");
Error("lost connection");
}
2001-07-26 17:05:04 -07:00
break;
}
if (fStrip) {
for (i = 0; i < nc; ++i)
acMesg[i] &= 127;
}
2002-10-14 14:03:35 -07:00
SendOut(cfstdout, acMesg, nc);
2001-07-26 17:05:04 -07:00
}
2002-03-12 01:12:20 -08:00
2001-07-26 17:05:04 -07:00
/* anything from stdin? */
if (FD_ISSET(0, &rmask)) {
if ((nc = read(0, acMesg, sizeof(acMesg))) == 0)
break;
if (fStrip) {
for (i = 0; i < nc; ++i)
acMesg[i] &= 127;
}
2002-10-14 14:03:35 -07:00
SendOut(pcf, acMesg, nc);
2001-07-26 17:05:04 -07:00
}
}
2003-03-10 18:08:07 -08:00
C2Cooked();
2001-07-26 17:05:04 -07:00
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
2003-03-10 18:08:07 -08:00
#if PROTOTYPES
2002-10-14 14:03:35 -07:00
Indir(CONSFILE * pcf, char *pcMaster, char *pcMach, char *pcCmd,
char *pcWho)
2002-03-12 01:12:20 -08:00
#else
2002-10-14 14:03:35 -07:00
Indir(pcf, pcMaster, pcMach, pcCmd, pcWho)
CONSFILE *pcf;
2001-07-26 17:05:04 -07:00
char *pcMaster, *pcMach, *pcCmd, *pcWho;
2002-03-12 01:12:20 -08:00
#endif
{
2001-07-26 17:05:04 -07:00
char acPorts[4097];
/* send request for master list
*/
2003-03-10 18:08:07 -08:00
sprintf(acPorts, "call:%s\r\n", pcMach);
2002-10-14 14:03:35 -07:00
SendOut(pcf, acPorts, strlen(acPorts));
2001-07-26 17:05:04 -07:00
/* get the ports number */
2002-10-14 14:03:35 -07:00
if (0 >= ReadReply(pcf, acPorts, sizeof(acPorts), (char *)0)) {
2001-07-26 17:05:04 -07:00
Error("master forward broken");
exit(EX_UNAVAILABLE);
}
if ('@' == acPorts[0]) {
static int iLimit = 0;
if (iLimit++ > 10) {
Error("forwarding level too deep!");
return 1;
}
return Gather(Indir, acPorts, pcMaster, pcMach, pcCmd, pcWho);
}
/* to the command to each master
*/
return Gather(CallUp, acPorts, pcMaster, pcMach, pcCmd, pcWho);
}
2002-03-12 01:12:20 -08:00
#define BUF_G1 1024
#define BUF_MIN 80
#define BUF_CHUNK (2*132)
/* Cmd is implemented separately 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.
*/
2002-03-12 01:12:20 -08:00
static int
2003-03-10 18:08:07 -08:00
#if PROTOTYPES
2002-10-14 14:03:35 -07:00
Cmd(CONSFILE * pcf, char *pcMaster, char *pcMach, char *pcCmd, char *pcWho)
2002-03-12 01:12:20 -08:00
#else
2002-10-14 14:03:35 -07:00
Cmd(pcf, pcMaster, pcMach, pcCmd, pcWho)
CONSFILE *pcf;
2001-07-26 17:05:04 -07:00
char *pcMaster, *pcMach, *pcCmd, *pcWho;
2002-03-12 01:12:20 -08:00
#endif
{
2001-07-26 17:05:04 -07:00
static int iMax = 0;
static char *pcBuf = (char *)0;
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
*/
if (*pcCmd == 'b') {
2003-03-10 18:08:07 -08:00
sprintf(acMesg, "%c%c%c%s:%s\r%c%c.", DEFATTN, DEFESC, *pcCmd,
pcWho, pcMach, DEFATTN, DEFESC);
2002-10-14 14:03:35 -07:00
SendOut(pcf, acMesg, strlen(acMesg));
2001-07-26 17:05:04 -07:00
} else {
2003-03-10 18:08:07 -08:00
sprintf(acMesg, "%c%c%c%c%c.", DEFATTN, DEFESC, *pcCmd, DEFATTN,
DEFESC);
2002-10-14 14:03:35 -07:00
SendOut(pcf, acMesg, 6);
2001-07-26 17:05:04 -07:00
}
/* 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;
2003-03-10 18:08:07 -08:00
while (0 < (nr = FileRead(pcf, pcBuf + i, iRem))) {
2001-07-26 17:05:04 -07:00
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;
2003-03-10 18:08:07 -08:00
putchar('\n');
2001-07-26 17:05:04 -07:00
iRem = 0;
continue;
case '[':
fBrace = 1;
continue;
}
2003-03-10 18:08:07 -08:00
putchar(pcBuf[nr]);
2001-07-26 17:05:04 -07:00
iRem = 1;
}
2003-03-10 18:08:07 -08:00
/* SendOut(1, pcBuf, i); */
fflush(stdout);
2001-07-26 17:05:04 -07:00
return 0;
}
/* the masters tell us the group masters with a "groups" command (ksb)
*/
static int
2003-03-10 18:08:07 -08:00
#if PROTOTYPES
2002-10-14 14:03:35 -07:00
CmdGroup(CONSFILE * pcf, char *pcMaster, char *pcMach, char *pcCmd,
char *pcWho)
2002-03-12 01:12:20 -08:00
#else
2002-10-14 14:03:35 -07:00
CmdGroup(pcf, pcMaster, pcMach, pcCmd, pcWho)
CONSFILE *pcf;
2001-07-26 17:05:04 -07:00
char *pcMaster, *pcMach, *pcCmd, *pcWho;
2002-03-12 01:12:20 -08:00
#endif
{
2001-07-26 17:05:04 -07:00
char acPorts[4097];
/* send request for master list
*/
2003-03-10 18:08:07 -08:00
sprintf(acPorts, "groups\r\n");
2002-10-14 14:03:35 -07:00
SendOut(pcf, acPorts, strlen(acPorts));
2001-07-26 17:05:04 -07:00
/* get the ports number */
2002-10-14 14:03:35 -07:00
if (0 >= ReadReply(pcf, acPorts, sizeof(acPorts), (char *)0)) {
2001-07-26 17:05:04 -07:00
Error("master forward broken");
exit(EX_UNAVAILABLE);
}
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
2003-03-10 18:08:07 -08:00
#if PROTOTYPES
2002-10-14 14:03:35 -07:00
CmdMaster(CONSFILE * pcf, char *pcMaster, char *pcMach, char *pcCmd,
char *pcWho)
2002-03-12 01:12:20 -08:00
#else
2002-10-14 14:03:35 -07:00
CmdMaster(pcf, pcMaster, pcMach, pcCmd, pcWho)
CONSFILE *pcf;
2001-07-26 17:05:04 -07:00
char *pcMaster, *pcMach, *pcCmd, *pcWho;
2002-03-12 01:12:20 -08:00
#endif
{
2001-07-26 17:05:04 -07:00
char acPorts[4097];
/* send request for master list
*/
2002-10-14 14:03:35 -07:00
SendOut(pcf, "master\r\n", 8);
2001-07-26 17:05:04 -07:00
/* get the ports number */
2002-10-14 14:03:35 -07:00
if (0 >= ReadReply(pcf, acPorts, sizeof(acPorts), (char *)0)) {
2001-07-26 17:05:04 -07:00
Error("master forward broken");
exit(EX_UNAVAILABLE);
}
/* 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.
*/
2002-03-12 01:12:20 -08:00
static int
2003-03-10 18:08:07 -08:00
#if PROTOTYPES
2002-10-14 14:03:35 -07:00
Ctl(CONSFILE * pcf, char *pcMaster, char *pcMach, char *pcCmd, char *pcWho)
2002-03-12 01:12:20 -08:00
#else
2002-10-14 14:03:35 -07:00
Ctl(pcf, pcMaster, pcMach, pcCmd, pcWho)
CONSFILE *pcf;
2001-07-26 17:05:04 -07:00
char *pcMaster, *pcMach, *pcCmd, *pcWho;
2002-03-12 01:12:20 -08:00
#endif
{
2001-07-26 17:05:04 -07:00
char acPorts[4097];
/* send request for master list
*/
2003-03-10 18:08:07 -08:00
sprintf(acPorts, "%s:%s\r\n", pcCmd, pcMach);
2002-10-14 14:03:35 -07:00
SendOut(pcf, acPorts, strlen(acPorts));
2001-07-26 17:05:04 -07:00
/* get the ports number */
2002-10-14 14:03:35 -07:00
if (0 >= ReadReply(pcf, acPorts, sizeof(acPorts), (char *)0)) {
2001-07-26 17:05:04 -07:00
Error("group leader died?");
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
2003-03-10 18:08:07 -08:00
#if PROTOTYPES
2002-10-14 14:03:35 -07:00
CtlMaster(CONSFILE * pcf, char *pcMaster, char *pcMach, char *pcCmd,
char *pcWho)
2002-03-12 01:12:20 -08:00
#else
2002-10-14 14:03:35 -07:00
CtlMaster(pcf, pcMaster, pcMach, pcCmd, pcWho)
CONSFILE *pcf;
2001-07-26 17:05:04 -07:00
char *pcMaster, *pcMach, *pcCmd, *pcWho;
2002-03-12 01:12:20 -08:00
#endif
{
2001-07-26 17:05:04 -07:00
char acPorts[4097];
/* send request for master list
*/
2002-10-14 14:03:35 -07:00
SendOut(pcf, "master\r\n", 8);
2001-07-26 17:05:04 -07:00
/* get the ports number */
2002-10-14 14:03:35 -07:00
if (0 >= ReadReply(pcf, acPorts, sizeof(acPorts), (char *)0)) {
2001-07-26 17:05:04 -07:00
Error("master forward broken");
exit(EX_UNAVAILABLE);
}
/* 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
2003-03-10 18:08:07 -08:00
#if PROTOTYPES
2002-03-12 01:12:20 -08:00
main(int argc, char **argv)
#else
main(argc, argv)
2001-07-26 17:05:04 -07:00
int argc;
char **argv;
2002-03-12 01:12:20 -08:00
#endif
{
2002-10-14 14:03:35 -07:00
char *pcCmd, *pcTo;
2003-01-27 17:48:36 -08:00
struct passwd *pwdMe = (struct passwd *)0;
2001-07-26 17:05:04 -07:00
int opt;
int fLocal;
2003-03-10 18:08:07 -08:00
static STRING *acPorts = (STRING *) 0;
2001-07-26 17:05:04 -07:00
char *pcUser = (char *)0;
char *pcMsg = (char *)0;
int (*pfiCall) ();
2003-03-10 18:08:07 -08:00
static char acOpts[] = "7aAb:B:c:De:EfFGhiIl:M:p:PqQrRsSuvVwWx";
2001-07-26 17:05:04 -07:00
extern int optind;
extern int optopt;
extern char *optarg;
int i;
2003-03-10 18:08:07 -08:00
STRING *tmpString = (STRING *) 0;
isMultiProc = 0; /* make sure stuff DOESN'T have the pid */
2001-07-26 17:05:04 -07:00
2003-03-10 18:08:07 -08:00
if (tmpString == (STRING *) 0)
tmpString = AllocString();
if (acPorts == (STRING *) 0)
acPorts = AllocString();
2001-07-26 17:05:04 -07:00
if ((char *)0 == (progname = strrchr(argv[0], '/'))) {
progname = argv[0];
} else {
++progname;
}
/* command line parsing
*/
pcCmd = (char *)0;
fLocal = 0;
while (EOF != (opt = getopt(argc, argv, acOpts))) {
switch (opt) {
case '7': /* strip high-bit */
fStrip = 1;
break;
2001-07-26 17:05:04 -07:00
case 'A': /* attach with log replay */
fReplay = 1;
/* fall through */
case 'a': /* attach */
pcCmd = "attach";
break;
2003-03-10 18:08:07 -08:00
case 'B': /* broadcast message */
fReplay = 1;
/* fall through */
2001-07-26 17:05:04 -07:00
case 'b':
pcCmd = "broadcast";
pcMsg = optarg;
break;
2002-10-14 14:03:35 -07:00
case 'c':
#if HAVE_OPENSSL
pcCredFile = optarg;
#endif
break;
2001-07-26 17:05:04 -07:00
case 'D':
2002-03-12 01:12:20 -08:00
fDebug++;
2001-07-26 17:05:04 -07:00
break;
2002-10-14 14:03:35 -07:00
case 'E':
#if HAVE_OPENSSL
fReqEncryption = 0;
#endif
break;
2001-07-26 17:05:04 -07:00
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 'G':
fRaw = 1;
if ((char *)0 == pcCmd) {
pcCmd = "spy";
}
2001-07-26 17:05:04 -07:00
break;
2003-01-27 17:48:36 -08:00
case 'I':
fLocal = 1;
/* fall through */
2002-03-12 01:12:20 -08:00
case 'i':
pcCmd = "info";
break;
2001-07-26 17:05:04 -07:00
case 'l':
pcUser = optarg;
break;
2001-07-05 09:18:19 -07:00
2001-07-26 17:05:04 -07:00
case 'M':
pcInMaster = optarg;
break;
case 'p':
pcPort = optarg;
break;
2001-07-05 09:18:19 -07:00
2001-07-26 17:05:04 -07:00
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;
2001-07-05 09:18:19 -07:00
2001-07-26 17:05:04 -07:00
case 'R':
fLocal = 1;
/*fallthrough */
case 'r': /* display daemon version */
pcCmd = "version";
break;
case 'S': /* spy with log replay */
fReplay = 1;
/* fall through */
case 's': /* spy */
pcCmd = "spy";
break;
case 'u':
pcCmd = "users";
break;
2002-03-12 01:12:20 -08:00
case 'W':
fLocal = 1;
/*fallthrough */
2001-07-26 17:05:04 -07:00
case 'w': /* who */
pcCmd = "groups";
break;
case 'x':
pcCmd = "xamine";
break;
case 'v':
fVerbose = 1;
break;
case 'V':
fVersion = 1;
break;
2003-03-10 18:08:07 -08:00
case 'h': /* huh? */
Usage(1);
2001-07-26 17:05:04 -07:00
exit(EX_OK);
2003-03-10 18:08:07 -08:00
case '\?': /* huh? */
Usage(0);
exit(EX_UNAVAILABLE);
default:
Error("option %c needs a parameter", optopt);
exit(EX_UNAVAILABLE);
2001-07-26 17:05:04 -07:00
}
}
if (fVersion) {
Version();
exit(EX_OK);
}
if (pcPort == NULL) {
Error("Severe error: pcPort is NULL???? How can that be?");
exit(EX_UNAVAILABLE);
}
/* Look for non-numeric characters */
for (i = 0; pcPort[i] != '\000'; i++)
if (!isdigit((int)pcPort[i]))
break;
if (pcPort[i] == '\000') {
/* numeric only */
bindPort = atoi(pcPort);
} else {
/* non-numeric only */
struct servent *pSE;
if ((struct servent *)0 == (pSE = getservbyname(pcPort, "tcp"))) {
2003-03-10 18:08:07 -08:00
Error("getservbyname(%s): %s", pcPort, strerror(errno));
2001-07-26 17:05:04 -07:00
exit(EX_UNAVAILABLE);
2001-07-05 09:18:19 -07:00
} else {
2001-07-26 17:05:04 -07:00
bindPort = ntohs((u_short) pSE->s_port);
2001-07-05 09:18:19 -07:00
}
2001-07-26 17:05:04 -07:00
}
2001-07-05 09:18:19 -07:00
2002-10-14 14:03:35 -07:00
if (pcUser == (char *)0 || pcUser[0] == '\000') {
if (((pcUser = getenv("LOGNAME")) == (char *)0) &&
((pcUser = getenv("USER")) == (char *)0) &&
((pwdMe = getpwuid(getuid())) == (struct passwd *)0)) {
Error
("$LOGNAME and $USER do not exist and getpwuid fails: %d: %s",
(int)(getuid()), strerror(errno));
2001-07-26 17:05:04 -07:00
exit(EX_UNAVAILABLE);
2001-07-05 09:18:19 -07:00
}
2002-10-14 14:03:35 -07:00
if (pcUser == (char *)0) {
if (pwdMe->pw_name == (char *)0 || pwdMe->pw_name[0] == '\000') {
Error("Username for uid %d does not exist",
(int)(getuid()));
exit(EX_UNAVAILABLE);
} else {
pcUser = pwdMe->pw_name;
}
}
2001-07-26 17:05:04 -07:00
}
/* finish resolving the command to do, call Gather
*/
if ((char *)0 == pcCmd) {
pcCmd = "attach";
}
if ('a' == *pcCmd || 'f' == *pcCmd || 's' == *pcCmd) {
if (optind >= argc) {
Error("missing console name");
exit(EX_UNAVAILABLE);
}
pcTo = argv[optind++];
2002-03-12 01:12:20 -08:00
} else if ('b' == *pcCmd) {
pcTo = pcMsg;
2001-07-26 17:05:04 -07:00
} else {
pcTo = "*";
}
2002-03-12 01:12:20 -08:00
2001-07-26 17:05:04 -07:00
if (optind < argc) {
Error("extra garbage on command line? (%s...)", argv[optind]);
exit(EX_UNAVAILABLE);
}
2002-03-12 01:12:20 -08:00
2003-03-10 18:08:07 -08:00
cfstdout = FileOpenFD(1, simpleFile);
2002-10-14 14:03:35 -07:00
2003-03-10 18:08:07 -08:00
BuildString((char *)0, acPorts);
BuildStringChar('@', acPorts);
BuildString(pcInMaster, acPorts);
2002-03-12 01:12:20 -08:00
2002-10-14 14:03:35 -07:00
#if HAVE_OPENSSL
2003-03-10 18:08:07 -08:00
SetupSSL(); /* should only do if we want ssl - provide flag! */
2002-10-14 14:03:35 -07:00
#endif
2001-07-26 17:05:04 -07:00
if ('q' == *pcCmd) {
2003-03-10 18:08:07 -08:00
BuildString((char *)0, tmpString);
2001-07-26 17:05:04 -07:00
#if defined(HAVE_GETPASSPHRASE)
2003-03-10 18:08:07 -08:00
BuildString(getpassphrase("Enter root password:"), tmpString);
2001-07-05 09:18:19 -07:00
#else
2003-03-10 18:08:07 -08:00
BuildString(getpass("Enter root password:"), tmpString);
2001-07-05 09:18:19 -07:00
#endif
2001-07-26 17:05:04 -07:00
pfiCall = fLocal ? Ctl : CtlMaster;
2003-03-10 18:08:07 -08:00
if (tmpString->string == (char *)0)
2002-03-12 01:12:20 -08:00
pcTo = "";
else
2003-03-10 18:08:07 -08:00
pcTo = tmpString->string;
2001-07-26 17:05:04 -07:00
} else if ('v' == *pcCmd) {
pfiCall = fLocal ? Ctl : CtlMaster;
} else if ('p' == *pcCmd) {
pfiCall = CtlMaster;
} else if ('a' == *pcCmd || 'f' == *pcCmd || 's' == *pcCmd) {
ValidateEsc();
pfiCall = Indir;
2003-03-10 18:08:07 -08:00
} else if ('g' == *pcCmd || 'i' == *pcCmd || 'b' == *pcCmd) {
2002-03-12 01:12:20 -08:00
pfiCall = fLocal ? CmdGroup : CmdMaster;
2001-07-26 17:05:04 -07:00
} else {
pfiCall = CmdMaster;
}
2003-03-10 18:08:07 -08:00
exit(Gather
(pfiCall, acPorts->string, pcInMaster, pcTo, pcCmd, pcUser));
}