conserver/console/console.c

2335 lines
57 KiB
C
Raw Normal View History

/*
2005-09-05 23:15:33 +00:00
* $Id: console.c,v 5.176 2005/09/05 22:34:39 bryan Exp $
*
2002-01-21 10:58:05 +00:00
* Copyright conserver.com, 2000
2000-12-15 00:32:51 +00:00
*
* Maintainer/Enhancer: Bryan Stansell (bryan@conserver.com)
*
2000-12-15 00:32:51 +00: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.
*/
2003-09-22 20:49:53 +00:00
#include <compat.h>
2001-02-19 06:50:29 +00:00
#include <pwd.h>
2003-09-22 20:49:53 +00:00
#include <getpassword.h>
2003-11-20 14:49:09 +00:00
#include <cutil.h>
2001-02-19 06:50:29 +00:00
#include <version.h>
2004-05-26 00:41:29 +00:00
#include <readconf.h>
2003-04-09 15:30:48 +00:00
#if HAVE_OPENSSL
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <openssl/opensslv.h>
#endif
2004-05-26 00:41:29 +00:00
int fReplay = 0, fVersion = 0;
2004-05-07 16:05:25 +00:00
int showExecData = 1;
int chAttn = -1, chEsc = -1;
2001-07-27 00:05:04 +00:00
unsigned short bindPort;
2002-10-14 21:03:35 +00:00
CONSFILE *cfstdout;
2003-09-22 20:49:53 +00:00
int disconnectCount = 0;
2003-12-02 16:40:59 +00:00
STRING *execCmd = (STRING *)0;
CONSFILE *execCmdFile = (CONSFILE *)0;
pid_t execCmdPid = 0;
2004-03-23 01:14:45 +00:00
CONSFILE *gotoConsole = (CONSFILE *)0;
CONSFILE *prevConsole = (CONSFILE *)0;
char *gotoName = (char *)0;
char *prevName = (char *)0;
2004-05-26 00:41:29 +00:00
CONFIG *optConf = (CONFIG *)0;
CONFIG *config = (CONFIG *)0;
2005-09-05 23:15:33 +00:00
FLAG interact = FLAGFALSE;
2002-10-14 21:03:35 +00:00
#if HAVE_OPENSSL
2003-09-22 20:49:53 +00:00
SSL_CTX *ctx = (SSL_CTX *)0;
2002-10-14 21:03:35 +00:00
void
2003-03-11 02:08:07 +00:00
#if PROTOTYPES
SetupSSL(void)
2002-10-14 21:03:35 +00:00
#else
2003-03-11 02:08:07 +00:00
SetupSSL()
2002-10-14 21:03:35 +00:00
#endif
{
2003-09-22 20:49:53 +00:00
if (ctx == (SSL_CTX *)0) {
2005-09-05 23:15:33 +00:00
char *ciphers;
2002-10-14 21:03:35 +00:00
SSL_load_error_strings();
if (!SSL_library_init()) {
Error("SSL library initialization failed");
2003-09-29 15:50:27 +00:00
Bye(EX_UNAVAILABLE);
2002-10-14 21:03:35 +00:00
}
2003-09-22 20:49:53 +00:00
if ((ctx = SSL_CTX_new(SSLv23_method())) == (SSL_CTX *)0) {
2002-10-14 21:03:35 +00:00
Error("Creating SSL context failed");
2003-09-29 15:50:27 +00:00
Bye(EX_UNAVAILABLE);
2002-10-14 21:03:35 +00:00
}
if (SSL_CTX_set_default_verify_paths(ctx) != 1) {
Error("Could not load SSL default CA file and/or directory");
2003-09-29 15:50:27 +00:00
Bye(EX_UNAVAILABLE);
2002-10-14 21:03:35 +00:00
}
2004-05-26 00:41:29 +00:00
if (config->sslcredentials != (char *)0) {
if (SSL_CTX_use_certificate_chain_file
(ctx, config->sslcredentials) != 1) {
2002-10-14 21:03:35 +00:00
Error("Could not load SSL certificate from '%s'",
2004-05-26 00:41:29 +00:00
config->sslcredentials);
2003-09-29 15:50:27 +00:00
Bye(EX_UNAVAILABLE);
2002-10-14 21:03:35 +00:00
}
if (SSL_CTX_use_PrivateKey_file
2004-05-26 00:41:29 +00:00
(ctx, config->sslcredentials, SSL_FILETYPE_PEM) != 1) {
Error("Could not SSL private key from '%s'",
config->sslcredentials);
2003-09-29 15:50:27 +00:00
Bye(EX_UNAVAILABLE);
2002-10-14 21:03:35 +00:00
}
2005-09-05 23:15:33 +00:00
ciphers = "ALL:!LOW:!EXP:!MD5:!aNULL:@STRENGTH";
} else {
ciphers = "ALL:!LOW:!EXP:!MD5:@STRENGTH";
2002-10-14 21:03:35 +00:00
}
2003-03-11 02:08:07 +00:00
SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, SSLVerifyCallback);
2002-10-14 21:03:35 +00: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);
2005-09-05 23:15:33 +00:00
if (SSL_CTX_set_cipher_list(ctx, ciphers) != 1) {
2002-10-14 21:03:35 +00:00
Error("Setting SSL cipher list failed");
2003-09-29 15:50:27 +00:00
Bye(EX_UNAVAILABLE);
2002-10-14 21:03:35 +00:00
}
}
}
void
2003-03-11 02:08:07 +00:00
#if PROTOTYPES
2003-09-22 20:49:53 +00:00
AttemptSSL(CONSFILE *pcf)
2002-10-14 21:03:35 +00:00
#else
2003-03-11 02:08:07 +00:00
AttemptSSL(pcf)
2002-10-14 21:03:35 +00:00
CONSFILE *pcf;
#endif
{
SSL *ssl;
2003-09-22 20:49:53 +00:00
if (ctx == (SSL_CTX *)0) {
2002-10-14 21:03:35 +00:00
Error("WTF? The SSL context disappeared?!?!?");
2003-09-29 15:50:27 +00:00
Bye(EX_UNAVAILABLE);
2002-10-14 21:03:35 +00:00
}
if (!(ssl = SSL_new(ctx))) {
Error("Couldn't create new SSL context");
2003-09-29 15:50:27 +00:00
Bye(EX_UNAVAILABLE);
2002-10-14 21:03:35 +00:00
}
2003-03-11 02:08:07 +00:00
FileSetSSL(pcf, ssl);
SSL_set_fd(ssl, FileFDNum(pcf));
2003-09-22 20:49:53 +00:00
CONDDEBUG((1, "About to SSL_connect() on fd %d", FileFDNum(pcf)));
2002-10-14 21:03:35 +00:00
if (SSL_connect(ssl) <= 0) {
Error("SSL negotiation failed");
ERR_print_errors_fp(stderr);
2003-09-29 15:50:27 +00:00
Bye(EX_UNAVAILABLE);
2002-10-14 21:03:35 +00:00
}
2003-03-11 02:08:07 +00:00
FileSetType(pcf, SSLSocket);
2003-09-22 20:49:53 +00:00
CONDDEBUG((1, "SSL Connection: %s :: %s", SSL_get_cipher_version(ssl),
SSL_get_cipher_name(ssl)));
2002-10-14 21:03:35 +00:00
}
#endif
/* output a control (or plain) character as a UNIX user would expect it (ksb)
*/
static void
2003-03-11 02:08:07 +00:00
#if PROTOTYPES
2003-09-22 20:49:53 +00:00
PutCtlc(int c, FILE *fp)
2002-03-12 09:12:20 +00:00
#else
2003-03-11 02:08:07 +00:00
PutCtlc(c, fp)
2001-07-27 00:05:04 +00:00
int c;
FILE *fp;
2002-03-12 09:12:20 +00:00
#endif
{
2001-07-27 00:05:04 +00:00
if (0 != (0200 & c)) {
2003-03-11 02:08:07 +00:00
putc('M', fp);
putc('-', fp);
2001-07-27 00:05:04 +00:00
c &= ~0200;
}
if (isprint(c)) {
2003-03-11 02:08:07 +00:00
putc(c, fp);
2001-07-27 00:05:04 +00:00
return;
}
2003-03-11 02:08:07 +00:00
putc('^', fp);
2001-07-27 00:05:04 +00:00
if (c == 0177) {
2003-03-11 02:08:07 +00:00
putc('?', fp);
2001-07-27 00:05:04 +00:00
return;
}
2003-03-11 02:08:07 +00:00
putc(c + 0100, fp);
}
2003-03-11 02:08:07 +00:00
/* output a long message to the user
*/
static void
#if PROTOTYPES
Usage(int wantfull)
2002-10-14 21:03:35 +00:00
#else
2003-03-11 02:08:07 +00:00
Usage(wantfull)
int wantfull;
2002-10-14 21:03:35 +00:00
#endif
2003-03-11 02:08:07 +00:00
{
static char *full[] = {
2004-05-26 00:41:29 +00:00
"7 strip the high bit off 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 21:03:35 +00:00
#if HAVE_OPENSSL
2004-05-26 00:41:29 +00:00
"c cred load an SSL certificate and key from the PEM encoded file",
2002-10-14 21:03:35 +00:00
#else
2004-05-26 00:41:29 +00:00
"c cred ignored - encryption not compiled into code",
2002-10-14 21:03:35 +00:00
#endif
2004-05-26 00:41:29 +00:00
"C config override per-user config file",
"d disconnect [user][@console]",
"D enable debug output, sent to stderr",
"e esc set the initial escape characters",
2003-03-11 02:08:07 +00:00
#if HAVE_OPENSSL
2004-05-26 00:41:29 +00:00
"E don't attempt encrypted connections",
2002-03-12 09:12:20 +00:00
#else
2004-05-26 00:41:29 +00:00
"E ignored - encryption not compiled into code",
2002-03-12 09:12:20 +00:00
#endif
2004-05-26 00:41:29 +00:00
"f(F) force read/write connection (and replay)",
"h output this message",
2005-09-05 23:15:33 +00:00
"i(I) display status info in machine-parseable form (on master)",
2004-05-26 00:41:29 +00:00
"l user use username instead of current username",
"M master master server to poll first",
"n do not read system-wide config file",
"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)",
"t send a text message to [user][@console]",
"u show users on the various consoles",
2004-05-07 16:05:25 +00:00
#if HAVE_OPENSSL
2004-05-26 00:41:29 +00:00
"U allow unencrypted connections if SSL not available",
2004-05-07 16:05:25 +00:00
#else
2004-05-26 00:41:29 +00:00
"U ignored - encryption not compiled into code",
2004-05-07 16:05:25 +00:00
#endif
2004-05-26 00:41:29 +00:00
"v be more verbose",
"V show version information",
"w(W) show who is on which console (on master)",
"x examine ports and baud rates",
2005-09-05 23:15:33 +00:00
"z(Z) cmd send a command to the (master) server (think 'z'ap)",
2003-03-11 02:08:07 +00:00
(char *)0
};
2005-09-05 23:15:33 +00:00
fprintf(stderr, "usage: %s [generic-args] [-aAfFsS] [-e esc] console\n\
%s [generic-args] [-iIuwWx] [console]\n\
%s [generic-args] [-hPqQrRV] [-[bB] message] [-d [user][@console]]\n\
[-t [user][@console] message] [-[zZ] cmd]\n\n\
generic-args: [-7DEnUv] [-c cred] [-C config] [-M master]\n\
[-p port] [-l username]\n", progname, progname, progname);
2003-03-11 02:08:07 +00:00
if (wantfull) {
int i;
2005-09-05 23:15:33 +00:00
fprintf(stderr, "\n");
2003-03-11 02:08:07 +00:00
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-11 02:08:07 +00:00
#if PROTOTYPES
Version()
2002-03-12 09:12:20 +00:00
#else
Version()
#endif
{
2001-07-27 00:05:04 +00:00
int i;
2003-09-22 20:49:53 +00:00
static STRING *acA1 = (STRING *)0;
static STRING *acA2 = (STRING *)0;
2002-10-14 21:03:35 +00:00
char *optionlist[] = {
2003-04-09 15:30:48 +00:00
#if HAVE_DMALLOC
"dmalloc",
#endif
2002-10-14 21:03:35 +00:00
#if USE_LIBWRAP
"libwrap",
#endif
#if HAVE_OPENSSL
"openssl",
#endif
#if HAVE_PAM
"pam",
2004-05-26 00:41:29 +00:00
#endif
#if USE_UNIX_DOMAIN_SOCKETS
"uds",
2002-10-14 21:03:35 +00:00
#endif
(char *)0
};
2001-07-27 00:05:04 +00:00
2003-09-22 20:49:53 +00:00
if (acA1 == (STRING *)0)
2003-03-11 02:08:07 +00:00
acA1 = AllocString();
2003-09-22 20:49:53 +00:00
if (acA2 == (STRING *)0)
2003-03-11 02:08:07 +00:00
acA2 = AllocString();
Msg("%s", THIS_VERSION);
2004-04-13 20:30:28 +00:00
#if USE_UNIX_DOMAIN_SOCKETS
Msg("default socket directory `%s\'", UDSDIR);
#else
2003-09-22 20:49:53 +00:00
Msg("default initial master server `%s\'", MASTERHOST);
2004-04-13 20:30:28 +00:00
Msg("default port referenced as `%s'", DEFPORT);
#endif
2003-03-11 02:08:07 +00:00
Msg("default escape sequence `%s%s\'", FmtCtl(DEFATTN, acA1),
FmtCtl(DEFESC, acA2));
2004-05-26 00:41:29 +00:00
Msg("default site-wide configuration in `%s'", CLIENTCONFIGFILE);
Msg("default per-user configuration in `%s'", "$HOME/.consolerc");
2001-07-27 00:05:04 +00:00
2003-03-11 02:08:07 +00:00
BuildString((char *)0, acA1);
2002-10-14 21:03:35 +00:00
if (optionlist[0] == (char *)0)
2003-03-11 02:08:07 +00:00
BuildString("none", acA1);
2002-10-14 21:03:35 +00:00
for (i = 0; optionlist[i] != (char *)0; i++) {
if (i == 0)
2003-03-11 02:08:07 +00:00
BuildString(optionlist[i], acA1);
2002-10-14 21:03:35 +00:00
else {
2003-03-11 02:08:07 +00:00
BuildString(", ", acA1);
BuildString(optionlist[i], acA1);
2002-10-14 21:03:35 +00:00
}
}
2003-03-11 02:08:07 +00:00
Msg("options: %s", acA1->string);
2003-04-09 15:30:48 +00: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);
2004-05-26 00:41:29 +00:00
#if defined(DMALLOC_VERSION_BETA)
2003-04-09 15:30:48 +00:00
if (DMALLOC_VERSION_BETA != 0) {
BuildString("-b", acA1);
BuildStringChar('0' + DMALLOC_VERSION_BETA, acA1);
}
2004-05-26 00:41:29 +00:00
#endif
2003-04-09 15:30:48 +00:00
Msg("dmalloc version: %s", acA1->string);
#endif
#if HAVE_OPENSSL
Msg("openssl version: %s", OPENSSL_VERSION_TEXT);
#endif
2003-03-11 02:08:07 +00:00
Msg("built with `%s'", CONFIGINVOCATION);
2001-07-27 00:05:04 +00: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-11 02:08:07 +00:00
#if PROTOTYPES
2002-03-12 09:12:20 +00:00
ParseChar(char **ppcSrc, char *pcOut)
#else
ParseChar(ppcSrc, pcOut)
2001-07-27 00:05:04 +00:00
char **ppcSrc, *pcOut;
2002-03-12 09:12:20 +00:00
#endif
{
2001-07-27 00:05:04 +00: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-27 00:05:04 +00:00
return 1;
}
2001-07-27 00:05:04 +00:00
} else {
cvt |= n;
}
2001-07-27 00:05:04 +00:00
if ((char *)0 != pcOut) {
*pcOut = cvt;
}
*ppcSrc = pcScan;
return 0;
}
2001-07-27 00:05:04 +00:00
/*
*/
static void
2003-03-11 02:08:07 +00:00
#if PROTOTYPES
2002-03-12 09:12:20 +00:00
ValidateEsc()
#else
2001-07-27 00:05:04 +00:00
ValidateEsc()
2002-03-12 09:12:20 +00:00
#endif
2001-07-27 00:05:04 +00:00
{
unsigned char c1, c2;
2004-05-26 00:41:29 +00:00
if (config->striphigh != FLAGTRUE)
2001-07-27 00:05:04 +00:00
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");
2003-09-29 15:50:27 +00:00
Bye(EX_UNAVAILABLE);
2001-07-27 00:05:04 +00:00
}
}
/* find the two characters that makeup the users escape sequence (ksb)
*/
static void
2003-03-11 02:08:07 +00:00
#if PROTOTYPES
2002-03-12 09:12:20 +00:00
ParseEsc(char *pcText)
#else
ParseEsc(pcText)
2001-07-27 00:05:04 +00:00
char *pcText;
2002-03-12 09:12:20 +00:00
#endif
{
2001-07-27 00:05:04 +00:00
char *pcTemp;
char c1, c2;
pcTemp = pcText;
if (ParseChar(&pcTemp, &c1) || ParseChar(&pcTemp, &c2)) {
Error("poorly formed escape sequence `%s\'", pcText);
2003-09-29 15:50:27 +00:00
Bye(EX_UNAVAILABLE);
2001-07-27 00:05:04 +00:00
}
if ('\000' != *pcTemp) {
Error("too many characters in new escape sequence at ...`%s\'",
pcTemp);
2003-09-29 15:50:27 +00:00
Bye(EX_UNAVAILABLE);
2001-07-27 00:05:04 +00:00
}
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 21:03:35 +00:00
CONSFILE *
2003-03-11 02:08:07 +00:00
#if PROTOTYPES
2003-09-22 20:49:53 +00:00
GetPort(char *pcToHost, unsigned short sPort)
2002-03-12 09:12:20 +00:00
#else
2003-09-22 20:49:53 +00:00
GetPort(pcToHost, sPort)
2001-07-27 00:05:04 +00:00
char *pcToHost;
unsigned short sPort;
2002-03-12 09:12:20 +00:00
#endif
{
2001-07-27 00:05:04 +00:00
int s;
2004-04-13 20:30:28 +00:00
#if USE_UNIX_DOMAIN_SOCKETS
struct sockaddr_un port;
static STRING *portPath = (STRING *)0;
#else
2001-07-27 00:05:04 +00:00
struct hostent *hp = (struct hostent *)0;
2003-09-22 20:49:53 +00:00
struct sockaddr_in port;
2004-04-13 20:30:28 +00:00
#endif
2001-02-19 06:50:29 +00:00
#if HAVE_MEMSET
2003-09-22 20:49:53 +00:00
memset((void *)(&port), '\000', sizeof(port));
#else
2003-09-22 20:49:53 +00:00
bzero((char *)(&port), sizeof(port));
2001-02-19 06:50:29 +00:00
#endif
2001-07-27 00:05:04 +00:00
2004-04-13 20:30:28 +00:00
#if USE_UNIX_DOMAIN_SOCKETS
if (portPath == (STRING *)0)
portPath = AllocString();
2004-05-26 00:41:29 +00:00
BuildStringPrint(portPath, "%s/%hu", config->master, sPort);
2004-04-13 20:30:28 +00:00
port.sun_family = AF_UNIX;
if (portPath->used > sizeof(port.sun_path)) {
Error("GetPort: path to socket too long: %s", portPath->string);
return (CONSFILE *)0;
}
strcpy(port.sun_path, portPath->string);
CONDDEBUG((1, "GetPort: socket=%s", port.sun_path));
/* 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_UNIX, SOCK_STREAM, 0)) < 0) {
Error("socket(AF_UNIX,SOCK_STREAM): %s", strerror(errno));
return (CONSFILE *)0;
}
if (connect(s, (struct sockaddr *)(&port), sizeof(port)) < 0) {
Error("connect(): %s: %s", port.sun_path, strerror(errno));
return (CONSFILE *)0;
}
2003-09-22 20:49:53 +00:00
#else
2004-04-13 20:30:28 +00:00
# if HAVE_INET_ATON
if (inet_aton(pcToHost, &(port.sin_addr)) == 0)
# else
2003-09-22 20:49:53 +00:00
port.sin_addr.s_addr = inet_addr(pcToHost);
if ((in_addr_t) (-1) == port.sin_addr.s_addr)
2004-04-13 20:30:28 +00:00
# endif
2003-09-22 20:49:53 +00:00
{
2001-02-19 06:50:29 +00:00
if ((struct hostent *)0 != (hp = gethostbyname(pcToHost))) {
2004-04-13 20:30:28 +00:00
# if HAVE_MEMCPY
2003-09-22 20:49:53 +00:00
memcpy((char *)&port.sin_addr.s_addr, (char *)hp->h_addr,
2001-07-27 00:05:04 +00:00
hp->h_length);
2004-04-13 20:30:28 +00:00
# else
2003-09-22 20:49:53 +00:00
bcopy((char *)hp->h_addr, (char *)&port.sin_addr.s_addr,
2003-03-11 02:08:07 +00:00
hp->h_length);
2004-04-13 20:30:28 +00:00
# endif
} else {
2003-03-11 02:08:07 +00:00
Error("gethostbyname(%s): %s", pcToHost, hstrerror(h_errno));
2003-09-22 20:49:53 +00:00
return (CONSFILE *)0;
2001-07-27 00:05:04 +00:00
}
}
2003-09-22 20:49:53 +00:00
port.sin_port = sPort;
port.sin_family = AF_INET;
2001-07-27 00:05:04 +00:00
if (fDebug) {
2003-09-22 20:49:53 +00:00
if ((struct hostent *)0 != hp && (char *)0 != hp->h_name) {
CONDDEBUG((1, "GetPort: hostname=%s (%s), ip=%s, port=%hu",
hp->h_name, pcToHost, inet_ntoa(port.sin_addr),
ntohs(sPort)));
} else {
CONDDEBUG((1,
"GetPort: hostname=<unresolved> (%s), ip=%s, port=%hu",
pcToHost, inet_ntoa(port.sin_addr), ntohs(sPort)));
}
2001-07-27 00:05:04 +00: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-11 02:08:07 +00:00
Error("socket(AF_INET,SOCK_STREAM): %s", strerror(errno));
2003-09-22 20:49:53 +00:00
return (CONSFILE *)0;
2001-07-27 00:05:04 +00:00
}
2004-04-13 20:30:28 +00:00
2003-09-22 20:49:53 +00:00
if (connect(s, (struct sockaddr *)(&port), sizeof(port)) < 0) {
Error("connect(): %hu@%s: %s", ntohs(port.sin_port), pcToHost,
2001-07-27 00:05:04 +00:00
strerror(errno));
2003-09-22 20:49:53 +00:00
return (CONSFILE *)0;
2001-07-27 00:05:04 +00:00
}
2004-04-13 20:30:28 +00:00
#endif
2001-07-27 00:05:04 +00:00
2003-03-11 02:08:07 +00: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;
static struct termios o_tios;
/*
* 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-11 02:08:07 +00:00
#if PROTOTYPES
C2Raw()
2002-03-12 09:12:20 +00:00
#else
2003-03-11 02:08:07 +00:00
C2Raw()
2002-03-12 09:12:20 +00:00
#endif
{
2001-07-27 00:05:04 +00:00
struct termios n_tios;
2001-07-27 00:05:04 +00:00
if (!isatty(0) || 0 != screwy)
return;
2003-03-11 02:08:07 +00:00
if (0 != tcgetattr(0, &o_tios)) {
Error("tcgetattr(0): %s", strerror(errno));
2003-09-29 15:50:27 +00:00
Bye(EX_UNAVAILABLE);
2003-03-11 02:08:07 +00:00
}
2001-07-27 00:05:04 +00: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;
2003-03-11 02:08:07 +00:00
if (0 != tcsetattr(0, TCSANOW, &n_tios)) {
Error("tcsetattr(0, TCSANOW): %s", strerror(errno));
2003-09-29 15:50:27 +00:00
Bye(EX_UNAVAILABLE);
2003-03-11 02:08:07 +00:00
}
2001-07-27 00:05:04 +00:00
screwy = 1;
}
/*
* put the tty back as it was, however that was
*/
static void
2003-03-11 02:08:07 +00:00
#if PROTOTYPES
C2Cooked()
2002-03-12 09:12:20 +00:00
#else
2003-03-11 02:08:07 +00:00
C2Cooked()
2002-03-12 09:12:20 +00:00
#endif
{
2001-07-27 00:05:04 +00:00
if (!screwy)
return;
tcsetattr(0, TCSANOW, &o_tios);
screwy = 0;
}
2003-09-29 15:50:27 +00:00
void
#if PROTOTYPES
DestroyDataStructures(void)
#else
DestroyDataStructures()
#endif
{
C2Cooked();
2004-03-23 01:14:45 +00:00
if (cfstdout != (CONSFILE *)0)
FileUnopen(cfstdout);
2004-05-26 00:41:29 +00:00
DestroyConfig(pConfig);
DestroyConfig(optConf);
DestroyConfig(config);
DestroyTerminal(pTerm);
if (myAddrs != (struct in_addr *)0)
free(myAddrs);
2004-03-23 01:14:45 +00:00
DestroyStrings();
2005-09-05 23:15:33 +00:00
if (substData != (SUBST *)0)
2004-05-26 00:41:29 +00:00
free(substData);
2003-09-29 15:50:27 +00:00
}
2003-09-22 20:49:53 +00:00
char *
2003-03-11 02:08:07 +00:00
#if PROTOTYPES
2003-09-22 20:49:53 +00:00
ReadReply(CONSFILE *fd, int toEOF)
2002-03-12 09:12:20 +00:00
#else
2003-09-22 20:49:53 +00:00
ReadReply(fd)
2002-10-14 21:03:35 +00:00
CONSFILE *fd;
2003-09-22 20:49:53 +00:00
int toEOF;
2002-03-12 09:12:20 +00:00
#endif
{
2001-07-27 00:05:04 +00:00
int nr;
2003-09-22 20:49:53 +00:00
static char buf[1024];
static STRING *result = (STRING *)0;
2001-07-27 00:05:04 +00:00
2003-09-22 20:49:53 +00:00
if (result == (STRING *)0)
result = AllocString();
else
BuildString((char *)0, result);
2003-09-22 20:49:53 +00:00
while (1) {
2004-01-18 17:31:24 +00:00
int l;
2003-09-22 20:49:53 +00:00
switch (nr = FileRead(fd, buf, sizeof(buf))) {
2001-07-27 00:05:04 +00:00
case 0:
/* fall through */
case -1:
2003-09-22 20:49:53 +00:00
if (result->used > 1 || toEOF)
break;
2003-03-11 02:08:07 +00:00
C2Cooked();
2001-07-27 00:05:04 +00:00
Error("lost connection");
2003-09-29 15:50:27 +00:00
Bye(EX_UNAVAILABLE);
2001-07-27 00:05:04 +00:00
default:
2004-01-18 17:31:24 +00:00
while ((l = ParseIACBuf(fd, buf, &nr)) >= 0) {
if (l == 0)
continue;
BuildStringN(buf, l, result);
nr -= l;
MemMove(buf, buf + l, nr);
}
2003-09-22 20:49:53 +00:00
BuildStringN(buf, nr, result);
if (toEOF) /* if toEOF, read until EOF */
continue;
if ((result->used > 1) &&
2003-10-06 01:08:18 +00:00
strchr(result->string, '\n') != (char *)0)
2001-07-27 00:05:04 +00:00
break;
continue;
}
break;
}
if (fDebug) {
2003-09-22 20:49:53 +00:00
static STRING *tmpString = (STRING *)0;
if (tmpString == (STRING *)0)
2003-03-11 02:08:07 +00:00
tmpString = AllocString();
BuildString((char *)0, tmpString);
2003-09-22 20:49:53 +00:00
FmtCtlStr(result->string, result->used - 1, tmpString);
CONDDEBUG((1, "ReadReply: `%s'", tmpString->string));
2001-07-27 00:05:04 +00:00
}
2003-09-22 20:49:53 +00:00
return result->string;
}
2003-12-02 16:40:59 +00:00
static void
#if PROTOTYPES
ReapVirt(void)
#else
ReapVirt()
#endif
{
pid_t pid;
int UWbuf;
while (-1 != (pid = waitpid(-1, &UWbuf, WNOHANG | WUNTRACED))) {
if (0 == pid)
break;
/* stopped child is just continued
*/
if (WIFSTOPPED(UWbuf) && 0 == kill(pid, SIGCONT)) {
Msg("child pid %lu: stopped, sending SIGCONT",
(unsigned long)pid);
continue;
}
if (WIFEXITED(UWbuf))
Verbose("child process %lu: exit(%d)", pid,
WEXITSTATUS(UWbuf));
if (WIFSIGNALED(UWbuf))
Verbose("child process %lu: signal(%d)", pid, WTERMSIG(UWbuf));
if (pid == execCmdPid) {
if (WIFEXITED(UWbuf))
FilePrint(cfstdout, FLAGFALSE,
"[local command terminated - pid %lu: exit(%d)]\r\n",
pid, WEXITSTATUS(UWbuf));
if (WIFSIGNALED(UWbuf))
FilePrint(cfstdout, FLAGFALSE,
"[local command terminated - pid %lu: signal(%d)]\r\n",
pid, WTERMSIG(UWbuf));
}
}
}
static sig_atomic_t fSawReapVirt = 0;
#if HAVE_SIGACTION
static
#endif
RETSIGTYPE
#if PROTOTYPES
FlagReapVirt(int sig)
#else
FlagReapVirt(sig)
int sig;
#endif
{
fSawReapVirt = 1;
#if !HAVE_SIGACTION
SimpleSignal(SIGCHLD, FlagReapVirt);
#endif
}
/* invoke the execcmd command */
void
#if PROTOTYPES
ExecCmd(void)
#else
ExecCmd()
#endif
{
int i;
pid_t iNewGrp;
extern char **environ;
int pin[2];
int pout[2];
static char *apcArgv[] = {
"/bin/sh", "-ce", (char *)0, (char *)0
};
if (execCmd == (STRING *)0 || execCmd->used <= 1)
return;
CONDDEBUG((1, "ExecCmd(): `%s'", execCmd->string));
/* pin[0] = parent read, pin[1] = child write */
if (pipe(pin) != 0) {
Error("ExecCmd(): pipe(): %s", strerror(errno));
return;
}
/* pout[0] = child read, pout[l] = parent write */
if (pipe(pout) != 0) {
close(pin[0]);
close(pin[1]);
Error("ExecCmd(): pipe(): %s", strerror(errno));
return;
}
fflush(stdout);
fflush(stderr);
switch (execCmdPid = fork()) {
case -1:
return;
case 0:
thepid = getpid();
break;
default:
close(pout[0]);
close(pin[1]);
if ((execCmdFile =
FileOpenPipe(pin[0], pout[1])) == (CONSFILE *)0) {
Error("ExecCmd(): FileOpenPipe(%d,%d) failed", pin[0],
pout[1]);
close(pin[0]);
close(pout[1]);
kill(execCmdPid, SIGHUP);
return;
}
FilePrint(cfstdout, FLAGFALSE,
"[local command running - pid %lu]\r\n", execCmdPid);
FD_SET(pin[0], &rinit);
if (maxfd < pin[0] + 1)
maxfd = pin[0] + 1;
fflush(stderr);
return;
}
close(pin[0]);
close(pout[1]);
/* put the signals back that we ignore (trapped auto-reset to default)
*/
SimpleSignal(SIGPIPE, SIG_DFL);
SimpleSignal(SIGCHLD, SIG_DFL);
/* setup new process with clean file descriptors
* stderr still goes to stderr...so user sees it
*/
i = GetMaxFiles();
for ( /* i above */ ; --i > 3;) {
if (i != pout[0] && i != pin[1])
close(i);
}
close(1);
close(0);
# if HAVE_SETSID
iNewGrp = setsid();
if (-1 == iNewGrp) {
Error("ExecCmd(): setsid(): %s", strerror(errno));
2004-01-18 17:31:24 +00:00
iNewGrp = thepid;
2003-12-02 16:40:59 +00:00
}
# else
2004-01-18 17:31:24 +00:00
iNewGrp = thepid;
2003-12-02 16:40:59 +00:00
# endif
if (dup(pout[0]) != 0 || dup(pin[1]) != 1) {
Error("ExecCmd(): fd sync error");
Bye(EX_OSERR);
}
close(pout[0]);
close(pin[1]);
tcsetpgrp(0, iNewGrp);
apcArgv[2] = execCmd->string;
execve(apcArgv[0], apcArgv, environ);
Error("ExecCmd(): execve(%s): %s", apcArgv[2], strerror(errno));
Bye(EX_OSERR);
return;
}
2004-01-18 17:31:24 +00:00
void
#if PROTOTYPES
2004-03-11 17:54:13 +00:00
GetUserInput(STRING *str)
2004-01-18 17:31:24 +00:00
#else
2004-03-11 17:54:13 +00:00
GetUserInput(str)
STRING *str;
2004-01-18 17:31:24 +00:00
#endif
{
2004-03-11 17:54:13 +00:00
char c;
if (str == (STRING *)0)
return;
BuildString((char *)0, str);
2004-01-18 17:31:24 +00:00
for (;;) {
if (read(0, &c, 1) == 0)
break;
if (c == '\n' || c == '\r') {
break;
}
2004-03-11 17:54:13 +00:00
if (c >= ' ' && c <= '~') {
BuildStringChar(c, str);
2004-01-18 17:31:24 +00:00
FileWrite(cfstdout, FLAGFALSE, &c, 1);
2004-03-11 17:54:13 +00:00
} else if ((c == '\b' || c == 0x7f) && str->used > 1) {
FileWrite(cfstdout, FLAGFALSE, "\b \b", 3);
str->string[str->used - 2] = '\000';
str->used--;
} else if ((c == 0x15) && str->used > 1) {
while (str->used > 1) {
2004-01-18 17:31:24 +00:00
FileWrite(cfstdout, FLAGFALSE, "\b \b", 3);
2004-03-11 17:54:13 +00:00
str->string[str->used - 2] = '\000';
str->used--;
2004-01-18 17:31:24 +00:00
}
2004-03-11 17:54:13 +00:00
} else if ((c == 0x17) && str->used > 1) {
while (str->used > 1 &&
isspace((int)(str->string[str->used - 2]))) {
FileWrite(cfstdout, FLAGFALSE, "\b \b", 3);
str->string[str->used - 2] = '\000';
str->used--;
}
while (str->used > 1 &&
!isspace((int)(str->string[str->used - 2]))) {
FileWrite(cfstdout, FLAGFALSE, "\b \b", 3);
str->string[str->used - 2] = '\000';
str->used--;
2004-01-18 17:31:24 +00:00
}
}
}
2004-03-11 17:54:13 +00:00
}
void
#if PROTOTYPES
DoExec(CONSFILE *pcf)
#else
DoExec(pcf)
CONSFILE *pcf;
#endif
{
2004-05-07 16:05:25 +00:00
showExecData = 1;
2004-03-11 17:54:13 +00:00
FileWrite(cfstdout, FLAGFALSE, "exec: ", 6);
GetUserInput(execCmd);
FileWrite(cfstdout, FLAGFALSE, "]\r\n", 3);
2004-01-18 17:31:24 +00:00
if (execCmd != (STRING *)0 && execCmd->used > 1) {
ExecCmd();
BuildString((char *)0, execCmd);
if (execCmdFile == (CONSFILE *)0) { /* exec failed */
/* say forget it */
FileSetQuoteIAC(pcf, FLAGFALSE);
FilePrint(pcf, FLAGFALSE, "%c%c", OB_IAC, OB_ABRT);
FileSetQuoteIAC(pcf, FLAGTRUE);
} else {
char *r;
/* go back to blocking mode */
SetFlags(FileFDNum(pcf), 0, O_NONBLOCK);
/* say we're ready */
FileSetQuoteIAC(pcf, FLAGFALSE);
FilePrint(pcf, FLAGFALSE, "%c%c", OB_IAC, OB_EXEC);
FileSetQuoteIAC(pcf, FLAGTRUE);
r = ReadReply(pcf, 0);
/* now back to non-blocking, now that we've got reply */
SetFlags(FileFDNum(pcf), O_NONBLOCK, 0);
/* if we aren't still r/w, abort */
if (strncmp(r, "[rw]", 4) != 0) {
FileWrite(cfstdout, FLAGFALSE,
"[no longer read-write - aborting command]\r\n",
-1);
FD_CLR(FileFDNum(execCmdFile), &rinit);
FD_CLR(FileFDOutNum(execCmdFile), &winit);
FileClose(&execCmdFile);
FileSetQuoteIAC(pcf, FLAGFALSE);
FilePrint(pcf, FLAGFALSE, "%c%c", OB_IAC, OB_ABRT);
FileSetQuoteIAC(pcf, FLAGTRUE);
kill(execCmdPid, SIGHUP);
}
}
} else {
/* say forget it */
FileSetQuoteIAC(pcf, FLAGFALSE);
FilePrint(pcf, FLAGFALSE, "%c%c", OB_IAC, OB_ABRT);
FileSetQuoteIAC(pcf, FLAGTRUE);
}
}
2004-05-26 00:41:29 +00:00
void
#if PROTOTYPES
ExpandString(char *str, CONSFILE *c)
#else
ExpandString(str, c)
char *str;
CONSFILE *c;
#endif
{
char s;
short backslash = 0;
short cntrl = 0;
char oct = '\000';
short octs = 0;
static STRING *exp = (STRING *)0;
if (str == (char *)0 || c == (CONSFILE *)0)
return;
if (exp == (STRING *)0)
exp = AllocString();
BuildString((char *)0, exp);
backslash = 0;
cntrl = 0;
while ((s = (*str++)) != '\000') {
if (octs > 0 && octs < 3 && s >= '0' && s <= '7') {
++octs;
oct = oct * 8 + (s - '0');
continue;
}
if (octs != 0) {
BuildStringChar(oct, exp);
octs = 0;
oct = '\000';
}
if (backslash) {
backslash = 0;
if (s == 'a')
s = '\a';
else if (s == 'b')
s = '\b';
else if (s == 'f')
s = '\f';
else if (s == 'n')
s = '\n';
else if (s == 'r')
s = '\r';
else if (s == 't')
s = '\t';
else if (s == 'v')
s = '\v';
else if (s == '^')
s = '^';
else if (s >= '0' && s <= '7') {
++octs;
oct = oct * 8 + (s - '0');
continue;
}
BuildStringChar(s, exp);
continue;
}
if (cntrl) {
cntrl = 0;
if (s == '?')
s = 0x7f; /* delete */
else
s = s & 0x1f;
BuildStringChar(s, exp);
continue;
}
if (s == '\\') {
backslash = 1;
continue;
}
if (s == '^') {
cntrl = 1;
continue;
}
BuildStringChar(s, exp);
}
if (octs != 0)
BuildStringChar(oct, exp);
if (backslash)
BuildStringChar('\\', exp);
if (cntrl)
BuildStringChar('^', exp);
if (exp->used > 1)
FileWrite(c, FLAGFALSE, exp->string, exp->used - 1);
}
void
#if PROTOTYPES
PrintSubst(CONSFILE *pcf, char *pcMach, char *string, char *subst)
#else
PrintSubst(pcf, pcMach, string, subst)
CONSFILE *pcf;
char *pcMach;
char *string;
char *subst;
#endif
{
if (string == (char *)0)
return;
if (subst != (char *)0) {
char *str;
if ((str = StrDup(string)) == (char *)0)
OutOfMem();
substData->data = (void *)config;
config->console = pcMach;
ProcessSubst(substData, &str, (char **)0, (char *)0, subst);
ExpandString(str, pcf);
free(str);
} else
ExpandString(string, pcf);
}
2004-03-23 01:14:45 +00:00
void
2003-03-11 02:08:07 +00:00
#if PROTOTYPES
2004-03-23 01:14:45 +00:00
Interact(CONSFILE *pcf, char *pcMach)
2002-03-12 09:12:20 +00:00
#else
2004-03-23 01:14:45 +00:00
Interact(pcf, pcMach)
2002-10-14 21:03:35 +00:00
CONSFILE *pcf;
2004-03-23 01:14:45 +00:00
char *pcMach;
2002-03-12 09:12:20 +00:00
#endif
{
2004-03-23 01:14:45 +00:00
int i;
2001-07-27 00:05:04 +00:00
int nc;
2003-12-02 16:40:59 +00:00
fd_set rmask, wmask;
2004-01-18 17:31:24 +00:00
int justSuspended = 0;
2003-11-16 19:33:39 +00:00
static char acMesg[8192];
2001-07-27 00:05:04 +00:00
2004-03-23 01:14:45 +00:00
/* if this is true, it means we successfully moved to a new console
* so we need to close the old one.
2003-09-22 20:49:53 +00:00
*/
2004-05-26 00:41:29 +00:00
if (prevConsole != (CONSFILE *)0) {
2004-03-23 01:14:45 +00:00
FileClose(&prevConsole);
2004-05-26 00:41:29 +00:00
PrintSubst(cfstdout, prevName, pTerm->detach, pTerm->detachsubst);
}
2004-03-23 01:14:45 +00:00
if (prevName != (char *)0) {
free(prevName);
prevName = (char *)0;
2003-09-22 20:49:53 +00:00
}
2004-03-23 01:14:45 +00:00
/* this is only true in other parts of the code iff pcf == gotoConsole */
if (gotoConsole != (CONSFILE *)0) {
gotoConsole = (CONSFILE *)0;
FilePrint(cfstdout, FLAGFALSE, "[returning to `%s'", pcMach);
FileWrite(pcf, FLAGFALSE, "\n", 1);
2001-07-27 00:05:04 +00:00
}
2004-05-26 00:41:29 +00:00
PrintSubst(cfstdout, pcMach, pTerm->attach, pTerm->attachsubst);
2003-03-11 02:08:07 +00:00
C2Raw();
2001-07-27 00:05:04 +00:00
2003-12-02 16:40:59 +00:00
/* set socket to non-blocking */
SetFlags(FileFDNum(pcf), O_NONBLOCK, 0);
2001-07-27 00:05:04 +00: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-12-02 16:40:59 +00:00
FD_ZERO(&winit);
2003-03-11 02:08:07 +00:00
FD_SET(FileFDNum(pcf), &rinit);
2001-07-27 00:05:04 +00:00
FD_SET(0, &rinit);
2003-09-22 20:49:53 +00:00
if (maxfd < FileFDNum(pcf) + 1)
maxfd = FileFDNum(pcf) + 1;
2001-07-27 00:05:04 +00:00
for (;;) {
2004-01-18 17:31:24 +00:00
justSuspended = 0;
2003-12-02 16:40:59 +00:00
if (fSawReapVirt) {
fSawReapVirt = 0;
ReapVirt();
}
2001-07-27 00:05:04 +00:00
/* reset read mask and select on it
*/
rmask = rinit;
2003-12-02 16:40:59 +00:00
wmask = winit;
2003-09-22 20:49:53 +00:00
if (-1 ==
2003-12-02 16:40:59 +00:00
select(maxfd, &rmask, &wmask, (fd_set *)0,
2003-09-22 20:49:53 +00:00
(struct timeval *)0)) {
if (errno != EINTR) {
Error("Master(): select(): %s", strerror(errno));
break;
2001-07-27 00:05:04 +00:00
}
2003-09-22 20:49:53 +00:00
continue;
2001-07-27 00:05:04 +00:00
}
2003-12-02 16:40:59 +00:00
/* anything from execCmd */
if (execCmdFile != (CONSFILE *)0) {
if (FileCanRead(execCmdFile, &rmask, &wmask)) {
if ((nc =
FileRead(execCmdFile, acMesg, sizeof(acMesg))) < 0) {
FD_CLR(FileFDNum(execCmdFile), &rinit);
FD_CLR(FileFDOutNum(execCmdFile), &winit);
FileClose(&execCmdFile);
2004-01-18 17:31:24 +00:00
FileSetQuoteIAC(pcf, FLAGFALSE);
FilePrint(pcf, FLAGFALSE, "%c%c", OB_IAC, OB_ABRT);
FileSetQuoteIAC(pcf, FLAGTRUE);
2003-12-02 16:40:59 +00:00
} else {
2004-05-26 00:41:29 +00:00
if (config->striphigh == FLAGTRUE) {
2003-12-02 16:40:59 +00:00
for (i = 0; i < nc; ++i)
acMesg[i] &= 127;
}
FileWrite(pcf, FLAGFALSE, acMesg, nc);
}
} else if (!FileBufEmpty(execCmdFile) &&
FileCanWrite(execCmdFile, &rmask, &wmask)) {
2004-03-23 01:14:45 +00:00
CONDDEBUG((1, "Interact(): flushing fd %d",
2003-12-02 16:40:59 +00:00
FileFDNum(execCmdFile)));
if (FileWrite(execCmdFile, FLAGFALSE, (char *)0, 0) < 0) {
/* -bryan */
break;
}
}
}
2001-07-27 00:05:04 +00:00
/* anything from socket? */
2003-12-02 16:40:59 +00:00
if (FileCanRead(pcf, &rmask, &wmask)) {
2004-01-18 17:31:24 +00:00
int l;
2003-09-22 20:49:53 +00:00
if ((nc = FileRead(pcf, acMesg, sizeof(acMesg))) < 0) {
/* if we got an error/eof after returning from suspend */
2004-01-18 17:31:24 +00:00
if (justSuspended) {
2003-09-22 20:49:53 +00:00
fprintf(stderr, "\n");
2002-03-12 09:12:20 +00:00
Error("lost connection");
}
2001-07-27 00:05:04 +00:00
break;
}
2004-01-18 17:31:24 +00:00
while ((l = ParseIACBuf(pcf, acMesg, &nc)) >= 0) {
if (l == 0) {
2004-03-11 17:54:13 +00:00
if (execCmdFile == (CONSFILE *)0) {
if (FileSawQuoteExec(pcf) == FLAGTRUE)
DoExec(pcf);
else if (FileSawQuoteSusp(pcf) == FLAGTRUE) {
justSuspended = 1;
2004-01-18 17:31:24 +00:00
#if defined(SIGSTOP)
2004-03-11 17:54:13 +00:00
FileWrite(cfstdout, FLAGFALSE, "stop]", 5);
C2Cooked();
kill(thepid, SIGSTOP);
C2Raw();
FileWrite(cfstdout, FLAGFALSE,
"[press any character to continue",
32);
2004-01-18 17:31:24 +00:00
#else
2004-03-11 17:54:13 +00:00
FileWrite(cfstdout, FLAGFALSE,
"stop not supported -- press any character to continue",
53);
2004-01-18 17:31:24 +00:00
#endif
2004-03-11 17:54:13 +00:00
} else if (FileSawQuoteGoto(pcf) == FLAGTRUE) {
2004-03-23 01:14:45 +00:00
gotoConsole = pcf;
if (gotoName != (char *)0)
free(gotoName);
if ((gotoName = StrDup(pcMach)) == (char *)0)
OutOfMem();
C2Cooked();
return;
2004-03-11 17:54:13 +00:00
}
} else {
if (FileSawQuoteAbrt(pcf) == FLAGTRUE) {
FD_CLR(FileFDNum(execCmdFile), &rinit);
FD_CLR(FileFDOutNum(execCmdFile), &winit);
FileClose(&execCmdFile);
kill(execCmdPid, SIGHUP);
}
2004-01-18 17:31:24 +00:00
}
continue;
}
2004-05-26 00:41:29 +00:00
if (config->striphigh == FLAGTRUE) {
2004-01-18 17:31:24 +00:00
for (i = 0; i < l; ++i)
acMesg[i] &= 127;
}
if (execCmdFile != (CONSFILE *)0) {
FileWrite(execCmdFile, FLAGFALSE, acMesg, l);
2004-05-07 16:05:25 +00:00
if (showExecData)
FileWrite(cfstdout, FLAGFALSE, acMesg, l);
} else
FileWrite(cfstdout, FLAGFALSE, acMesg, l);
2004-01-18 17:31:24 +00:00
nc -= l;
MemMove(acMesg, acMesg + l, nc);
2003-12-02 16:40:59 +00:00
}
} else if (!FileBufEmpty(pcf) && FileCanWrite(pcf, &rmask, &wmask)) {
2004-03-23 01:14:45 +00:00
CONDDEBUG((1, "Interact(): flushing fd %d", FileFDNum(pcf)));
2003-12-02 16:40:59 +00:00
if (FileWrite(pcf, FLAGFALSE, (char *)0, 0) < 0) {
/* -bryan */
break;
}
2001-07-27 00:05:04 +00:00
}
2002-03-12 09:12:20 +00:00
2001-07-27 00:05:04 +00:00
/* anything from stdin? */
if (FD_ISSET(0, &rmask)) {
2003-09-22 20:49:53 +00:00
if ((nc = read(0, acMesg, sizeof(acMesg))) == 0) {
if (screwy)
break;
else {
FD_SET(0, &rinit);
continue;
}
}
2003-12-02 16:40:59 +00:00
if (execCmdFile == (CONSFILE *)0) {
2004-05-26 00:41:29 +00:00
if (config->striphigh == FLAGTRUE) {
2003-12-02 16:40:59 +00:00
for (i = 0; i < nc; ++i)
acMesg[i] &= 127;
}
FileWrite(pcf, FLAGFALSE, acMesg, nc);
} else {
for (i = 0; i < nc; ++i) {
if (acMesg[i] == '\n' || acMesg[i] == '\r')
FilePrint(cfstdout, FLAGFALSE,
"[local command running - pid %lu]\r\n",
execCmdPid);
else if (acMesg[i] == 0x03) { /* ctrl-c */
kill(execCmdPid, SIGHUP);
FilePrint(cfstdout, FLAGFALSE,
"[local command sent SIGHUP - pid %lu]\r\n",
execCmdPid);
} else if (acMesg[i] == 0x1c) { /* ctrl-\ */
kill(execCmdPid, SIGKILL);
FilePrint(cfstdout, FLAGFALSE,
"[local command sent SIGKILL - pid %lu]\r\n",
execCmdPid);
2004-05-07 16:05:25 +00:00
} else if (acMesg[i] == 'o' || acMesg[i] == 'O') {
showExecData = !showExecData;
FilePrint(cfstdout, FLAGFALSE,
"[local command data %s]\r\n",
showExecData ? "on" : "off");
2003-12-02 16:40:59 +00:00
}
}
2001-07-27 00:05:04 +00:00
}
}
}
2004-05-26 00:41:29 +00:00
2003-03-11 02:08:07 +00:00
C2Cooked();
2004-05-26 00:41:29 +00:00
PrintSubst(cfstdout, pcMach, pTerm->detach, pTerm->detachsubst);
2001-07-27 00:05:04 +00:00
if (fVerbose)
printf("Console %s closed.\n", pcMach);
2004-03-23 01:14:45 +00:00
}
/* interact with a group server (ksb)
*/
void
#if PROTOTYPES
CallUp(CONSFILE *pcf, char *pcMaster, char *pcMach, char *pcHow,
char *result)
#else
CallUp(pcf, pcMaster, pcMach, pcHow, result)
CONSFILE *pcf;
char *pcMaster, *pcMach, *pcHow, *result;
#endif
{
int fIn = '-';
char *r = (char *)0;
if (fVerbose) {
Msg("%s to %s (on %s)", pcHow, pcMach, pcMaster);
}
#if !defined(__CYGWIN__)
# if defined(F_SETOWN)
if (fcntl(FileFDNum(pcf), F_SETOWN, thepid) == -1) {
Error("fcntl(F_SETOWN,%lu): %d: %s", (unsigned long)thepid,
FileFDNum(pcf), strerror(errno));
}
# else
# if defined(SIOCSPGRP)
{
int iTemp;
/* on the HP-UX systems if different
*/
iTemp = -thepid;
if (ioctl(FileFDNum(pcf), SIOCSPGRP, &iTemp) == -1) {
Error("ioctl(%d,SIOCSPGRP): %s", FileFDNum(pcf),
strerror(errno));
}
}
# endif
# endif
#endif
SimpleSignal(SIGCHLD, FlagReapVirt);
/* 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).
*/
/* how did we do, did we get a read-only or read-write?
*/
if (0 == strcmp(result, "[attached]\r\n")) {
/* OK -- we are good as gold */
fIn = 'a';
} else if (0 == strcmp(result, "[spy]\r\n") ||
0 == strcmp(result, "[ok]\r\n") ||
0 == strcmp(result, "[read-only -- initializing]\r\n")) {
/* Humph, someone else is on
* or we have an old version of the server (4.X)
*/
fIn = 's';
} else if (0 == strcmp(result, "[console is read-only]\r\n")) {
fIn = 'r';
} else if (0 == strcmp(result, "[line to console is down]\r\n")) {
/* ouch, the machine is down on the server */
fIn = '-';
Error("%s is down", pcMach);
if (fVerbose) {
printf("[use `");
PutCtlc(chAttn, stdout);
PutCtlc(chEsc, stdout);
printf("o\' to open console line]\n");
}
} else {
FilePrint(cfstdout, FLAGFALSE, "%s: %s", pcMach, result);
Bye(EX_UNAVAILABLE);
}
/* 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, assume OK
* (we'll find out soon enough)
*/
FilePrint(pcf, FLAGFALSE, "%c%ce%c%c", DEFATTN, DEFESC, chAttn,
chEsc);
r = ReadReply(pcf, 0);
if (strncmp(r, "[redef:", 7) != 0) {
Error("protocol botch on redef of escape sequence");
Bye(EX_UNAVAILABLE);
}
}
2004-11-09 09:34:27 +00:00
/* try to grok the state of the console */
FilePrint(pcf, FLAGFALSE, "%c%c=", chAttn, chEsc);
r = ReadReply(pcf, 0);
2005-09-05 23:15:33 +00:00
if (strncmp(r, "[unknown", 8) != 0 && strncmp(r, "[up]", 4) != 0)
2004-11-09 09:34:27 +00:00
FileWrite(cfstdout, FLAGFALSE, r, -1);
2004-03-23 01:14:45 +00:00
printf("[Enter `");
PutCtlc(chAttn, stdout);
PutCtlc(chEsc, stdout);
printf("?\' for help]\n");
/* try and display the MOTD */
FilePrint(pcf, FLAGFALSE, "%c%cm", chAttn, chEsc);
r = ReadReply(pcf, 0);
if (strncmp(r, "[unknown", 8) != 0 &&
strncmp(r, "[-- MOTD --]", 12) != 0)
FileWrite(cfstdout, FLAGFALSE, r, -1);
FilePrint(pcf, FLAGFALSE, "%c%c;", chAttn, chEsc);
r = ReadReply(pcf, 0);
if (strncmp(r, "[unknown", 8) != 0 &&
strncmp(r, "[connected]", 11) != 0)
FileWrite(cfstdout, FLAGFALSE, r, -1);
/* if the host is not down, finish the connection, and force
* the correct attachment for the user
*/
if (fIn != '-') {
if (fIn == 'r') {
if (*pcHow != 's') {
Error("%s is read-only", pcMach);
}
} else if (fIn != (*pcHow == 'f' ? 'a' : *pcHow)) {
FilePrint(pcf, FLAGFALSE, "%c%c%c", chAttn, chEsc, *pcHow);
}
if (fReplay) {
FilePrint(pcf, FLAGFALSE, "%c%cr", chAttn, chEsc);
} else if (fVerbose) {
FilePrint(pcf, FLAGFALSE, "%c%c\022", chAttn, chEsc);
}
}
fflush(stdout);
fflush(stderr);
Interact(pcf, pcMach);
}
2003-09-22 20:49:53 +00:00
/* shouldn't need more than 3 levels of commands (but alloc 4 just 'cause)
* worst case so far: master, groups, broadcast
* (cmdarg == broadcast msg)
*/
2003-09-22 20:49:53 +00:00
char *cmds[4] = { (char *)0, (char *)0, (char *)0, (char *)0 };
char *cmdarg = (char *)0;
2003-09-22 20:49:53 +00:00
/* 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
*/
2003-09-22 20:49:53 +00:00
int
2003-03-11 02:08:07 +00:00
#if PROTOTYPES
2004-03-23 01:14:45 +00:00
DoCmds(char *master, char *pports, int cmdi)
2002-03-12 09:12:20 +00:00
#else
2004-03-23 01:14:45 +00:00
DoCmds(master, pports, cmdi)
2003-09-22 20:49:53 +00:00
char *master;
2004-03-23 01:14:45 +00:00
char *pports;
2003-09-22 20:49:53 +00:00
int cmdi;
2002-03-12 09:12:20 +00:00
#endif
{
2003-09-22 20:49:53 +00:00
CONSFILE *pcf;
char *t;
char *next;
char *server;
unsigned short port;
char *result = (char *)0;
int len;
2004-03-23 01:14:45 +00:00
char *ports;
char *pcopy;
2004-04-13 20:30:28 +00:00
char *serverName;
2004-03-23 01:14:45 +00:00
if ((pcopy = ports = StrDup(pports)) == (char *)0)
OutOfMem();
2003-09-22 20:49:53 +00:00
len = strlen(ports);
while (len > 0 && (ports[len - 1] == '\r' || ports[len - 1] == '\n'))
len--;
ports[len] = '\000';
for ( /* param */ ; *ports != '\000'; ports = next) {
if ((next = strchr(ports, ':')) == (char *)0)
next = "";
else
*next++ = '\000';
if ((server = strchr(ports, '@')) != (char *)0) {
*server++ = '\000';
if (*server == '\000')
server = master;
} else
server = master;
2004-04-13 20:30:28 +00:00
#if USE_UNIX_DOMAIN_SOCKETS
serverName = "localhost";
#else
serverName = server;
#endif
2003-09-22 20:49:53 +00:00
if (*ports == '\000') {
2004-04-13 20:30:28 +00:00
#if USE_UNIX_DOMAIN_SOCKETS
port = 0;
#else
2003-09-22 20:49:53 +00:00
port = htons(bindPort);
2004-04-13 20:30:28 +00:00
#endif
2003-09-22 20:49:53 +00:00
} else if (!isdigit((int)(ports[0]))) {
2004-04-13 20:30:28 +00:00
Error("invalid port spec for %s: `%s'", serverName, ports);
2003-09-22 20:49:53 +00:00
continue;
} else {
2004-04-13 20:30:28 +00:00
#if USE_UNIX_DOMAIN_SOCKETS
port = (short)atoi(ports);
#else
2003-09-22 20:49:53 +00:00
port = htons((short)atoi(ports));
2004-04-13 20:30:28 +00:00
#endif
2001-07-27 00:05:04 +00:00
}
2003-09-22 20:49:53 +00:00
attemptLogin:
if ((pcf = GetPort(server, port)) == (CONSFILE *)0)
continue;
2001-07-27 00:05:04 +00:00
2004-01-18 17:31:24 +00:00
FileSetQuoteIAC(pcf, FLAGTRUE);
2003-09-22 20:49:53 +00:00
t = ReadReply(pcf, 0);
if (strcmp(t, "ok\r\n") != 0) {
FileClose(&pcf);
2004-04-13 20:30:28 +00:00
FilePrint(cfstdout, FLAGFALSE, "%s: %s", serverName, t);
2001-07-27 00:05:04 +00:00
continue;
}
2003-09-22 20:49:53 +00:00
#if HAVE_OPENSSL
2004-05-26 00:41:29 +00:00
if (config->sslenabled == FLAGTRUE) {
2004-01-18 17:31:24 +00:00
FileWrite(pcf, FLAGFALSE, "ssl\r\n", 5);
t = ReadReply(pcf, 0);
if (strcmp(t, "ok\r\n") == 0) {
AttemptSSL(pcf);
2004-05-07 16:05:25 +00:00
if (FileGetType(pcf) != SSLSocket) {
Error("Encryption not supported by server `%s'",
serverName);
FileClose(&pcf);
continue;
}
2004-05-26 00:41:29 +00:00
} else if (config->sslrequired == FLAGTRUE) {
2004-04-13 20:30:28 +00:00
Error("Encryption not supported by server `%s'",
serverName);
2004-01-18 17:31:24 +00:00
FileClose(&pcf);
continue;
}
2001-07-27 00:05:04 +00:00
}
2003-09-22 20:49:53 +00:00
#endif
2004-05-26 00:41:29 +00:00
FilePrint(pcf, FLAGFALSE, "login %s\r\n", config->username);
2003-09-22 20:49:53 +00:00
t = ReadReply(pcf, 0);
2003-09-29 15:50:27 +00:00
if (strncmp(t, "passwd?", 7) == 0) {
2003-09-22 20:49:53 +00:00
static int count = 0;
static STRING *tmpString = (STRING *)0;
2003-09-29 15:50:27 +00:00
char *hostname = (char *)0;
if (t[7] == ' ') {
hostname = PruneSpace(t + 7);
if (*hostname == '\000')
2004-04-13 20:30:28 +00:00
hostname = serverName;
2003-09-29 15:50:27 +00:00
} else
2004-04-13 20:30:28 +00:00
hostname = serverName;
2003-09-22 20:49:53 +00:00
if (tmpString == (STRING *)0)
tmpString = AllocString();
if (tmpString->used <= 1) {
char *pass;
2003-11-16 19:33:39 +00:00
BuildStringPrint(tmpString, "Enter %s@%s's password: ",
2004-05-26 00:41:29 +00:00
config->username, hostname);
2003-11-16 19:33:39 +00:00
pass = GetPassword(tmpString->string);
2003-09-22 20:49:53 +00:00
if (pass == (char *)0) {
Error("could not get password from tty for `%s'",
2004-04-13 20:30:28 +00:00
serverName);
2003-09-22 20:49:53 +00:00
FileClose(&pcf);
2001-07-27 00:05:04 +00:00
continue;
2003-09-22 20:49:53 +00:00
}
2003-11-16 19:33:39 +00:00
BuildString((char *)0, tmpString);
2003-09-22 20:49:53 +00:00
BuildString(pass, tmpString);
BuildString("\r\n", tmpString);
}
2003-10-06 01:08:18 +00:00
FileWrite(pcf, FLAGFALSE, tmpString->string,
tmpString->used - 1);
2003-09-22 20:49:53 +00:00
t = ReadReply(pcf, 0);
if (strcmp(t, "ok\r\n") != 0) {
2004-04-13 20:30:28 +00:00
FilePrint(cfstdout, FLAGFALSE, "%s: %s", serverName, t);
2003-09-22 20:49:53 +00:00
if (++count < 3) {
BuildString((char *)0, tmpString);
goto attemptLogin;
}
2004-04-13 20:30:28 +00:00
Error("too many bad passwords for `%s'", serverName);
2003-09-22 20:49:53 +00:00
count = 0;
FileClose(&pcf);
2001-07-27 00:05:04 +00:00
continue;
2003-09-22 20:49:53 +00:00
} else
count = 0;
2004-01-18 17:31:24 +00:00
} else if (strcmp(t, "ok\r\n") != 0) {
FileClose(&pcf);
2004-04-13 20:30:28 +00:00
FilePrint(cfstdout, FLAGFALSE, "%s: %s", serverName, t);
2004-01-18 17:31:24 +00:00
continue;
2001-07-27 00:05:04 +00:00
}
2003-09-22 20:49:53 +00:00
/* now that we're logged in, we can do something */
/* if we're on the last cmd or the command is 'call' and we
* have an arg (always true if it's 'call'), then send the arg
*/
if ((cmdi == 0 || cmds[cmdi][0] == 'c') && cmdarg != (char *)0)
2003-10-06 01:08:18 +00:00
FilePrint(pcf, FLAGFALSE, "%s %s\r\n", cmds[cmdi], cmdarg);
2003-09-22 20:49:53 +00:00
else
2003-10-06 01:08:18 +00:00
FilePrint(pcf, FLAGFALSE, "%s\r\n", cmds[cmdi]);
2003-09-22 20:49:53 +00:00
/* if we haven't gone down the stack, do "normal" stuff.
* if we did hit the bottom, we send the exit\r\n now so
* that the ReadReply can stop once the socket closes.
*/
if (cmdi != 0) {
t = ReadReply(pcf, 0);
/* save the result */
2004-03-23 01:14:45 +00:00
if (result != (char *)0)
free(result);
2003-10-06 01:08:18 +00:00
if ((result = StrDup(t)) == (char *)0)
2003-09-22 20:49:53 +00:00
OutOfMem();
}
2003-09-22 20:49:53 +00:00
/* if we're working on finding a console */
if (cmds[cmdi][0] == 'c') {
2004-09-21 23:48:49 +00:00
static int limit = 0;
2003-09-22 20:49:53 +00:00
/* did we get a redirect? */
if (result[0] == '@' || (result[0] >= '0' && result[0] <= '9')) {
if (limit++ > 10) {
Error("forwarding level too deep!");
2003-09-29 15:50:27 +00:00
Bye(EX_SOFTWARE);
2003-09-22 20:49:53 +00:00
}
2005-09-05 23:15:33 +00:00
FileWrite(pcf, FLAGFALSE, "exit\r\n", 6);
t = ReadReply(pcf, 1);
2003-09-22 20:49:53 +00:00
} else {
2005-09-05 23:15:33 +00:00
/* if we're not trying to connect to a console */
if (interact == FLAGFALSE) {
FilePrint(cfstdout, FLAGFALSE, "%s: %s", serverName,
result);
2004-03-23 01:14:45 +00:00
FileClose(&pcf);
2005-09-05 23:15:33 +00:00
continue;
}
if (result[0] != '[') { /* did we not get a connection? */
limit = 0;
FilePrint(cfstdout, FLAGFALSE, "%s: %s", serverName,
result);
FileClose(&pcf);
continue;
} else {
limit = 0;
CallUp(pcf, server, cmdarg, cmds[0], result);
if (pcf != gotoConsole)
FileClose(&pcf);
break;
}
2003-09-22 20:49:53 +00:00
}
} else if (cmds[cmdi][0] == 'q') {
2005-09-05 23:15:33 +00:00
if (cmdi == 0) {
t = ReadReply(pcf, 0);
FilePrint(cfstdout, FLAGFALSE, "%s: %s", serverName, t);
} else {
FilePrint(cfstdout, FLAGFALSE, "%s: %s", serverName,
result);
}
/* only say 'exit' if 'quit' failed...since it's dying anyway */
2003-09-22 20:49:53 +00:00
if (t[0] != 'o' || t[1] != 'k') {
2003-10-06 01:08:18 +00:00
FileWrite(pcf, FLAGFALSE, "exit\r\n", 6);
2003-09-22 20:49:53 +00:00
t = ReadReply(pcf, 1);
}
} else {
/* all done */
2005-09-05 23:15:33 +00:00
/* ok, this is whacky. if cmdi==0, we haven't read back the
* reply yet, so 't' is going to have multiple lines out output
* since we send the 'exit' command...first line (or set of
* lines) would be the previous command, and then a 'goodbye'
* (ideally). we monkey around below because of this.
* like i said. wacky.
*/
2003-10-06 01:08:18 +00:00
FileWrite(pcf, FLAGFALSE, "exit\r\n", 6);
2003-09-22 20:49:53 +00:00
t = ReadReply(pcf, cmdi == 0 ? 1 : 0);
if (cmdi == 0) {
int len;
/* if we hit bottom, this is where we get our results */
2004-03-23 01:14:45 +00:00
if (result != (char *)0)
free(result);
2003-10-06 01:08:18 +00:00
if ((result = StrDup(t)) == (char *)0)
2003-09-22 20:49:53 +00:00
OutOfMem();
2005-09-05 23:15:33 +00:00
/* strip off the goodbye from the tail of the result */
2003-09-22 20:49:53 +00:00
len = strlen(result);
if (len > 8 &&
strcmp("goodbye\r\n", result + len - 9) == 0) {
len -= 9;
*(result + len) = '\000';
}
/* if (not 'broadcast' and not 'textmsg') or
* result doesn't start with 'ok' (only checks this if
* it's a 'broadcast' or 'textmsg')
*/
if (cmds[0][0] == 'd') {
if (result[0] != 'o' || result[1] != 'k') {
2004-04-13 20:30:28 +00:00
FileWrite(cfstdout, FLAGTRUE, serverName, -1);
2003-10-06 01:08:18 +00:00
FileWrite(cfstdout, FLAGTRUE, ": ", 2);
FileWrite(cfstdout, FLAGFALSE, result, len);
2003-09-22 20:49:53 +00:00
} else {
disconnectCount += atoi(result + 19);
}
} else if ((cmds[0][0] != 'b' && cmds[0][0] != 't') ||
(result[0] != 'o' || result[1] != 'k')) {
2005-09-05 23:15:33 +00:00
/* did a 'master' before this or doing a 'disconnect',
* 'reconfig', 'newlogs', or 'up'
*/
2004-04-13 20:30:28 +00:00
if ((cmds[1] != (char *)0 && cmds[1][0] == 'm') ||
2005-09-05 23:15:33 +00:00
cmds[0][0] == 'd' || cmds[0][0] == 'r' ||
cmds[0][0] == 'n' || cmds[0][0] == 'u') {
2004-04-13 20:30:28 +00:00
FileWrite(cfstdout, FLAGTRUE, serverName, -1);
2003-10-06 01:08:18 +00:00
FileWrite(cfstdout, FLAGTRUE, ": ", 2);
2003-09-22 20:49:53 +00:00
}
2003-10-06 01:08:18 +00:00
FileWrite(cfstdout, FLAGFALSE, result, len);
2003-09-22 20:49:53 +00:00
}
}
}
2001-07-27 00:05:04 +00:00
2003-09-22 20:49:53 +00:00
FileClose(&pcf);
2001-07-27 00:05:04 +00:00
2003-09-22 20:49:53 +00:00
/* this would only be true if we got extra redirects (@... above) */
2005-09-05 23:15:33 +00:00
if (cmds[cmdi][0] == 'c' && interact == FLAGTRUE)
2003-09-22 20:49:53 +00:00
DoCmds(server, result, cmdi);
else if (cmdi > 0)
DoCmds(server, result, cmdi - 1);
2004-03-23 01:14:45 +00:00
if (result != (char *)0)
free(result);
result = (char *)0;
2001-07-27 00:05:04 +00:00
}
2004-03-23 01:14:45 +00:00
if (result != (char *)0)
free(result);
free(pcopy);
2001-07-27 00:05:04 +00:00
return 0;
}
/* 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-11 02:08:07 +00:00
#if PROTOTYPES
2002-03-12 09:12:20 +00:00
main(int argc, char **argv)
#else
main(argc, argv)
2001-07-27 00:05:04 +00:00
int argc;
char **argv;
2002-03-12 09:12:20 +00:00
#endif
{
2003-09-22 20:49:53 +00:00
char *pcCmd;
2003-01-28 01:48:36 +00:00
struct passwd *pwdMe = (struct passwd *)0;
2001-07-27 00:05:04 +00:00
int opt;
int fLocal;
2003-09-22 20:49:53 +00:00
static STRING *acPorts = (STRING *)0;
2005-09-05 23:15:33 +00:00
static char acOpts[] =
"7aAb:B:c:C:d:De:EfFhiIl:M:np:PqQrRsSt:uUvVwWxz:Z:";
2001-07-27 00:05:04 +00:00
extern int optind;
extern int optopt;
extern char *optarg;
2004-03-11 17:54:13 +00:00
static STRING *textMsg = (STRING *)0;
2003-09-22 20:49:53 +00:00
int cmdi;
2004-03-11 17:54:13 +00:00
static STRING *consoleName = (STRING *)0;
2004-05-26 00:41:29 +00:00
short readSystemConf = 1;
char *userConf = (char *)0;
2005-09-05 23:15:33 +00:00
typedef struct zaps {
char *opt;
char *cmd;
char *desc;
} ZAPS;
ZAPS zap[] = {
{"bringup, SIGUSR1", "up", "bring up any consoles that are down"},
{"help", (char *)0, "this help message"},
{"pid", "pid", "display master process ids"},
{"quit, SIGTERM", "quit", "terminate the server"},
{"reconfig, SIGHUP", "reconfig",
"reread configuration file, then do 'reopen' actions"},
{"reopen, SIGUSR2", "newlogs",
"reopen all logfiles, then do 'bringup' actions"},
{"version", "version", "display version information"}
};
int isZap = 0;
2003-03-11 02:08:07 +00:00
isMultiProc = 0; /* make sure stuff DOESN'T have the pid */
2001-07-27 00:05:04 +00:00
2004-01-18 17:31:24 +00:00
thepid = getpid();
2003-09-22 20:49:53 +00:00
if (textMsg == (STRING *)0)
textMsg = AllocString();
if (acPorts == (STRING *)0)
2003-03-11 02:08:07 +00:00
acPorts = AllocString();
2001-07-27 00:05:04 +00:00
if ((char *)0 == (progname = strrchr(argv[0], '/'))) {
progname = argv[0];
} else {
++progname;
}
2004-05-26 00:41:29 +00:00
/* prep the config options */
if ((optConf = (CONFIG *)calloc(1, sizeof(CONFIG))) == (CONFIG *)0)
OutOfMem();
if ((config = (CONFIG *)calloc(1, sizeof(CONFIG))) == (CONFIG *)0)
OutOfMem();
if ((pConfig = (CONFIG *)calloc(1, sizeof(CONFIG))) == (CONFIG *)0)
OutOfMem();
/* and the terminal options */
if ((pTerm = (TERM *)calloc(1, sizeof(TERM))) == (TERM *)0)
OutOfMem();
2001-07-27 00:05:04 +00:00
/* command line parsing
*/
pcCmd = (char *)0;
fLocal = 0;
2003-09-22 20:49:53 +00:00
while ((opt = getopt(argc, argv, acOpts)) != EOF) {
2001-07-27 00:05:04 +00:00
switch (opt) {
case '7': /* strip high-bit */
2004-05-26 00:41:29 +00:00
optConf->striphigh = FLAGTRUE;
2001-07-27 00:05:04 +00:00
break;
2001-07-27 00:05:04 +00:00
case 'A': /* attach with log replay */
fReplay = 1;
/* fall through */
case 'a': /* attach */
pcCmd = "attach";
break;
2003-03-11 02:08:07 +00:00
case 'B': /* broadcast message */
2003-09-22 20:49:53 +00:00
fLocal = 1;
2003-03-11 02:08:07 +00:00
/* fall through */
2001-07-27 00:05:04 +00:00
case 'b':
pcCmd = "broadcast";
2004-04-13 20:30:28 +00:00
if (cmdarg != (char *)0)
free(cmdarg);
if ((cmdarg = StrDup(optarg)) == (char *)0)
OutOfMem();
2001-07-27 00:05:04 +00:00
break;
2004-05-26 00:41:29 +00:00
case 'C':
userConf = optarg;
break;
2002-10-14 21:03:35 +00:00
case 'c':
#if HAVE_OPENSSL
2004-05-26 00:41:29 +00:00
if ((optConf->sslcredentials =
StrDup(optarg)) == (char *)0)
OutOfMem();
2002-10-14 21:03:35 +00:00
#endif
break;
2001-07-27 00:05:04 +00:00
case 'D':
2002-03-12 09:12:20 +00:00
fDebug++;
2001-07-27 00:05:04 +00:00
break;
2003-09-22 20:49:53 +00:00
case 'd':
pcCmd = "disconnect";
2004-04-13 20:30:28 +00:00
if (cmdarg != (char *)0)
free(cmdarg);
if ((cmdarg = StrDup(optarg)) == (char *)0)
OutOfMem();
2003-09-22 20:49:53 +00:00
break;
2002-10-14 21:03:35 +00:00
case 'E':
#if HAVE_OPENSSL
2004-05-26 00:41:29 +00:00
optConf->sslenabled = FLAGFALSE;
2002-10-14 21:03:35 +00:00
#endif
break;
2001-07-27 00:05:04 +00:00
case 'e': /* set escape chars */
2004-05-26 00:41:29 +00:00
if ((optConf->escape = StrDup(optarg)) == (char *)0)
OutOfMem();
2001-07-27 00:05:04 +00:00
break;
case 'F': /* force attach with log replay */
fReplay = 1;
/* fall through */
case 'f': /* force attach */
pcCmd = "force";
break;
2003-01-28 01:48:36 +00:00
case 'I':
fLocal = 1;
/* fall through */
2002-03-12 09:12:20 +00:00
case 'i':
pcCmd = "info";
break;
2001-07-27 00:05:04 +00:00
case 'l':
2004-05-26 00:41:29 +00:00
if ((optConf->username = StrDup(optarg)) == (char *)0)
OutOfMem();
2001-07-27 00:05:04 +00:00
break;
2001-07-05 16:18:19 +00:00
2001-07-27 00:05:04 +00:00
case 'M':
2004-05-26 00:41:29 +00:00
if ((optConf->master = StrDup(optarg)) == (char *)0)
OutOfMem();
break;
case 'n':
readSystemConf = 0;
2001-07-27 00:05:04 +00:00
break;
case 'p':
2004-05-26 00:41:29 +00:00
if ((optConf->port = StrDup(optarg)) == (char *)0)
OutOfMem();
2001-07-27 00:05:04 +00:00
break;
2001-07-05 16:18:19 +00:00
2001-07-27 00:05:04 +00: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 16:18:19 +00:00
2001-07-27 00:05:04 +00: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;
2003-09-22 20:49:53 +00:00
case 't':
BuildString((char *)0, textMsg);
if (optarg == (char *)0 || *optarg == '\000') {
Error("no destination specified for -t", optarg);
2003-09-29 15:50:27 +00:00
Bye(EX_UNAVAILABLE);
2003-09-22 20:49:53 +00:00
} else if (strchr(optarg, ' ') != (char *)0) {
Error("-t option cannot contain a space: `%s'",
optarg);
2003-09-29 15:50:27 +00:00
Bye(EX_UNAVAILABLE);
2003-09-22 20:49:53 +00:00
}
BuildString("textmsg ", textMsg);
BuildString(optarg, textMsg);
pcCmd = textMsg->string;
break;
2004-05-07 16:05:25 +00:00
case 'U':
#if HAVE_OPENSSL
2004-05-26 00:41:29 +00:00
optConf->sslrequired = FLAGFALSE;
2004-05-07 16:05:25 +00:00
#endif
break;
2001-07-27 00:05:04 +00:00
case 'u':
2003-09-22 20:49:53 +00:00
pcCmd = "hosts";
2001-07-27 00:05:04 +00:00
break;
2002-03-12 09:12:20 +00:00
case 'W':
fLocal = 1;
/*fallthrough */
2001-07-27 00:05:04 +00:00
case 'w': /* who */
2003-09-22 20:49:53 +00:00
pcCmd = "group";
2001-07-27 00:05:04 +00:00
break;
case 'x':
2003-09-22 20:49:53 +00:00
pcCmd = "examine";
2001-07-27 00:05:04 +00:00
break;
case 'v':
fVerbose = 1;
break;
case 'V':
fVersion = 1;
break;
2005-09-05 23:15:33 +00:00
case 'Z': /* only send cmd this host */
fLocal = 1;
/*fallthough */
case 'z': /* send a command to the server */
pcCmd = (char *)0;
for (isZap = sizeof(zap) / sizeof(ZAPS) - 1; isZap >= 0;
isZap--) {
char *token = (char *)0;
char *str = (char *)0;
if (zap[isZap].cmd == (char *)0) /* skip non-action ones */
continue;
BuildTmpString((char *)0);
str = BuildTmpString(zap[isZap].opt);
for (token = strtok(str, ", "); token != (char *)0;
token = strtok(NULL, ", ")) {
if (strcasecmp(optarg, token) == 0) {
pcCmd = zap[isZap].cmd;
isZap++;
break;
}
}
if (pcCmd)
break;
}
if (isZap < 0) {
if (strcasecmp(optarg, "help") == 0) {
STRING *help;
help = AllocString();
BuildString("available -z commands:\n\n", help);
for (isZap = 0; isZap < sizeof(zap) / sizeof(ZAPS);
isZap++) {
char *str;
BuildTmpString((char *)0);
str =
BuildTmpStringPrint(" %16s %s\n",
zap[isZap].opt,
zap[isZap].desc);
BuildString(str, help);
}
Error(help->string);
} else
Error("invalid -z command: `%s' (try `help')",
optarg);
Bye(EX_UNAVAILABLE);
}
break;
2003-03-11 02:08:07 +00:00
case 'h': /* huh? */
Usage(1);
2003-09-29 15:50:27 +00:00
Bye(EX_OK);
2003-03-11 02:08:07 +00:00
case '\?': /* huh? */
Usage(0);
2003-09-29 15:50:27 +00:00
Bye(EX_UNAVAILABLE);
2003-03-11 02:08:07 +00:00
default:
Error("option %c needs a parameter", optopt);
2003-09-29 15:50:27 +00:00
Bye(EX_UNAVAILABLE);
2001-07-27 00:05:04 +00:00
}
}
if (fVersion) {
Version();
2003-09-29 15:50:27 +00:00
Bye(EX_OK);
2001-07-27 00:05:04 +00:00
}
2004-05-26 00:41:29 +00:00
ProbeInterfaces(INADDR_ANY);
if (readSystemConf)
ReadConf(CLIENTCONFIGFILE, FLAGFALSE);
if (userConf == (char *)0) {
/* read the config files */
char *h = (char *)0;
if (((h = getenv("HOME")) == (char *)0) &&
((pwdMe = getpwuid(getuid())) == (struct passwd *)0)) {
Error("$HOME does not exist and getpwuid fails: %d: %s",
(int)(getuid()), strerror(errno));
} else {
if (h == (char *)0) {
if (pwdMe->pw_dir == (char *)0 ||
pwdMe->pw_dir[0] == '\000') {
Error("Home directory for uid %d is not defined",
(int)(getuid()));
Bye(EX_UNAVAILABLE);
} else {
h = pwdMe->pw_dir;
}
}
}
if (h != (char *)0) {
BuildTmpString((char *)0);
BuildTmpString(h);
h = BuildTmpString("/.consolerc");
ReadConf(h, FLAGFALSE);
BuildTmpString((char *)0);
}
} else
ReadConf(userConf, FLAGTRUE);
if (optConf->striphigh != FLAGUNKNOWN)
config->striphigh = optConf->striphigh;
else if (pConfig->striphigh != FLAGUNKNOWN)
config->striphigh = pConfig->striphigh;
else
config->striphigh = FLAGFALSE;
if (optConf->escape != (char *)0)
ParseEsc(optConf->escape);
else if (pConfig->escape != (char *)0)
ParseEsc(pConfig->escape);
if (optConf->username != (char *)0)
config->username = StrDup(optConf->username);
else if (pConfig->username != (char *)0)
config->username = StrDup(pConfig->username);
else
config->username = (char *)0;
if (optConf->master != (char *)0 && optConf->master[0] != '\000')
config->master = StrDup(optConf->master);
else if (pConfig->master != (char *)0 && pConfig->master[0] != '\000')
config->master = StrDup(pConfig->master);
else
config->master = StrDup(
#if USE_UNIX_DOMAIN_SOCKETS
UDSDIR
#else
MASTERHOST /* which machine is current */
#endif
);
if (config->master == (char *)0)
OutOfMem();
if (optConf->port != (char *)0 && optConf->port[0] != '\000')
config->port = StrDup(optConf->port);
else if (pConfig->port != (char *)0 && pConfig->port[0] != '\000')
config->port = StrDup(pConfig->port);
else
config->port = StrDup(DEFPORT);
if (config->port == (char *)0)
OutOfMem();
#if HAVE_OPENSSL
if (optConf->sslcredentials != (char *)0 &&
optConf->sslcredentials[0] != '\000')
config->sslcredentials = StrDup(optConf->sslcredentials);
else if (pConfig->sslcredentials != (char *)0 &&
pConfig->sslcredentials[0] != '\000')
config->sslcredentials = StrDup(pConfig->sslcredentials);
else
config->sslcredentials = (char *)0;
if (optConf->sslenabled != FLAGUNKNOWN)
config->sslenabled = optConf->sslenabled;
else if (pConfig->sslenabled != FLAGUNKNOWN)
config->sslenabled = pConfig->sslenabled;
else
config->sslenabled = FLAGTRUE;
if (optConf->sslrequired != FLAGUNKNOWN)
config->sslrequired = optConf->sslrequired;
else if (pConfig->sslrequired != FLAGUNKNOWN)
config->sslrequired = pConfig->sslrequired;
else
config->sslrequired = FLAGTRUE;
#endif
2003-09-22 20:49:53 +00:00
/* finish resolving the command to do */
if (pcCmd == (char *)0) {
pcCmd = "attach";
}
if (*pcCmd == 'a' || *pcCmd == 'f' || *pcCmd == 's') {
2005-09-05 23:15:33 +00:00
/* attach, force-attach, and spy */
2003-09-22 20:49:53 +00:00
if (optind >= argc) {
Error("missing console name");
2003-09-29 15:50:27 +00:00
Bye(EX_UNAVAILABLE);
2003-09-22 20:49:53 +00:00
}
2004-04-13 20:30:28 +00:00
if (cmdarg != (char *)0)
free(cmdarg);
2004-03-23 01:14:45 +00:00
if ((cmdarg = StrDup(argv[optind++])) == (char *)0)
OutOfMem();
2003-09-22 20:49:53 +00:00
} else if (*pcCmd == 't') {
2005-09-05 23:15:33 +00:00
/* text message */
2003-09-22 20:49:53 +00:00
if (optind >= argc) {
Error("missing message text");
2003-09-29 15:50:27 +00:00
Bye(EX_UNAVAILABLE);
2003-09-22 20:49:53 +00:00
}
2004-04-13 20:30:28 +00:00
if (cmdarg != (char *)0)
free(cmdarg);
if ((cmdarg = StrDup(argv[optind++])) == (char *)0)
OutOfMem();
2005-09-05 23:15:33 +00:00
} else if (*pcCmd == 'i' || *pcCmd == 'e' || *pcCmd == 'h' ||
*pcCmd == 'g') {
/* info, e(x)amine, hosts (u), groups (w) */
if (optind < argc) {
if (cmdarg != (char *)0)
free(cmdarg);
if ((cmdarg = StrDup(argv[optind++])) == (char *)0)
OutOfMem();
}
2003-09-22 20:49:53 +00:00
}
if (optind < argc) {
Error("extra garbage on command line? (%s...)", argv[optind]);
2003-09-29 15:50:27 +00:00
Bye(EX_UNAVAILABLE);
2001-07-27 00:05:04 +00:00
}
2004-04-13 20:30:28 +00:00
#if !USE_UNIX_DOMAIN_SOCKETS
2001-07-27 00:05:04 +00:00
/* Look for non-numeric characters */
2004-05-26 00:41:29 +00:00
for (opt = 0; config->port[opt] != '\000'; opt++)
if (!isdigit((int)config->port[opt]))
2001-07-27 00:05:04 +00:00
break;
2004-05-26 00:41:29 +00:00
if (config->port[opt] == '\000') {
2001-07-27 00:05:04 +00:00
/* numeric only */
2004-05-26 00:41:29 +00:00
bindPort = atoi(config->port);
2001-07-27 00:05:04 +00:00
} else {
/* non-numeric only */
struct servent *pSE;
2004-05-26 00:41:29 +00:00
if ((pSE =
getservbyname(config->port, "tcp")) == (struct servent *)0) {
Error("getservbyname(%s) failed", config->port);
2003-09-29 15:50:27 +00:00
Bye(EX_UNAVAILABLE);
2001-07-05 16:18:19 +00:00
} else {
2001-07-27 00:05:04 +00:00
bindPort = ntohs((u_short) pSE->s_port);
2001-07-05 16:18:19 +00:00
}
2001-07-27 00:05:04 +00:00
}
2004-04-13 20:30:28 +00:00
#endif
2001-07-05 16:18:19 +00:00
2004-05-26 00:41:29 +00:00
if (config->username == (char *)0 || config->username[0] == '\000') {
if (config->username != (char *)0)
free(config->username);
if (((config->username = getenv("LOGNAME")) == (char *)0) &&
((config->username = getenv("USER")) == (char *)0) &&
2002-10-14 21:03:35 +00:00
((pwdMe = getpwuid(getuid())) == (struct passwd *)0)) {
Error
("$LOGNAME and $USER do not exist and getpwuid fails: %d: %s",
(int)(getuid()), strerror(errno));
2003-09-29 15:50:27 +00:00
Bye(EX_UNAVAILABLE);
2001-07-05 16:18:19 +00:00
}
2004-05-26 00:41:29 +00:00
if (config->username == (char *)0) {
2002-10-14 21:03:35 +00:00
if (pwdMe->pw_name == (char *)0 || pwdMe->pw_name[0] == '\000') {
Error("Username for uid %d does not exist",
(int)(getuid()));
2003-09-29 15:50:27 +00:00
Bye(EX_UNAVAILABLE);
2002-10-14 21:03:35 +00:00
} else {
2004-05-26 00:41:29 +00:00
config->username = pwdMe->pw_name;
2002-10-14 21:03:35 +00:00
}
}
2004-05-26 00:41:29 +00:00
if ((config->username = StrDup(config->username)) == (char *)0)
OutOfMem();
2001-07-27 00:05:04 +00:00
}
2003-12-02 16:40:59 +00:00
if (execCmd == (STRING *)0)
execCmd = AllocString();
2001-07-27 00:05:04 +00:00
2003-09-22 20:49:53 +00:00
SimpleSignal(SIGPIPE, SIG_IGN);
2002-03-12 09:12:20 +00:00
2003-03-11 02:08:07 +00:00
cfstdout = FileOpenFD(1, simpleFile);
2002-10-14 21:03:35 +00:00
2003-03-11 02:08:07 +00:00
BuildString((char *)0, acPorts);
BuildStringChar('@', acPorts);
2004-05-26 00:41:29 +00:00
BuildString(config->master, acPorts);
2002-03-12 09:12:20 +00:00
2002-10-14 21:03:35 +00:00
#if HAVE_OPENSSL
2003-03-11 02:08:07 +00:00
SetupSSL(); /* should only do if we want ssl - provide flag! */
2002-10-14 21:03:35 +00:00
#endif
2003-09-22 20:49:53 +00:00
/* stack up the commands for DoCmds() */
cmdi = -1;
cmds[++cmdi] = pcCmd;
2005-09-05 23:15:33 +00:00
if (*pcCmd == 'q' || *pcCmd == 'v' || *pcCmd == 'p' || *pcCmd == 'r' ||
isZap) {
2003-09-22 20:49:53 +00:00
if (!fLocal)
cmds[++cmdi] = "master";
} else if (*pcCmd == 'a' || *pcCmd == 'f' || *pcCmd == 's') {
2001-07-27 00:05:04 +00:00
ValidateEsc();
2003-09-22 20:49:53 +00:00
cmds[++cmdi] = "call";
2005-09-05 23:15:33 +00:00
interact = FLAGTRUE;
} else if (cmdarg != (char *)0 &&
(*pcCmd == 'i' || *pcCmd == 'e' || *pcCmd == 'h' ||
*pcCmd == 'g')) {
cmds[++cmdi] = "call";
2001-07-27 00:05:04 +00:00
} else {
2003-09-22 20:49:53 +00:00
cmds[++cmdi] = "groups";
if (!fLocal)
cmds[++cmdi] = "master";
2001-07-27 00:05:04 +00:00
}
2003-09-22 20:49:53 +00:00
2004-03-11 17:54:13 +00:00
for (;;) {
2004-03-23 01:14:45 +00:00
if (gotoConsole == (CONSFILE *)0)
2004-05-26 00:41:29 +00:00
DoCmds(config->master, acPorts->string, cmdi);
2004-03-23 01:14:45 +00:00
else
Interact(gotoConsole, gotoName);
/* if we didn't ask for another console, done */
if (gotoConsole == (CONSFILE *)0 && prevConsole == (CONSFILE *)0)
2004-03-11 17:54:13 +00:00
break;
if (consoleName == (STRING *)0)
consoleName = AllocString();
C2Raw();
2004-03-23 01:14:45 +00:00
if (prevConsole == (CONSFILE *)0)
2004-03-11 17:54:13 +00:00
FileWrite(cfstdout, FLAGFALSE, "console: ", 9);
2004-03-23 01:14:45 +00:00
else
FileWrite(cfstdout, FLAGFALSE, "[console: ", 10);
2004-03-11 17:54:13 +00:00
GetUserInput(consoleName);
FileWrite(cfstdout, FLAGFALSE, "]\r\n", 3);
C2Cooked();
2004-03-23 01:14:45 +00:00
if (consoleName->used > 1) {
if (cmdarg != (char *)0)
free(cmdarg);
if ((cmdarg = StrDup(consoleName->string)) == (char *)0)
OutOfMem();
if (prevConsole == (CONSFILE *)0) {
prevConsole = gotoConsole;
gotoConsole = (CONSFILE *)0;
prevName = gotoName;
gotoName = (char *)0;
}
} else {
if (prevConsole != (CONSFILE *)0) {
gotoConsole = prevConsole;
prevConsole = (CONSFILE *)0;
gotoName = prevName;
prevName = (char *)0;
}
}
2004-03-11 17:54:13 +00:00
}
2003-09-22 20:49:53 +00:00
2004-03-23 01:14:45 +00:00
if (cmdarg != (char *)0)
free(cmdarg);
2003-09-22 20:49:53 +00:00
if (*pcCmd == 'd')
2004-04-13 20:30:28 +00:00
FilePrint(cfstdout, FLAGFALSE, "disconnected %d %s\n",
disconnectCount,
disconnectCount == 1 ? "user" : "users");
2003-09-22 20:49:53 +00:00
2004-03-23 01:14:45 +00:00
Bye(0);
2003-09-29 15:50:27 +00:00
return 0; /* noop - Bye() terminates us */
}