Commit 34499cb6 authored by Leigh Stoller's avatar Leigh Stoller

Checkpoint new version of capture/tip that is sockets based instead

of pty/tty based (since they have several annoying problems
associated). Note that permission is granted via the use of an "acl"
file; /dev/tip/machine.acl, which must be set to the group of the
project the node is in, so the user can read out the process id number
and the random bits that are used by capture to grant permission to
use (tip sends the random bits across first thing). This handshake is
due to change to a request/challenge scheme as described by Dave in
email to the testbed list.
parent 4b433e5d
......@@ -15,7 +15,7 @@ DESTDIR=
# Define LOG_DROPS to record warnings in syslog whenever chars were dropped
# due to the output device/pty being full.
#
CFLAGS= -g -O2 -DLOG_DROPS
CFLAGS= -g -O2 -DLOG_DROPS -DUSESOCKETS
capture: capture.c
cc $(CFLAGS) -o capture $<
......
......@@ -44,13 +44,19 @@
#include <signal.h>
#include <sys/ioctl.h>
#include <sys/termios.h>
#ifdef USESOCKETS
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#endif
#define geterr(e) strerror(e)
void quit(int);
void reinit(int);
void newrun(int);
void shutdown(int);
void terminate(int);
void cleanup(void);
void capture();
void usage();
......@@ -78,6 +84,7 @@ void usage();
#define RUNNAME "%s/%s.run"
#define TTYNAME "%s/tip/%s"
#define PTYNAME "%s/tip/%s-pty"
#define ACLNAME "%s/tip/%s.acl"
#define DEVNAME "%s/%s"
#define BUFSIZE 4096
#define DROP_THRESH (32*1024)
......@@ -92,6 +99,11 @@ char *Devname;
char *Machine;
int logfd, runfd, devfd, ptyfd;
int hwflow = 0, speed = B9600, debug = 0, runfile = 0;
#ifdef USESOCKETS
int sockfd, tipactive, portnum;
struct sockaddr_in tipclient;
int ACLbits[3];
#endif
int
main(argc, argv)
......@@ -102,6 +114,9 @@ main(argc, argv)
int flags, op, i;
extern int optind;
extern char *optarg;
#ifdef USESOCKETS
struct sockaddr_in name;
#endif
Progname = (Progname = rindex(argv[0], '/')) ? ++Progname : *argv;
......@@ -161,7 +176,7 @@ main(argc, argv)
signal(SIGHUP, reinit);
if (runfile)
signal(SIGUSR1, newrun);
signal(SIGUSR2, shutdown);
signal(SIGUSR2, terminate);
/*
* Open up run/log file, console tty, and controlling pty.
......@@ -177,9 +192,42 @@ main(argc, argv)
if (chmod(Runname, 0640) < 0)
die("%s: chmod: %s", Runname, geterr(errno));
}
#ifdef USESOCKETS
/*
* Create and bind our socket.
*/
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0)
die("socket(): opening stream socket: %s", geterr(errno));
i = 1;
if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR,
(char *)&i, sizeof(i)) < 0)
die("setsockopt(): SO_REUSEADDR: %s", geterr(errno));
/* Create wildcard name. */
name.sin_family = AF_INET;
name.sin_addr.s_addr = INADDR_ANY;
name.sin_port = 0;
if (bind(sockfd, (struct sockaddr *) &name, sizeof(name)))
die("bind(): binding stream socket: %s", geterr(errno));
/* Find assigned port value and print it out. */
i = sizeof(name);
if (getsockname(sockfd, (struct sockaddr *)&name, &i))
die("getsockname(): %s", geterr(errno));
portnum = ntohs(name.sin_port);
if (listen(sockfd, 1) < 0)
die("listen(): listening on socket: %s", geterr(errno));
genaclfile();
dolog(LOG_NOTICE, "listening on TCP port %d", portnum);
#else
if ((ptyfd = open(Ptyname, O_RDWR, 0666)) < 0)
die("%s: open: %s", Ptyname, geterr(errno));
#endif
if ((devfd = open(Devname, O_RDWR|O_NONBLOCK, 0666)) < 0)
die("%s: open: %s", Devname, geterr(errno));
......@@ -281,11 +329,13 @@ out()
}
}
#else
static fd_set sfds;
static int fdcount;
void
capture()
{
fd_set sfds, fds;
int n, i, cc, lcc, omask;
fd_set fds;
int i, cc, lcc, omask;
char buf[BUFSIZE];
struct timeval timeout;
#ifdef LOG_DROPS
......@@ -311,6 +361,7 @@ capture()
*/
if (fcntl(devfd, F_SETFL, O_NONBLOCK) < 0)
die("%s: fcntl(O_NONBLOCK): %s", Devname, geterr(errno));
#ifndef USESOCKETS
/*
* It gets better!
* In FreeBSD 4.0 and beyond, the fcntl fails because the slave
......@@ -331,14 +382,23 @@ capture()
#ifdef __FreeBSD__
close(n);
#endif
#endif /* USESOCKETS */
n = devfd;
if (devfd < ptyfd)
n = ptyfd;
++n;
FD_ZERO(&sfds);
FD_SET(devfd, &sfds);
fdcount = devfd;
#ifdef USESOCKETS
if (devfd < sockfd)
fdcount = sockfd;
FD_SET(sockfd, &sfds);
#else
if (devfd < ptyfd)
fdcount = ptyfd;
FD_SET(ptyfd, &sfds);
#endif /* USESOCKETS */
fdcount++;
for (;;) {
#ifdef LOG_DROPS
if (drop_topty_chars >= DROP_THRESH) {
......@@ -351,7 +411,7 @@ capture()
}
#endif
fds = sfds;
i = select(n, &fds, NULL, NULL, NULL);
i = select(fdcount, &fds, NULL, NULL, NULL);
if (i < 0) {
if (errno == EINTR) {
warn("input select interrupted, continuing");
......@@ -364,6 +424,12 @@ capture()
sleep(1);
continue;
}
#ifdef USESOCKETS
if (FD_ISSET(sockfd, &fds)) {
if (clientconnect())
continue;
}
#endif /* USESOCKETS */
if (FD_ISSET(devfd, &fds)) {
errno = 0;
cc = read(devfd, buf, sizeof(buf));
......@@ -375,6 +441,10 @@ capture()
omask = sigblock(sigmask(SIGHUP)|sigmask(SIGTERM)|
sigmask(SIGUSR1)|sigmask(SIGUSR2));
#ifdef USESOCKETS
if (!tipactive)
goto dropped;
#endif
for (lcc = 0; lcc < cc; lcc += i) {
i = write(ptyfd, &buf[lcc], cc-lcc);
if (i < 0) {
......@@ -394,8 +464,13 @@ capture()
die("%s: write: %s",
Ptyname, geterr(errno));
}
if (i == 0)
if (i == 0) {
#ifdef USESOCKETS
goto disconnected;
#else
die("%s: write: zero-length", Ptyname);
#endif
}
}
dropped:
i = write(logfd, buf, cc);
......@@ -426,8 +501,21 @@ dropped:
die("%s: read: %s", Ptyname, geterr(errno));
}
if (cc == 0) {
#ifdef USESOCKETS
disconnected:
/*
* Other end disconnected.
*/
dolog(LOG_INFO, "%s disconnecting",
inet_ntoa(tipclient.sin_addr));
FD_CLR(ptyfd, &sfds);
close(ptyfd);
tipactive = 0;
continue;
#else
select(0, 0, 0, 0, &timeout);
continue;
#endif
}
errno = 0;
......@@ -512,8 +600,22 @@ newrun(int sig)
* sure everyone is gone.
*/
void
shutdown(int sig)
terminate(int sig)
{
#ifdef USESOCKETS
genaclfile();
if (!tipactive)
return;
shutdown(ptyfd, SHUT_RDWR);
close(ptyfd);
FD_CLR(ptyfd, &sfds);
ptyfd = 0;
tipactive = 0;
dolog(LOG_INFO, "%s shutdown", inet_ntoa(tipclient.sin_addr));
#else
int ofd = ptyfd;
/*
......@@ -552,6 +654,7 @@ shutdown(int sig)
#endif
dolog(LOG_NOTICE, "pty reset");
#endif /* USESOCKETS */
}
/*
......@@ -583,12 +686,18 @@ die(format, arg0, arg1, arg2, arg3)
quit(0);
}
dolog(level, msg)
dolog(level, format, arg0, arg1, arg2, arg3)
int level;
char *format, *arg0, *arg1, *arg2, *arg3;
{
char msgbuf[BUFSIZE];
snprintf(msgbuf, BUFSIZE, "%s - %s", Machine, msg);
syslog(level, msgbuf);
snprintf(msgbuf, BUFSIZE, format, arg0, arg1, arg2, arg3);
if (debug) {
fprintf(stderr, "%s - %s\n", Machine, msgbuf);
fflush(stderr);
}
syslog(level, "%s - %s\n", Machine, msgbuf);
}
void
......@@ -790,3 +899,100 @@ val2speed(val)
return (0);
}
#ifdef USESOCKETS
int
clientconnect()
{
int length = sizeof(tipclient);
int newfd;
char bits[sizeof(ACLbits)];
newfd = accept(sockfd, (struct sockaddr *)&tipclient, &length);
if (newfd < 0)
die("accept(): accepting new client: %s", geterr(errno));
/*
* Is there a better way to do this? I suppose we
* could shut the main socket down, and recreate
* it later when the client disconnects, but that
* sounds horribly brutish!
*/
if (tipactive) {
close(newfd);
dolog(LOG_NOTICE, "%s connecting, but tip is active",
inet_ntoa(tipclient.sin_addr));
return 1;
}
ptyfd = newfd;
/*
* Read the first part to verify the ACLbits. We must get the proper
* bits or this is not a valid tip connection.
*/
if (read(ptyfd, bits, sizeof(bits)) != sizeof(bits)) {
close(ptyfd);
dolog(LOG_NOTICE, "%s connecting, error reading aclbits",
inet_ntoa(tipclient.sin_addr));
return 1;
}
if (bcmp(bits, ACLbits, sizeof(ACLbits))) {
close(ptyfd);
dolog(LOG_NOTICE, "%s connecting, aclbits do not match",
inet_ntoa(tipclient.sin_addr));
return 1;
}
/*
* See Mike comments (use threads) above.
*/
if (fcntl(ptyfd, F_SETFL, O_NONBLOCK) < 0)
die("fcntl(O_NONBLOCK): %s", geterr(errno));
FD_SET(ptyfd, &sfds);
if (ptyfd >= fdcount) {
fdcount = ptyfd;
fdcount++;
}
tipactive = 1;
dolog(LOG_INFO, "%s connecting", inet_ntoa(tipclient.sin_addr));
return 0;
}
/*
* Simple access permission system for now.
* Write out an acl file, which has the port number and "key". If
* the user can read this file, then he knows what port to connect on
* and what magic string to send. We eventually need to extend this to a
* tipserver of some kind, that talks to the database and does the
* permission checks.
*/
genaclfile()
{
char aclname[BUFSIZ], buf[BUFSIZ];
int fd;
if ((fd = open("/dev/urandom", O_RDONLY, 0666)) < 0)
die("/dev/random: open: %s", geterr(errno));
if (read(fd, ACLbits, sizeof(ACLbits)) != sizeof(ACLbits))
die("/dev/random: read: %s", geterr(errno));
close(fd);
(void) sprintf(aclname, ACLNAME, DEVPATH, Machine);
if ((fd = open(aclname, O_WRONLY|O_CREAT|O_TRUNC, 0666)) < 0)
die("%s: open: %s", aclname, geterr(errno));
(void) sprintf(buf, "%d\n0x%x 0x%x 0x%x\n", portnum,
ACLbits[0], ACLbits[1], ACLbits[2]);
if (write(fd, buf, strlen(buf)) != strlen(buf))
die("%s: write: %s", aclname, geterr(errno));
close(fd);
if (chmod(aclname, 0640) < 0)
die("%s: chmod: %s", aclname, geterr(errno));
}
#endif
......@@ -130,6 +130,16 @@ foreach my $node ( keys %nodepid ) {
chmod(0600, $tipdevname) or
die("Could not chmod(0600) $tipdevname: $!");
#
# Ditto for the "acl" file, which is how socket based tip/capture enforce
# protection of the console line.
#
$aclname = "$TIPDEVDIR/$node.acl";
if (-e $aclname) {
chmod(0600, $aclname) or
die("Could not chmod(0600) $aclname: $!");
}
#
# Now send a USR2 signal to the capture process so that it closes down
# any tip thats attached to it.
......@@ -146,6 +156,16 @@ foreach my $node ( keys %nodepid ) {
die("Could not chown(0, $gid) $tipdevname: $!");
chmod(0660, $tipdevname) or
die("Could not chmod(0660) $tipdevname: $!");
#
# Ditto for "acl" file, which new tip needs access to.
#
if (-e $aclname) {
chown(0, $gid, $aclname) or
die("Could not chown(0, $gid) $aclname: $!");
chmod(0660, $aclname) or
die("Could not chmod(0660) $aclname: $!");
}
}
exit 0;
......@@ -130,6 +130,16 @@ foreach my $node ( keys %nodepid ) {
chmod(0600, $tipdevname) or
die("Could not chmod(0600) $tipdevname: $!");
#
# Ditto for the "acl" file, which is how socket based tip/capture enforce
# protection of the console line.
#
$aclname = "$TIPDEVDIR/$node.acl";
if (-e $aclname) {
chmod(0600, $aclname) or
die("Could not chmod(0600) $aclname: $!");
}
#
# Now send a USR2 signal to the capture process so that it closes down
# any tip thats attached to it.
......@@ -146,6 +156,16 @@ foreach my $node ( keys %nodepid ) {
die("Could not chown(0, $gid) $tipdevname: $!");
chmod(0660, $tipdevname) or
die("Could not chmod(0660) $tipdevname: $!");
#
# Ditto for "acl" file, which new tip needs access to.
#
if (-e $aclname) {
chown(0, $gid, $aclname) or
die("Could not chown(0, $gid) $aclname: $!");
chmod(0660, $aclname) or
die("Could not chmod(0660) $aclname: $!");
}
}
exit 0;
......@@ -9,7 +9,7 @@ all: tip
include $(TESTBED_SRCDIR)/GNUmakerules
CC = gcc -g -O2
CC = gcc -g -O2 -DUSESOCKETS
OBJS = cmds.o cmdtab.o hunt.o partab.o \
remote.o tip.o value.o vars.o getcap.o
......
......@@ -36,7 +36,7 @@
static char sccsid[] = "@(#)hunt.c 8.1 (Berkeley) 6/6/93";
#endif
static const char rcsid[] =
"$Id: hunt.c,v 1.2 2000-12-27 00:49:34 mike Exp $";
"$Id: hunt.c,v 1.3 2001-07-24 15:13:40 stoller Exp $";
#endif /* not lint */
#include "tip.h"
......@@ -47,6 +47,12 @@ static const char rcsid[] =
#include <libutil.h>
#endif
#ifdef USESOCKETS
#include <sys/socket.h>
#include <netinet/in.h>
int socket_open(char *devname);
#endif
extern char *getremote();
extern char *rindex();
......@@ -91,9 +97,13 @@ hunt(name)
break;
if (setjmp(deadline) == 0) {
alarm(10);
#ifdef USESOCKETS
FD = socket_open(cp);
#else
FD = open(cp, O_RDWR);
if (FD >= 0)
ioctl(FD, TIOCEXCL, 0);
#endif
}
alarm(0);
if (FD < 0) {
......@@ -101,6 +111,7 @@ hunt(name)
deadfl = 1;
}
if (!deadfl) {
#ifndef USESOCKETS
#if HAVE_TERMIOS
struct termios t;
......@@ -113,6 +124,7 @@ hunt(name)
ioctl(FD, TIOCHPCL, 0);
#endif
#endif /* HAVE_TERMIOS */
#endif /* USESOCKETS */
signal(SIGALRM, SIG_DFL);
return ((int)cp);
}
......@@ -123,3 +135,55 @@ hunt(name)
signal(SIGALRM, f);
return (deadfl ? -1 : (int)cp);
}
#ifdef USESOCKETS
/*
*
*/
int
socket_open(char *devname)
{
int sock;
struct sockaddr_in name;
char aclname[BUFSIZ], buf[BUFSIZ];
int aclbits[3];
int port;
FILE *fp;
(void) sprintf(aclname, "%s.acl", devname);
if ((fp = fopen(aclname, "r")) < 0) {
return -1;
}
fscanf(fp, "%d %x %x %x", &port,
&aclbits[0], &aclbits[1], &aclbits[2]);
fclose(fp);
/* Create socket from which to read. */
sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock < 0) {
return sock;
}
/* Create name. */
name.sin_family = AF_INET;
inet_aton("127.0.0.1", &name.sin_addr);
name.sin_port = htons(port);
/* Caller picks up and displays error */
if (connect(sock, (struct sockaddr *) &name, sizeof(name)) < 0) {
close(sock);
return -1;
}
/*
* Send the acl bits.
*/
if (write(sock, aclbits, sizeof(aclbits)) != sizeof(aclbits)) {
close(sock);
return -1;
}
return sock;
}
#endif
......@@ -42,7 +42,7 @@ static const char copyright[] =
static char sccsid[] = "@(#)tip.c 8.1 (Berkeley) 6/6/93";
#endif
static const char rcsid[] =
"$Id: tip.c,v 1.2 2000-12-27 00:49:35 mike Exp $";
"$Id: tip.c,v 1.3 2001-07-24 15:13:40 stoller Exp $";
#endif /* not lint */
/*
......@@ -725,6 +725,7 @@ help(c)
void
ttysetup (int speed)
{
#ifndef USESOCKETS
#if HAVE_TERMIOS
struct termios termios;
tcgetattr (FD, &termios);
......@@ -749,6 +750,7 @@ ttysetup (int speed)
ioctl(FD, TIOCSETP, (char *)&arg);
ioctl(FD, TIOCLBIS, (char *)&bits);
#endif /* HAVE_TERMIOS */
#endif /* USESOCKETS */
}
/*
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment