Commit 463ee6b1 authored by Timothy Stack's avatar Timothy Stack

Mote and robot related stuff.  The main thing is the addition of relay
capabilities to capture and related things.

	* GNUmakefile.in: Add the capture and tip subdirectories to the
	client and client-install targets.

	* configure, configure.in, config.h.in: Detect srandomdev() for
	capture and add "mote/newmote" script.

	* capture/GNUmakefile.in, capture/capture.c: Add "relay"
	capabilities to capture.

	* capture/capquery.c: Query the capserver for the relay receiver's
	port number.

	* capture/capserver.c: Small hack to return the port number
        for a node.

	* db/libdb.pm.in, db/xmlconvert.in: Add virt_tiptunnels table.

	* event/program-agent/program-agent.c: Change log file names to
	something a little more user-friendly.  Add a "MODIFY" event
	handler that lets the user set agent attributes (command, tag,
	timeout) without having to run a program.

	* event/sched/GNUmakefile.in, event/sched/console-agent.cc,
	event/sched/console-agent.h, event/sched/event-sched.c: Add
	console agents that can be used to snapshot a section of the
	capture log file.

	* event/sched/node-agent.cc: Some minor cleanup.

	* event/sched/simulator-agent.cc, event/sched/simulator-agent.h:
	Add the config data to the report mail.  Add a "RESET" event
	handler that runs "loghole clean".  Save the report mail in a file
	so it gets archived with the rest of the logs.

	* lib/libtb/tbdefs.h: Add CONSOLE object type.

	* mote/GNUmakefile.in, mote/newmote: Add newmote script, just a
	quick hack to add motes to the DB.

	* mote/tbuisp.in: Add another backend for loading motes through
	their relay capture server.

	* robots/mtp/mtp_dump.c: Dump the min/max values for x and y,
	handy for figuring out the bounds of the camera.

	* sql/database-fill.sql: Change the RELOAD-MOTE/SHUTDOWN ->
	ALWAYSUP/SHUTDOWN mode transition to ALWAYSUP/ISUP since stated
	doesn't seem to run triggers after a state change by a mode
	transition.

	* tbsetup/tbreport.in: Change the ordering of the eventlist so it
	displays event-sequences appropriately.

	* tbsetup/ns2ir/GNUmakefile.in, tbsetup/ns2ir/console.tcl,
	tbsetup/ns2ir/node.tcl, tbsetup/ns2ir/parse.tcl.in,
	tbsetup/ns2ir/sim.tcl.in: Add a "console" agent that represents
	the serial console for a node.

	* tbsetup/ns2ir/sequence.tcl: Add an "append" method so it is
	easier to build sequences dynamically.

	* tbsetup/ns2ir/topography.tcl: Make checkdest available to
	regular users.

	* tip/GNUmakefile.in, tip/tiptunnel.c: Add support for uploading a
	file to a relay version of capture and exporting the end
	connection as a pty.

	* tmcd/decls.h, tmcd/common/libsetup.pm: Bump version number since
	the dosubnodelist change is not backwards compatible.

	* tmcd/tmcd.c: Make dosubnodelist and dosubconfig callable even
	when a node isn't allocated.  Add dotiptunnels command that
	returns which serial consoles are to be mounted on a node.  Add
	mote version of subconfig that returns information needed to
	startup the relay version of capture.

	* tmcd/common/bootsubnodes: For motes, startup the relay version
	of capture (XXX stargate specific).

	* tmcd/common/libsetup.pm, tmcd/common/libtmcc.pm,
	tmcd/common/config/rc.config, tmcd/common/config/rc.tiptunnels:
	Client side changes for mounting another nodes serial line.

	* tmcd/common/rc.bootsetup: Always boot the subnodes, even when
	free.  This is used for motes since their capture needs to be up
	for reloading at the time.

	* tmcd/linux/ixpboot: Shuffle some code around so the script
	doesn't fail if the ixp isn't allocated.

	* utils/loghole.in: Add "digest.out" and "report.mail" as global
	logs to be saved in archives and display the "report.mail" file
	when showing a loghole archive.

	* xmlrpc/emulabserver.py.in: Scrub more of the return values to
	get rid of "None"s.
parent d75ced68
......@@ -114,6 +114,8 @@ ifeq ($(EVENTSYS),1)
@$(MAKE) -C event client
endif
@$(MAKE) -C os client
@$(MAKE) -C capture client
@$(MAKE) -C tip client
@$(MAKE) -C sensors client
@$(MAKE) -C tmcd client
......@@ -122,6 +124,8 @@ ifeq ($(EVENTSYS),1)
@$(MAKE) -C event client-install
endif
@$(MAKE) -C os client-install
@$(MAKE) -C capture client-install
@$(MAKE) -C tip client-install
@$(MAKE) -C sensors client-install
@$(MAKE) -C tmcd client-install
......
#
# EMULAB-COPYRIGHT
# Copyright (c) 2000-2004 University of Utah and the Flux Group.
# Copyright (c) 2000-2005 University of Utah and the Flux Group.
# All rights reserved.
#
......@@ -9,11 +9,14 @@ TESTBED_SRCDIR = @top_srcdir@
OBJDIR = ..
SUBDIR = capture
SYSTEM := $(shell uname -s)
include $(OBJDIR)/Makeconf
all: boss-all tipserv-all
boss-all: capserver
tipserv-all: capture capture-tty
tipserv-all: capture capture-tty capquery
client: capture capquery
include $(TESTBED_SRCDIR)/GNUmakerules
......@@ -25,25 +28,44 @@ 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 -I${OBJDIR} -DLOG_TESTBED=$(LOG_TESTBED)
CFLAGS += -g -O2 -DLOG_DROPS -I${OBJDIR} -DLOG_TESTBED=$(LOG_TESTBED)
ifeq ($(SYSTEM),Linux)
ifeq ($(host_cpu),arm)
LDFLAGS += -static
else
CFLAGS += -I/usr/kerberos/include
LDFLAGS += -L/usr/kerberos/lib -lkrb5 -lk5crypto -lcom_err
endif
else
LDFLAGS += -static
endif
capture: capture.c capdecls.h
cc -static $(CFLAGS) -DUSESOCKETS -DWITHSSL -DPREFIX=\"$(TBROOT)\" -o capture $< -lssl -lcrypto
$(CC) $(CFLAGS) -DUSESOCKETS -DWITHSSL -DPREFIX=\"$(TBROOT)\" -o capture $< -lssl -lcrypto $(LDFLAGS)
capquery: capquery.c capdecls.h
$(CC) $(CFLAGS) -DPREFIX=\"$(TBROOT)\" -o $@ $<
capture-nossl: capture.c capdecls.h
cc $(CFLAGS) -DUSESOCKETS -DPREFIX=\"$(TBROOT)\" -o capture-nossl $<
$(CC) $(CFLAGS) -DUSESOCKETS -DPREFIX=\"$(TBROOT)\" -o capture-nossl $<
capture-tty: capture.c capdecls.h
cc $(CFLAGS) -o capture-tty $<
$(CC) $(CFLAGS) -o capture-tty $<
capserver: capserver.c capdecls.h
cc $(CFLAGS) $(DBFLAGS) -o capserver $< \
$(CC) $(CFLAGS) $(DBFLAGS) -o capserver $< \
-L/usr/local/lib/mysql -lmysqlclient
#
# Do not capture install by default.
#
install: all $(INSTALL_SBINDIR)/capserver
$(INSTALL_PROGRAM) capture $(INSTALL_DIR)/opsdir/sbin/capture
client-install: client
$(INSTALL_PROGRAM) capture$(EXE) $(DESTDIR)$(CLIENT_BINDIR)/capture$(EXE)
$(INSTALL_PROGRAM) capquery$(EXE) $(DESTDIR)$(CLIENT_BINDIR)/capquery$(EXE)
real-install: all $(INSTALL_SBINDIR)/capserver $(INSTALL_SBINDIR)/capture
......
/*
* EMULAB-COPYRIGHT
* Copyright (c) 2005 University of Utah and the Flux Group.
* All rights reserved.
*/
#include "config.h"
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.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 <sys/time.h>
#include <grp.h>
#include "capdecls.h"
static int debug = 0;
static char *usagestr =
"usage: capquery [-d] [-s server] [-p #] name\n"
" -d Turn on debugging.\n"
" -s server Specify a name to connect to.\n"
" -p portnum Specify a port number to connect to.\n"
"\n";
void
usage()
{
fprintf(stderr, usagestr);
exit(1);
}
static int
mygethostbyname(struct sockaddr_in *host_addr, char *host, int port)
{
struct hostent *host_ent;
int retval = 0;
assert(host_addr != NULL);
assert(host != NULL);
assert(strlen(host) > 0);
memset(host_addr, 0, sizeof(struct sockaddr_in));
#ifndef linux
host_addr->sin_len = sizeof(struct sockaddr_in);
#endif
host_addr->sin_family = AF_INET;
host_addr->sin_port = htons(port);
if( (host_ent = gethostbyname(host)) != NULL ) {
memcpy((char *)&host_addr->sin_addr.s_addr,
host_ent->h_addr,
host_ent->h_length);
retval = 1;
}
else {
retval = inet_aton(host, &host_addr->sin_addr);
}
return( retval );
}
int
main(int argc, char **argv)
{
int sock, ch, rc, retval = EXIT_FAILURE;
int length, port = SERVERPORT;
char *server = BOSSNODE;
struct sockaddr_in sin;
whoami_t whoami;
while ((ch = getopt(argc, argv, "ds:p:")) != -1)
switch(ch) {
case 's':
server = optarg;
break;
case 'p':
port = atoi(optarg);
break;
case 'd':
debug++;
break;
case 'h':
case '?':
default:
usage();
}
argc -= optind;
argv += optind;
if (argc != 1)
usage();
if (strlen(argv[0]) >= sizeof(whoami.name))
fprintf(stderr, "Name too long: %s\n", argv[0]);
if (getuid() != 0)
fprintf(stderr, "Must be run as root\n");
memset(&whoami, 0, sizeof(whoami));
strcpy(whoami.name, argv[0]);
whoami.portnum = -1;
if (!mygethostbyname(&sin, server, port))
fprintf(stderr, "Bad server name: %s\n", server);
if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0)
perror("socket");
else if (bindresvport(sock, NULL) < 0)
perror("bindresvport");
else if (connect(sock, (struct sockaddr *)&sin, sizeof(sin)) < 0)
perror("connect");
else if (write(sock, &whoami, sizeof(whoami)) != sizeof(whoami))
perror("write");
else if ((rc = read(sock, &port, sizeof(port))) != sizeof(port))
perror("read");
else {
printf("%d\n", port);
retval = EXIT_SUCCESS;
}
close(sock);
return retval;
}
/*
* EMULAB-COPYRIGHT
* Copyright (c) 2000-2003 University of Utah and the Flux Group.
* Copyright (c) 2000-2003, 2005 University of Utah and the Flux Group.
* All rights reserved.
*/
......@@ -137,6 +137,8 @@ main(int argc, char **argv)
unsigned char buf[BUFSIZ], node_id[64];
secretkey_t secretkey;
tipowner_t tipown;
void *reply = &tipown;
size_t reply_size = sizeof(tipown);
if ((clientsock = accept(tcpsock,
(struct sockaddr *)&client,
......@@ -193,9 +195,9 @@ main(int argc, char **argv)
* message in the log file. Local tip will still work but
* remote tip will not.
*/
res = mydb_query("select server,node_id from tiplines "
res = mydb_query("select server,node_id,portnum from tiplines "
"where tipname='%s'",
2, whoami.name);
3, whoami.name);
if (!res) {
syslog(LOG_ERR, "DB Error getting tiplines for %s!",
whoami.name);
......@@ -209,6 +211,8 @@ main(int argc, char **argv)
}
row = mysql_fetch_row(res);
strcpy(node_id, row[1]);
port = -1;
sscanf(row[2], "%d", &port);
mysql_free_result(res);
/*
......@@ -246,10 +250,14 @@ main(int argc, char **argv)
}
mysql_free_result(res);
if (whoami.portnum == -1) {
reply = &port;
reply_size = sizeof(port);
}
/*
* Update the DB.
*/
if (mydb_update("update tiplines set portnum=%d, "
else if (mydb_update("update tiplines set portnum=%d, "
"keylen=%d, keydata='%s' "
"where tipname='%s'",
whoami.portnum,
......@@ -263,13 +271,13 @@ main(int argc, char **argv)
/*
* And now send the reply.
*/
if ((cc = write(clientsock, &tipown, sizeof(tipown))) <= 0) {
if ((cc = write(clientsock, reply, reply_size)) <= 0) {
if (cc < 0)
syslog(LOG_ERR, "Writing reply: %m");
syslog(LOG_ERR, "Connection aborted (write)");
goto done;
}
if (cc != sizeof(tipown)) {
if (cc != reply_size) {
syslog(LOG_ERR, "Wrong byte count (write)!");
goto done;
}
......
/*
* EMULAB-COPYRIGHT
* Copyright (c) 2000-2004 University of Utah and the Flux Group.
* Copyright (c) 2000-2005 University of Utah and the Flux Group.
* All rights reserved.
*/
......@@ -30,6 +30,9 @@
#include <errno.h>
#include <stdlib.h>
#include <stdarg.h>
#include <time.h>
#include <assert.h>
#include <paths.h>
#include <sys/param.h>
#include <sys/file.h>
......@@ -89,8 +92,10 @@ void dolog(int level, char *format, ...);
#define DEVNAME "%s/%s"
#define BUFSIZE 4096
#define DROP_THRESH (32*1024)
#define MAX_UPLOAD_SIZE (1 * 1024 * 1024)
#define DEFAULT_CERTFILE PREFIX"/etc/capture.pem"
#define DEFAULT_CLIENT_CERTFILE PREFIX"/etc/client.pem"
#define DEFAULT_CAFILE PREFIX"/etc/emulab.pem"
char *Progname;
char *Pidname;
......@@ -100,34 +105,187 @@ char *Ttyname;
char *Ptyname;
char *Devname;
char *Machine;
int logfd, runfd, devfd, ptyfd;
int logfd = -1, runfd, devfd = -1, ptyfd = -1;
int hwflow = 0, speed = B9600, debug = 0, runfile = 0, standalone = 0;
int stampinterval = -1;
sigset_t actionsigmask;
sigset_t allsigmask;
#ifdef USESOCKETS
#ifndef USESOCKETS
#define relay_snd 0
#define relay_rcv 0
#else
char *Bossnode = BOSSNODE;
struct sockaddr_in Bossaddr;
char *Aclname;
int serverport = SERVERPORT;
int sockfd, tipactive, portnum;
int sockfd, tipactive, portnum, relay_snd, relay_rcv;
int upportnum = -1, upfd = -1, upfilefd = -1;
char uptmpnam[64];
size_t upfilesize = 0;
struct sockaddr_in tipclient;
struct sockaddr_in relayclient;
struct in_addr relayaddr;
secretkey_t secretkey;
char ourhostname[MAXHOSTNAMELEN];
int needshake;
gid_t tipgid;
uid_t tipuid;
char *uploadCommand;
#ifdef WITHSSL
SSL_CTX * ctx;
SSL * sslCon;
SSL * sslRelay;
SSL * sslUpload;
int initializedSSL = 0;
int usingSSL = 0;
const char * certfile = NULL;
const char * cafile = NULL;
int
initializessl(void)
{
static int initializedSSL = 0;
if (initializedSSL)
return 0;
SSL_load_error_strings();
SSL_library_init();
ctx = SSL_CTX_new( SSLv23_method() );
if (ctx == NULL) {
dolog( LOG_NOTICE, "Failed to create context.");
return 1;
}
#ifndef PREFIX
#define PREFIX
#endif
if (relay_snd) {
if (!cafile) { cafile = DEFAULT_CAFILE; }
if (SSL_CTX_load_verify_locations(ctx, cafile, NULL) == 0) {
die("cannot load verify locations");
}
/*
* Make it so the client must provide authentication.
*/
SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER |
SSL_VERIFY_FAIL_IF_NO_PEER_CERT, 0);
/*
* No session caching! Useless and eats up memory.
*/
SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_OFF);
if (!certfile) { certfile = DEFAULT_CLIENT_CERTFILE; }
if (SSL_CTX_use_certificate_file( ctx,
certfile,
SSL_FILETYPE_PEM ) <= 0) {
dolog(LOG_NOTICE,
"Could not load %s as certificate file.",
certfile );
return 1;
}
if (SSL_CTX_use_PrivateKey_file( ctx,
certfile,
SSL_FILETYPE_PEM ) <= 0) {
dolog(LOG_NOTICE,
"Could not load %s as key file.",
certfile );
return 1;
}
}
else {
if (!certfile) { certfile = DEFAULT_CERTFILE; }
if (SSL_CTX_use_certificate_file( ctx,
certfile,
SSL_FILETYPE_PEM ) <= 0) {
dolog(LOG_NOTICE,
"Could not load %s as certificate file.",
certfile );
return 1;
}
if (SSL_CTX_use_PrivateKey_file( ctx,
certfile,
SSL_FILETYPE_PEM ) <= 0) {
dolog(LOG_NOTICE,
"Could not load %s as key file.",
certfile );
return 1;
}
}
initializedSSL = 1;
return 0;
}
int
sslverify(SSL *ssl, char *requiredunit)
{
X509 *peer = NULL;
char cname[256], unitname[256];
assert(ssl != NULL);
assert(requiredunit != NULL);
if (SSL_get_verify_result(ssl) != X509_V_OK) {
dolog(LOG_NOTICE,
"sslverify: Certificate did not verify!\n");
return -1;
}
if (! (peer = SSL_get_peer_certificate(ssl))) {
dolog(LOG_NOTICE, "sslverify: No certificate presented!\n");
return -1;
}
/*
* Grab stuff from the cert.
*/
X509_NAME_get_text_by_NID(X509_get_subject_name(peer),
NID_organizationalUnitName,
unitname, sizeof(unitname));
X509_NAME_get_text_by_NID(X509_get_subject_name(peer),
NID_commonName,
cname, sizeof(cname));
X509_free(peer);
/*
* On the server, things are a bit more difficult since
* we share a common cert locally and a per group cert remotely.
*
* Make sure common name matches.
*/
if (strcmp(cname, BOSSNODE)) {
dolog(LOG_NOTICE,
"sslverify: commonname mismatch: %s!=%s\n",
cname, BOSSNODE);
return -1;
}
/*
* If the node is remote, then the unitname must match the type.
* Simply a convention.
*/
if (strcmp(unitname, requiredunit)) {
dolog(LOG_NOTICE,
"sslverify: unitname mismatch: %s!=Capture Server\n",
unitname);
return -1;
}
return 0;
}
#endif /* WITHSSL */
#endif /* USESOCKETS */
......@@ -145,7 +303,7 @@ main(int argc, char **argv)
Progname = (Progname = rindex(argv[0], '/')) ? ++Progname : *argv;
while ((op = getopt(argc, argv, "rds:Hb:ip:c:T:")) != EOF)
while ((op = getopt(argc, argv, "rds:Hb:ip:c:T:aou:v:")) != EOF)
switch (op) {
#ifdef USESOCKETS
#ifdef WITHSSL
......@@ -187,6 +345,23 @@ main(int argc, char **argv)
if (stampinterval < 0)
usage();
break;
#ifdef WITHSSL
case 'a':
relay_snd = 1;
break;
case 'o':
relay_rcv = 1;
break;
case 'u':
uploadCommand = optarg;
break;
case 'v':
cafile = optarg;
break;
#endif
}
argc -= optind;
......@@ -232,9 +407,11 @@ main(int argc, char **argv)
sa.sa_mask = allsigmask;
sigaction(SIGINT, &sa, NULL);
sigaction(SIGTERM, &sa, NULL);
if (!relay_snd) {
sa.sa_handler = reinit;
sa.sa_mask = actionsigmask;
sigaction(SIGHUP, &sa, NULL);
}
if (runfile) {
sa.sa_handler = newrun;
sigaction(SIGUSR1, &sa, NULL);
......@@ -242,16 +419,15 @@ main(int argc, char **argv)
sa.sa_handler = terminate;
sigaction(SIGUSR2, &sa, NULL);
#ifdef HAVE_SRANDOMDEV
srandomdev();
#else
srand(time(NULL));
#endif
/*
* Open up run/log file, console tty, and controlling pty.
*/
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) {
unlink(Runname);
......@@ -313,18 +489,80 @@ main(int argc, char **argv)
createkey();
dolog(LOG_NOTICE, "Ready! Listening on TCP port %d", portnum);
if (relay_snd) {
struct sockaddr_in sin;
struct hostent *he;
secretkey_t key;
char *port_idx;
int port;
if ((port_idx = strchr(argv[0], ':')) == NULL)
die("%s: bad format, expecting 'host:port'", argv[0]);
*port_idx = '\0';
port_idx += 1;
if (sscanf(port_idx, "%d", &port) != 1)
die("%s: bad port number", port_idx);
he = gethostbyname(argv[0]);
if (he == 0) {
die("gethostbyname(%s): %s",
argv[0], hstrerror(h_errno));
}
bzero(&sin, sizeof(sin));
memcpy ((char *)&sin.sin_addr, he->h_addr, he->h_length);
sin.sin_family = AF_INET;
sin.sin_port = htons(port);
if ((ptyfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
die("socket(): %s", geterr(errno));
if (connect(ptyfd, (struct sockaddr *)&sin, sizeof(sin)) < 0)
die("connect(): %s", geterr(errno));
sprintf(key.key, "RELAY %d", portnum);
key.keylen = strlen(key.key);
if (write(ptyfd, &key, sizeof(key)) != sizeof(key))
die("write(): %s", geterr(errno));
initializessl();
sslRelay = SSL_new(ctx);
if (!sslRelay)
die("SSL_new()");
if (SSL_set_fd(sslRelay, ptyfd) <= 0)
die("SSL_set_fd()");
if (SSL_connect(sslRelay) <= 0)
die("SSL_connect()");
if (sslverify(sslRelay, "Capture Server"))
die("SSL connection did not verify");
if (fcntl(ptyfd, F_SETFL, O_NONBLOCK) < 0)
die("fcntl(O_NONBLOCK): %s", geterr(errno));
tipactive = 1;
}
if (relay_rcv) {
struct hostent *he;
he = gethostbyname(argv[1]);
if (he == 0) {
die("gethostbyname(%s): %s",
argv[1], hstrerror(h_errno));
}
memcpy ((char *)&relayaddr, he->h_addr, he->h_length);
}
#else
if ((ptyfd = open(Ptyname, O_RDWR)) < 0)
die("%s: open: %s", Ptyname, geterr(errno));
#endif
if ((devfd = open(Devname, O_RDWR|O_NONBLOCK)) < 0)
die("%s: open: %s", Devname, geterr(errno));