conserver/conserver/consent.c
1999-01-13 14:38:26 -08:00

718 lines
16 KiB
C

/*
* $Id: consent.c,v 5.33 1999-01-13 11:52:21-08 bryan Exp $
*
* GNAC, Inc., 1998
*
* Maintainer/Enhancer: Bryan Stansell (bryan@gnac.com)
*/
/*
* Copyright 1992 Purdue Research Foundation, West Lafayette, Indiana
* 47907. All rights reserved.
*
* Written by Kevin S Braunsdorf, ksb@cc.purdue.edu, purdue!ksb
*
* This software is not subject to any license of the American Telephone
* and Telegraph Company or the Regents of the University of California.
*
* Permission is granted to anyone to use this software for any purpose on
* any computer system, and to alter it and redistribute it freely, subject
* to the following restrictions:
*
* 1. Neither the authors nor Purdue University are responsible for any
* consequences of the use of this software.
*
* 2. The origin of this software must not be misrepresented, either by
* explicit claim or by omission. Credit to the authors and Purdue
* University must appear in documentation and sources.
*
* 3. Altered versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
*
* 4. This notice may not be removed or altered.
*/
/*
* Network console modifications by Robert Olson, olson@mcs.anl.gov.
*/
#ifndef lint
static char copyright[] =
"@(#) Copyright 1992 Purdue Research Foundation.\nAll rights reserved.\n";
#endif
#include <sys/types.h>
#include <sys/param.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <sys/file.h>
#include <sys/stat.h>
#include <sys/resource.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <netdb.h>
#include <stdio.h>
#include <ctype.h>
#include <errno.h>
#include <signal.h>
#include <pwd.h>
#include "cons.h"
#include "port.h"
#include "consent.h"
#include "client.h"
#include "main.h"
#if USE_TERMIO
#include <termio.h>
#else
#if USE_TERMIOS
#include <termios.h>
#include <unistd.h>
#else /* use ioctl stuff */
#include <sgtty.h>
#include <sys/ioctl.h>
#endif
#endif
#if USE_STREAMS
#include <stropts.h>
#endif
#if USE_STRINGS
#include <strings.h>
#else
#include <string.h>
#endif
BAUD baud [] = {
{ "Netwk", 0 },
#if defined(B38400)
{ "38400", B38400 },
#endif
#if defined(B19200)
{ "19200", B19200 },
#endif
{ "9600", B9600 },
{ "4800", B4800 },
{ "2400", B2400 },
#if defined(B1800)
{ "1800", B1800 },
#endif
{ "1200", B1200 },
#if defined(B600)
{ "600", B600 },
#endif
};
/* find a baud rate for the string "9600x" -> B9600 (ksb)
*/
BAUD *
FindBaud(pcMode)
char *pcMode;
{
register int i;
for (i = 0; i < sizeof(baud)/sizeof(struct baud); ++i) {
if (0 != strncmp(pcMode, baud[i].acrate, strlen(baud[i].acrate)))
continue;
return baud+i;
}
return baud;
}
PARITY parity[] = {
#if USE_TERMIOS
#if !defined(PAREXT)
#define PAREXT 0
#endif
{' ', 0, 0}, /* Blank for network */
{'e', PARENB|CS7, 0}, /* even */
{'m', PARENB|CS7|PARODD|PAREXT, 0}, /* mark */
{'o', PARENB|CS7|PARODD, 0}, /* odd */
{'p', CS8, 0}, /* pass 8 bits, no parity */
{'s', PARENB|CS7|PAREXT, 0}, /* space */
#else
{' ', 0, 0}, /* Blank for network */
{'e', EVENP, ODDP}, /* even */
{'m', EVENP|ODDP, 0}, /* mark */
{'o', ODDP, EVENP}, /* odd */
#if defined(PASS8)
{'p', PASS8,EVENP|ODDP},/* pass 8 bits, no parity */
#endif
{'s', 0, EVENP|ODDP} /* space */
#endif
};
/* find a parity on the end of a baud "9600even" -> EVEN (ksb)
*/
PARITY *
FindParity(pcMode)
char *pcMode;
{
register int i;
auto char acFirst;
while (isdigit(*pcMode)) {
++pcMode;
}
acFirst = *pcMode;
if (isupper(acFirst))
acFirst = tolower(acFirst);
for (i = 0; i < sizeof(parity)/sizeof(struct parity); ++i) {
if (acFirst != parity[i].ckey)
continue;
return parity+i;
}
return parity;
}
#if USE_TERMIOS
/* setup a tty device (ksb)
*/
static int
TtyDev(pCE)
CONSENT *pCE;
{
struct termios termp;
auto struct stat stPerm;
/* here we should fstat for `read-only' checks
*/
if (-1 == fstat(pCE->fdtty, & stPerm)) {
fprintf(stderr, "%s: fstat: %s: %s\n", progname, pCE->dfile, strerror(errno));
} else if (0 == (stPerm.st_mode & 0222)) {
/* any device that is read-only we won't write to
*/
pCE->fronly = 1;
}
/*
* Get terminal attributes
*/
if (-1 == tcgetattr(pCE->fdtty, &termp)) {
fprintf(stderr, "%s: tcgetattr: %s(%d): %s\n", progname, pCE->dfile, pCE->fdtty, strerror(errno));
return -1;
}
/*
* Turn off: echo
* icrnl
* opost No post processing
* icanon No line editing
* isig No signal generation
* Turn on: ixoff
*/
termp.c_iflag = IXON|IXOFF|BRKINT;
termp.c_oflag = 0;
termp.c_cflag = CREAD;
termp.c_cflag |= pCE->pparity->iset;
termp.c_lflag = 0;
/*
* Set the VMIN == 128
* Set the VTIME == 1 (0.1 sec)
* Don't bother with the control characters as they are not used
*/
termp.c_cc[VMIN] = 128;
termp.c_cc[VTIME] = 1;
if (-1 == cfsetospeed(&termp,pCE->pbaud->irate)) {
fprintf(stderr, "%s: cfsetospeed: %s(%d): %s\n", progname, pCE->dfile, pCE->fdtty, strerror(errno));
return -1;
}
if (-1 == cfsetispeed(&termp,pCE->pbaud->irate)) {
fprintf(stderr, "%s: cfsetispeed: %s(%d): %s\n", progname, pCE->dfile, pCE->fdtty, strerror(errno));
return -1;
}
/*
* Set terminal attributes
*/
if (-1 == tcsetattr(pCE->fdtty, TCSADRAIN, &termp)) {
fprintf(stderr, "%s: tcsetattr: %s(%d): %s\n", progname, pCE->dfile, pCE->fdtty, strerror(errno));
return -1;
}
#if USE_STREAMS
/*
* eat all the streams modules upto and including ttcompat
*/
while (ioctl(pCE->fdtty, I_FIND, "ttcompat") == 0) {
(void)ioctl(pCE->fdtty, I_POP, 0);
}
#endif
pCE->fup = 1;
return 0;
}
#else
/* setup a tty device (ksb)
*/
static int
TtyDev(pCE)
CONSENT *pCE;
{
struct sgttyb sty;
struct tchars m_tchars;
struct ltchars m_ltchars;
auto struct stat stPerm;
/* here we should fstat for `read-only' checks
*/
if (-1 == fstat(pCE->fdtty, & stPerm)) {
fprintf(stderr, "%s: fstat: %s: %s\n", progname, pCE->dfile, strerror(errno));
} else if (0 == (stPerm.st_mode & 0222)) {
/* any device that is read-only we won't write to
*/
pCE->fronly = 1;
}
#if USE_SOFTCAR
#if defined(TIOCSSOFTCAR)
if (-1 == ioctl(pCE->fdtty, TIOCSSOFTCAR, &fSoftcar)) {
fprintf(stderr, "%s: softcar: %d: %s\n", progname, pCE->fdtty, strerror(errno));
return -1;
}
#endif
#endif
/* stty 9600 raw cs7
*/
if (-1 == ioctl(pCE->fdtty, TIOCGETP, (char *)&sty)) {
fprintf(stderr, "%s: ioctl1: %s(%d): %s\n", progname, pCE->dfile, pCE->fdtty, strerror(errno));
return -1;
}
sty.sg_flags &= ~(ECHO|CRMOD|pCE->pparity->iclr);
sty.sg_flags |= (CBREAK|TANDEM|pCE->pparity->iset);
sty.sg_erase = -1;
sty.sg_kill = -1;
sty.sg_ispeed = pCE->pbaud->irate;
sty.sg_ospeed = pCE->pbaud->irate;
if (-1 == ioctl(pCE->fdtty, TIOCSETP, (char *)&sty)) {
fprintf(stderr, "%s: ioctl2: %d: %s\n", progname, pCE->fdtty, strerror(errno));
return -1;
}
/* stty undef all tty chars
* (in cbreak mode we may not need to this... but we do)
*/
if (-1 == ioctl(pCE->fdtty, TIOCGETC, (char *)&m_tchars)) {
fprintf(stderr, "%s: ioctl3: %d: %s\n", progname, pCE->fdtty, strerror(errno));
return -1;
}
m_tchars.t_intrc = -1;
m_tchars.t_quitc = -1;
m_tchars.t_startc = -1;
m_tchars.t_stopc = -1;
m_tchars.t_eofc = -1;
m_tchars.t_brkc = -1;
if (-1 == ioctl(pCE->fdtty, TIOCSETC, (char *)&m_tchars)) {
fprintf(stderr, "%s: ioctl4: %d: %s\n", progname, pCE->fdtty, strerror(errno));
return -1;
}
if (-1 == ioctl(pCE->fdtty, TIOCGLTC, (char *)&m_ltchars)) {
fprintf(stderr, "%s: ioctl5: %d: %s\n", progname, pCE->fdtty, strerror(errno));
return -1;
}
m_ltchars.t_werasc = -1;
m_ltchars.t_flushc = -1;
m_ltchars.t_lnextc = -1;
m_ltchars.t_suspc = -1;
m_ltchars.t_dsuspc = -1;
if (-1 == ioctl(pCE->fdtty, TIOCSLTC, (char *)&m_ltchars)) {
fprintf(stderr, "%s: ioctl6: %d: %s\n", progname, pCE->fdtty, strerror(errno));
return -1;
}
#if USE_STREAMS
/* pop off the un-needed streams modules (on a sun3 machine esp.)
* (Idea by jrs@ecn.purdue.edu)
*/
while (ioctl(pCE->fdtty, I_POP, 0) == 0) {
/* eat all the streams modules */;
}
#endif
pCE->fup = 1;
return 0;
}
#endif
#if DO_VIRTUAL
/* setup a virtual device (ksb)
*/
static int
VirtDev(pCE)
CONSENT *pCE;
{
#if USE_TERMIOS
static struct termios n_tio;
#else
auto struct sgttyb sty;
auto struct tchars m_tchars;
auto struct ltchars m_ltchars;
#endif
#if HAVE_RLIMIT
auto struct rlimit rl;
#endif
auto int i, iNewGrp;
auto int fd;
extern char **environ;
register char *pcShell, **ppcArgv;
(void)fflush(stdout);
switch (pCE->ipid = fork()) {
case -1:
return -1;
case 0:
break;
default:
fprintf(stderr, "%s: %d is the pid on %s\n", progname, pCE->ipid, pCE->acslave);
(void)fflush(stderr);
pCE->fup = 1;
sleep(2); /* chance to open line */
return 0;
}
/* put the signals back that we trap
*/
signal(SIGINT, SIG_DFL);
signal(SIGQUIT, SIG_DFL);
signal(SIGTSTP, SIG_DFL);
/* setup new process with clean filew descriptors
*/
#if HAVE_RLIMIT
getrlimit(RLIMIT_NOFILE, &rl);
i = rl.rlim_cur;
#else
i = getdtablesize();
#endif
for (/* i above */; i-- > 2; ) {
close(i);
}
/* leave 2 until we *have to close it*
*/
close(1);
close(0);
#if defined(TIOCNOTTY)
if (-1 != (i = open("/dev/tty", 2, 0))) {
ioctl(i, TIOCNOTTY, (char *)0);
close(i);
}
#endif
#if HAVE_SETSID
iNewGrp = setsid();
if (-1 == iNewGrp) {
fprintf(stderr, "%s: %s: setsid: %s\n", progname, pCE->server, strerror(errno));
iNewGrp = getpid();
}
#else
iNewGrp = getpid();
#endif
if (0 != open(pCE->acslave, 2, 0) || 1 != dup(0)) {
fprintf(stderr, "%s: %s: fd sync error\n", progname, pCE->server);
exit(1);
}
#if HAVE_PTSNAME
/* SYSVr4 semantics for opening stream ptys (gregf)
* under PTX (others?) we have to push the compatibility
* streams modules `ptem' and `ld'
*/
(void)ioctl(0, I_PUSH, "ptem");
(void)ioctl(0, I_PUSH, "ld");
#endif
#if HAVE_LDTERM
(void)ioctl(0, I_PUSH, "ptem");
(void)ioctl(0, I_PUSH, "ldterm");
#endif
#if HAVE_STTY_LD
(void)ioctl(0, I_PUSH, "stty_ld");
#endif
#if USE_TERMIOS
if (0 != ioctl(0, TCGETS, & n_tio)) {
fprintf(stderr, "%s: iotcl: getsw: %s\n", progname, strerror(errno));
exit(1);
}
n_tio.c_iflag &= ~(IGNCR|IUCLC);
n_tio.c_iflag |= ICRNL|IXON|IXANY;
n_tio.c_oflag &= ~(OLCUC|ONOCR|ONLRET|OFILL|NLDLY|CRDLY|TABDLY|BSDLY);
n_tio.c_oflag |= OPOST|ONLCR;
n_tio.c_lflag &= ~(XCASE|NOFLSH|ECHOK|ECHONL);
n_tio.c_lflag |= ISIG|ICANON|ECHO;
n_tio.c_cc[VEOF] = '\004';
n_tio.c_cc[VEOL] = '\000';
n_tio.c_cc[VERASE] = '\010';
n_tio.c_cc[VINTR] = '\003';
n_tio.c_cc[VKILL] = '@';
/* MIN */
n_tio.c_cc[VQUIT] = '\034';
n_tio.c_cc[VSTART] = '\021';
n_tio.c_cc[VSTOP] = '\023';
n_tio.c_cc[VSUSP] = '\032';
if (0 != ioctl(0, TCSETS, & n_tio)) {
fprintf(stderr, "%s: getarrt: %s\n", progname, strerror(errno));
exit(1);
}
tcsetpgrp(0, iNewGrp);
#else
/* stty 9600 raw cs7
*/
if (-1 == ioctl(0, TIOCGETP, (char *)&sty)) {
fprintf(stderr, "%s: ioctl1: %s: %s\n", progname, pCE->fdtty, strerror(errno));
exit(1);
}
sty.sg_flags &= ~(CBREAK|TANDEM|pCE->pparity->iclr);
sty.sg_flags |= (ECHO|CRMOD|pCE->pparity->iset);
sty.sg_erase = '\b';
sty.sg_kill = '\025';
sty.sg_ispeed = pCE->pbaud->irate;
sty.sg_ospeed = pCE->pbaud->irate;
if (-1 == ioctl(0, TIOCSETP, (char *)&sty)) {
fprintf(stderr, "%s: ioctl2: %s\n", progname, strerror(errno));
exit(1);
}
/* stty undef all tty chars
* (in cbreak mode we may not need to this... but we do)
*/
if (-1 == ioctl(0, TIOCGETC, (char *)&m_tchars)) {
fprintf(stderr, "%s: ioctl3: %s\n", progname, strerror(errno));
exit(1);
}
m_tchars.t_intrc = '\003';
m_tchars.t_quitc = '\034';
m_tchars.t_startc = '\021';
m_tchars.t_stopc = '\023';
m_tchars.t_eofc = '\004';
m_tchars.t_brkc = '\033';
if (-1 == ioctl(0, TIOCSETC, (char *)&m_tchars)) {
fprintf(stderr, "%s: ioctl4: %s\n", progname, strerror(errno));
exit(1);
}
if (-1 == ioctl(0, TIOCGLTC, (char *)&m_ltchars)) {
fprintf(stderr, "%s: ioctl5: %s\n", progname, strerror(errno));
exit(1);
}
m_ltchars.t_werasc = '\027';
m_ltchars.t_flushc = '\017';
m_ltchars.t_lnextc = '\026';
m_ltchars.t_suspc = '\032';
m_ltchars.t_dsuspc = '\031';
if (-1 == ioctl(0, TIOCSLTC, (char *)&m_ltchars)) {
fprintf(stderr, "%s: ioctl6: %s\n", progname, strerror(errno));
exit(1);
}
/* give us a process group to work in
*/
ioctl(0, TIOCGPGRP, (char *)&i);
setpgrp(0, i);
ioctl(0, TIOCSPGRP, (char *)&iNewGrp);
setpgrp(0, iNewGrp);
#endif
close(2);
(void)dup(1); /* better be 2, but it is too late now */
/* if the command is null we should run root's shell, directly
* if we can't find root's shell run /bin/sh
*/
pcShell = "/bin/sh";
if ('\000' == pCE->pccmd[0]) {
static char *apcArgv[] = {
"-shell", "-i", (char *)0
};
register struct passwd *pwd;
if ((struct passwd *)0 != (pwd = getpwuid(0)) && '\000' != pwd->pw_shell[0]) {
pcShell = pwd->pw_shell;
}
ppcArgv = apcArgv;
} else {
static char *apcArgv[] = {
"/bin/sh", "-ce", (char *)0, (char *)0
};
apcArgv[2] = pCE->pccmd;
ppcArgv = apcArgv;
}
execve(pcShell, ppcArgv, environ);
fprintf(stderr, "execve: %s\n", strerror(errno));
exit(1);
/*NOTREACHED*/
}
#endif
/* down a console, virtual or real (ksb)
*/
void
ConsDown(pCE, pfdSet)
CONSENT *pCE;
fd_set *pfdSet;
{
#if DO_VIRTUAL
if (-1 != pCE->ipid) {
if (-1 != kill(pCE->ipid, SIGHUP))
sleep(1);
pCE->ipid = -1;
}
#endif
if (-1 != pCE->fdtty) {
FD_CLR(pCE->fdtty, pfdSet);
#if DO_VIRTUAL
if (0 == pCE->fvirtual) {
(void)close(pCE->fdtty);
pCE->fdtty = -1;
}
#else
(void)close(pCE->fdtty);
pCE->fdtty = -1;
#endif
}
if (-1 != pCE->fdlog) {
if (pCE->nolog) {
CSTROUT(pCE->fdlog, "[Console logging restored]\r\n");
}
(void)close(pCE->fdlog);
pCE->fdlog = -1;
}
pCE->fup = 0;
pCE->nolog = 0;
}
/* set up a console the way it should be for use to work with it (ksb)
* also, recover from silo over runs by dropping the line and re-opening
* We also maintian the select set for the caller.
*/
void
ConsInit(pCE, pfdSet)
CONSENT *pCE;
fd_set *pfdSet;
{
/* clean up old stuff
*/
ConsDown(pCE, pfdSet);
pCE->fronly = 0;
pCE->nolog = 0;
(void)strcpy(pCE->acline, pCE->server);
pCE->inamelen = strlen(pCE->server);
pCE->acline[pCE->inamelen++] = ':';
pCE->acline[pCE->inamelen++] = ' ';
pCE->iend = pCE->inamelen;
/* try to open them again
*/
if (-1 ==
(pCE->fdlog = open(pCE->lfile, O_RDWR|O_CREAT|O_APPEND, 0644)))
{
fprintf(stderr,
"%s: open: %s: %s\n",
progname, pCE->lfile, strerror(errno));
return;
}
#if DO_VIRTUAL
if (0 != pCE->fvirtual)
{
/* still open, never ever close it, but set the bit */
FD_SET(pCE->fdtty, pfdSet);
}
else if (pCE->isNetworkConsole)
{
struct sockaddr_in port;
struct hostent *hp;
int one = 1;
#if USLEEP_FOR_SLOW_PORTS
usleep( USLEEP_FOR_SLOW_PORTS ); /* Sleep for slow network ports */
#endif
bzero(&port, sizeof(port));
if ((hp = gethostbyname(pCE->networkConsoleHost)) == NULL)
{
fprintf(stderr, "%s: gethostbyname %s failed\n",
progname, pCE->networkConsoleHost);
exit(1);
}
bcopy(hp->h_addr, &port.sin_addr, hp->h_length);
port.sin_family = hp->h_addrtype;
port.sin_port = htons(pCE->networkConsolePort);
if ((pCE->fdtty = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
fprintf(stderr, "%s: socket: %s\n", progname, strerror(errno));
exit(1);
}
if (setsockopt(pCE->fdtty, SOL_SOCKET, SO_KEEPALIVE,
(char *) &one, sizeof(one)) < 0)
{
fprintf(stderr, "%s: setsockopt SO_KEEPALIVE: %s\n",
progname, strerror(errno));
}
if (connect(pCE->fdtty,
(struct sockaddr *)&port, sizeof(port)) < 0)
{
fprintf(stderr, "%s: connect: %s (%d@%s): %s: forcing down\n",
progname, pCE->server, ntohs(port.sin_port),
pCE->networkConsoleHost, strerror(errno));
ConsDown(pCE, pfdSet);
return;
}
/*
* Poke the connection to get the annex to wake up and
* register this connection.
*/
#ifdef POKE_ANNEX
write(pCE->fdtty, "\r\n", 2);
#endif
} else if (-1 == (pCE->fdtty = open(pCE->dfile, O_RDWR|O_NDELAY, 0600))) {
fprintf(stderr, "%s: open: %s: %s\n", progname, pCE->dfile, strerror(errno));
(void)close(pCE->fdlog);
pCE->fdlog = -1;
return;
}
FD_SET(pCE->fdtty, pfdSet);
/* ok, now setup the device
*/
if (pCE->fvirtual) {
VirtDev(pCE);
}
else if (pCE->isNetworkConsole)
{
pCE->fup = 1;
}
else
{
TtyDev(pCE);
}
#else
if (-1 == (pCE->fdtty = open(pCE->dfile, O_RDWR|O_NDELAY, 0600))) {
fprintf(stderr, "%s: open: %s: %s\n", progname, pCE->dfile, strerror(errno));
(void)close(pCE->fdlog);
pCE->fdlog = -1;
return;
}
FD_SET(pCE->fdtty, pfdSet);
/* ok, now setup the device
*/
TtyDev(pCE);
#endif
}