conserver/console/console.c

1736 lines
41 KiB
C
Raw Permalink Normal View History

/*
2003-12-02 08:40:59 -08:00
* $Id: console.c,v 5.152 2003/11/28 00:47:30 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.
*/
2003-09-22 13:49:53 -07:00
#include <compat.h>
2001-02-18 22:50:29 -08:00
#include <pwd.h>
2003-09-22 13:49:53 -07:00
#include <getpassword.h>
2003-11-20 06:49:09 -08:00
#include <cutil.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-09-22 13:49:53 -07:00
int fReplay = 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;
2003-09-22 13:49:53 -07:00
char *pcUser = (char *)0;
int disconnectCount = 0;
2003-12-02 08:40:59 -08:00
STRING *execCmd = (STRING *)0;
CONSFILE *execCmdFile = (CONSFILE *)0;
pid_t execCmdPid = 0;
2002-10-14 14:03:35 -07:00
#if HAVE_OPENSSL
2003-09-22 13:49:53 -07:00
SSL_CTX *ctx = (SSL_CTX *)0;
2002-10-14 14:03:35 -07:00
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
{
2003-09-22 13:49:53 -07:00
if (ctx == (SSL_CTX *)0) {
2002-10-14 14:03:35 -07:00
SSL_load_error_strings();
if (!SSL_library_init()) {
Error("SSL library initialization failed");
2003-09-29 08:50:27 -07:00
Bye(EX_UNAVAILABLE);
2002-10-14 14:03:35 -07:00
}
2003-09-22 13:49:53 -07:00
if ((ctx = SSL_CTX_new(SSLv23_method())) == (SSL_CTX *)0) {
2002-10-14 14:03:35 -07:00
Error("Creating SSL context failed");
2003-09-29 08:50:27 -07:00
Bye(EX_UNAVAILABLE);
2002-10-14 14:03:35 -07:00
}
if (SSL_CTX_set_default_verify_paths(ctx) != 1) {
Error("Could not load SSL default CA file and/or directory");
2003-09-29 08:50:27 -07:00
Bye(EX_UNAVAILABLE);
2002-10-14 14:03:35 -07:00
}
if (pcCredFile != (char *)0) {
if (SSL_CTX_use_certificate_chain_file(ctx, pcCredFile) != 1) {
Error("Could not load SSL certificate from '%s'",
pcCredFile);
2003-09-29 08:50:27 -07:00
Bye(EX_UNAVAILABLE);
2002-10-14 14:03:35 -07:00
}
if (SSL_CTX_use_PrivateKey_file
(ctx, pcCredFile, SSL_FILETYPE_PEM) != 1) {
Error("Could not SSL private key from '%s'", pcCredFile);
2003-09-29 08:50:27 -07:00
Bye(EX_UNAVAILABLE);
2002-10-14 14:03:35 -07:00
}
}
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");
2003-09-29 08:50:27 -07:00
Bye(EX_UNAVAILABLE);
2002-10-14 14:03:35 -07:00
}
}
}
void
2003-03-10 18:08:07 -08:00
#if PROTOTYPES
2003-09-22 13:49:53 -07:00
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;
2003-09-22 13:49:53 -07:00
if (ctx == (SSL_CTX *)0) {
2002-10-14 14:03:35 -07:00
Error("WTF? The SSL context disappeared?!?!?");
2003-09-29 08:50:27 -07:00
Bye(EX_UNAVAILABLE);
2002-10-14 14:03:35 -07:00
}
if (!(ssl = SSL_new(ctx))) {
Error("Couldn't create new SSL context");
2003-09-29 08:50:27 -07:00
Bye(EX_UNAVAILABLE);
2002-10-14 14:03:35 -07:00
}
2003-03-10 18:08:07 -08:00
FileSetSSL(pcf, ssl);
SSL_set_fd(ssl, FileFDNum(pcf));
2003-09-22 13:49:53 -07:00
CONDDEBUG((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);
2003-09-29 08:50:27 -07:00
Bye(EX_UNAVAILABLE);
2002-10-14 14:03:35 -07:00
}
2003-03-10 18:08:07 -08:00
FileSetType(pcf, SSLSocket);
2003-09-22 13:49:53 -07:00
CONDDEBUG((1, "SSL Connection: %s :: %s", SSL_get_cipher_version(ssl),
SSL_get_cipher_name(ssl)));
2002-10-14 14:03:35 -07:00
}
#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
2003-09-22 13:49:53 -07:00
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[] = {
2003-09-29 08:50:27 -07:00
"7 strip the high bit off all console data",
2003-03-10 18:08:07 -08:00
"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-09-22 13:49:53 -07:00
"d disconnect [user][@console]",
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)",
"h output this message",
2003-09-29 08:50:27 -07:00
"i(I) display information in machine-parseable form (on master)",
2003-03-10 18:08:07 -08:00
"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)",
2003-09-22 13:49:53 -07:00
"t send a text message to [user][@console]",
2003-03-10 18:08:07 -08:00
"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,
2003-09-22 13:49:53 -07:00
"usage: %s [-aAEfFsS] [-7Dv] [-c cred] [-M mach] [-p port] [-e esc] [-l username] console\n",
2003-03-10 18:08:07 -08:00
progname);
fprintf(stderr,
2003-09-22 13:49:53 -07:00
"usage: %s [-hiIPrRuVwWx] [-7Dv] [-M mach] [-p port] [-d [user][@console]] [-[bB] message] [-t [user][@console] message]\n",
2003-03-10 18:08:07 -08:00
progname);
2003-09-22 13:49:53 -07:00
fprintf(stderr, "usage: %s [-qQ] [-7Dv] [-M mach] [-p port]\n",
2003-03-10 18:08:07 -08:00
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-09-22 13:49:53 -07: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
(char *)0
};
2001-07-26 17:05:04 -07:00
2003-09-22 13:49:53 -07:00
if (acA1 == (STRING *)0)
2003-03-10 18:08:07 -08:00
acA1 = AllocString();
2003-09-22 13:49:53 -07:00
if (acA2 == (STRING *)0)
2003-03-10 18:08:07 -08:00
acA2 = AllocString();
Msg("%s", THIS_VERSION);
2003-09-22 13:49:53 -07:00
Msg("default initial master server `%s\'", MASTERHOST);
2003-03-10 18:08:07 -08:00
Msg("default escape sequence `%s%s\'", FmtCtl(DEFATTN, acA1),
FmtCtl(DEFESC, acA2));
2003-09-22 13:49:53 -07:00
Msg("default port referenced as `%s'", DEFPORT);
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");
2003-09-29 08:50:27 -07:00
Bye(EX_UNAVAILABLE);
2001-07-26 17:05:04 -07:00
}
}
/* 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);
2003-09-29 08:50:27 -07:00
Bye(EX_UNAVAILABLE);
2001-07-26 17:05:04 -07:00
}
if ('\000' != *pcTemp) {
Error("too many characters in new escape sequence at ...`%s\'",
pcTemp);
2003-09-29 08:50:27 -07:00
Bye(EX_UNAVAILABLE);
2001-07-26 17:05:04 -07: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 14:03:35 -07:00
CONSFILE *
2003-03-10 18:08:07 -08:00
#if PROTOTYPES
2003-09-22 13:49:53 -07:00
GetPort(char *pcToHost, unsigned short sPort)
2002-03-12 01:12:20 -08:00
#else
2003-09-22 13:49:53 -07:00
GetPort(pcToHost, sPort)
2001-07-26 17:05:04 -07:00
char *pcToHost;
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;
2003-09-22 13:49:53 -07:00
struct sockaddr_in port;
2001-02-18 22:50:29 -08:00
#if HAVE_MEMSET
2003-09-22 13:49:53 -07:00
memset((void *)(&port), '\000', sizeof(port));
#else
2003-09-22 13:49:53 -07:00
bzero((char *)(&port), sizeof(port));
2001-02-18 22:50:29 -08:00
#endif
2001-07-26 17:05:04 -07:00
2003-09-22 13:49:53 -07:00
#if HAVE_INET_ATON
if (inet_aton(pcToHost, &(port.sin_addr)) == 0)
#else
port.sin_addr.s_addr = inet_addr(pcToHost);
if ((in_addr_t) (-1) == port.sin_addr.s_addr)
#endif
{
2001-02-18 22:50:29 -08:00
if ((struct hostent *)0 != (hp = gethostbyname(pcToHost))) {
#if HAVE_MEMCPY
2003-09-22 13:49:53 -07:00
memcpy((char *)&port.sin_addr.s_addr, (char *)hp->h_addr,
2001-07-26 17:05:04 -07:00
hp->h_length);
2001-02-18 22:50:29 -08:00
#else
2003-09-22 13:49:53 -07:00
bcopy((char *)hp->h_addr, (char *)&port.sin_addr.s_addr,
2003-03-10 18:08:07 -08:00
hp->h_length);
#endif
} else {
2003-03-10 18:08:07 -08:00
Error("gethostbyname(%s): %s", pcToHost, hstrerror(h_errno));
2003-09-22 13:49:53 -07:00
return (CONSFILE *)0;
2001-07-26 17:05:04 -07:00
}
}
2003-09-22 13:49:53 -07:00
port.sin_port = sPort;
port.sin_family = AF_INET;
2001-07-26 17:05:04 -07:00
if (fDebug) {
2003-09-22 13:49:53 -07: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-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));
2003-09-22 13:49:53 -07:00
return (CONSFILE *)0;
2001-07-26 17:05:04 -07:00
}
2003-09-22 13:49:53 -07:00
if (connect(s, (struct sockaddr *)(&port), sizeof(port)) < 0) {
Error("connect(): %hu@%s: %s", ntohs(port.sin_port), pcToHost,
2001-07-26 17:05:04 -07:00
strerror(errno));
2003-09-22 13:49:53 -07:00
return (CONSFILE *)0;
2001-07-26 17:05:04 -07:00
}
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;
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-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-07-26 17:05:04 -07:00
struct termios n_tios;
2001-07-26 17:05:04 -07:00
if (!isatty(0) || 0 != screwy)
return;
2003-03-10 18:08:07 -08:00
if (0 != tcgetattr(0, &o_tios)) {
Error("tcgetattr(0): %s", strerror(errno));
2003-09-29 08:50:27 -07:00
Bye(EX_UNAVAILABLE);
2003-03-10 18:08:07 -08:00
}
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;
2003-03-10 18:08:07 -08:00
if (0 != tcsetattr(0, TCSANOW, &n_tios)) {
Error("tcsetattr(0, TCSANOW): %s", strerror(errno));
2003-09-29 08:50:27 -07:00
Bye(EX_UNAVAILABLE);
2003-03-10 18:08:07 -08:00
}
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;
tcsetattr(0, TCSANOW, &o_tios);
screwy = 0;
}
2003-09-29 08:50:27 -07:00
void
#if PROTOTYPES
DestroyDataStructures(void)
#else
DestroyDataStructures()
#endif
{
C2Cooked();
}
2003-09-22 13:49:53 -07:00
char *
2003-03-10 18:08:07 -08:00
#if PROTOTYPES
2003-09-22 13:49:53 -07:00
ReadReply(CONSFILE *fd, int toEOF)
2002-03-12 01:12:20 -08:00
#else
2003-09-22 13:49:53 -07:00
ReadReply(fd)
2002-10-14 14:03:35 -07:00
CONSFILE *fd;
2003-09-22 13:49:53 -07:00
int toEOF;
2002-03-12 01:12:20 -08:00
#endif
{
2001-07-26 17:05:04 -07:00
int nr;
2003-09-22 13:49:53 -07:00
static char buf[1024];
static STRING *result = (STRING *)0;
2001-07-26 17:05:04 -07:00
2003-09-22 13:49:53 -07:00
if (result == (STRING *)0)
result = AllocString();
else
BuildString((char *)0, result);
2003-09-22 13:49:53 -07:00
while (1) {
switch (nr = FileRead(fd, buf, sizeof(buf))) {
2001-07-26 17:05:04 -07:00
case 0:
/* fall through */
case -1:
2003-09-22 13:49:53 -07:00
if (result->used > 1 || toEOF)
break;
2003-03-10 18:08:07 -08:00
C2Cooked();
2001-07-26 17:05:04 -07:00
Error("lost connection");
2003-09-29 08:50:27 -07:00
Bye(EX_UNAVAILABLE);
2001-07-26 17:05:04 -07:00
default:
2003-09-22 13:49:53 -07:00
BuildStringN(buf, nr, result);
if (toEOF) /* if toEOF, read until EOF */
continue;
if ((result->used > 1) &&
2003-10-05 18:08:18 -07:00
strchr(result->string, '\n') != (char *)0)
2001-07-26 17:05:04 -07:00
break;
continue;
}
break;
}
if (fDebug) {
2003-09-22 13:49:53 -07:00
static STRING *tmpString = (STRING *)0;
if (tmpString == (STRING *)0)
2003-03-10 18:08:07 -08:00
tmpString = AllocString();
BuildString((char *)0, tmpString);
2003-09-22 13:49:53 -07:00
FmtCtlStr(result->string, result->used - 1, tmpString);
CONDDEBUG((1, "ReadReply: `%s'", tmpString->string));
2001-07-26 17:05:04 -07:00
}
2003-09-22 13:49:53 -07:00
return result->string;
}
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
2003-12-02 08:40:59 -08:00
ProcessUrgentData(CONSFILE *pcf)
2002-03-12 01:12:20 -08:00
#else
2003-12-02 08:40:59 -08:00
ProcessUrgentData(pcf)
CONSFILE *pcf;
2002-03-12 01:12:20 -08:00
#endif
{
2002-03-12 01:12:20 -08:00
static char acCmd;
2003-12-02 08:40:59 -08:00
int s;
2001-07-26 17:05:04 -07:00
SawUrg = 0;
2003-12-02 08:40:59 -08:00
s = FileFDNum(pcf);
2001-07-26 17:05:04 -07:00
/* 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);
2003-12-02 08:40:59 -08:00
FileWrite(cfstdout, FLAGFALSE, ".", 1);
2001-07-26 17:05:04 -07:00
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) {
2003-12-02 08:40:59 -08:00
case OB_EXEC:
FileWrite(cfstdout, FLAGFALSE, "exec: ", 6);
BuildString((char *)0, execCmd);
for (;;) {
char c;
if (read(0, &c, 1) == 0)
break;
if (c == '\n' || c == '\r') {
FileWrite(cfstdout, FLAGFALSE, "]\r\n", 3);
if (execCmd->used <= 1) {
char s = OB_DROP;
FileWrite(pcf, FLAGFALSE, &s, 1);
}
break;
}
if (c == '\a' || (c >= ' ' && c <= '~')) {
BuildStringChar(c, execCmd);
FileWrite(cfstdout, FLAGFALSE, &c, 1);
} else if ((c == '\b' || c == 0x7f) && execCmd->used > 1) {
if (execCmd->string[execCmd->used - 2] != '\a') {
FileWrite(cfstdout, FLAGFALSE, "\b \b", 3);
}
execCmd->string[execCmd->used - 2] = '\000';
execCmd->used--;
} else if ((c == 0x15) && execCmd->used > 1) {
while (execCmd->used > 1) {
if (execCmd->string[execCmd->used - 2] != '\a') {
FileWrite(cfstdout, FLAGFALSE, "\b \b", 3);
}
execCmd->string[execCmd->used - 2] = '\000';
execCmd->used--;
}
}
}
break;
case OB_SUSP:
#if defined(SIGSTOP)
2003-12-02 08:40:59 -08:00
FileWrite(cfstdout, FLAGFALSE, "stop]", 5);
2003-03-10 18:08:07 -08:00
C2Cooked();
kill(getpid(), SIGSTOP);
C2Raw();
2003-12-02 08:40:59 -08:00
FileWrite(cfstdout, FLAGFALSE,
"[press any character to continue", 32);
#else
2003-12-02 08:40:59 -08:00
FileWrite(cfstdout, FLAGFALSE,
"stop not supported -- press any character to continue",
53);
#endif
2001-07-26 17:05:04 -07:00
break;
case OB_DROP:
2003-12-02 08:40:59 -08:00
FileWrite(cfstdout, FLAGFALSE, "dropped by server]\r\n", 20);
2003-03-10 18:08:07 -08:00
C2Cooked();
2003-09-29 08:50:27 -07:00
Bye(EX_UNAVAILABLE);
2001-07-26 17:05:04 -07:00
/*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;
}
}
2003-12-02 08:40:59 -08: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)
*/
#if defined(SIGURG)
SimpleSignal(SIGURG, SIG_DFL);
#endif
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));
iNewGrp = getpid();
}
# else
iNewGrp = getpid();
# 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;
}
/* interact with a group server (ksb)
*/
static int
2003-03-10 18:08:07 -08:00
#if PROTOTYPES
2003-09-22 13:49:53 -07:00
CallUp(CONSFILE *pcf, char *pcMaster, char *pcMach, char *pcHow,
char *result)
2002-03-12 01:12:20 -08:00
#else
2003-09-22 13:49:53 -07:00
CallUp(pcf, pcMaster, pcMach, pcHow, result)
2002-10-14 14:03:35 -07:00
CONSFILE *pcf;
2003-09-22 13:49:53 -07:00
char *pcMaster, *pcMach, *pcHow, *result;
2002-03-12 01:12:20 -08:00
#endif
{
2001-07-26 17:05:04 -07:00
int nc;
int fIn = '-';
2003-12-02 08:40:59 -08:00
fd_set rmask, wmask;
2001-07-26 17:05:04 -07:00
int i;
2002-03-12 01:12:20 -08:00
int justProcessedUrg = 0;
2003-10-05 18:08:18 -07:00
char *r = (char *)0;
2003-11-16 11:33:39 -08:00
static char acMesg[8192];
2001-07-26 17:05:04 -07:00
if (fVerbose) {
2003-09-22 13:49:53 -07:00
Msg("%s to %s (on %s)", pcHow, pcMach, pcMaster);
2001-07-26 17:05:04 -07:00
}
#if !defined(__CYGWIN__)
# if defined(F_SETOWN)
2003-09-22 13:49:53 -07:00
if (fcntl(FileFDNum(pcf), F_SETOWN, getpid()) == -1) {
2003-03-10 18:08:07 -08:00
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-09-22 13:49:53 -07:00
if (ioctl(FileFDNum(pcf), SIOCSPGRP, &iTemp) == -1) {
2003-03-10 18:08:07 -08:00
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
2003-12-02 08:40:59 -08:00
SimpleSignal(SIGCHLD, FlagReapVirt);
2003-09-22 13:49:53 -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).
*/
/* 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") ||
2003-09-29 08:50:27 -07:00
0 == strcmp(result, "[ok]\r\n") ||
0 == strcmp(result, "[read-only -- initializing]\r\n")) {
2003-09-22 13:49:53 -07:00
/* Humph, someone else is on
* or we have an old version of the server (4.X)
*/
fIn = 's';
2003-09-29 08:50:27 -07:00
} else if (0 == strcmp(result, "[console is read-only]\r\n")) {
2003-09-22 13:49:53 -07:00
fIn = 'r';
2003-09-29 08:50:27 -07:00
} else if (0 == strcmp(result, "[line to console is down]\r\n")) {
2003-09-22 13:49:53 -07:00
/* 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 {
2003-10-05 18:08:18 -07:00
FilePrint(cfstdout, FLAGFALSE, "%s: %s", pcMach, result);
2003-09-29 08:50:27 -07:00
Bye(EX_UNAVAILABLE);
2003-09-22 13:49:53 -07:00
}
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-11-16 11:33:39 -08:00
FilePrint(pcf, FLAGFALSE, "%c%ce%c%c", DEFATTN, DEFESC, chAttn,
chEsc);
2003-09-22 13:49:53 -07:00
r = ReadReply(pcf, 0);
if (strncmp(r, "[redef:", 7) != 0) {
2002-03-12 01:12:20 -08:00
Error("protocol botch on redef of escape sequence");
2003-09-29 08:50:27 -07:00
Bye(EX_UNAVAILABLE);
2001-07-26 17:05:04 -07:00
}
}
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");
2003-10-05 18:08:18 -07:00
/* 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);
2001-07-26 17:05:04 -07:00
/* if the host is not down, finish the connection, and force
* the correct attachment for the user
*/
2003-09-22 13:49:53 -07:00
if (fIn != '-') {
2001-07-26 17:05:04 -07:00
if (fIn == 'r') {
2003-09-22 13:49:53 -07:00
if (*pcHow != 's') {
2001-07-26 17:05:04 -07:00
Error("%s is read-only", pcMach);
}
2003-09-22 13:49:53 -07:00
} else if (fIn != (*pcHow == 'f' ? 'a' : *pcHow)) {
2003-11-16 11:33:39 -08:00
FilePrint(pcf, FLAGFALSE, "%c%c%c", chAttn, chEsc, *pcHow);
2001-07-26 17:05:04 -07:00
}
if (fReplay) {
2003-11-16 11:33:39 -08:00
FilePrint(pcf, FLAGFALSE, "%c%cr", chAttn, chEsc);
2001-07-26 17:05:04 -07:00
} else if (fVerbose) {
2003-11-16 11:33:39 -08:00
FilePrint(pcf, FLAGFALSE, "%c%c\022", chAttn, chEsc);
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
2003-12-02 08:40:59 -08:00
/* set socket to non-blocking */
SetFlags(FileFDNum(pcf), O_NONBLOCK, 0);
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-12-02 08:40:59 -08:00
FD_ZERO(&winit);
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);
2003-09-22 13:49:53 -07:00
if (maxfd < FileFDNum(pcf) + 1)
maxfd = FileFDNum(pcf) + 1;
2001-07-26 17:05:04 -07:00
for (;;) {
2002-03-12 01:12:20 -08:00
justProcessedUrg = 0;
2001-07-26 17:05:04 -07:00
if (SawUrg) {
2003-12-02 08:40:59 -08:00
ProcessUrgentData(pcf);
2002-03-12 01:12:20 -08:00
justProcessedUrg = 1;
2001-07-26 17:05:04 -07:00
}
2003-12-02 08:40:59 -08:00
if (execCmd != (STRING *)0 && execCmd->used > 1) {
char *r;
char s = OB_EXEC;
ExecCmd();
BuildString((char *)0, execCmd);
if (execCmdFile == (CONSFILE *)0) { /* exec failed */
s = OB_DROP;
FileWrite(pcf, FLAGFALSE, &s, 1); /* say forget it */
} else {
/* go back to blocking mode */
SetFlags(FileFDNum(pcf), 0, O_NONBLOCK);
FileWrite(pcf, FLAGFALSE, &s, 1); /* say we're ready */
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);
kill(execCmdPid, SIGHUP);
}
}
}
if (fSawReapVirt) {
fSawReapVirt = 0;
ReapVirt();
}
2001-07-26 17:05:04 -07:00
/* reset read mask and select on it
*/
rmask = rinit;
2003-12-02 08:40:59 -08:00
wmask = winit;
2003-09-22 13:49:53 -07:00
if (-1 ==
2003-12-02 08:40:59 -08:00
select(maxfd, &rmask, &wmask, (fd_set *)0,
2003-09-22 13:49:53 -07:00
(struct timeval *)0)) {
if (errno != EINTR) {
Error("Master(): select(): %s", strerror(errno));
break;
2001-07-26 17:05:04 -07:00
}
2003-09-22 13:49:53 -07:00
continue;
2001-07-26 17:05:04 -07:00
}
2003-12-02 08:40:59 -08: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);
} else {
if (fStrip) {
for (i = 0; i < nc; ++i)
acMesg[i] &= 127;
}
FileWrite(pcf, FLAGFALSE, acMesg, nc);
}
} else if (!FileBufEmpty(execCmdFile) &&
FileCanWrite(execCmdFile, &rmask, &wmask)) {
CONDDEBUG((1, "CallUp(): flushing fd %d",
FileFDNum(execCmdFile)));
if (FileWrite(execCmdFile, FLAGFALSE, (char *)0, 0) < 0) {
/* -bryan */
break;
}
}
}
2001-07-26 17:05:04 -07:00
/* anything from socket? */
2003-12-02 08:40:59 -08:00
if (FileCanRead(pcf, &rmask, &wmask)) {
2003-09-22 13:49:53 -07:00
if ((nc = FileRead(pcf, acMesg, sizeof(acMesg))) < 0) {
/* if we got an error/eof after returning from suspend */
2002-03-12 01:12:20 -08:00
if (justProcessedUrg) {
2003-09-22 13:49:53 -07:00
fprintf(stderr, "\n");
2002-03-12 01:12:20 -08:00
Error("lost connection");
}
2001-07-26 17:05:04 -07:00
break;
}
if (fStrip) {
for (i = 0; i < nc; ++i)
acMesg[i] &= 127;
}
2003-10-05 18:08:18 -07:00
FileWrite(cfstdout, FLAGFALSE, acMesg, nc);
2003-12-02 08:40:59 -08:00
if (execCmdFile != (CONSFILE *)0) {
FileWrite(execCmdFile, FLAGFALSE, acMesg, nc);
}
} else if (!FileBufEmpty(pcf) && FileCanWrite(pcf, &rmask, &wmask)) {
CONDDEBUG((1, "CallUp(): flushing fd %d", FileFDNum(pcf)));
if (FileWrite(pcf, FLAGFALSE, (char *)0, 0) < 0) {
/* -bryan */
break;
}
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)) {
2003-09-22 13:49:53 -07:00
if ((nc = read(0, acMesg, sizeof(acMesg))) == 0) {
if (screwy)
break;
else {
FD_SET(0, &rinit);
continue;
}
}
2003-12-02 08:40:59 -08:00
if (execCmdFile == (CONSFILE *)0) {
if (fStrip) {
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);
}
}
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;
}
2003-09-22 13:49:53 -07: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 13:49:53 -07:00
char *cmds[4] = { (char *)0, (char *)0, (char *)0, (char *)0 };
char *cmdarg = (char *)0;
2003-09-22 13:49:53 -07: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 13:49:53 -07:00
int
2003-03-10 18:08:07 -08:00
#if PROTOTYPES
2003-09-22 13:49:53 -07:00
DoCmds(char *master, char *ports, int cmdi)
2002-03-12 01:12:20 -08:00
#else
2003-09-22 13:49:53 -07:00
DoCmds(master, ports, cmdi)
char *master;
char *ports;
int cmdi;
2002-03-12 01:12:20 -08:00
#endif
{
2003-09-22 13:49:53 -07:00
CONSFILE *pcf;
char *t;
char *next;
char *server;
unsigned short port;
char *result = (char *)0;
int len;
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;
if (*ports == '\000') {
port = htons(bindPort);
} else if (!isdigit((int)(ports[0]))) {
Error("invalid port spec for %s: `%s'", server, ports);
continue;
} else {
port = htons((short)atoi(ports));
2001-07-26 17:05:04 -07:00
}
2003-09-22 13:49:53 -07:00
attemptLogin:
if ((pcf = GetPort(server, port)) == (CONSFILE *)0)
continue;
2001-07-26 17:05:04 -07:00
2003-09-22 13:49:53 -07:00
t = ReadReply(pcf, 0);
if (strcmp(t, "ok\r\n") != 0) {
FileClose(&pcf);
2003-10-05 18:08:18 -07:00
FilePrint(cfstdout, FLAGFALSE, "%s: %s", server, t);
2001-07-26 17:05:04 -07:00
continue;
}
2003-09-22 13:49:53 -07:00
#if HAVE_OPENSSL
2003-10-05 18:08:18 -07:00
FileWrite(pcf, FLAGFALSE, "ssl\r\n", 5);
2003-09-22 13:49:53 -07:00
t = ReadReply(pcf, 0);
if (strcmp(t, "ok\r\n") == 0) {
AttemptSSL(pcf);
2001-07-26 17:05:04 -07:00
}
2003-09-22 13:49:53 -07:00
if (fReqEncryption && FileGetType(pcf) != SSLSocket) {
Error("Encryption not supported by server `%s'", server);
FileClose(&pcf);
2001-07-26 17:05:04 -07:00
continue;
}
2003-09-22 13:49:53 -07:00
#endif
2003-11-16 11:33:39 -08:00
FilePrint(pcf, FLAGFALSE, "login %s\r\n", pcUser);
2003-09-22 13:49:53 -07:00
t = ReadReply(pcf, 0);
2003-09-29 08:50:27 -07:00
if (strncmp(t, "passwd?", 7) == 0) {
2003-09-22 13:49:53 -07:00
static int count = 0;
static STRING *tmpString = (STRING *)0;
2003-09-29 08:50:27 -07:00
char *hostname = (char *)0;
if (t[7] == ' ') {
hostname = PruneSpace(t + 7);
if (*hostname == '\000')
hostname = server;
} else
hostname = server;
2003-09-22 13:49:53 -07:00
if (tmpString == (STRING *)0)
tmpString = AllocString();
if (tmpString->used <= 1) {
char *pass;
2003-11-16 11:33:39 -08:00
BuildStringPrint(tmpString, "Enter %s@%s's password: ",
pcUser, hostname);
pass = GetPassword(tmpString->string);
2003-09-22 13:49:53 -07:00
if (pass == (char *)0) {
Error("could not get password from tty for `%s'",
server);
FileClose(&pcf);
2001-07-26 17:05:04 -07:00
continue;
2003-09-22 13:49:53 -07:00
}
2003-11-16 11:33:39 -08:00
BuildString((char *)0, tmpString);
2003-09-22 13:49:53 -07:00
BuildString(pass, tmpString);
BuildString("\r\n", tmpString);
}
2003-10-05 18:08:18 -07:00
FileWrite(pcf, FLAGFALSE, tmpString->string,
tmpString->used - 1);
2003-09-22 13:49:53 -07:00
t = ReadReply(pcf, 0);
if (strcmp(t, "ok\r\n") != 0) {
2003-10-05 18:08:18 -07:00
FilePrint(cfstdout, FLAGFALSE, "%s: %s", server, t);
2003-09-22 13:49:53 -07:00
if (++count < 3) {
BuildString((char *)0, tmpString);
goto attemptLogin;
}
Error("too many bad passwords for `%s'", server);
count = 0;
FileClose(&pcf);
2001-07-26 17:05:04 -07:00
continue;
2003-09-22 13:49:53 -07:00
} else
count = 0;
2001-07-26 17:05:04 -07:00
}
2003-09-22 13:49:53 -07: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-05 18:08:18 -07:00
FilePrint(pcf, FLAGFALSE, "%s %s\r\n", cmds[cmdi], cmdarg);
2003-09-22 13:49:53 -07:00
else
2003-10-05 18:08:18 -07:00
FilePrint(pcf, FLAGFALSE, "%s\r\n", cmds[cmdi]);
2003-09-22 13:49:53 -07: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 */
2003-10-05 18:08:18 -07:00
if ((result = StrDup(t)) == (char *)0)
2003-09-22 13:49:53 -07:00
OutOfMem();
}
2003-09-22 13:49:53 -07:00
/* if we're working on finding a console */
if (cmds[cmdi][0] == 'c') {
/* did we get a redirect? */
if (result[0] == '@' || (result[0] >= '0' && result[0] <= '9')) {
static int limit = 0;
if (limit++ > 10) {
Error("forwarding level too deep!");
2003-09-29 08:50:27 -07:00
Bye(EX_SOFTWARE);
2003-09-22 13:49:53 -07:00
}
} else if (result[0] != '[') { /* did we not get a connection? */
2003-10-05 18:08:18 -07:00
FilePrint(cfstdout, FLAGFALSE, "%s: %s", server, result);
2003-09-22 13:49:53 -07:00
FileClose(&pcf);
continue;
} else {
/* right now, we can only connect to one console, so it's ok
* to clear the password. if we were allowed to connect to
* multiple consoles (somehow), either in parallel or serial,
* we wouldn't want to do this here */
ClearPassword();
CallUp(pcf, server, cmdarg, cmds[0], result);
return 0;
}
} else if (cmds[cmdi][0] == 'q') {
t = ReadReply(pcf, 0);
2003-10-05 18:08:18 -07:00
FileWrite(cfstdout, FLAGFALSE, t, -1);
2003-09-22 13:49:53 -07:00
if (t[0] != 'o' || t[1] != 'k') {
2003-10-05 18:08:18 -07:00
FileWrite(pcf, FLAGFALSE, "exit\r\n", 6);
2003-09-22 13:49:53 -07:00
t = ReadReply(pcf, 1);
}
} else {
/* all done */
2003-10-05 18:08:18 -07:00
FileWrite(pcf, FLAGFALSE, "exit\r\n", 6);
2003-09-22 13:49:53 -07:00
t = ReadReply(pcf, cmdi == 0 ? 1 : 0);
if (cmdi == 0) {
int len;
/* if we hit bottom, this is where we get our results */
2003-10-05 18:08:18 -07:00
if ((result = StrDup(t)) == (char *)0)
2003-09-22 13:49:53 -07:00
OutOfMem();
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') {
2003-10-05 18:08:18 -07:00
FileWrite(cfstdout, FLAGTRUE, server, -1);
FileWrite(cfstdout, FLAGTRUE, ": ", 2);
FileWrite(cfstdout, FLAGFALSE, result, len);
2003-09-22 13:49:53 -07:00
} else {
disconnectCount += atoi(result + 19);
}
} else if ((cmds[0][0] != 'b' && cmds[0][0] != 't') ||
(result[0] != 'o' || result[1] != 'k')) {
/* did a 'master' before this or doing a 'disconnect' */
if (cmds[1][0] == 'm' || cmds[0][0] == 'd') {
2003-10-05 18:08:18 -07:00
FileWrite(cfstdout, FLAGTRUE, server, -1);
FileWrite(cfstdout, FLAGTRUE, ": ", 2);
2003-09-22 13:49:53 -07:00
}
2003-10-05 18:08:18 -07:00
FileWrite(cfstdout, FLAGFALSE, result, len);
2003-09-22 13:49:53 -07:00
}
}
}
2001-07-26 17:05:04 -07:00
2003-09-22 13:49:53 -07:00
FileClose(&pcf);
2001-07-26 17:05:04 -07:00
2003-09-22 13:49:53 -07:00
/* this would only be true if we got extra redirects (@... above) */
if (cmds[cmdi][0] == 'c')
DoCmds(server, result, cmdi);
else if (cmdi > 0)
DoCmds(server, result, cmdi - 1);
free(result);
2001-07-26 17:05:04 -07: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-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
{
2003-09-22 13:49:53 -07:00
char *pcCmd;
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-09-22 13:49:53 -07:00
static STRING *acPorts = (STRING *)0;
static char acOpts[] = "7aAb:B:c:d:De:EfFhiIl:M:p:PqQrRsSt:uvVwWx";
2001-07-26 17:05:04 -07:00
extern int optind;
extern int optopt;
extern char *optarg;
int i;
2003-09-22 13:49:53 -07:00
STRING *textMsg = (STRING *)0;
int cmdi;
int retval;
2003-03-10 18:08:07 -08:00
isMultiProc = 0; /* make sure stuff DOESN'T have the pid */
2001-07-26 17:05:04 -07:00
2003-09-22 13:49:53 -07:00
if (textMsg == (STRING *)0)
textMsg = AllocString();
if (acPorts == (STRING *)0)
2003-03-10 18:08:07 -08:00
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;
2003-09-22 13:49:53 -07:00
while ((opt = getopt(argc, argv, acOpts)) != EOF) {
2001-07-26 17:05:04 -07:00
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 */
2003-09-22 13:49:53 -07:00
fLocal = 1;
2003-03-10 18:08:07 -08:00
/* fall through */
2001-07-26 17:05:04 -07:00
case 'b':
pcCmd = "broadcast";
2003-09-22 13:49:53 -07:00
cmdarg = optarg;
2001-07-26 17:05:04 -07:00
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;
2003-09-22 13:49:53 -07:00
case 'd':
pcCmd = "disconnect";
cmdarg = optarg;
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;
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;
2003-09-22 13:49:53 -07:00
case 't':
BuildString((char *)0, textMsg);
if (optarg == (char *)0 || *optarg == '\000') {
Error("no destination specified for -t", optarg);
2003-09-29 08:50:27 -07:00
Bye(EX_UNAVAILABLE);
2003-09-22 13:49:53 -07:00
} else if (strchr(optarg, ' ') != (char *)0) {
Error("-t option cannot contain a space: `%s'",
optarg);
2003-09-29 08:50:27 -07:00
Bye(EX_UNAVAILABLE);
2003-09-22 13:49:53 -07:00
}
BuildString("textmsg ", textMsg);
BuildString(optarg, textMsg);
pcCmd = textMsg->string;
break;
2001-07-26 17:05:04 -07:00
case 'u':
2003-09-22 13:49:53 -07:00
pcCmd = "hosts";
2001-07-26 17:05:04 -07:00
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 */
2003-09-22 13:49:53 -07:00
pcCmd = "group";
2001-07-26 17:05:04 -07:00
break;
case 'x':
2003-09-22 13:49:53 -07:00
pcCmd = "examine";
2001-07-26 17:05:04 -07:00
break;
case 'v':
fVerbose = 1;
break;
case 'V':
fVersion = 1;
break;
2003-03-10 18:08:07 -08:00
case 'h': /* huh? */
Usage(1);
2003-09-29 08:50:27 -07:00
Bye(EX_OK);
2003-03-10 18:08:07 -08:00
case '\?': /* huh? */
Usage(0);
2003-09-29 08:50:27 -07:00
Bye(EX_UNAVAILABLE);
2003-03-10 18:08:07 -08:00
default:
Error("option %c needs a parameter", optopt);
2003-09-29 08:50:27 -07:00
Bye(EX_UNAVAILABLE);
2001-07-26 17:05:04 -07:00
}
}
if (fVersion) {
Version();
2003-09-29 08:50:27 -07:00
Bye(EX_OK);
2001-07-26 17:05:04 -07:00
}
2003-09-22 13:49:53 -07:00
/* finish resolving the command to do */
if (pcCmd == (char *)0) {
pcCmd = "attach";
}
if (*pcCmd == 'a' || *pcCmd == 'f' || *pcCmd == 's') {
if (optind >= argc) {
Error("missing console name");
2003-09-29 08:50:27 -07:00
Bye(EX_UNAVAILABLE);
2003-09-22 13:49:53 -07:00
}
cmdarg = argv[optind++];
} else if (*pcCmd == 't') {
if (optind >= argc) {
Error("missing message text");
2003-09-29 08:50:27 -07:00
Bye(EX_UNAVAILABLE);
2003-09-22 13:49:53 -07:00
}
cmdarg = argv[optind++];
}
if (optind < argc) {
Error("extra garbage on command line? (%s...)", argv[optind]);
2003-09-29 08:50:27 -07:00
Bye(EX_UNAVAILABLE);
2001-07-26 17:05:04 -07:00
}
2003-09-22 13:49:53 -07:00
/* if we somehow lost the port (or got an empty string), reset */
if (pcPort == (char *)0 || pcPort[0] == '\000')
pcPort = DEFPORT;
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);
} else {
/* non-numeric only */
struct servent *pSE;
2003-09-22 13:49:53 -07:00
if ((pSE = getservbyname(pcPort, "tcp")) == (struct servent *)0) {
2003-11-16 11:33:39 -08:00
Error("getservbyname(%s) failed", pcPort);
2003-09-29 08:50:27 -07:00
Bye(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));
2003-09-29 08:50:27 -07:00
Bye(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()));
2003-09-29 08:50:27 -07:00
Bye(EX_UNAVAILABLE);
2002-10-14 14:03:35 -07:00
} else {
pcUser = pwdMe->pw_name;
}
}
2001-07-26 17:05:04 -07:00
}
2003-12-02 08:40:59 -08:00
if (execCmd == (STRING *)0)
execCmd = AllocString();
2001-07-26 17:05:04 -07:00
2003-09-22 13:49:53 -07:00
SimpleSignal(SIGPIPE, SIG_IGN);
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
2003-09-22 13:49:53 -07:00
/* stack up the commands for DoCmds() */
cmdi = -1;
cmds[++cmdi] = pcCmd;
if (*pcCmd == 'q' || *pcCmd == 'v' || *pcCmd == 'p') {
if (!fLocal)
cmds[++cmdi] = "master";
} else if (*pcCmd == 'a' || *pcCmd == 'f' || *pcCmd == 's') {
2001-07-26 17:05:04 -07:00
ValidateEsc();
2003-09-22 13:49:53 -07:00
cmds[++cmdi] = "call";
2001-07-26 17:05:04 -07:00
} else {
2003-09-22 13:49:53 -07:00
cmds[++cmdi] = "groups";
if (!fLocal)
cmds[++cmdi] = "master";
2001-07-26 17:05:04 -07:00
}
2003-09-22 13:49:53 -07:00
retval = DoCmds(pcInMaster, acPorts->string, cmdi);
if (*pcCmd == 'd')
2003-10-05 18:08:18 -07:00
FilePrint(cfstdout, FLAGFALSE, "Disconnected %d users\n",
disconnectCount);
2003-09-22 13:49:53 -07:00
2003-09-29 08:50:27 -07:00
Bye(retval);
return 0; /* noop - Bye() terminates us */
}