Commit 7e92b603 authored by Leigh B. Stoller's avatar Leigh B. Stoller
Browse files

The next generation tip stuff. More DB interaction, to support

distributed tiplines (and capture processes).
parent 4e6418b2
......@@ -5,10 +5,12 @@ SUBDIR = capture
include $(OBJDIR)/Makeconf
all: capture
all: capture capserver
include $(TESTBED_SRCDIR)/GNUmakerules
DBFLAGS += -DTBDBNAME='"$(TBDBNAME)"' -I/usr/local/include
DESTDIR=
#
......@@ -17,14 +19,18 @@ DESTDIR=
#
CFLAGS= -g -O2 -DLOG_DROPS -DUSESOCKETS
capture: capture.c
capture: capture.c capdecls.h
cc $(CFLAGS) -o capture $<
capserver: capserver.c capdecls.h
cc $(CFLAGS) $(DBFLAGS) -o capserver $< \
-L/usr/local/lib/mysql -lmysqlclient
#
# Do not install by default.
#
install:
real-install: all
real-install: all $(INSTALL_SBINDIR)/capserver
install -s capture $(DESTDIR)/usr/site/bin
clean:
......
/*
* File: decls.h
* Description:
* Author: Leigh Stoller
* Computer Science Dept.
* University of Utah
* Date: 2-Aug-2001
*
* (c) Copyright 1992, 2000, University of Utah, all rights reserved.
*/
#define BOSSNODE "boss.emulab.net"
#define SERVERPORT 855
#define DEVPATH "/dev"
#define TIPPATH "/dev/tip"
/*
* The key is transferred in ascii text.
*/
typedef struct {
int keylen; /* of the key string */
char key[256]; /* and the string itself. */
} secretkey_t;
#define DEFAULTKEYLEN 32
/*
* The remote capture sends this back when it starts up
*/
typedef struct {
char nodeid[64];
int portnum;
secretkey_t key;
} whoami_t;
/*
* File: capture.c
* Description:
* Author: Leigh Stoller
* Computer Science Dept.
* University of Utah
* Date: 27-Jul-92
*
* (c) Copyright 2001, University of Utah, all rights reserved.
*/
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <syslog.h>
#include <assert.h>
#include <stdarg.h>
#include <mysql/mysql.h>
#include "capdecls.h"
#define TESTMODE
/* Defined in configure and passed in via the makefile */
#define DBNAME_SIZE 64
#define DEFAULT_DBNAME TBDBNAME
static int debug = 0;
static int portnum = SERVERPORT;
static char *dbname = DEFAULT_DBNAME;
MYSQL_RES * mydb_query(char *query, int ncols, ...);
int mydb_update(char *query, ...);
char *usagestr =
"usage: capserver [-d] [-p #]\n"
" -d Turn on debugging.\n"
" -p portnum Specify a port number to listen on.\n"
"\n";
void
usage()
{
fprintf(stderr, usagestr);
exit(1);
}
int
main(int argc, char **argv)
{
MYSQL_RES *res;
int tcpsock, ch;
int length, i, err = 0;
struct sockaddr_in name;
while ((ch = getopt(argc, argv, "dp:")) != -1)
switch(ch) {
case 'p':
portnum = atoi(optarg);
break;
case 'd':
debug++;
break;
case 'h':
case '?':
default:
usage();
}
argc -= optind;
argv += optind;
if (argc)
usage();
openlog("capserver", LOG_PID, LOG_USER);
syslog(LOG_NOTICE, "daemon starting");
if (!debug)
(void)daemon(0, 0);
/*
* Setup TCP socket
*/
/* Create socket from which to read. */
tcpsock = socket(AF_INET, SOCK_STREAM, 0);
if (tcpsock < 0) {
syslog(LOG_ERR, "opening stream socket: %m");
exit(1);
}
i = 1;
if (setsockopt(tcpsock, SOL_SOCKET, SO_REUSEADDR,
(char *)&i, sizeof(i)) < 0)
syslog(LOG_ERR, "control setsockopt: %m");;
/* Create name. */
name.sin_family = AF_INET;
name.sin_addr.s_addr = INADDR_ANY;
name.sin_port = htons((u_short) portnum);
if (bind(tcpsock, (struct sockaddr *) &name, sizeof(name))) {
syslog(LOG_ERR, "binding stream socket: %m");
exit(1);
}
/* Find assigned port value and print it out. */
length = sizeof(name);
if (getsockname(tcpsock, (struct sockaddr *) &name, &length)) {
syslog(LOG_ERR, "getting socket name: %m");
exit(1);
}
if (listen(tcpsock, 20) < 0) {
syslog(LOG_ERR, "listening on socket: %m");
exit(1);
}
syslog(LOG_NOTICE, "listening on TCP port %d", ntohs(name.sin_port));
while (1) {
struct sockaddr_in client;
int clientsock, length = sizeof(client);
int cc;
whoami_t whoami;
unsigned char buf[BUFSIZ];
secretkey_t secretkey;
if ((clientsock = accept(tcpsock,
(struct sockaddr *)&client,
&length)) < 0) {
syslog(LOG_ERR, "accept failed: %m");
exit(1);
}
syslog(LOG_INFO, "%s connected", inet_ntoa(client.sin_addr));
/*
* Read in the whoami info.
*/
if ((cc = read(clientsock, &whoami, sizeof(whoami))) <= 0) {
if (cc < 0)
syslog(LOG_ERR, "Reading request: %m");
syslog(LOG_ERR, "Connection aborted");
goto done;
}
if (cc != sizeof(whoami)) {
syslog(LOG_ERR, "Wrong byte count!");
goto done;
}
/*
* Make sure there is an entry for this tipline in
* the DB. If not, we just drop the info with an error
* message in the log file. Local tip will still work but
* remote tip will not.
*/
res = mydb_query("select server from tiplines "
"where node_id='%s'",
1, whoami.nodeid);
if (!res) {
syslog(LOG_ERR, "DB Error getting tiplines for %s!",
whoami.nodeid);
goto done;
}
if ((int)mysql_num_rows(res) == 0) {
syslog(LOG_ERR, "No tipline info for %s!",
whoami.nodeid);
mysql_free_result(res);
goto done;
}
mysql_free_result(res);
/*
* Update the DB.
*/
if (mydb_update("update tiplines set portnum=%d, "
"keylen=%d, keydata='%s' "
"where node_id='%s'",
whoami.portnum,
whoami.key.keylen, whoami.key.key,
whoami.nodeid)) {
syslog(LOG_ERR, "DB Error updating tiplines for %s!",
whoami.nodeid);
goto done;
}
syslog(LOG_INFO, "Nodeid %s, Portnum %d, Keylen %d, Key %s\n",
whoami.nodeid, whoami.portnum,
whoami.key.keylen, whoami.key.key);
done:
close(clientsock);
}
close(tcpsock);
syslog(LOG_NOTICE, "daemon terminating");
exit(0);
}
/*
* DB stuff
*/
MYSQL_RES *
mydb_query(char *query, int ncols, ...)
{
MYSQL db;
MYSQL_RES *res;
char querybuf[2*BUFSIZ];
va_list ap;
int n;
va_start(ap, ncols);
n = vsnprintf(querybuf, sizeof(querybuf), query, ap);
if (n > sizeof(querybuf)) {
syslog(LOG_ERR, "query too long for buffer");
return (MYSQL_RES *) 0;
}
mysql_init(&db);
if (mysql_real_connect(&db, 0, 0, 0, dbname, 0, 0, 0) == 0) {
syslog(LOG_ERR, "%s: connect failed: %s",
dbname, mysql_error(&db));
return (MYSQL_RES *) 0;
}
if (mysql_real_query(&db, querybuf, n) != 0) {
syslog(LOG_ERR, "%s: query failed: %s",
dbname, mysql_error(&db));
mysql_close(&db);
return (MYSQL_RES *) 0;
}
res = mysql_store_result(&db);
if (res == 0) {
syslog(LOG_ERR, "%s: store_result failed: %s",
dbname, mysql_error(&db));
mysql_close(&db);
return (MYSQL_RES *) 0;
}
mysql_close(&db);
if (ncols && ncols != (int)mysql_num_fields(res)) {
syslog(LOG_ERR, "%s: Wrong number of fields returned "
"Wanted %d, Got %d",
dbname, ncols, (int)mysql_num_fields(res));
mysql_free_result(res);
return (MYSQL_RES *) 0;
}
return res;
}
int
mydb_update(char *query, ...)
{
MYSQL db;
char querybuf[BUFSIZ];
va_list ap;
int n;
va_start(ap, query);
n = vsnprintf(querybuf, sizeof(querybuf), query, ap);
if (n > sizeof(querybuf)) {
syslog(LOG_ERR, "query too long for buffer");
return 1;
}
mysql_init(&db);
if (mysql_real_connect(&db, 0, 0, 0, dbname, 0, 0, 0) == 0) {
syslog(LOG_ERR, "%s: connect failed: %s",
dbname, mysql_error(&db));
return 1;
}
if (mysql_real_query(&db, querybuf, n) != 0) {
syslog(LOG_ERR, "%s: query failed: %s",
dbname, mysql_error(&db));
mysql_close(&db);
return 1;
}
mysql_close(&db);
return 0;
}
......@@ -6,7 +6,7 @@
* University of Utah
* Date: 27-Jul-92
*
* (c) Copyright 1992, 2000, University of Utah, all rights reserved.
* (c) Copyright 1992, 2000-2001, University of Utah, all rights reserved.
*/
/*
......@@ -22,6 +22,8 @@
* A LITTLE hack to record output from a tty device to a file, and still
* have it available to tip using a pty/tty pair.
*/
#define SAFEMODE
#include <sys/param.h>
......@@ -49,7 +51,10 @@
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <setjmp.h>
#include <netdb.h>
#endif
#include "capdecls.h"
#define geterr(e) strerror(e)
......@@ -73,7 +78,6 @@ void usage();
/*
* Configurable things.
*/
#define DEVPATH "/dev"
#ifdef HPBSD
#define LOGPATH "/usr/adm/tiplogs"
#else
......@@ -82,9 +86,9 @@ void usage();
#define PIDNAME "%s/%s.pid"
#define LOGNAME "%s/%s.log"
#define RUNNAME "%s/%s.run"
#define TTYNAME "%s/tip/%s"
#define PTYNAME "%s/tip/%s-pty"
#define ACLNAME "%s/tip/%s.acl"
#define TTYNAME "%s/%s"
#define PTYNAME "%s/%s-pty"
#define ACLNAME "%s/%s.acl"
#define DEVNAME "%s/%s"
#define BUFSIZE 4096
#define DROP_THRESH (32*1024)
......@@ -97,12 +101,15 @@ char *Ttyname;
char *Ptyname;
char *Devname;
char *Machine;
char *Bossnode = BOSSNODE;
int logfd, runfd, devfd, ptyfd;
int hwflow = 0, speed = B9600, debug = 0, runfile = 0;
#ifdef USESOCKETS
int sockfd, tipactive, portnum;
int sockfd, tipactive, portnum;
struct sockaddr_in tipclient;
int ACLbits[3];
secretkey_t secretkey;
char ourhostname[MAXHOSTNAMELEN];
int needshake;
#endif
int
......@@ -120,9 +127,13 @@ main(argc, argv)
Progname = (Progname = rindex(argv[0], '/')) ? ++Progname : *argv;
while ((op = getopt(argc, argv, "rds:H")) != EOF)
while ((op = getopt(argc, argv, "rds:Hb:it")) != EOF)
switch (op) {
case 'b':
Bossnode = optarg;
break;
case 'H':
++hwflow;
break;
......@@ -161,9 +172,9 @@ main(argc, argv)
Logname = newstr(strbuf);
(void) sprintf(strbuf, RUNNAME, LOGPATH, argv[0]);
Runname = newstr(strbuf);
(void) sprintf(strbuf, TTYNAME, DEVPATH, argv[0]);
(void) sprintf(strbuf, TTYNAME, TIPPATH, argv[0]);
Ttyname = newstr(strbuf);
(void) sprintf(strbuf, PTYNAME, DEVPATH, argv[0]);
(void) sprintf(strbuf, PTYNAME, TIPPATH, argv[0]);
Ptyname = newstr(strbuf);
(void) sprintf(strbuf, DEVNAME, DEVPATH, argv[1]);
Devname = newstr(strbuf);
......@@ -181,13 +192,13 @@ main(argc, argv)
/*
* Open up run/log file, console tty, and controlling pty.
*/
if ((logfd = open(Logname, O_WRONLY|O_CREAT|O_APPEND, 0666)) < 0)
if ((logfd = open(Logname, O_WRONLY|O_CREAT|O_APPEND, 0640)) < 0)
die("%s: open: %s", Logname, geterr(errno));
if (chmod(Logname, 0640) < 0)
die("%s: chmod: %s", Logname, geterr(errno));
if (runfile) {
if ((runfd = open(Runname,O_WRONLY|O_CREAT|O_APPEND,0666)) < 0)
if ((runfd = open(Runname,O_WRONLY|O_CREAT|O_APPEND,0640)) < 0)
die("%s: open: %s", Runname, geterr(errno));
if (chmod(Runname, 0640) < 0)
die("%s: chmod: %s", Runname, geterr(errno));
......@@ -219,16 +230,18 @@ main(argc, argv)
portnum = ntohs(name.sin_port);
if (listen(sockfd, 1) < 0)
die("listen(): listening on socket: %s", geterr(errno));
die("listen(): %s", geterr(errno));
genaclfile();
if (gethostname(ourhostname, sizeof(ourhostname)) < 0)
die("gethostname(): %s", geterr(errno));
dolog(LOG_NOTICE, "listening on TCP port %d", portnum);
createkey();
dolog(LOG_NOTICE, "Ready! Listening on TCP port %d", portnum);
#else
if ((ptyfd = open(Ptyname, O_RDWR, 0666)) < 0)
if ((ptyfd = open(Ptyname, O_RDWR)) < 0)
die("%s: open: %s", Ptyname, geterr(errno));
#endif
if ((devfd = open(Devname, O_RDWR|O_NONBLOCK, 0666)) < 0)
if ((devfd = open(Devname, O_RDWR|O_NONBLOCK)) < 0)
die("%s: open: %s", Devname, geterr(errno));
if (ioctl(devfd, TIOCEXCL, 0) < 0)
......@@ -343,9 +356,6 @@ capture()
int drop_todev_chars = 0;
#endif
timeout.tv_sec = 0;
timeout.tv_usec = 10000; /* ~115 chars at 115.2 kbaud */
/*
* XXX for now we make both directions non-blocking. This is a
* quick hack to achieve the goal that capture never block
......@@ -374,7 +384,7 @@ capture()
* and close the slave again.
*/
#ifdef __FreeBSD__
if ((n = open(Ttyname, O_RDONLY, 0666)) < 0)
if ((n = open(Ttyname, O_RDONLY)) < 0)
die("%s: open: %s", Ttyname, geterr(errno));
#endif
if (fcntl(ptyfd, F_SETFL, O_NONBLOCK) < 0)
......@@ -411,7 +421,13 @@ capture()
}
#endif
fds = sfds;
i = select(fdcount, &fds, NULL, NULL, NULL);
#ifdef USESOCKETS
if (needshake)
timeout.tv_sec = 15;
else
#endif
timeout.tv_sec = 30;
i = select(fdcount, &fds, NULL, NULL, &timeout);
if (i < 0) {
if (errno == EINTR) {
warn("input select interrupted, continuing");
......@@ -420,14 +436,20 @@ capture()
die("%s: select: %s", Devname, geterr(errno));
}
if (i == 0) {
#ifdef USESOCKETS
if (needshake) {
handshake();
continue;
}
#else
warn("No fds ready!");
sleep(1);
#endif
continue;
}
#ifdef USESOCKETS
if (FD_ISSET(sockfd, &fds)) {
if (clientconnect())
continue;
clientconnect();
}
#endif /* USESOCKETS */
if (FD_ISSET(devfd, &fds)) {
......@@ -512,7 +534,10 @@ dropped:
close(ptyfd);
tipactive = 0;
continue;
#else
#else
/* ~115 chars at 115.2 kbaud */
timeout.tv_sec = 0;
timeout.tv_usec = 10000;
select(0, 0, 0, 0, &timeout);
continue;
#endif
......@@ -561,7 +586,7 @@ reinit(int sig)
*/
close(logfd);
if ((logfd = open(Logname, O_WRONLY|O_CREAT|O_APPEND, 0666)) < 0)
if ((logfd = open(Logname, O_WRONLY|O_CREAT|O_APPEND, 0640)) < 0)
die("%s: open: %s", Logname, geterr(errno));
if (chmod(Logname, 0640) < 0)
die("%s: chmod: %s", Logname, geterr(errno));
......@@ -573,8 +598,9 @@ reinit(int sig)
}
/*
* SIGUSR1 means we want to close the old run file (because it has probably
* been moved) and start a new version of it.
* SIGUSR1 means we want to close the old run file and start a new version
* of it. The run file is not rolled or saved, so we unlink it to make sure
* that no one can hang onto an open fd.
*/
void
newrun(int sig)
......@@ -584,8 +610,9 @@ newrun(int sig)
* because we blocked SIGUSR1 during the write.
*/
close(runfd);
unlink(Runname);
if ((runfd = open(Runname, O_WRONLY|O_CREAT|O_APPEND, 0666)) < 0)
if ((runfd = open(Runname, O_WRONLY|O_CREAT|O_APPEND, 0640)) < 0)
die("%s: open: %s", Runname, geterr(errno));
if (chmod(Runname, 0640) < 0)
die("%s: chmod: %s", Runname, geterr(errno));
......@@ -603,18 +630,19 @@ void
terminate(int sig)
{
#ifdef USESOCKETS
genaclfile();
if (!tipactive)
return;
shutdown(ptyfd, SHUT_RDWR);
close(ptyfd);
FD_CLR(ptyfd, &sfds);
ptyfd = 0;
tipactive = 0;
if (tipactive) {
shutdown(ptyfd, SHUT_RDWR);
close(ptyfd);
FD_CLR(ptyfd, &sfds);
ptyfd = 0;
tipactive = 0;
dolog(LOG_INFO, "%s revoked", inet_ntoa(tipclient.sin_addr));
}
else
dolog(LOG_INFO, "revoked");
dolog(LOG_INFO, "%s shutdown", inet_ntoa(tipclient.sin_addr));
/* Must be done *after* all the above stuff is done! */
createkey();
#else
int ofd = ptyfd;
......@@ -632,7 +660,7 @@ terminate(int sig)
dolog(LOG_WARNING, "could not revoke access to tty");
close(ptyfd);
if ((ptyfd = open(Ptyname, O_RDWR, 0666)) < 0)
if ((ptyfd = open(Ptyname, O_RDWR)) < 0)
die("%s: open: %s", Ptyname, geterr(errno));
/* XXX so we don't have to recompute the select mask */
......@@ -644,7 +672,7 @@ terminate(int sig)
#ifdef __FreeBSD__
/* see explanation in capture() above */
if ((ofd = open(Ttyname, O_RDONLY, 0666)) < 0)
if ((ofd = open(Ttyname, O_RDONLY)) < 0)
die("%s: open: %s", Ttyname, geterr(errno));
#endif
if (fcntl(ptyfd, F_SETFL, O_NONBLOCK) < 0)
......@@ -756,7 +784,7 @@ writepid()
int fd;
char buf[8];
if ((fd = open(Pidname, O_WRONLY|O_CREAT|O_TRUNC, 0666)) < 0)
if ((fd