From 463ee6b1b2be0ca607217f6c79012721e04e31e6 Mon Sep 17 00:00:00 2001 From: Timothy Stack <stack@flux.utah.edu> Date: Mon, 4 Apr 2005 20:27:55 +0000 Subject: [PATCH] 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. --- GNUmakefile.in | 4 + capture/GNUmakefile.in | 36 +- capture/capquery.c | 132 ++++++ capture/capserver.c | 30 +- capture/capture.c | 675 ++++++++++++++++++++++------ config.h.in | 2 + configure | 76 +++- configure.in | 4 +- db/libdb.pm.in | 1 + db/xmlconvert.in | 6 +- event/program-agent/program-agent.c | 99 ++-- event/sched/GNUmakefile.in | 4 +- event/sched/console-agent.cc | 235 ++++++++++ event/sched/console-agent.h | 57 +++ event/sched/event-sched.c | 31 +- event/sched/node-agent.cc | 10 +- event/sched/simulator-agent.cc | 36 +- event/sched/simulator-agent.h | 5 + lib/libtb/tbdefs.h | 1 + mote/GNUmakefile.in | 4 +- mote/newmote.in | 124 +++++ mote/tbuisp.in | 13 +- sql/database-fill.sql | 2 +- tbsetup/ns2ir/GNUmakefile.in | 3 +- tbsetup/ns2ir/console.tcl | 49 ++ tbsetup/ns2ir/node.tcl | 19 +- tbsetup/ns2ir/parse.tcl.in | 1 + tbsetup/ns2ir/sequence.tcl | 10 + tbsetup/ns2ir/sim.tcl.in | 86 +++- tbsetup/ns2ir/topography.tcl | 26 +- tbsetup/tbreport.in | 2 +- tip/GNUmakefile.in | 18 +- tip/tiptunnel.c | 341 +++++++++++++- tmcd/common/bootsubnodes | 32 ++ tmcd/common/config/GNUmakefile.in | 5 +- tmcd/common/config/rc.config | 4 +- tmcd/common/config/rc.tiptunnels | 139 ++++++ tmcd/common/libsetup.pm | 47 +- tmcd/common/libtmcc.pm | 3 + tmcd/common/rc.bootsetup | 8 + tmcd/decls.h | 2 +- tmcd/linux/ixpboot | 12 +- tmcd/tmcd.c | 112 ++++- utils/GNUmakefile.in | 2 +- utils/loghole.in | 6 +- xmlrpc/emulabserver.py.in | 16 +- 46 files changed, 2249 insertions(+), 281 deletions(-) create mode 100644 capture/capquery.c create mode 100644 event/sched/console-agent.cc create mode 100644 event/sched/console-agent.h create mode 100644 mote/newmote.in create mode 100644 tbsetup/ns2ir/console.tcl create mode 100644 tmcd/common/config/rc.tiptunnels diff --git a/GNUmakefile.in b/GNUmakefile.in index 98afa02a36..fe1e21aa54 100644 --- a/GNUmakefile.in +++ b/GNUmakefile.in @@ -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 diff --git a/capture/GNUmakefile.in b/capture/GNUmakefile.in index 4eb1b47bab..ae882200b9 100644 --- a/capture/GNUmakefile.in +++ b/capture/GNUmakefile.in @@ -1,6 +1,6 @@ # # 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 diff --git a/capture/capquery.c b/capture/capquery.c new file mode 100644 index 0000000000..89f48d0722 --- /dev/null +++ b/capture/capquery.c @@ -0,0 +1,132 @@ +/* + * 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; +} diff --git a/capture/capserver.c b/capture/capserver.c index 222a55d2a3..d550fe0941 100644 --- a/capture/capserver.c +++ b/capture/capserver.c @@ -1,6 +1,6 @@ /* * 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,15 +250,19 @@ 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, " - "keylen=%d, keydata='%s' " - "where tipname='%s'", - whoami.portnum, - whoami.key.keylen, whoami.key.key, - whoami.name)) { + else if (mydb_update("update tiplines set portnum=%d, " + "keylen=%d, keydata='%s' " + "where tipname='%s'", + whoami.portnum, + whoami.key.keylen, whoami.key.key, + whoami.name)) { syslog(LOG_ERR, "DB Error updating tiplines for %s!", whoami.name); goto done; @@ -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; } diff --git a/capture/capture.c b/capture/capture.c index 91f1fd1d58..48863d127b 100644 --- a/capture/capture.c +++ b/capture/capture.c @@ -1,6 +1,6 @@ /* * 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); - sa.sa_handler = reinit; - sa.sa_mask = actionsigmask; - sigaction(SIGHUP, &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)); - - if (ioctl(devfd, TIOCEXCL, 0) < 0) - warning("TIOCEXCL %s: %s", Devname, geterr(errno)); - + + if (!relay_snd) { + 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 (!relay_rcv) { + rawmode(Devname, speed); + } + writepid(); - rawmode(speed); capture(); @@ -443,7 +681,7 @@ capture(void) * I keep thinking (use threads) that there is a better way to do * this (use threads). Hmm... */ - if (fcntl(devfd, F_SETFL, O_NONBLOCK) < 0) + if ((devfd >= 0) && (fcntl(devfd, F_SETFL, O_NONBLOCK) < 0)) die("%s: fcntl(O_NONBLOCK): %s", Devname, geterr(errno)); #ifndef USESOCKETS /* @@ -469,17 +707,19 @@ capture(void) #endif /* USESOCKETS */ FD_ZERO(&sfds); - FD_SET(devfd, &sfds); + if (devfd >= 0) + 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 */ + if (ptyfd >= 0) { + if (devfd < ptyfd) + fdcount = ptyfd; + FD_SET(ptyfd, &sfds); + } fdcount++; @@ -525,10 +765,25 @@ capture(void) if (FD_ISSET(sockfd, &fds)) { clientconnect(); } + if ((upfd >=0) && FD_ISSET(upfd, &fds)) { + handleupload(); + } #endif /* USESOCKETS */ - if (FD_ISSET(devfd, &fds)) { + if ((devfd >= 0) && FD_ISSET(devfd, &fds)) { errno = 0; - cc = read(devfd, buf, sizeof(buf)); +#ifdef WITHSSL + if (relay_rcv) { + cc = SSL_read(sslRelay, buf, sizeof(buf)); + if (cc <= 0) { + FD_CLR(devfd, &sfds); + devfd = -1; + bzero(&relayclient, sizeof(relayclient)); + continue; + } + } + else +#endif + cc = read(devfd, buf, sizeof(buf)); if (cc < 0) die("%s: read: %s", Devname, geterr(errno)); if (cc == 0) @@ -542,7 +797,10 @@ capture(void) #endif for (lcc = 0; lcc < cc; lcc += i) { #ifdef WITHSSL - if (usingSSL) { + if (relay_snd) { + i = SSL_write(sslRelay, &buf[lcc], cc-lcc); + } + else if (sslCon != NULL) { i = SSL_write(sslCon, &buf[lcc], cc-lcc); if (i < 0) { i = 0; } /* XXX Hack */ } else @@ -595,11 +853,13 @@ dropped: } laststamp = now; } - i = write(logfd, buf, cc); - if (i < 0) - die("%s: write: %s", Logname, geterr(errno)); - if (i != cc) - die("%s: write: incomplete", Logname); + if (logfd >= 0) { + i = write(logfd, buf, cc); + if (i < 0) + die("%s: write: %s", Logname, geterr(errno)); + if (i != cc) + die("%s: write: incomplete", Logname); + } if (runfile) { i = write(runfd, buf, cc); if (i < 0) @@ -611,19 +871,32 @@ dropped: sigprocmask(SIG_SETMASK, &omask, NULL); } - if (FD_ISSET(ptyfd, &fds)) { + if ((ptyfd >= 0) && FD_ISSET(ptyfd, &fds)) { int lerrno; sigprocmask(SIG_BLOCK, &actionsigmask, &omask); errno = 0; #ifdef WITHSSL - if (usingSSL) { + if (relay_snd) { + cc = SSL_read( sslRelay, buf, sizeof(buf) ); + if (cc < 0) { /* XXX hack */ + cc = 0; + SSL_free(sslRelay); + sslRelay = NULL; + upportnum = -1; + } + } + else if (sslCon != NULL) { cc = SSL_read( sslCon, buf, sizeof(buf) ); - if (cc < 0) { cc = 0; } /* XXX hack */ + if (cc < 0) { /* XXX hack */ + cc = 0; + SSL_free(sslCon); + sslCon = NULL; + } } else #endif /* WITHSSL */ { - cc = read(ptyfd, buf, sizeof(buf), 0); + cc = read(ptyfd, buf, sizeof(buf)); } lerrno = errno; sigprocmask(SIG_SETMASK, &omask, NULL); @@ -646,6 +919,8 @@ dropped: /* * Other end disconnected. */ + if (relay_snd) + die("relay receiver died"); dolog(LOG_INFO, "%s disconnecting", inet_ntoa(tipclient.sin_addr)); FD_CLR(ptyfd, &sfds); @@ -678,7 +953,21 @@ dropped: sigprocmask(SIG_BLOCK, &actionsigmask, &omask); for (lcc = 0; lcc < cc; lcc += i) { - i = write(devfd, &buf[lcc], cc-lcc); + if (relay_rcv) { +#ifdef USESOCKETS + if (sslRelay != NULL) { + i = SSL_write(sslRelay, + &buf[lcc], + cc - lcc); + } + else { + i = cc - lcc; + } +#endif + } + else { + i = write(devfd, &buf[lcc], cc-lcc); + } if (i < 0) { /* * Device backed up (or FUBARed) @@ -830,11 +1119,11 @@ terminate(int sig) char *optstr = #ifdef USESOCKETS #ifdef WITHSSL -"[-c certfile] " +"[-c certfile] [-v calist] [-u uploadcmd] " #endif "[-b bossnode] [-p bossport] [-i] " #endif -"-Hdr [-s speed] [-T stampinterval]"; +"-Hdrao [-s speed] [-T stampinterval]"; void usage(void) { @@ -919,6 +1208,9 @@ writepid(void) { int fd; char buf[8]; + + if (relay_snd) + return; if ((fd = open(Pidname, O_WRONLY|O_CREAT|O_TRUNC, 0644)) < 0) die("%s: open: %s", Pidname, geterr(errno)); @@ -937,10 +1229,15 @@ writepid(void) /* * Put the console line into raw mode. */ -rawmode(int speed) +rawmode(char *devname, int speed) { struct termios t; + if ((devfd = open(devname, O_RDWR|O_NONBLOCK)) < 0) + die("%s: open: %s", devname, geterr(errno)); + + if (ioctl(devfd, TIOCEXCL, 0) < 0) + warning("TIOCEXCL %s: %s", Devname, geterr(errno)); if (tcgetattr(devfd, &t) < 0) die("%s: tcgetattr: %s", Devname, geterr(errno)); (void) cfsetispeed(&t, speed); @@ -1051,148 +1348,193 @@ val2speed(int val) int clientconnect(void) { - int cc, length = sizeof(tipclient); + struct sockaddr_in sin; + int cc, length = sizeof(sin); + int dorelay = 0, doupload = 0; int ret; int newfd; secretkey_t key; capret_t capret; + SSL *newssl; - newfd = accept(sockfd, (struct sockaddr *)&tipclient, &length); + newfd = accept(sockfd, (struct sockaddr *)&sin, &length); if (newfd < 0) { dolog(LOG_NOTICE, "accept()ing new client: %s", geterr(errno)); return 1; } - /* - * 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) { - capret = CAPBUSY; - if ((cc = write(newfd, &capret, sizeof(capret))) <= 0) { - dolog(LOG_NOTICE, "%s refusing. error writing status", - inet_ntoa(tipclient.sin_addr)); - } - dolog(LOG_NOTICE, "%s connecting, but tip is active", - inet_ntoa(tipclient.sin_addr)); - - close(newfd); - return 1; - } - ptyfd = newfd; - /* * Read the first part to verify the key. We must get the * proper bits or this is not a valid tip connection. */ - if ((cc = read(ptyfd, &key, sizeof(key))) <= 0) { - close(ptyfd); + if ((cc = read(newfd, &key, sizeof(key))) <= 0) { + close(newfd); dolog(LOG_NOTICE, "%s connecting, error reading key", - inet_ntoa(tipclient.sin_addr)); + inet_ntoa(sin.sin_addr)); return 1; } #ifdef WITHSSL - usingSSL = 0; - if (cc == sizeof(key) && - 0 == strncmp( key.key, "USESSL", 6 )) { - usingSSL = 1; + (0 == strncmp( key.key, "USESSL", 6 ) || + (dorelay = (0 == strncmp( key.key, "RELAY", 5 ))) || + (doupload = (0 == strncmp( key.key, "UPLOAD", 6 ))))) { /* dolog(LOG_NOTICE, "Client %s wants to use SSL", - inet_ntoa(tipclient.sin_addr) ); + inet_ntoa(sin.sin_addr) ); */ - if (!initializedSSL) { - SSL_load_error_strings(); - SSL_library_init(); - - ctx = SSL_CTX_new( SSLv23_method() ); - if (ctx == NULL) { - dolog( LOG_NOTICE, "Failed to create context."); - close( ptyfd ); - return 1; - } - -#ifndef PREFIX -#define PREFIX -#endif - - 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 ); - close(ptyfd); - 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 ); - close(ptyfd); - return 1; - } - - initializedSSL = 1; - } + initializessl(); /* - if ( write( ptyfd, "OKAY", 4 ) <= 0) { + if ( write( newfd, "OKAY", 4 ) <= 0) { dolog( LOG_NOTICE, "Failed to send OKAY to client." ); - close( ptyfd ); + close( newfd ); return 1; } */ - sslCon = SSL_new( ctx ); - if (!sslCon) { + newssl = SSL_new( ctx ); + if (!newssl) { dolog(LOG_NOTICE, "SSL_new failed."); - close(ptyfd); + close(newfd); return 1; } - if ((ret = SSL_set_fd( sslCon, ptyfd )) <= 0) { + if ((ret = SSL_set_fd( newssl, newfd )) <= 0) { dolog(LOG_NOTICE, "SSL_set_fd failed."); - close(ptyfd); + close(newfd); return 1; } dolog(LOG_NOTICE, "going to accept" ); - if ((ret = SSL_accept( sslCon )) <= 0) { + if ((ret = SSL_accept( newssl )) <= 0) { dolog(LOG_NOTICE, "%s connecting, SSL_accept error.", - inet_ntoa(tipclient.sin_addr)); - goto sslerror; + inet_ntoa(sin.sin_addr)); + ERR_print_errors_fp( stderr ); + SSL_free(newssl); + close(newfd); + return 1; } - dolog(LOG_NOTICE, "going to read key" ); - - if ((cc = SSL_read(sslCon, (void *)&key, sizeof(key))) <= 0) { - ret = cc; - close(ptyfd); - dolog(LOG_NOTICE, "%s connecting, error reading capturekey.", - inet_ntoa(tipclient.sin_addr)); - sslerror: - /* - { - FILE * foo = fopen("/tmp/err.txt", "w"); - ERR_print_errors_fp( foo ); - fclose( foo ); + if (doupload) { + strcpy(uptmpnam, _PATH_TMP "capture.upload.XXXXXX"); + if (upfd >= 0 || !relay_snd || !uploadCommand) { + dolog(LOG_NOTICE, "%s upload already connected.", + inet_ntoa(sin.sin_addr)); + SSL_free(newssl); + close(newfd); + return 1; + } + else if (sslverify(newssl, "Capture Server")) { + SSL_free(newssl); + close(newfd); + return 1; + } + else if ((upfilefd = mkstemp(uptmpnam)) < 0) { + dolog(LOG_NOTICE, "failed to create upload file"); + printf(" %s\n", uptmpnam); + perror("mkstemp"); + SSL_free(newssl); + close(newfd); + return 1; + } + else { + upfd = newfd; + upfilesize = 0; + FD_SET(upfd, &sfds); + if (upfd >= fdcount) { + fdcount = upfd; + fdcount += 1; + } + sslUpload = newssl; + if (fcntl(upfd, F_SETFL, O_NONBLOCK) < 0) + die("fcntl(O_NONBLOCK): %s", geterr(errno)); + return 0; + } + } + else if (dorelay) { + if (devfd >= 0) { + dolog(LOG_NOTICE, "%s relay already connected.", + inet_ntoa(sin.sin_addr)); + SSL_free(newssl); + shutdown(newfd, SHUT_RDWR); + close(newfd); + return 1; + } + else if (memcmp(&relayaddr, + &sin.sin_addr, + sizeof(relayaddr)) != 0) { + dolog(LOG_NOTICE, "%s is not the relay host.", + inet_ntoa(sin.sin_addr)); + SSL_free(newssl); + shutdown(newfd, SHUT_RDWR); + close(newfd); + return 1; + } + else { + relayclient = sin; + devfd = newfd; + sscanf(key.key, "RELAY %d", &upportnum); + FD_SET(devfd, &sfds); + if (devfd >= fdcount) { + fdcount = devfd; + fdcount += 1; + } + sslRelay = newssl; + if (fcntl(devfd, F_SETFL, O_NONBLOCK) < 0) + die("fcntl(O_NONBLOCK): %s", geterr(errno)); + createkey(); + return 0; + } + } + else if (!tipactive) { + sslCon = newssl; + tipclient = sin; + ptyfd = newfd; + dolog(LOG_NOTICE, "going to read key" ); + if ((cc = SSL_read(newssl, (void *)&key, sizeof(key))) <= 0) { + ret = cc; + close(newfd); + dolog(LOG_NOTICE, "%s connecting, error reading capturekey.", + inet_ntoa(sin.sin_addr)); + /* + { + FILE * foo = fopen("/tmp/err.txt", "w"); + ERR_print_errors_fp( foo ); + fclose( foo ); + } + */ + close(ptyfd); + return 1; } - */ - close(ptyfd); - return 1; } dolog(LOG_NOTICE, "got key" ); } + else if (!tipactive) { + tipclient = sin; + ptyfd = newfd; + } #endif /* WITHSSL */ + /* + * 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) { + capret = CAPBUSY; + if ((cc = write(newfd, &capret, sizeof(capret))) <= 0) { + dolog(LOG_NOTICE, "%s refusing. error writing status", + inet_ntoa(tipclient.sin_addr)); + } + dolog(LOG_NOTICE, "%s connecting, but tip is active", + inet_ntoa(tipclient.sin_addr)); + + close(newfd); + return 1; + } /* Verify size of the key is sane */ if (cc != sizeof(key) || key.keylen != strlen(key.key) || @@ -1202,7 +1544,7 @@ clientconnect(void) */ capret = CAPNOPERM; #ifdef WITHSSL - if (usingSSL) { + if (sslCon != NULL) { if ((cc = SSL_write(sslCon, (void *)&capret, sizeof(capret))) <= 0) { dolog(LOG_NOTICE, "%s connecting, error perm status", inet_ntoa(tipclient.sin_addr)); @@ -1226,12 +1568,13 @@ clientconnect(void) dolog(LOG_INFO, "Key: %d: %s", secretkey.keylen, secretkey.key); #endif + /* * Tell the other side that all is okay. */ capret = CAPOK; #ifdef WITHSSL - if (usingSSL) { + if (sslCon != NULL) { if ((cc = SSL_write(sslCon, (void *)&capret, sizeof(capret))) <= 0) { close(ptyfd); dolog(LOG_NOTICE, "%s connecting, error writing status", @@ -1266,6 +1609,49 @@ clientconnect(void) return 0; } +int +handleupload(void) +{ + int drop = 0, rc, retval = 0; + char buffer[BUFSIZE]; + + if ((rc = SSL_read(sslUpload, buffer, sizeof(buffer))) < 0) { + if ((errno != EINTR) && (errno != EAGAIN)) { + drop = 1; + } + } + else if ((upfilesize + rc) > MAX_UPLOAD_SIZE) { + dolog(LOG_NOTICE, "upload to large"); + drop = 1; + } + else if (rc == 0) { + snprintf(buffer, sizeof(buffer), uploadCommand, uptmpnam); + dolog(LOG_NOTICE, "upload done"); + drop = 1; + close(devfd); + /* XXX run uisp */ + system(buffer); + rawmode(Devname, speed); + } + else { + write(upfilefd, buffer, rc); + upfilesize += rc; + } + + if (drop) { + SSL_free(sslUpload); + sslUpload = NULL; + FD_CLR(upfd, &sfds); + close(upfd); + upfd = -1; + close(upfilefd); + upfilefd = -1; + unlink(uptmpnam); + } + + return retval; +} + /* * Generate our secret key and write out the file that local tip uses * to do a secure connect. @@ -1277,6 +1663,9 @@ createkey(void) unsigned char buf[BUFSIZ]; FILE *fp; + if (relay_snd) + return 1; + /* * Generate the key. Should probably generate a random * number of random bits ... @@ -1343,6 +1732,10 @@ createkey(void) fprintf(fp, "host: %s\n", ourhostname); fprintf(fp, "port: %d\n", portnum); + if (upportnum > 0) { + fprintf(fp, "uphost: %s\n", inet_ntoa(relayaddr)); + fprintf(fp, "upport: %d\n", upportnum); + } fprintf(fp, "keylen: %d\n", secretkey.keylen); fprintf(fp, "key: %s\n", secretkey.key); fclose(fp); @@ -1384,7 +1777,7 @@ handshake(void) /* * In standalone, do not contact the capserver. */ - if (standalone) + if (standalone || relay_snd) return err; /* @@ -1420,7 +1813,7 @@ handshake(void) * number does not matter. */ if (bindresvport(sock, NULL) < 0) { - warnc("Could not bind reserved port"); + warning("Could not bind reserved port"); close(sock); return -1; } diff --git a/config.h.in b/config.h.in index 342c3febf7..e7df8b1f1a 100644 --- a/config.h.in +++ b/config.h.in @@ -30,5 +30,7 @@ #undef EVENTSERVER #undef BOSSEVENTPORT +#undef HAVE_SRANDOMDEV + #undef HAVE_LINUX_VIDEODEV_H #undef HAVE_MEZZANINE diff --git a/configure b/configure index d608bd8757..2dc077c00b 100755 --- a/configure +++ b/configure @@ -1222,6 +1222,62 @@ fi +for ac_func in srandomdev +do +echo $ac_n "checking for $ac_func""... $ac_c" 1>&6 +echo "configure:1229: checking for $ac_func" >&5 +if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext <<EOF +#line 1234 "configure" +#include "confdefs.h" +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func(); below. */ +#include <assert.h> +/* Override any gcc2 internal prototype to avoid an error. */ +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char $ac_func(); + +int main() { + +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_$ac_func) || defined (__stub___$ac_func) +choke me +#else +$ac_func(); +#endif + +; return 0; } +EOF +if { (eval echo configure:1257: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + rm -rf conftest* + eval "ac_cv_func_$ac_func=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_func_$ac_func=no" +fi +rm -f conftest* +fi + +if eval "test \"`echo '$ac_cv_func_'$ac_func`\" = yes"; then + echo "$ac_t""yes" 1>&6 + ac_tr_func=HAVE_`echo $ac_func | tr 'abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'` + cat >> confdefs.h <<EOF +#define $ac_tr_func 1 +EOF + +else + echo "$ac_t""no" 1>&6 +fi +done + + # @@ -1854,17 +1910,17 @@ for ac_hdr in ulxmlrpcpp/ulxr_config.h do ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'` echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6 -echo "configure:1858: checking for $ac_hdr" >&5 +echo "configure:1914: checking for $ac_hdr" >&5 if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext <<EOF -#line 1863 "configure" +#line 1919 "configure" #include "confdefs.h" #include <$ac_hdr> EOF ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" -{ (eval echo configure:1868: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +{ (eval echo configure:1924: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` if test -z "$ac_err"; then rm -rf conftest* @@ -1903,17 +1959,17 @@ for ac_hdr in linux/videodev.h do ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'` echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6 -echo "configure:1907: checking for $ac_hdr" >&5 +echo "configure:1963: checking for $ac_hdr" >&5 if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext <<EOF -#line 1912 "configure" +#line 1968 "configure" #include "confdefs.h" #include <$ac_hdr> EOF ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" -{ (eval echo configure:1917: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +{ (eval echo configure:1973: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` if test -z "$ac_err"; then rm -rf conftest* @@ -1946,7 +2002,7 @@ done # Extract the first word of "gtk-config", so it can be a program name with args. set dummy gtk-config; ac_word=$2 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 -echo "configure:1950: checking for $ac_word" >&5 +echo "configure:2006: checking for $ac_word" >&5 if eval "test \"`echo '$''{'ac_cv_prog_GTK_CONFIG'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -2025,7 +2081,7 @@ fi # SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff" # ./install, which can be erroneously created by make from ./install.sh. echo $ac_n "checking for a BSD compatible install""... $ac_c" 1>&6 -echo "configure:2029: checking for a BSD compatible install" >&5 +echo "configure:2085: checking for a BSD compatible install" >&5 if test -z "$INSTALL"; then if eval "test \"`echo '$''{'ac_cv_path_install'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 @@ -2086,7 +2142,7 @@ esac # Extract the first word of "rsync", so it can be a program name with args. set dummy rsync; ac_word=$2 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 -echo "configure:2090: checking for $ac_word" >&5 +echo "configure:2146: checking for $ac_word" >&5 if eval "test \"`echo '$''{'ac_cv_path_RSYNC'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -2256,7 +2312,7 @@ outfiles="$outfiles Makeconf GNUmakefile \ dhcpd/dhcpd.conf.template dhcpd/GNUmakefile \ install/GNUmakefile install/ops-install install/boss-install \ install/newnode_sshkeys/GNUmakefile install/smb.conf.head \ - mote/GNUmakefile mote/tbuisp mote/tbsgmotepower \ + mote/GNUmakefile mote/tbuisp mote/tbsgmotepower mote/newmote \ robots/GNUmakefile robots/tbsetdest/GNUmakefile \ robots/mtp/GNUmakefile robots/emc/GNUmakefile \ robots/emc/test_emcd.sh robots/emc/loclistener \ diff --git a/configure.in b/configure.in index 28abd1c969..6be143df31 100755 --- a/configure.in +++ b/configure.in @@ -57,6 +57,8 @@ AC_PATH_PROG(JAR,jar) AC_CHECK_TOOL(SSH,ssh) +AC_CHECK_FUNCS(srandomdev) + AC_SUBST(optional_subdirs) # @@ -751,7 +753,7 @@ outfiles="$outfiles Makeconf GNUmakefile \ dhcpd/dhcpd.conf.template dhcpd/GNUmakefile \ install/GNUmakefile install/ops-install install/boss-install \ install/newnode_sshkeys/GNUmakefile install/smb.conf.head \ - mote/GNUmakefile mote/tbuisp mote/tbsgmotepower \ + mote/GNUmakefile mote/tbuisp mote/tbsgmotepower mote/newmote \ robots/GNUmakefile robots/tbsetdest/GNUmakefile \ robots/mtp/GNUmakefile robots/emc/GNUmakefile \ robots/emc/test_emcd.sh robots/emc/loclistener \ diff --git a/db/libdb.pm.in b/db/libdb.pm.in index 946f72b3d7..d01dfd8586 100644 --- a/db/libdb.pm.in +++ b/db/libdb.pm.in @@ -3144,6 +3144,7 @@ sub TBRobotLabExpt($$) "event_groups", "firewalls", "firewall_rules", + "virt_tiptunnels", "ipsubnets", "nsfiles"); diff --git a/db/xmlconvert.in b/db/xmlconvert.in index 42ae982bdd..3959b1415c 100644 --- a/db/xmlconvert.in +++ b/db/xmlconvert.in @@ -130,7 +130,11 @@ my %virtual_tables = "firewall_rules" => { rows => undef, tag => "firewall_rules", row => "firewall_rule", - attrs => [ "fwname", "ruleno", "rule" ]} + attrs => [ "fwname", "ruleno", "rule" ]}, + "virt_tiptunnels" => { rows => undef, + tag => "tiptunnels", + row => "tiptunnel", + attrs => [ "host", "vnode" ]} ); # XXX diff --git a/event/program-agent/program-agent.c b/event/program-agent/program-agent.c index bc4d4c72fd..19a426d80c 100644 --- a/event/program-agent/program-agent.c +++ b/event/program-agent/program-agent.c @@ -182,6 +182,8 @@ static void start_program(struct proginfo *pinfo, unsigned long token, char *args); +static void set_program(struct proginfo *pinfo, char *args); + /** * Stop a running program. * @@ -825,12 +827,40 @@ callback(event_handle_t handle, event_notification_t notification, void *data) else if (strcmp(event, TBDB_EVENTTYPE_KILL) == 0) { signal_program(pinfo, args); } + else if (strcmp(event, TBDB_EVENTTYPE_MODIFY) == 0) { + set_program(pinfo, args); + } else { error("Invalid event: %s\n", event); return; } } +static char *fileext(char *path) +{ + int has_token = 0, lpc, len; + char *retval = NULL; + + assert(path != NULL); + + len = strlen(path); + for (lpc = len - 1; lpc > 0; lpc--) { + if (path[lpc] == '.') { + if (has_token) { + retval = &path[lpc + 1]; + } + else if (sscanf(&path[lpc + 1], "%*d") == 1) { + has_token = 1; + } + else { + retval = &path[lpc + 1]; + } + } + } + + return retval; +} + static void start_callback(event_handle_t handle, event_notification_t notification, @@ -880,20 +910,17 @@ start_callback(event_handle_t handle, char path[1024]; while ((de = readdir(dir)) != NULL) { - unsigned int token = 0; - char ext[16]; + char *ext = NULL; if ((strlen(de->d_name) < sizeof(path)) && - ((sscanf(de->d_name, - "%[^.].%u.%16s", - path, &token, ext) == 3) || - (sscanf(de->d_name, - "%[^.].%16s", - path, ext) == 2)) && + (sscanf(de->d_name, + "%1024[^.].", + path) == 1) && (find_agent(path) != NULL) && - ((strcmp(ext, "out") == 0) || - (strcmp(ext, "err") == 0) || - (strcmp(ext, "status") == 0))) { + ((ext = fileext(de->d_name)) != NULL) && + ((strncmp(ext, "out", 3) == 0) || + (strncmp(ext, "err", 3) == 0) || + (strncmp(ext, "status", 6) == 0))) { snprintf(path, sizeof(path), "%s/%s", @@ -940,16 +967,16 @@ open_logfile(struct proginfo *pinfo, const char *type) * agent name, the event token, and the type (e.g. out, err). */ snprintf(buf, sizeof(buf), - "%s/%s.%lu.%s", - LOGDIR, pinfo->name, pinfo->token, type); + "%s/%s.%s.%lu", + LOGDIR, pinfo->name, type, pinfo->token); if ((retval = open(buf, O_WRONLY|O_CREAT|O_APPEND, 0640)) >= 0) { /* * We've successfully created the file, now create the * symlinks to that refer to the last run and a tagged run. */ snprintf(buf, sizeof(buf), - "./%s.%lu.%s", - pinfo->name, pinfo->token, type); + "./%s.%s.%lu", + pinfo->name, type, pinfo->token); snprintf(buf2, sizeof(buf2), "%s/%s.%s", LOGDIR, pinfo->name, type); @@ -968,16 +995,11 @@ open_logfile(struct proginfo *pinfo, const char *type) } static void -start_program(struct proginfo *pinfo, unsigned long token, char *args) +set_program(struct proginfo *pinfo, char *args) { - int pid, in_fd, out_fd = -1, err_fd = -1; + assert(pinfo != NULL); + assert(args != NULL); - if (pinfo->pid != 0) { - warning("start_program: %s is still running: %d\n", - pinfo->name, pinfo->pid); - return; - } - /* * The args string holds the command line to execute. We allow * this to be reset in dynamic events, but is optional; the cuurent @@ -1043,17 +1065,30 @@ start_program(struct proginfo *pinfo, unsigned long token, char *args) value = NULL; } } +} + +static void +start_program(struct proginfo *pinfo, unsigned long token, char *args) +{ + int pid, in_fd, out_fd = -1, err_fd = -1; + + if (pinfo->pid != 0) { + warning("start_program: %s is still running: %d\n", + pinfo->name, pinfo->pid); + return; + } + + set_program(pinfo, args); gettimeofday(&pinfo->started, NULL); pinfo->token = token; - if ((pinfo->timeout > 0) && (pinfo->timeout_handle = elvin_sync_add_timeout(NULL, pinfo->timeout * 1000, timeout_callback, - pinfo, + pinfo, elvin_error)) == NULL) { error("Could not add timeout for %s!", pinfo->name); } @@ -1525,13 +1560,16 @@ child_callback(elvin_io_handler_t handler, } } else { - exit_code = status; + if (status == pi->expected_exit_code) + exit_code = 0; + else + exit_code = status; } /* Dump a status file and */ snprintf(path, sizeof(path), - "%s/%s.%lu.status", + "%s/%s.status.%lu", LOGDIR, pi->name, pi->token); @@ -1580,6 +1618,11 @@ child_callback(elvin_io_handler_t handler, ru.ru_maxrss); fclose(file); } + snprintf(path, + sizeof(path), + "./%s.status.%lu", + pi->name, + pi->token); snprintf(path2, sizeof(path), "%s/%s.status", @@ -1591,7 +1634,7 @@ child_callback(elvin_io_handler_t handler, snprintf(path2, sizeof(path), "%s/%s.%s.status", - LOGDIR, pi->tag, pi->name); + LOGDIR, pi->name, pi->tag); unlink(path2); symlink(path, path2); } diff --git a/event/sched/GNUmakefile.in b/event/sched/GNUmakefile.in index 614a99bfa7..739885bb97 100644 --- a/event/sched/GNUmakefile.in +++ b/event/sched/GNUmakefile.in @@ -46,6 +46,7 @@ version.c: event-sched.c echo >$@ "char build_info[] = \"Built on `date +%d-%b-%Y` by `id -nu`@`hostname | sed 's/\..*//'`:`pwd`\";" OBJS = \ + console-agent.o \ error-record.o \ event-sched_rpc.o \ group-agent.o \ @@ -62,7 +63,7 @@ event-sched_rrpc: $(OBJS) event-sched.h ../lib/libevent.a $(CXX) $(CFLAGS) -static $(LDFLAGS) -o $@ $(OBJS) $(ULXRLIBS) $(LIBS) DEPS = \ - error-record.h event-sched.h group-agent.h listNode.h \ + console-agent.h error-record.h event-sched.h group-agent.h listNode.h \ local-agent.h node-agent.h rpc.h simulator-agent.h timeline-agent.h \ ../lib/event.h @@ -72,6 +73,7 @@ error-record.o: error-record.c $(DEPS) local-agent.o: local-agent.c $(DEPS) group-agent.o: group-agent.c $(DEPS) simulator-agent.o: simulator-agent.cc $(DEPS) +console-agent.o: console-agent.cc $(DEPS) node-agent.o: node-agent.cc $(DEPS) event-sched_rpc.o: event-sched.c $(DEPS) $(CC) $(CFLAGS) -DRPC -c -o $@ $< diff --git a/event/sched/console-agent.cc b/event/sched/console-agent.cc new file mode 100644 index 0000000000..52450a21ac --- /dev/null +++ b/event/sched/console-agent.cc @@ -0,0 +1,235 @@ +/* + * EMULAB-COPYRIGHT + * Copyright (c) 2004, 2005 University of Utah and the Flux Group. + * All rights reserved. + */ + +#include "config.h" + +#include <errno.h> +#include <fcntl.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/param.h> + +#include "console-agent.h" + +#ifndef min +#define min(x, y) ((x) < (y)) ? (x) : (y) +#endif + +/** + * A "looper" function for console agents that dequeues and processes events + * for a particular console. This function will be passed to pthread_create + * when a new thread needs to be created to handle events. + * + * @param arg The console agent object to handle events for. + * @return NULL + * + * @see reload_with + * @see do_reboot + * @see local_agent_queue + * @see local_agent_dequeue + */ +static void *console_agent_looper(void *arg); + +console_agent_t create_console_agent(void) +{ + console_agent_t ca, retval; + + if ((ca = (console_agent_t) + malloc(sizeof(struct _console_agent))) == NULL) { + retval = NULL; + errno = ENOMEM; + } + else if (local_agent_init(&ca->ca_local_agent) != 0) { + retval = NULL; + } + else { + ca->ca_local_agent.la_flags |= LAF_MULTIPLE; + ca->ca_local_agent.la_looper = console_agent_looper; + ca->ca_mark = -1; + retval = ca; + ca = NULL; + } + + free(ca); + ca = NULL; + + return retval; +} + +int console_agent_invariant(console_agent_t ca) +{ + assert(ca != NULL); + assert(local_agent_invariant(&ca->ca_local_agent)); +} + +static void do_start(console_agent_t ca, sched_event_t *se) +{ + int lpc; + + assert(ca != NULL); + assert(console_agent_invariant(ca)); + assert(se != NULL); + + for (lpc = 0; lpc < se->length; lpc++) { + char path[MAXPATHLEN]; + struct agent *agent; + struct stat st; + + if (se->length == 1) + agent = se->agent.s; + else + agent = se->agent.m[lpc]; + + snprintf(path, sizeof(path), + "%s/%s.run", + TIPLOGDIR, agent->nodeid); + if (stat(path, &st) < 0) { + error("could not stat %s\n", path); + } + else { + ca = (console_agent_t)agent->handler; + ca->ca_mark = st.st_size; + } + } +} + +static void do_stop(console_agent_t ca, sched_event_t *se, char *args) +{ + char *filename; + int rc, lpc; + + assert(ca != NULL); + assert(console_agent_invariant(ca)); + assert(se != NULL); + assert(args != NULL); + + if (ca->ca_mark == -1) { + error("CONSOLE STOP event without a START"); + return; + } + + if ((rc = event_arg_get(args, "FILE", &filename)) < 0) { + error("no filename given in CONSOLE STOP event"); + return; + } + filename[rc] = '\0'; + + for (lpc = 0; lpc < se->length; lpc++) { + char path[MAXPATHLEN], outpath[MAXPATHLEN]; + int infd = -1, outfd = -1; + struct agent *agent; + size_t end_mark; + struct stat st; + + if (se->length == 1) + agent = se->agent.s; + else + agent = se->agent.m[lpc]; + + snprintf(path, sizeof(path), + "%s/%s.run", + TIPLOGDIR, agent->nodeid); + if (stat(path, &st) < 0) { + error("could not stat %s\n", path); + ca->ca_mark = -1; + continue; + } + + snprintf(outpath, sizeof(outpath), + "logs/%s-%s.log", + agent->name, + filename); + + ca = (console_agent_t)agent->handler; + end_mark = st.st_size; + if ((infd = open(path, O_RDONLY)) < 0) { + error("could not open %s\n", path); + } + else if (lseek(infd, ca->ca_mark, SEEK_SET) < 0) { + error("could not seek to right place\n"); + } + else if ((outfd = open(outpath, + O_WRONLY|O_CREAT|O_TRUNC, + 0640)) < 0) { + error("could not create output file\n"); + } + else { + size_t remaining = end_mark - ca->ca_mark; + char buffer[4096]; + + while ((rc = read(infd, + buffer, + min(remaining, + sizeof(buffer)))) > 0) { + write(outfd, buffer, rc); + + remaining -= rc; + } + } + + if (infd != -1) + close(infd); + if (outfd != -1) + close(outfd); + + ca->ca_mark = -1; + } +} + +static void *console_agent_looper(void *arg) +{ + console_agent_t ca = (console_agent_t)arg; + event_handle_t handle; + void *retval = NULL; + sched_event_t se; + + assert(arg != NULL); + + handle = ca->ca_local_agent.la_handle; + + while (local_agent_dequeue(&ca->ca_local_agent, 0, &se) == 0) { + char evtype[TBDB_FLEN_EVEVENTTYPE]; + event_notification_t en; + char argsbuf[BUFSIZ]; + + en = se.notification; + + if (!event_notification_get_eventtype( + handle, en, evtype, sizeof(evtype))) { + error("couldn't get event type from notification %p\n", + en); + } + else { + struct agent **agent_array, *agent_singleton[1]; + int rc, lpc, token = ~0; + + event_notification_get_arguments(handle, + en, + argsbuf, + sizeof(argsbuf)); + event_notification_get_int32(handle, + en, + "TOKEN", + (int32_t *)&token); + argsbuf[sizeof(argsbuf) - 1] = '\0'; + + if (strcmp(evtype, TBDB_EVENTTYPE_START) == 0) { + do_start(ca, &se); + } + else if (strcmp(evtype, TBDB_EVENTTYPE_STOP) == 0) { + do_stop(ca, &se, argsbuf); + } + else { + error("cannot handle CONSOLE event %s.", + evtype); + rc = -1; + } + } + sched_event_free(handle, &se); + } + + return retval; +} diff --git a/event/sched/console-agent.h b/event/sched/console-agent.h new file mode 100644 index 0000000000..0fcc9ba9bc --- /dev/null +++ b/event/sched/console-agent.h @@ -0,0 +1,57 @@ +/* + * EMULAB-COPYRIGHT + * Copyright (c) 2005 University of Utah and the Flux Group. + * All rights reserved. + */ + +/** + * @file console-agent.h + */ + +#ifndef _console_agent_h +#define _console_agent_h + +#include "event-sched.h" +#include "local-agent.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define TIPLOGDIR "/var/log/tiplogs" + +/** + * A local agent structure for Console objects. + */ +struct _console_agent { + struct _local_agent ca_local_agent; /*< Local agent base. */ + off_t ca_mark; +}; + +/** + * Pointer type for the _console_agent structure. + */ +typedef struct _console_agent *console_agent_t; + +/** + * Create a console agent and intialize it with the default values. + * + * @return An initialized console agent object. + */ +console_agent_t create_console_agent(void); + +/** + * Check a console agent object against the following invariants: + * + * @li na_local_agent is sane + * + * @param na An initialized console agent object. + * @return True. + */ +int console_agent_invariant(console_agent_t na); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/event/sched/event-sched.c b/event/sched/event-sched.c index 0081480e70..97725fec25 100644 --- a/event/sched/event-sched.c +++ b/event/sched/event-sched.c @@ -44,6 +44,7 @@ #include "simulator-agent.h" #include "group-agent.h" #include "node-agent.h" +#include "console-agent.h" #include "timeline-agent.h" #define EVENT_SCHED_PATH_ENV \ @@ -141,6 +142,9 @@ main(int argc, char *argv[]) lnNewList(&sequences); lnNewList(&groups); + if ((primary_simulator_agent = create_simulator_agent()) == NULL) + fatal("cannot allocate simulator agent"); + while ((c = getopt(argc, argv, "hVrs:p:dl:k:")) != -1) { switch (c) { case 'h': @@ -449,6 +453,7 @@ int sends_complete(struct agent *agent, const char *evtype) { TBDB_OBJECTTYPE_NODE, node_completes }, { TBDB_OBJECTTYPE_TIMELINE, run_completes }, { TBDB_OBJECTTYPE_SEQUENCE, run_completes }, + { TBDB_OBJECTTYPE_CONSOLE, NULL }, { NULL, NULL } }; @@ -766,6 +771,9 @@ AddUserEnv(char *name, char *path) if ((idx = strchr(buf, '\n')) != NULL) *idx = '\0'; if ((idx = strchr(buf, '=')) != NULL) { + add_report_data(primary_simulator_agent, + SA_RDK_CONFIG, + buf); *idx = '\0'; retval = setenv(strdup(buf), idx + 1, 1); } @@ -822,15 +830,10 @@ AddAgent(event_handle_t handle, } if (strcmp(type, TBDB_OBJECTTYPE_SIMULATOR) == 0) { - if ((primary_simulator_agent = - create_simulator_agent()) != NULL) { - primary_simulator_agent->sa_local_agent.la_link. - ln_Name = agentp->name; - primary_simulator_agent->sa_local_agent.la_agent = - agentp; - agentp->handler = &primary_simulator_agent-> - sa_local_agent; - } + primary_simulator_agent->sa_local_agent.la_link.ln_Name = + agentp->name; + primary_simulator_agent->sa_local_agent.la_agent =agentp; + agentp->handler = &primary_simulator_agent->sa_local_agent; } else if ((strcmp(type, TBDB_OBJECTTYPE_TIMELINE) == 0) || (strcmp(type, TBDB_OBJECTTYPE_SEQUENCE) == 0)) { @@ -868,6 +871,16 @@ AddAgent(event_handle_t handle, agentp->handler = &na->na_local_agent; } } + else if (strcmp(type, TBDB_OBJECTTYPE_CONSOLE) == 0) { + console_agent_t ca; + + if ((ca = create_console_agent()) == NULL) { + } + else { + ca->ca_local_agent.la_agent = agentp; + agentp->handler = &ca->ca_local_agent; + } + } if (agentp->handler != NULL) { agentp->handler->la_handle = handle; diff --git a/event/sched/node-agent.cc b/event/sched/node-agent.cc index c752d668db..015b126c4f 100644 --- a/event/sched/node-agent.cc +++ b/event/sched/node-agent.cc @@ -229,7 +229,7 @@ static int do_reboot(node_agent_t na, char *nodeids) warning("failed to sync log hole for node %s\n", nodeids); } - printf("rebooting; %s\n", nodeids); + info("rebooting; %s\n", nodeids); /* ... start the reboot. */ if ((retval = RPC_invoke("node.reboot", @@ -258,21 +258,17 @@ static int do_snapshot(node_agent_t na, char *nodeids, char *args) handle = na->na_local_agent.la_handle; - /* - * Get any logs off the node(s) before we destroy them with the disk - * reload, then - */ if (systemf("loghole --port=%d --quiet sync %s", DEFAULT_RPC_PORT, nodeids) != 0) { warning("failed to sync log hole for node %s\n", nodeids); } - /* ... reload the default image, or */ if ((rc = event_arg_get(args, "IMAGE", &image_name)) < 0) { warning("no image name given: %s\n", nodeids); + + retval = -1; } - /* ... a user-specified image. */ else { image_name[rc] = '\0'; if ((retval = RPC_invoke("node.create_image", diff --git a/event/sched/simulator-agent.cc b/event/sched/simulator-agent.cc index 488379ee95..b528b6d7d5 100644 --- a/event/sched/simulator-agent.cc +++ b/event/sched/simulator-agent.cc @@ -115,9 +115,9 @@ int simulator_agent_invariant(simulator_agent_t sa) return 1; } -static int add_report_data(simulator_agent_t sa, - sa_report_data_kind_t rdk, - char *data) +int add_report_data(simulator_agent_t sa, + sa_report_data_kind_t rdk, + char *data) { char *new_data; int retval; @@ -228,7 +228,8 @@ static int do_modify(simulator_agent_t sa, int token, char *args) static void dump_report_data(FILE *file, simulator_agent_t sa, - sa_report_data_kind_t srdk) + sa_report_data_kind_t srdk, + int clear) { assert(file != NULL); assert(sa != NULL); @@ -272,7 +273,8 @@ static int send_report(simulator_agent_t sa, char *args) error("failed to sync log holes\n"); } - if ((file = popenf("mail -s \"%s: %s experiment report\" %s", + if ((file = popenf("tee logs/report.mail | " + "mail -s \"%s: %s experiment report\" %s", "w", OURDOMAIN, pideid, @@ -288,7 +290,7 @@ static int send_report(simulator_agent_t sa, char *args) retval = 0; /* Dump user supplied stuff first, */ - dump_report_data(file, sa, SA_RDK_MESSAGE); + dump_report_data(file, sa, SA_RDK_MESSAGE, 1); /* ... run the user-specified log digester, then */ if ((rc = event_arg_get(args, "DIGESTER", &digester)) > 0) { @@ -331,8 +333,10 @@ static int send_report(simulator_agent_t sa, char *args) fprintf(file, "loghole-archive: %s\n\n", buf); } + + dump_report_data(file, sa, SA_RDK_CONFIG, 0); - dump_report_data(file, sa, SA_RDK_LOG); + dump_report_data(file, sa, SA_RDK_LOG, 1); /* ... dump the error records. */ if (dump_error_records(&error_records, file) != 0) { @@ -352,6 +356,21 @@ static int send_report(simulator_agent_t sa, char *args) return retval; } +static int do_reset(simulator_agent_t sa, char *args) +{ + int retval = 0; + + assert(sa != NULL); + assert(args != NULL); + + if (systemf("loghole --port=%d --quiet clean", + DEFAULT_RPC_PORT) != 0) { + error("failed to clean log holes\n"); + } + + return retval; +} + static void *simulator_agent_looper(void *arg) { simulator_agent_t sa = (simulator_agent_t)arg; @@ -419,6 +438,9 @@ static void *simulator_agent_looper(void *arg) else if (strcmp(evtype, TBDB_EVENTTYPE_REPORT) == 0) { send_report(sa, argsbuf); } + else if (strcmp(evtype, TBDB_EVENTTYPE_RESET) == 0) { + do_reset(sa, argsbuf); + } else { error("cannot handle SIMULATOR event %s.", evtype); diff --git a/event/sched/simulator-agent.h b/event/sched/simulator-agent.h index 279322fc05..d3942cf6f4 100644 --- a/event/sched/simulator-agent.h +++ b/event/sched/simulator-agent.h @@ -30,6 +30,7 @@ typedef enum { the experiment. */ SA_RDK_LOG, /*< Log data for the tail of the report, used for machine generated data mostly. */ + SA_RDK_CONFIG, /*< Config data for the tail of the report. */ SA_RDK_MAX /*< The maximum number of message types. */ } sa_report_data_kind_t; @@ -78,6 +79,10 @@ simulator_agent_t create_simulator_agent(void); */ int simulator_agent_invariant(simulator_agent_t sa); +int add_report_data(simulator_agent_t sa, + sa_report_data_kind_t rdk, + char *data); + #ifdef __cplusplus } #endif diff --git a/lib/libtb/tbdefs.h b/lib/libtb/tbdefs.h index c0305089ed..56c0a28a71 100644 --- a/lib/libtb/tbdefs.h +++ b/lib/libtb/tbdefs.h @@ -44,6 +44,7 @@ #define TBDB_OBJECTTYPE_GROUP "GROUP" #define TBDB_OBJECTTYPE_TIMELINE "TIMELINE" #define TBDB_OBJECTTYPE_SEQUENCE "SEQUENCE" +#define TBDB_OBJECTTYPE_CONSOLE "CONSOLE" #define TBDB_EVENTTYPE_START "START" #define TBDB_EVENTTYPE_STOP "STOP" diff --git a/mote/GNUmakefile.in b/mote/GNUmakefile.in index 50df052dba..bd52eb7a6f 100644 --- a/mote/GNUmakefile.in +++ b/mote/GNUmakefile.in @@ -1,6 +1,6 @@ # # EMULAB-COPYRIGHT -# Copyright (c) 2004 University of Utah and the Flux Group. +# Copyright (c) 2004, 2005 University of Utah and the Flux Group. # All rights reserved. # @@ -11,7 +11,7 @@ SUBDIR = mote include $(OBJDIR)/Makeconf -BIN_SCRIPTS = tbuisp tbsgmotepower +BIN_SCRIPTS = tbuisp tbsgmotepower newmote # # Force dependencies on the scripts so that they will be rerun through diff --git a/mote/newmote.in b/mote/newmote.in new file mode 100644 index 0000000000..ae45798171 --- /dev/null +++ b/mote/newmote.in @@ -0,0 +1,124 @@ +#!/usr/bin/perl -wT +# +# EMULAB-COPYRIGHT +# Copyright (c) 2005 University of Utah and the Flux Group. +# All rights reserved. +# + +# +# newmote - +# + +use lib '@prefix@/lib'; +my $TB = '@prefix@'; + +use libdb; +use English; +use Getopt::Std; + +# un-taint path +$ENV{'PATH'} = "/bin:/usr/bin:/usr/local/bin:$TB/bin"; +delete @ENV{'IFS', 'CDPATH', 'ENV', 'BASH_ENV'}; + +use strict; + +# +# Constants +# +my $DEBUG = 1; + +# +# Experiments we might put nodes into +# +my $PID_HWDOWN = NODEDEAD_PID(); +my $EID_HWDOWN = NODEDEAD_EID(); + +my $nalloc = "$TB/bin/nalloc"; +my $phys_nodeid = ""; +my $do_nalloc = 1; + +# +# Handle command-line arguments +# TODO: Allow a user to specify some of their own arguments to uisp +# +sub usage() { + print STDERR "Usage: $0 [-hdf] [-p phys] <type> <motes...>\n"; + exit(2); +} + +my $optlist = "dhfp:"; + +my %options = (); +if (! getopts($optlist, \%options)) { + usage(); +} +if (defined($options{"d"})) { + $DEBUG = 1; +} +if (defined($options{"h"})) { + usage(); +} +if (defined($options{"f"})) { + $do_nalloc = 0; +} +if (defined($options{"p"})) { + $phys_nodeid = $options{"p"}; + + if ($phys_nodeid =~ /^([-\w]+)$/) { + $phys_nodeid = $1; + } + else { + die("*** Bad phys_nodeid: $phys_nodeid.\n"); + } +} + +if (@ARGV < 2) { + usage(); +} + +my $type = shift(@ARGV); +my @node_ids = @ARGV; + +if ($type =~ /^([-\w]+)$/) { + $type = $1; +} +else { + die("*** Bad mote type: $type.\n"); +} + +foreach my $node_id (@node_ids) { + if ($node_id =~ /^([-\w]+)$/) { + $node_id = $1; + } + else { + die("*** Bad node id: $node_id.\n"); + } + + my $pnode; + if ($phys_nodeid eq "") { + $pnode = $node_id; + } else { + $pnode = $phys_nodeid; + } + + DBQueryFatal("REPLACE INTO nodes SET ". + "node_id='$node_id',type='$type',phys_nodeid='$pnode',". + "role='testnode',def_boot_osid='emulab-ops-TinyOS-STD',". + "bootstatus='okay',status='up',status_timestamp=NOW(),". + "failureaction='fatal',routertype='none',eventstate='ISUP',". + "state_timestamp=NOW(),op_mode='ALWAYSUP',". + "op_mode_timestamp=NOW(),allocstate='FREE',". + "allocstate_timestamp=NOW()"); + + DBQueryFatal("REPLACE INTO partitions SET ". + "node_id='$node_id',partition=1,". + "osid='emulab-ops-TinyOS-STD'"); + + DBQueryFatal("REPLACE INTO tiplines SET ". + "tipname='$node_id',node_id='$node_id',". + "server='@USERNODE@'"); # XXX Better subst for user node + + if ($do_nalloc) { + system "$nalloc $PID_HWDOWN $EID_HWDOWN $node_id"; + } +} diff --git a/mote/tbuisp.in b/mote/tbuisp.in index a9c2ba7a0f..9bee638526 100755 --- a/mote/tbuisp.in +++ b/mote/tbuisp.in @@ -37,6 +37,8 @@ my $UISP = "$TB/bin/uisp"; my $SGUISP = "/usr/local/bin/uisp"; my $SSHTB = "$TB/bin/sshtb"; my $POWER = "$TB/bin/power"; +my $TIP = "$TB/bin/tiptunnel"; +my $USERS = "@USERNODE@"; my $DEBUG = 1; # @@ -188,8 +190,15 @@ MOTE: foreach my $mote (@motes) { next MOTE; } if ($host eq $mote) { - warn "Error - no host found for $mote - skipping\n"; - $errors++; + print "Uploading code to $mote\n"; + my $commandstr = "$SSHTB -host $USERS $TIP -u $UID -l $mote - < $filename"; + my $OLDUID = $UID; + $UID = $EUID; + if (system($commandstr)) { + $errors++; + warn "Failed to upload code to $mote"; + } + $UID = $OLDUID; next MOTE; } my ($hosttype, $hostclass) = TBNodeType($host); diff --git a/sql/database-fill.sql b/sql/database-fill.sql index 672017a928..fb38194929 100644 --- a/sql/database-fill.sql +++ b/sql/database-fill.sql @@ -243,10 +243,10 @@ REPLACE INTO mode_transitions VALUES ('MINIMAL','SHUTDOWN','PXEFBSD','SHUTDOWN', REPLACE INTO mode_transitions VALUES ('NORMAL','REBOOTING','NORMALv2','SHUTDOWN',''); REPLACE INTO mode_transitions VALUES ('NORMALv2','SHUTDOWN','NORMAL','REBOOTING',''); REPLACE INTO mode_transitions VALUES ('NORMALv1','SHUTDOWN','NORMALv2','SHUTDOWN',''); -REPLACE INTO mode_transitions VALUES ('RELOAD-MOTE','SHUTDOWN','ALWAYSUP','SHUTDOWN','ReloadDone'); REPLACE INTO mode_transitions VALUES ('ALWAYSUP','SHUTDOWN','RELOAD-MOTE','SHUTDOWN','ReloadStart'); REPLACE INTO mode_transitions VALUES ('ALWAYSUP','ISUP','RELOAD-MOTE','SHUTDOWN','ReloadStart'); REPLACE INTO mode_transitions VALUES ('ALWAYSUP','ISUP','RELOAD-MOTE','ISUP','ReloadStart'); +REPLACE INTO mode_transitions VALUES ('RELOAD-MOTE','SHUTDOWN','ALWAYSUP','ISUP','ReloadDone'); -- -- Dumping data for table `state_timeouts` diff --git a/tbsetup/ns2ir/GNUmakefile.in b/tbsetup/ns2ir/GNUmakefile.in index 139aca84a7..8f5041f07d 100644 --- a/tbsetup/ns2ir/GNUmakefile.in +++ b/tbsetup/ns2ir/GNUmakefile.in @@ -17,7 +17,8 @@ include $(OBJDIR)/Makeconf LIB_STUFF = lanlink.tcl node.tcl sim.tcl tb_compat.tcl null.tcl \ nsobject.tcl traffic.tcl vtype.tcl parse.tcl program.tcl \ nsenode.tcl nstb_compat.tcl event.tcl firewall.tcl \ - elabinelab.ns fw.ns timeline.tcl sequence.tcl topography.tcl + elabinelab.ns fw.ns timeline.tcl sequence.tcl \ + topography.tcl console.tcl BOSSLIBEXEC = parse-ns USERLIBEXEC = parse.proxy diff --git a/tbsetup/ns2ir/console.tcl b/tbsetup/ns2ir/console.tcl new file mode 100644 index 0000000000..1b9f9d730f --- /dev/null +++ b/tbsetup/ns2ir/console.tcl @@ -0,0 +1,49 @@ +# -*- tcl -*- +# +# EMULAB-COPYRIGHT +# Copyright (c) 2000-2005 University of Utah and the Flux Group. +# All rights reserved. +# + +###################################################################### +# console.tcl +# +# This defines the console agent. +# +###################################################################### + +Class Console -superclass NSObject + +namespace eval GLOBALS { + set new_classes(Console) {} +} + +Console instproc init {s n} { + $self set sim $s + $self set node $n + $self set connected 0 +} + +Console instproc rename {old new} { + $self instvar sim + + $sim rename_console $old $new +} + +# updatedb DB +# This adds rows to the virt_trafgens table corresponding to this agent. +Console instproc updatedb {DB} { + var_import ::GLOBALS::pid + var_import ::GLOBALS::eid + var_import ::TBCOMPAT::objtypes + $self instvar node + $self instvar sim + + if {$node == {}} { + perror "\[updatedb] $self has no node." + return + } + + # Update the DB + $sim spitxml_data "virt_agents" [list "vnode" "vname" "objecttype"] [list $node $self $objtypes(CONSOLE)] +} diff --git a/tbsetup/ns2ir/node.tcl b/tbsetup/ns2ir/node.tcl index 99addd0219..e7d9cd6646 100644 --- a/tbsetup/ns2ir/node.tcl +++ b/tbsetup/ns2ir/node.tcl @@ -86,6 +86,11 @@ Node instproc init {s} { $self set Z_ 0.0 $self set orientation_ 0.0 + set cname "${self}-console" + Console $cname $s $self + $s add_console $cname + $self set console_ $cname + if { ${::GLOBALS::simulated} == 1 } { $self set simulated 1 } else { @@ -97,10 +102,16 @@ Node instproc init {s} { # The following procs support renaming (see README) Node instproc rename {old new} { $self instvar portlist + $self instvar console_ + foreach object $portlist { $object rename_node $old $new } [$self set sim] rename_node $old $new + $console_ set node $new + $console_ rename "${old}-console" "${new}-console" + uplevel "#0" rename "${old}-console" "${new}-console" + set console_ ${new}-console } Node instproc rename_lanlink {old new} { @@ -251,7 +262,7 @@ Node instproc updatedb {DB} { return } - if {! [$topo checkdest $self $X_ $Y_]} { + if {! [$topo checkdest $self $X_ $Y_ -showerror 1]} { return } @@ -542,3 +553,9 @@ Node instproc topography {topo} { $self set type "robot" } } + +Node instproc console {} { + $self instvar console_ + + return $console_ +} diff --git a/tbsetup/ns2ir/parse.tcl.in b/tbsetup/ns2ir/parse.tcl.in index d5725cee4d..cf85233570 100644 --- a/tbsetup/ns2ir/parse.tcl.in +++ b/tbsetup/ns2ir/parse.tcl.in @@ -352,6 +352,7 @@ source ${GLOBALS::libdir}/event.tcl source ${GLOBALS::libdir}/firewall.tcl source ${GLOBALS::libdir}/timeline.tcl source ${GLOBALS::libdir}/sequence.tcl +source ${GLOBALS::libdir}/console.tcl source ${GLOBALS::libdir}/topography.tcl ################################################## diff --git a/tbsetup/ns2ir/sequence.tcl b/tbsetup/ns2ir/sequence.tcl index 324afbf9f0..9148ffa267 100644 --- a/tbsetup/ns2ir/sequence.tcl +++ b/tbsetup/ns2ir/sequence.tcl @@ -43,6 +43,16 @@ EventSequence instproc init {s seq args} { set ::GLOBALS::last_class $self } +EventSequence instproc append {event} { + $self instvar sim + $self instvar event_list + + set rc [$sim make_event "sequence" $event] + if {$rc != {}} { + lappend event_list $rc + } +} + EventSequence instproc rename {old new} { $self instvar sim diff --git a/tbsetup/ns2ir/sim.tcl.in b/tbsetup/ns2ir/sim.tcl.in index c4c6db52d2..ab9426e489 100644 --- a/tbsetup/ns2ir/sim.tcl.in +++ b/tbsetup/ns2ir/sim.tcl.in @@ -78,6 +78,12 @@ Simulator instproc init {args} { $self instvar sequence_list; array set sequence_list {} + $self instvar console_list; + array set console_list {} + + $self instvar tiptunnel_list; + set tiptunnel_list {} + var_import ::GLOBALS::last_class set last_class $self @@ -319,6 +325,8 @@ Simulator instproc run {} { $self instvar firewall_list $self instvar timeline_list $self instvar sequence_list + $self instvar console_list + $self instvar tiptunnel_list $self instvar simulated $self instvar nseconfig var_import ::GLOBALS::pid @@ -502,6 +510,12 @@ Simulator instproc run {} { foreach seq [array names sequence_list] { $seq updatedb "sql" } + foreach con [array names console_list] { + $con updatedb "sql" + } + foreach tt $tiptunnel_list { + $self spitxml_data "virt_tiptunnels" [list "host" "vnode"] $tt + } set fields [list "mem_usage" "cpu_usage" "forcelinkdelays" "uselinkdelays" "usewatunnels" "uselatestwadata" "wa_delay_solverweight" "wa_bw_solverweight" "wa_plr_solverweight" "veth_encapsulate" "allowfixnode"] set values [list $mem_usage $cpu_usage $forcelinkdelays $uselinkdelays $usewatunnels $uselatestwadata $wa_delay_solverweight $wa_bw_solverweight $wa_plr_solverweight $veth_encapsulate $fix_current_resources] @@ -616,6 +630,25 @@ Simulator instproc attach-agent {node agent} { # connect <src> <dst> # Connects two agents together. Simulator instproc connect {src dst} { + $self instvar tiptunnel_list + + if {([$src info class Node] && [$dst info class Console]) || + ([$src info class Console] && [$dst info class Node])} { + if {[$src info class Node] && [$dst info class Console]} { + set node $src + set con $dst + } else { + set node $dst + set con $src + } + if {[$con set connected]} { + perror "\[connect] $con is already connected" + return + } + $con set connected 1 + lappend tiptunnel_list [list $node [$con set node]] + return + } set error 0 if {! [$src info class Agent]} { perror "\[connect] $src is not an Agent." @@ -784,6 +817,12 @@ Simulator instproc rename_sequence {old new} { set sequence_list($new) {} } +Simulator instproc rename_console {old new} { + $self instvar console_list + unset console_list($old) + set console_list($new) {} +} + # find_link <node1> <node2> # This is just an accesor to the link_map datastructure. If no # link is known between <node1> and <node2> the empty list is returned. @@ -946,6 +985,13 @@ Simulator instproc add_eventgroup {group} { set eventgroup_list($group) {} } +# add_console +# Link to a Console object. +Simulator instproc add_console {console} { + $self instvar console_list + set console_list($console) {} +} + # add_firewall # Link to a Firewall object. Simulator instproc add_firewall {fw} { @@ -1176,12 +1222,12 @@ Simulator instproc make_event {outer event} { } set x [lindex $evargs 0] set y [lindex $evargs 1] - if {! [$topo checkdest $self $x $y]} { + if {! [$topo checkdest $self $x $y -showerror 1]} { return } set speed [lindex $evargs 2] - if {$speed != 0.0 && $speed != 0.1} { - perror "Speed is currently locked at 0.0 or 0.1" + if {$speed != 0.0 && ($speed < 0.1) && ($speed > 0.4)} { + perror "Speed is currently locked at 0.0 or 0.1-0.4" return } ::GLOBALS::named-args [lrange $evargs 3 end] { @@ -1257,8 +1303,16 @@ Simulator instproc make_event {outer event} { } switch -- $cmd { + "set" - "run" { - set etype RUN + switch -- $cmd { + "set" { + set etype MODIFY + } + "run" { + set etype RUN + } + } if {[$obj info class] == "EventGroup"} { set default_command {} } else { @@ -1317,6 +1371,25 @@ Simulator instproc make_event {outer event} { } } } + "Console" { + set otype CONSOLE + set vname $obj + + switch -- $cmd { + "start" { + set etype START + } + "stop" { + set etype STOP + if {[llength $event] < 3} { + perror "Wrong number of arguments: $obj $cmd $evargs" + return + } + set arg [lindex $event 2] + set args "FILE=$arg" + } + } + } "Simulator" { set vnode "*" set vname $self @@ -1377,6 +1450,11 @@ Simulator instproc make_event {outer event} { set args "DIGESTER={$(-digester)}" } } + "cleanlogs" { + set otype SIMULATOR + set etype RESET + set args "ASPECT=LOGHOLE" + } unknown { punsup "$obj $cmd $evargs" return diff --git a/tbsetup/ns2ir/topography.tcl b/tbsetup/ns2ir/topography.tcl index 638431ba6a..01d85003ef 100644 --- a/tbsetup/ns2ir/topography.tcl +++ b/tbsetup/ns2ir/topography.tcl @@ -46,8 +46,8 @@ Topography instproc load_area {area} { $self set area_name $area # XXX Load the width/height of the floor in here. - $self set width 10.0 - $self set height 10.0 + $self set width 50.0 + $self set height 50.0 } Topography instproc initialized {} { @@ -61,19 +61,27 @@ Topography instproc initialized {} { } } -Topography instproc checkdest {obj x y} { +Topography instproc checkdest {obj x y args} { var_import ::TBCOMPAT::obstacles var_import ::TBCOMPAT::cameras $self instvar area_name $self instvar width $self instvar height + ::GLOBALS::named-args $args { + -showerror 0 + } + if {$x < 0 || $x >= $width} { - perror "$x is out of bounds for node \"$obj\"" + if {$(-showerror)} { + perror "$x is out of bounds for node \"$obj\"" + } return 0 } if {$y < 0 || $y >= $height} { - perror "$y is out of bounds for node \"$obj\"" + if {$(-showerror)} { + perror "$y is out of bounds for node \"$obj\"" + } return 0 } @@ -86,7 +94,9 @@ Topography instproc checkdest {obj x y} { ($x <= [expr $obstacles($id,$area_name,x2) + 0.25]) && ($y >= [expr $obstacles($id,$area_name,y1) - 0.25]) && ($y <= [expr $obstacles($id,$area_name,y2) + 0.25])} { - perror "Destination $x,$y puts $obj in obstacle $value." + if {$(-showerror)} { + perror "Destination $x,$y puts $obj in obstacle $value." + } return 0 } } @@ -107,7 +117,9 @@ Topography instproc checkdest {obj x y} { } if {$in_cam == ""} { - perror "Destination $x,$y is out of view of the tracking cameras"; + if {$(-showerror)} { + perror "Destination $x,$y is out of view of the tracking cameras"; + } return 0 } } diff --git a/tbsetup/tbreport.in b/tbsetup/tbreport.in index 4a5831ea59..e7057942f3 100644 --- a/tbsetup/tbreport.in +++ b/tbsetup/tbreport.in @@ -758,7 +758,7 @@ if ($showevents) { "left join event_eventtypes as et on ex.eventtype=et.idx ". "left join event_objecttypes as ot on ex.objecttype=ot.idx ". "where ex.pid='$pid' and ex.eid='$eid' ". - "order by time,vnode,vname,ex.idx"); + "order by time,ex.idx,vnode,vname"); if ($result->numrows) { if ($verbose) { diff --git a/tip/GNUmakefile.in b/tip/GNUmakefile.in index 4d6a7fd680..b8f89df8ac 100644 --- a/tip/GNUmakefile.in +++ b/tip/GNUmakefile.in @@ -5,13 +5,16 @@ SUBDIR = tip include $(OBJDIR)/Makeconf -all: tip tiptunnel console +all: tip tiptunnel console tippty +client: tippty include $(TESTBED_SRCDIR)/GNUmakerules SSLFLAGS = -DWITHSSL SSLLIBS = -lssl -lcrypto +PTYLIBS = SYSTEM := $(shell uname -s) +PTYLIBS += -lutil ifeq ($(SYSTEM),Linux) NEEDKERB := $(shell nm /usr/lib/libssl.a | grep -q krb; echo $$?) ifeq ($(NEEDKERB),0) @@ -20,7 +23,7 @@ ifeq ($(NEEDKERB),0) endif endif -CC = gcc -g -O2 -DUSESOCKETS -I$(TESTBED_SRCDIR)/capture +CC = gcc -g -O2 -DUSESOCKETS -I$(TESTBED_SRCDIR)/capture -I$(OBJDIR) OBJS = cmds.o cmdtab.o hunt.o partab.o \ remote.o tip.o value.o vars.o getcap.o @@ -52,10 +55,19 @@ console.o: tiptunnel.c $(TESTBED_SRCDIR)/capture/capdecls.h console: console.o $(CC) -o console console.o +tippty.o: tiptunnel.c $(TESTBED_SRCDIR)/capture/capdecls.h + $(CC) -DTIPPTY -o $@ -c $< + +tippty: tippty.o + $(CC) $(PTYLIBS) -o $@ $< + $(OBJS): tipconf.h tip.h +client-install: client + $(INSTALL_PROGRAM) tippty$(EXE) $(DESTDIR)$(CLIENT_BINDIR)/tippty$(EXE) control-install tipserv-install install: all $(INSTALL_BINDIR)/tip $(INSTALL_BINDIR)/tiptunnel $(INSTALL_BINDIR)/console + $(INSTALL_PROGRAM) tiptunnel $(INSTALL_DIR)/opsdir/bin/tiptunnel clean: - rm -f $(OBJS) tiptunnel.o console.o tip tiptunnel console + rm -f $(OBJS) tiptunnel.o console.o tip tiptunnel console tippty.o tippty diff --git a/tip/tiptunnel.c b/tip/tiptunnel.c index a9adf8a287..62295bdae3 100644 --- a/tip/tiptunnel.c +++ b/tip/tiptunnel.c @@ -1,8 +1,21 @@ + +#include "config.h" + +#include <errno.h> #include <stdlib.h> #include <stdio.h> #include <string.h> #include <unistd.h> +#include <fcntl.h> +#include <grp.h> +#include <pwd.h> +#include <signal.h> +#include <assert.h> +#include <paths.h> + +#include <sys/time.h> +#include <sys/types.h> #include <sys/param.h> #include <sys/socket.h> #include <netinet/in.h> @@ -16,7 +29,9 @@ #include "capdecls.h" int localmode = 0; +int uploadmode = 0; +#define ACLDIR "/var/log/tiplogs" #define DEFAULT_PROGRAM "xterm -T TIP -e telnet localhost @s" int debug = 0; @@ -31,6 +46,9 @@ int tunnelSock = 0; char * programToLaunch = NULL; char * hostname = NULL; +char * certfile = NULL; +char * user = NULL; + secretkey_t key; typedef int WriteFunc( void * data, int size ); @@ -65,16 +83,54 @@ char * certString = NULL; #endif /* WITHSSL */ +#if defined(TIPPTY) + +#if defined(__FreeBSD__) +# include <libutil.h> +#endif + +#if defined(linux) +# if !defined(INFTIM) +# define INFTIM -1 +# endif +# include <pty.h> +# include <utmp.h> +#endif + +#include <poll.h> +#include <termios.h> + +#define TIPDIR "/dev/tip" +#define POLL_HUP_INTERVAL (250) /* ms */ + +typedef struct { + char data[4096]; + size_t inuse; +} buffer_t; + +static void pack_buffer(buffer_t *buffer, int amount); +static void dotippty(char *nodename); + +static char pidfilename[1024] = ""; + +static void sigquit(int sig) +{ + if (strlen(pidfilename) > 0) + unlink(pidfilename); + exit(0); +} +#endif + int main( int argc, char ** argv ) { const char * name = argv[0]; int op; -#ifdef LOCALBYDEFAULT +#if defined(LOCALBYDEFAULT) || defined(TIPPTY) localmode++; #endif - while ((op = getopt( argc, argv, "hlsp:rd" )) != -1) { + while ((op = getopt( argc, argv, "hlsp:rdu:c:" )) != -1) { switch (op) { case 'h': usage(name); @@ -91,6 +147,16 @@ int main( int argc, char ** argv ) case 'r': allowRemote++; break; +#ifdef WITHSSL + case 'u': + user = optarg; + uploadmode++; + usingSSL++; + break; + case 'c': + certfile = optarg; + break; +#endif } } @@ -109,7 +175,7 @@ int main( int argc, char ** argv ) if (localmode) { char localAclName[1024]; - sprintf( localAclName, "/var/log/tiplogs/%s.acl", argv[0] ); + sprintf( localAclName, "%s/%s.acl", ACLDIR, argv[0] ); loadAcl( localAclName ); } else { loadAcl( argv[0] ); @@ -131,7 +197,76 @@ int main( int argc, char ** argv ) doConnect(); } +#if defined(TIPPTY) + if (!debug) { + FILE *file; + + daemon(0, 0); + signal(SIGINT, sigquit); + signal(SIGTERM, sigquit); + signal(SIGQUIT, sigquit); + snprintf(pidfilename, sizeof(pidfilename), + "%s/tippty.%s.pid", + _PATH_VARRUN, argv[0]); + if ((file = fopen(pidfilename, "w")) != NULL) { + fprintf(file, "%d\n", getpid()); + fclose(file); + } + } +#endif + + if (user && (getuid() == 0)) { + struct passwd *pw; + struct group *gr; + uid_t uid; + int rc; + + if ((sscanf(user, "%d", &uid) == 1 && (pw = getpwuid(uid)) == NULL) && + (pw = getpwnam(user)) == NULL) { + fprintf(stderr, "invalid user: %s %d\n", user, uid); + exit(1); + } + + if ((gr = getgrgid(pw->pw_gid)) == NULL) { + fprintf(stderr, "invalid group: %d\n", pw->pw_gid); + exit(1); + } + + /* + * Initialize the group list, and then flip to uid. + */ + if (setgid(pw->pw_gid) || + initgroups(user, pw->pw_gid) || + setuid(pw->pw_uid)) { + fprintf(stderr, "Could not become user: %s\n", user); + exit(1); + } + } + + if (uploadmode) { + int fd = 0; + + if ((strcmp(argv[1], "-") != 0) && (fd = open(argv[1], O_RDONLY)) < 0) { + fprintf(stderr, "Cannot open file: %s\n", argv[1]); + exit(1); + } + else { + char buf[4096]; + int rc; + + while ((rc = read(fd, buf, sizeof(buf))) > 0) { + writeFunc(buf, rc); + } + close(fd); + } + exit(0); + } + doAuthenticate(); + +#if defined(TIPPTY) + dotippty(argv[0]); +#else doCreateTunnel(); if (programToLaunch) { @@ -179,11 +314,12 @@ int main( int argc, char ** argv ) //if (localmode) { sleep(3); } doTunnelConnection(); if (debug) { printf("tiptunnel closing.\n"); } +#endif } void usage(const char * name) { -#ifdef LOCALBYDEFAULT +#if defined(LOCALBYDEFAULT) printf("Usage:\n" "%s [-d] <pcname>\n" @@ -192,6 +328,12 @@ void usage(const char * name) name ); +#elif defined(TIPPTY) + + printf("Usage:\n" + "%s [-d] <node>\n", + name); + #else printf("No aclfile specified.\n" @@ -208,6 +350,7 @@ void usage(const char * name) "-p <portnum> specifies tunnel port number\n" "-d turns on more verbose debug messages\n" "-r allows connections to tunnel from non-localhost\n" + "-u <user> upload mode\n" "\n" "<program> (non-local-mode only)\n" " path of program to launch; default is\n" @@ -240,14 +383,22 @@ void loadAcl( const char * filename ) while (fscanf(aclFile, "%s %s\n", &b1, &b2) != EOF) { if ( strcmp(b1, "host:") == 0 ) { - hostname = strdup( b2 ); + if (!uploadmode) + hostname = strdup( b2 ); } else if ( strcmp(b1, "port:") == 0 ) { - port = atoi( b2 ); + if (!uploadmode) + port = atoi( b2 ); } else if ( strcmp(b1, "keylen:") == 0 ) { key.keylen = atoi( b2 ); } else if ( strcmp(b1, "key:") == 0 ) { strcpy( key.key, b2 ); #ifdef WITHSSL + } else if ( strcmp(b1, "uphost:") == 0 ) { + if (uploadmode) + hostname = strdup( b2 ); + } else if ( strcmp(b1, "upport:") == 0 ) { + if (uploadmode) + port = atoi( b2 ); } else if ( strcmp(b1, "ssl-server-cert:") == 0 ) { if (debug) { printf("Using SSL to connect to capture.\n"); } certString = strdup( b2 ); @@ -520,12 +671,27 @@ acceptor: #ifdef WITHSSL +#define DEFAULT_CERTFILE TBROOT"/etc/capture.pem" + void initSSL() { SSL_library_init(); ctx = SSL_CTX_new( SSLv23_method() ); SSL_load_error_strings(); - + + if (uploadmode) { + if (!certfile) { certfile = DEFAULT_CERTFILE; } + + if (SSL_CTX_use_certificate_file( ctx, certfile, SSL_FILETYPE_PEM ) <= 0) { + fprintf( stderr, "Could not load %s as certificate file.", certfile ); + exit(1); + } + + if (SSL_CTX_use_PrivateKey_file( ctx, certfile, SSL_FILETYPE_PEM ) <= 0) { + fprintf( stderr, "Could not load %s as key file.", certfile ); + exit(1); + } + } // if (!(SSL_CTX_load_verify_location( ctx, CA_LIST, 0 )) } @@ -544,7 +710,10 @@ void sslConnect() char ret[4]; sslHintKey.keylen = 7; - strncpy( sslHintKey.key, "USESSL", 7 ); + if (uploadmode) + strcpy( sslHintKey.key, "UPLOAD" ); + else + strncpy( sslHintKey.key, "USESSL", 7 ); write( sock, &sslHintKey, sizeof( sslHintKey ) ); /* if (4 != read( sock, ret, 4 ) || @@ -553,7 +722,7 @@ void sslConnect() exit(-1); } */ - } + } ssl = SSL_new( ctx ); SSL_set_fd( ssl, sock ); @@ -570,7 +739,7 @@ void sslConnect() // sbio = BIO_new_socket( sock, BIO_NOCLOSE ); // SSL_set_bio( ssl, sbio, sbio ); - sleep(1); + // sleep(1); if (SSL_connect( ssl ) <= 0) { fprintf(stderr, "SSL Connect error.\n"); @@ -578,6 +747,9 @@ void sslConnect() exit(-1); } + if (uploadmode) + return; + peer = SSL_get_peer_certificate( ssl ); // X509_print_fp( stdout ); @@ -587,6 +759,8 @@ void sslConnect() X509_digest( peer, EVP_sha(), digest, &len ); + X509_free( peer ); + for (i = 0; i < len; i++) { sprintf( digestHex + (i * 2), "%02x", (unsigned int) digest[i] ); } @@ -623,3 +797,150 @@ int readSSL( void * data, int size ) } #endif /* WITHSSL */ + +#ifdef TIPPTY + +static void pack_buffer(buffer_t *buffer, int amount) +{ + assert(buffer != NULL); + assert((amount == -1) || + ((amount >= 0) && (amount < sizeof(buffer->data)))); + + if (amount > 0) { + buffer->inuse -= amount; + memmove(buffer->data, &buffer->data[amount], buffer->inuse); + } +} + +static void dotippty(char *nodename) +{ + int rc, master, slave; + char path[64]; + + assert(nodename != NULL); + assert(strlen(nodename) > 0); + + if ((mkdir(TIPDIR, 0755) < 0) && (errno != EEXIST)) + perror("unable to make " TIPDIR); + + if ((rc = openpty(&master, &slave, path, NULL, NULL)) < 0) { + perror("openpty"); + exit(1); + } + else { + struct pollfd pf[2] = { + [0] = { .fd = sock, .events = POLLIN }, + [1] = { .fd = master, .events = POLLHUP }, + }; + + buffer_t from_pty = { .inuse = 0 }, to_pty = { .inuse = 0 }; + int tweaked = 0, fd_count = 2; + struct timeval now, before; + char linkpath[128]; + + fcntl(master, F_SETFL, O_NONBLOCK); + fcntl(sock, F_SETFL, O_NONBLOCK); + close(slave); + slave = -1; + + if (chmod(path, 0666) < 0) + perror("unable to change permissions"); + + snprintf(linkpath, sizeof(linkpath), "%s/%s", TIPDIR, nodename); + if ((symlink(path, linkpath) < 0) && (errno != EEXIST)) + perror("unable to create symlink"); + + gettimeofday(&now, NULL); + before = now; + while ((rc = poll(pf, + fd_count, + fd_count == 1 ? POLL_HUP_INTERVAL : INFTIM)) >= 0) { + if (rc == 0) { // Timeout + /* ... check for a slave connection on the next loop. */ + fd_count = 2; + } + else if (pf[1].revents & POLLHUP) { + struct timeval diff; + + /* No slave connection. */ + if (pf[0].revents & POLLIN) // Drain the input side. + rc = readFunc(to_pty.data, sizeof(to_pty.data)); + if (rc < 0) + exit(0); + if ((pf[0].revents & POLLOUT) && from_pty.inuse > 0) { + // Drain our buffer to the output. + rc = writeFunc(from_pty.data, from_pty.inuse); + if (rc < 0) + exit(0); + pack_buffer(&from_pty, rc); + } + to_pty.inuse = 0; // Nowhere to send. + gettimeofday(&now, NULL); + timersub(&now, &before, &diff); + if (diff.tv_sec > 0 || diff.tv_usec > (POLL_HUP_INTERVAL * 100)) { + before = now; + fd_count = 2; + } + else { + fd_count = 1; // Don't poll the master and turn on timeout. + } + tweaked = 0; + } + else { + if (!tweaked) { + if ((slave = open(path, O_RDONLY)) >= 0) { + struct termios tio; + + tcgetattr(slave, &tio); + cfmakeraw(&tio); + tio.c_lflag &= ~(ICANON|ECHO|ECHOE|ECHOK|ECHONL|ECHOCTL|ECHOKE); + tcsetattr(slave, TCSANOW, &tio); + close(slave); + slave = -1; + + tweaked = 1; + } + } + if (pf[1].revents & POLLIN) { + rc = read(pf[1].fd, + &from_pty.data[from_pty.inuse], + sizeof(from_pty.data) - from_pty.inuse); + if (rc > 0) + from_pty.inuse += rc; + } + if (pf[1].revents & POLLOUT) { + rc = write(pf[1].fd, to_pty.data, to_pty.inuse); + pack_buffer(&to_pty, rc); + } + if (pf[0].revents & POLLIN) { + rc = readFunc(&to_pty.data[to_pty.inuse], + sizeof(to_pty.data) - to_pty.inuse); + if (rc >= 0) + to_pty.inuse += rc; + else + exit(0); + } + if (pf[0].revents & POLLOUT) { + rc = writeFunc(from_pty.data, from_pty.inuse); + if (rc < 0) + exit(0); + pack_buffer(&from_pty, rc); + } + } + + pf[0].events = 0; + if (to_pty.inuse < sizeof(to_pty.data)) + pf[0].events |= POLLIN; + if (from_pty.inuse > 0) + pf[0].events |= POLLOUT; + + pf[1].events = POLLHUP; + if (from_pty.inuse < sizeof(from_pty.data)) + pf[1].events |= POLLIN; + if (to_pty.inuse > 0) + pf[1].events |= POLLOUT; + } + } +} + +#endif diff --git a/tmcd/common/bootsubnodes b/tmcd/common/bootsubnodes index 7739fe5425..f2b8dd4c0f 100755 --- a/tmcd/common/bootsubnodes +++ b/tmcd/common/bootsubnodes @@ -125,6 +125,38 @@ foreach my $subnode (keys(%subnodelist)) { /^mote$/i && do { libsetup_setvnodeid($subnode); configtmcc("subnode", $subnode); + tmcc(TMCCCMD_STATE, "BOOTING"); + tmcc(TMCCCMD_SUBCONFIG, undef, \@tmccresults) == 0 + or die("*** $0:\n". + " Could not get subnode config from server!\n"); + my $BAUD = 0; + my $CAPSERVER = ""; + my $CAPPORT = 0; + foreach my $str (@tmccresults) { + chomp($str); + SWITCH1: for ($str) { + /^TYPE=([-\w.]+)$/ && do { + if ($1 eq "mica2") { + $BAUD = 57600; + } + last SWITCH1; + }; + /^CAPSERVER=([-\w.]+)$/ && do { + $CAPSERVER = $1; + last SWITCH1; + }; + /^CAPPORT=(\d+)$/ && do { + $CAPPORT = $1; + last SWITCH1; + }; + printf STDERR "Unknown directive: $str\n"; + } + } + if (-x "$BINDIR/capture") { + system("$BINDIR/capture -a -s $BAUD -u '/usr/local/bin/uisp ". + "-dprog=sggpio -dpart=ATmega128 --wr_fuse_e=ff ". + "--erase --upload if=%s' $CAPSERVER:$CAPPORT tts/2"); + } tmcc(TMCCCMD_STATE, "ISUP"); last SWITCH; }; diff --git a/tmcd/common/config/GNUmakefile.in b/tmcd/common/config/GNUmakefile.in index 5b3892f10f..17e1926d33 100644 --- a/tmcd/common/config/GNUmakefile.in +++ b/tmcd/common/config/GNUmakefile.in @@ -1,6 +1,6 @@ # # 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. # @@ -22,7 +22,8 @@ SCRIPTS = $(addprefix $(SRCDIR)/, \ rc.tunnels rc.ifconfig rc.delays rc.hostnames \ rc.syncserver rc.linkagent rc.mkelab rc.localize \ rc.keys rc.trafgen rc.tarfiles rc.rpms rc.progagent \ - rc.startcmd rc.simulator rc.topomap rc.firewall) + rc.startcmd rc.simulator rc.topomap rc.firewall \ + rc.tiptunnels) include $(OBJDIR)/Makeconf diff --git a/tmcd/common/config/rc.config b/tmcd/common/config/rc.config index e56078c56e..051b4fa9e6 100755 --- a/tmcd/common/config/rc.config +++ b/tmcd/common/config/rc.config @@ -1,7 +1,7 @@ #!/usr/bin/perl -w # # EMULAB-COPYRIGHT -# Copyright (c) 2004 University of Utah and the Flux Group. +# Copyright (c) 2004, 2005 University of Utah and the Flux Group. # All rights reserved. # use English; @@ -99,7 +99,7 @@ else { "rc.route", "rc.tunnels", "rc.ifconfig", "rc.delays", "rc.hostnames", "rc.syncserver", "rc.trafgen", "rc.tarfiles", "rc.rpms", "rc.progagent", "rc.linkagent", - "rc.startcmd", "rc.simulator"); + "rc.tiptunnels", "rc.startcmd", "rc.simulator"); } # Execute the action. diff --git a/tmcd/common/config/rc.tiptunnels b/tmcd/common/config/rc.tiptunnels new file mode 100644 index 0000000000..5c4506f16c --- /dev/null +++ b/tmcd/common/config/rc.tiptunnels @@ -0,0 +1,139 @@ +#!/usr/bin/perl -w +# +# EMULAB-COPYRIGHT +# Copyright (c) 2005 University of Utah and the Flux Group. +# All rights reserved. +# +use English; +use Getopt::Std; +use POSIX qw(setsid); + +sub usage() +{ + print "Usage: " . + scriptname() . "boot|shutdown|reconfig|reset\n"; + exit(1); +} +my $action = "boot"; + +# Turn off line buffering on output +$| = 1; + +# Drag in path stuff so we can find emulab stuff. +BEGIN { require "/etc/emulab/paths.pm"; import emulabpaths; } + +# Only root. +if ($EUID != 0) { + die("*** $0:\n". + " Must be root to run this script!\n"); +} + +# +# Load the OS independent support library. It will load the OS dependent +# library and initialize itself. +# +use libsetup; +use libtmcc; +use librc; + +# +# Not all clients support this. +# +exit(0) + if (MFS() || PLAB() || JAILED()); + +# Protos. +sub doboot(); +sub doshutdown(); +sub doreconfig(); +sub docleanup(); + +# Allow default above. +if (@ARGV) { + $action = $ARGV[0]; +} + +# Execute the action. +SWITCH: for ($action) { + /^boot$/i && do { + doboot(); + last SWITCH; + }; + /^shutdown$/i && do { + doshutdown(); + last SWITCH; + }; + /^reconfig$/i && do { + doreconfig(); + last SWITCH; + }; + /^reset$/i && do { + docleanup(); + last SWITCH; + }; + fatal("Invalid action: $action\n"); +} +exit(0); + +# +# Boot Action. +# +sub doboot() +{ + my @tiptunnels; + + print STDOUT "Checking Testbed tiptunnel configuration ... \n"; + + if (gettiptunnelconfig(\@tiptunnels)) { + fatal("Could not get tiptunnel configuration from libsetup!"); + } + + foreach my $tiptunnel (@tiptunnels) { + if (-e "/var/run/tippty.$tiptunnel.pid") { + print STDOUT "Tunnel for '$tiptunnel' is already running.\n"; + } + else { + system("$BINDIR/tippty $tiptunnel"); + } + } + + return; +} + +# +# Shutdown Action. +# +sub doshutdown() +{ + my @tiptunnels; + + if (gettiptunnelconfig(\@tiptunnels)) { + fatal("Could not get tiptunnel configuration from libsetup!"); + } + + foreach my $tiptunnel (@tiptunnels) { + if (-e "/var/run/tippty.$tiptunnel.pid") { + system("kill `cat /var/run/tippty.$tiptunnel.pid`"); + unlink "/var/run/tippty.$tiptunnel.pid"; + } + } + + return; +} + +# +# Node Reconfig Action (without rebooting). +# +sub doreconfig() +{ + # Shutdown tunnels before doing reconfig. + doshutdown(); + return doboot(); +} + +# +# Node cleanup action (node is reset to completely clean state). +# +sub docleanup() +{ +} diff --git a/tmcd/common/libsetup.pm b/tmcd/common/libsetup.pm index 1a29f9cd17..d4ffc10b30 100644 --- a/tmcd/common/libsetup.pm +++ b/tmcd/common/libsetup.pm @@ -19,7 +19,7 @@ use Exporter; check_nickname bootsetup startcmdstatus whatsmynickname TBBackGround TBForkCmd vnodejailsetup plabsetup vnodeplabsetup jailsetup dojailconfig findiface libsetup_getvnodeid - ixpsetup libsetup_refresh gettopomap getfwconfig + ixpsetup libsetup_refresh gettopomap getfwconfig gettiptunnelconfig TBDebugTimeStamp TBDebugTimeStampsOn @@ -45,7 +45,7 @@ use libtmcc; # # BE SURE TO BUMP THIS AS INCOMPATIBILE CHANGES TO TMCD ARE MADE! # -sub TMCD_VERSION() { 23; }; +sub TMCD_VERSION() { 24; }; libtmcc::configtmcc("version", TMCD_VERSION()); # Control tmcc timeout. @@ -850,6 +850,49 @@ sub gettunnelconfig($) return 0; } +# +# Get tiptunnels configuration. +# +sub gettiptunnelconfig($) +{ + my ($rptr) = @_; + my @tiptunnels = (); + + if (tmcc(TMCCCMD_TIPTUNNELS, undef, \@tmccresults) < 0) { + warn("*** WARNING: Could not get tiptunnel config from server!\n"); + return -1; + } + + my $pat = q(VNODE=([-\w.]+) SERVER=([-\w.]+) PORT=(\d+) ); + $pat .= q(KEYLEN=(\d+) KEY=([-\w.]+)); + + my $ACLDIR = "/var/log/tiplogs"; + + mkdir("$ACLDIR", 0755); + foreach my $str (@tmccresults) { + if ($str =~ /$pat/) { + if (!open(ACL, "> $ACLDIR/$1.acl")) { + warn("*** WARNING: ". + "gettiptunnelconfig: Could not open $ACLDIR/$1.acl\n"); + return -1; + } + + print ACL "host: $2\n"; + print ACL "port: $3\n"; + print ACL "keylen: $4\n"; + print ACL "key: $5\n"; + close(ACL); + + push(@tiptunnels, $1); + } + else { + warn("*** WARNING: Bad tiptunnels line: $str\n"); + } + } + @$rptr = @tiptunnels; + return 0; +} + my %fwvars = (); # diff --git a/tmcd/common/libtmcc.pm b/tmcd/common/libtmcc.pm index f3a45f7d3a..eb599e8359 100644 --- a/tmcd/common/libtmcc.pm +++ b/tmcd/common/libtmcc.pm @@ -30,6 +30,7 @@ use Exporter; TMCCCMD_FIREWALLINFO TMCCCMD_EMULABCONFIG TMCCCMD_CREATOR TMCCCMD_HOSTINFO TMCCCMD_LOCALIZATION TMCCCMD_BOOTERRNO TMCCCMD_BOOTLOG TMCCCMD_BATTERY TMCCCMD_USERENV + TMCCCMD_TIPTUNNELS ); # Must come after package declaration! @@ -166,6 +167,7 @@ my %commandset = "bootlog" => {TAG => "bootlog"}, "battery" => {TAG => "battery"}, "userenv" => {TAG => "userenv"}, + "tiptunnels" => {TAG => "tiptunnels"}, ); # @@ -218,6 +220,7 @@ sub TMCCCMD_BOOTERRNO (){ $commandset{"booterrno"}->{TAG}; } sub TMCCCMD_BOOTLOG (){ $commandset{"bootlog"}->{TAG}; } sub TMCCCMD_BATTERY (){ $commandset{"battery"}->{TAG}; } sub TMCCCMD_USERENV (){ $commandset{"userenv"}->{TAG}; } +sub TMCCCMD_TIPTUNNELS (){ $commandset{"tiptunnels"}->{TAG}; } # # Caller uses this routine to set configuration of this library diff --git a/tmcd/common/rc.bootsetup b/tmcd/common/rc.bootsetup index 8e94ec55cd..8f4f979010 100755 --- a/tmcd/common/rc.bootsetup +++ b/tmcd/common/rc.bootsetup @@ -361,6 +361,14 @@ sub doboot() if (tmcc(TMCCCMD_STATE, "ISUP") < 0) { fatal("Error sending ISUP to Emulab Control!"); } + if (-x "$BINDIR/bootsubnodes") { + print("Booting up subnodes\n"); + # Foreground mode. + system("$BINDIR/bootsubnodes -f"); + if ($?) { + fatal("Error running $BINDIR/bootsubnodes"); + } + } } # diff --git a/tmcd/decls.h b/tmcd/decls.h index 74160faa5f..046c16b4d7 100644 --- a/tmcd/decls.h +++ b/tmcd/decls.h @@ -26,4 +26,4 @@ * NB: See ron/libsetup.pm. That is version 4! I'll merge that in. */ #define DEFAULT_VERSION 2 -#define CURRENT_VERSION 23 +#define CURRENT_VERSION 24 diff --git a/tmcd/linux/ixpboot b/tmcd/linux/ixpboot index 8dcc299466..e4c1dca736 100755 --- a/tmcd/linux/ixpboot +++ b/tmcd/linux/ixpboot @@ -1,7 +1,7 @@ #!/usr/bin/perl -wT # # 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. # # TODO: Startup command in rc.ixp. Use old version. @@ -94,11 +94,6 @@ if (!$debug && (my $childpid = TBBackGround($logname))) { print "Starting IXP bootup at " . POSIX::strftime("20%y/%m/%d %H:%M:%S", localtime()) . "\n"; -die("*** $0:\n". - " Could not chdir to $ixpdir\n") - if (! -d $ixpdir || - ! chdir($ixpdir)); - # Tell the library what vnode we are messing with. libsetup_setvnodeid($ixpid); # Tell tmcc library too, although thats already been done with previous call. @@ -125,6 +120,11 @@ if (! ixpsetup($ixpid)) { exit(0); } +die("*** $0:\n". + " Could not chdir to $ixpdir\n") + if (! -d $ixpdir || + ! chdir($ixpdir)); + # # Gen up a hostnames in the config dir. # diff --git a/tmcd/tmcd.c b/tmcd/tmcd.c index 09971c3d1d..27f9b0dbbc 100644 --- a/tmcd/tmcd.c +++ b/tmcd/tmcd.c @@ -236,6 +236,8 @@ COMMAND_PROTOTYPE(dobootlog); COMMAND_PROTOTYPE(dobattery); COMMAND_PROTOTYPE(dotopomap); COMMAND_PROTOTYPE(douserenv); +COMMAND_PROTOTYPE(dotiptunnels); +COMMAND_PROTOTYPE(dorelayconfig); /* * The fullconfig slot determines what routines get called when pushing @@ -289,14 +291,14 @@ struct command { { "state", FULLCONFIG_NONE, 0, dostate}, { "tunnels", FULLCONFIG_ALL, F_ALLOCATED, dotunnels}, { "vnodelist", FULLCONFIG_PHYS, 0, dovnodelist}, - { "subnodelist", FULLCONFIG_PHYS, F_ALLOCATED, dosubnodelist}, + { "subnodelist", FULLCONFIG_PHYS, 0, dosubnodelist}, { "isalive", FULLCONFIG_NONE, F_REMUDP|F_MINLOG, doisalive}, { "ipodinfo", FULLCONFIG_NONE, 0, doipodinfo}, { "ntpinfo", FULLCONFIG_PHYS, 0, dontpinfo}, { "ntpdrift", FULLCONFIG_NONE, 0, dontpdrift}, { "jailconfig", FULLCONFIG_VIRT, F_ALLOCATED, dojailconfig}, { "plabconfig", FULLCONFIG_VIRT, F_ALLOCATED, doplabconfig}, - { "subconfig", FULLCONFIG_NONE, F_ALLOCATED, dosubconfig}, + { "subconfig", FULLCONFIG_NONE, 0, dosubconfig}, { "sdparams", FULLCONFIG_PHYS, 0, doslothdparams}, { "programs", FULLCONFIG_ALL, F_ALLOCATED, doprogagents}, { "syncserver", FULLCONFIG_ALL, F_ALLOCATED, dosyncserver}, @@ -318,6 +320,7 @@ struct command { { "battery", FULLCONFIG_NONE, F_REMUDP|F_MINLOG, dobattery}, { "topomap", FULLCONFIG_NONE, F_MINLOG|F_ALLOCATED, dotopomap}, { "userenv", FULLCONFIG_NONE, F_ALLOCATED, douserenv}, + { "tiptunnels", FULLCONFIG_ALL, F_ALLOCATED, dotiptunnels}, }; static int numcommands = sizeof(command_array)/sizeof(struct command); @@ -3657,13 +3660,15 @@ COMMAND_PROTOTYPE(dovnodelist) */ COMMAND_PROTOTYPE(dosubnodelist) { - MYSQL_RES *res; + MYSQL_RES *res; MYSQL_ROW row; char buf[MYBUFSIZE]; int nrows; - res = mydb_query("select r.node_id,nt.class from reserved as r " - "left join nodes as n on r.node_id=n.node_id " + if (vers <= 23) + return 0; + + res = mydb_query("select n.node_id,nt.class from nodes as n " "left join node_types as nt on nt.type=n.type " "where nt.issubnode=1 and n.phys_nodeid='%s'", 2, reqp->nodeid); @@ -4576,6 +4581,9 @@ COMMAND_PROTOTYPE(doplabconfig) */ COMMAND_PROTOTYPE(dosubconfig) { + if (vers <= 23) + return 0; + if (!reqp->issubnode) { error("SUBCONFIG: %s: Not a subnode\n", reqp->nodeid); return 1; @@ -4584,6 +4592,9 @@ COMMAND_PROTOTYPE(dosubconfig) if (! strcmp(reqp->type, "ixp-bv")) return(doixpconfig(sock, reqp, rdata, tcp, vers)); + if (! strcmp(reqp->type, "mica2")) + return(dorelayconfig(sock, reqp, rdata, tcp, vers)); + error("SUBCONFIG: %s: Invalid subnode class %s\n", reqp->nodeid, reqp->class); return 1; @@ -5863,7 +5874,7 @@ COMMAND_PROTOTYPE(douserenv) 2, reqp->pid, reqp->eid); if (!res) { - error("PROGRAM: %s: DB Error getting virt_user_environment\n", + error("USERENV: %s: DB Error getting virt_user_environment\n", reqp->nodeid); return 1; } @@ -5882,7 +5893,94 @@ COMMAND_PROTOTYPE(douserenv) nrows--; if (verbose) - info("PROGAGENTS: %s", buf); + info("USERENV: %s", buf); + } + mysql_free_result(res); + return 0; +} + +/* + * Return tip tunnels for the node. + */ +COMMAND_PROTOTYPE(dotiptunnels) +{ + MYSQL_RES *res; + MYSQL_ROW row; + char buf[MYBUFSIZE]; + int nrows; + + res = mydb_query("select vtt.vnode,tl.server,tl.portnum,tl.keylen," + "tl.keydata " + "from virt_tiptunnels as vtt " + "left join reserved as r on r.vname=vtt.vnode and " + " r.pid=vtt.pid and r.eid=vtt.eid " + "left join tiplines as tl on tl.node_id=r.node_id " + "where vtt.pid='%s' and vtt.eid='%s' and " + "vtt.host='%s'", + 5, reqp->pid, reqp->eid, reqp->nickname); + + if (!res) { + error("TIPTUNNELS: %s: DB Error getting virt_tiptunnels\n", + reqp->nodeid); + return 1; + } + if ((nrows = (int)mysql_num_rows(res)) == 0) { + mysql_free_result(res); + return 0; + } + + while (nrows) { + row = mysql_fetch_row(res); + + if (row[1]) { + OUTPUT(buf, sizeof(buf), + "VNODE=%s SERVER=%s PORT=%s KEYLEN=%s KEY=%s\n", + row[0], row[1], row[2], row[3], row[4]); + client_writeback(sock, buf, strlen(buf), tcp); + } + + nrows--; + if (verbose) + info("TIPTUNNELS: %s", buf); + } + mysql_free_result(res); + return 0; +} + +COMMAND_PROTOTYPE(dorelayconfig) +{ + MYSQL_RES *res; + MYSQL_ROW row; + char buf[MYBUFSIZE]; + int nrows; + + res = mydb_query("select tl.server,tl.portnum from tiplines as tl " + "where tl.node_id='%s'", + 2, reqp->nodeid); + + if (!res) { + error("RELAYCONFIG: %s: DB Error getting relay config\n", + reqp->nodeid); + return 1; + } + if ((nrows = (int)mysql_num_rows(res)) == 0) { + mysql_free_result(res); + return 0; + } + + while (nrows) { + row = mysql_fetch_row(res); + + OUTPUT(buf, sizeof(buf), + "TYPE=%s\n" + "CAPSERVER=%s\n" + "CAPPORT=%s\n", + reqp->type, row[0], row[1]); + client_writeback(sock, buf, strlen(buf), tcp); + + nrows--; + if (verbose) + info("RELAYCONFIG: %s", buf); } mysql_free_result(res); return 0; diff --git a/utils/GNUmakefile.in b/utils/GNUmakefile.in index be52a9f64e..8331fd6906 100644 --- a/utils/GNUmakefile.in +++ b/utils/GNUmakefile.in @@ -15,7 +15,7 @@ include $(OBJDIR)/Makeconf SUBDIRS = nsgen BIN_SCRIPTS = delay_config sshtb create_image node_admin link_config \ - setdest webcopy + setdest loghole webcopy SBIN_SCRIPTS = vlandiff vlansync withadminprivs export_tables cvsupd.pl \ eventping grantnodetype import_commitlog dhcpd_wrapper \ opsreboot deletenode node_statewait grabwebcams \ diff --git a/utils/loghole.in b/utils/loghole.in index 77b50c25b0..a359e58a50 100644 --- a/utils/loghole.in +++ b/utils/loghole.in @@ -45,7 +45,7 @@ EXPDIR_FMT = os.path.join("/", DIRS["proj"], "%(PID)s", "exp", "%(EID)s") GLOBAL_LOGS = [ "event-sched.log", "feedback.log", "../tbdata/feedback_data.tcl", - "digest.out" + "digest.out", "report.mail" ] PID = None @@ -729,6 +729,10 @@ def do_show(args): print " Keep-until:\t\t%s" % cp.get("MAIN", "keep-until") print (" Keep-atleast:\t\t%s days" % cp.get("MAIN", "keep-atleast")) + if "report.mail" in lh.namelist(): + print "Report:" + print lh.read("report.mail") + pass for name in lh.namelist(): if re.match(r'loghole-comment.*\.txt', name): before = 38 - (len(name) / 2) diff --git a/xmlrpc/emulabserver.py.in b/xmlrpc/emulabserver.py.in index 2b14fa57e6..67ef5c0065 100755 --- a/xmlrpc/emulabserver.py.in +++ b/xmlrpc/emulabserver.py.in @@ -108,7 +108,10 @@ virtual_tables = { "attrs" : [ "vname" ]}, "eventlist" : { "rows" : None, "tag" : "events", - "attrs" : [ "vname" ]} + "attrs" : [ "vname" ]}, + "virt_tiptunnels" : { "rows" : None, + "tag" : "tiptunnels", + "attrs" : [ "host", "vnode" ]}, } # Base class for emulab specific exceptions. @@ -414,6 +417,7 @@ class emulab: ob["y2"] = ob["y2"] / ppm ob["z2"] = ob["z2"] / ppm pass + scrubdict(ob) pass return EmulabResponse(RESPONSE_SUCCESS, @@ -1624,7 +1628,8 @@ class experiment: tmp["load_15min"] = res[9] pass tmp["erole"] = res[11] - mapping[res[0]] = tmp + mapping[res[0]] = scrubdict(tmp, + defaultvals={ "status" : "up" }) pass pass @@ -2573,7 +2578,10 @@ class experiment: if not ipres or len(ipres) == 0: continue - ipaddr = ipres[0][0]; + ipaddr = ipres[0][0] + if not ipaddr: + ipaddr = "" + pass nodeid = agent[2] pass @@ -3845,7 +3853,7 @@ def nfspath(value): # def scrubdict(retval, prunelist=[], defaultvals={}): for key in retval.keys(): - if not retval[key] or key in prunelist: + if (retval[key] == None) or key in prunelist: if key in defaultvals: retval[key] = defaultvals[key] pass -- GitLab