Commit 7e92b603 authored by Leigh Stoller's avatar Leigh Stoller

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

distributed tiplines (and capture processes).
parent 4e6418b2
......@@ -5,10 +5,12 @@ SUBDIR = capture
include $(OBJDIR)/Makeconf
all: capture
all: capture capserver
include $(TESTBED_SRCDIR)/GNUmakerules
DBFLAGS += -DTBDBNAME='"$(TBDBNAME)"' -I/usr/local/include
DESTDIR=
#
......@@ -17,14 +19,18 @@ DESTDIR=
#
CFLAGS= -g -O2 -DLOG_DROPS -DUSESOCKETS
capture: capture.c
capture: capture.c capdecls.h
cc $(CFLAGS) -o capture $<
capserver: capserver.c capdecls.h
cc $(CFLAGS) $(DBFLAGS) -o capserver $< \
-L/usr/local/lib/mysql -lmysqlclient
#
# Do not install by default.
#
install:
real-install: all
real-install: all $(INSTALL_SBINDIR)/capserver
install -s capture $(DESTDIR)/usr/site/bin
clean:
......
/*
* File: decls.h
* Description:
* Author: Leigh Stoller
* Computer Science Dept.
* University of Utah
* Date: 2-Aug-2001
*
* (c) Copyright 1992, 2000, University of Utah, all rights reserved.
*/
#define BOSSNODE "boss.emulab.net"
#define SERVERPORT 855
#define DEVPATH "/dev"
#define TIPPATH "/dev/tip"
/*
* The key is transferred in ascii text.
*/
typedef struct {
int keylen; /* of the key string */
char key[256]; /* and the string itself. */
} secretkey_t;
#define DEFAULTKEYLEN 32
/*
* The remote capture sends this back when it starts up
*/
typedef struct {
char nodeid[64];
int portnum;
secretkey_t key;
} whoami_t;
/*
* File: capture.c
* Description:
* Author: Leigh Stoller
* Computer Science Dept.
* University of Utah
* Date: 27-Jul-92
*
* (c) Copyright 2001, University of Utah, all rights reserved.
*/
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <syslog.h>
#include <assert.h>
#include <stdarg.h>
#include <mysql/mysql.h>
#include "capdecls.h"
#define TESTMODE
/* Defined in configure and passed in via the makefile */
#define DBNAME_SIZE 64
#define DEFAULT_DBNAME TBDBNAME
static int debug = 0;
static int portnum = SERVERPORT;
static char *dbname = DEFAULT_DBNAME;
MYSQL_RES * mydb_query(char *query, int ncols, ...);
int mydb_update(char *query, ...);
char *usagestr =
"usage: capserver [-d] [-p #]\n"
" -d Turn on debugging.\n"
" -p portnum Specify a port number to listen on.\n"
"\n";
void
usage()
{
fprintf(stderr, usagestr);
exit(1);
}
int
main(int argc, char **argv)
{
MYSQL_RES *res;
int tcpsock, ch;
int length, i, err = 0;
struct sockaddr_in name;
while ((ch = getopt(argc, argv, "dp:")) != -1)
switch(ch) {
case 'p':
portnum = atoi(optarg);
break;
case 'd':
debug++;
break;
case 'h':
case '?':
default:
usage();
}
argc -= optind;
argv += optind;
if (argc)
usage();
openlog("capserver", LOG_PID, LOG_USER);
syslog(LOG_NOTICE, "daemon starting");
if (!debug)
(void)daemon(0, 0);
/*
* Setup TCP socket
*/
/* Create socket from which to read. */
tcpsock = socket(AF_INET, SOCK_STREAM, 0);
if (tcpsock < 0) {
syslog(LOG_ERR, "opening stream socket: %m");
exit(1);
}
i = 1;
if (setsockopt(tcpsock, SOL_SOCKET, SO_REUSEADDR,
(char *)&i, sizeof(i)) < 0)
syslog(LOG_ERR, "control setsockopt: %m");;
/* Create name. */
name.sin_family = AF_INET;
name.sin_addr.s_addr = INADDR_ANY;
name.sin_port = htons((u_short) portnum);
if (bind(tcpsock, (struct sockaddr *) &name, sizeof(name))) {
syslog(LOG_ERR, "binding stream socket: %m");
exit(1);
}
/* Find assigned port value and print it out. */
length = sizeof(name);
if (getsockname(tcpsock, (struct sockaddr *) &name, &length)) {
syslog(LOG_ERR, "getting socket name: %m");
exit(1);
}
if (listen(tcpsock, 20) < 0) {
syslog(LOG_ERR, "listening on socket: %m");
exit(1);
}
syslog(LOG_NOTICE, "listening on TCP port %d", ntohs(name.sin_port));
while (1) {
struct sockaddr_in client;
int clientsock, length = sizeof(client);
int cc;
whoami_t whoami;
unsigned char buf[BUFSIZ];
secretkey_t secretkey;
if ((clientsock = accept(tcpsock,
(struct sockaddr *)&client,
&length)) < 0) {
syslog(LOG_ERR, "accept failed: %m");
exit(1);
}
syslog(LOG_INFO, "%s connected", inet_ntoa(client.sin_addr));
/*
* Read in the whoami info.
*/
if ((cc = read(clientsock, &whoami, sizeof(whoami))) <= 0) {
if (cc < 0)
syslog(LOG_ERR, "Reading request: %m");
syslog(LOG_ERR, "Connection aborted");
goto done;
}
if (cc != sizeof(whoami)) {
syslog(LOG_ERR, "Wrong byte count!");
goto done;
}
/*
* Make sure there is an entry for this tipline in
* the DB. If not, we just drop the info with an error
* message in the log file. Local tip will still work but
* remote tip will not.
*/
res = mydb_query("select server from tiplines "
"where node_id='%s'",
1, whoami.nodeid);
if (!res) {
syslog(LOG_ERR, "DB Error getting tiplines for %s!",
whoami.nodeid);
goto done;
}
if ((int)mysql_num_rows(res) == 0) {
syslog(LOG_ERR, "No tipline info for %s!",
whoami.nodeid);
mysql_free_result(res);
goto done;
}
mysql_free_result(res);
/*
* Update the DB.
*/
if (mydb_update("update tiplines set portnum=%d, "
"keylen=%d, keydata='%s' "
"where node_id='%s'",
whoami.portnum,
whoami.key.keylen, whoami.key.key,
whoami.nodeid)) {
syslog(LOG_ERR, "DB Error updating tiplines for %s!",
whoami.nodeid);
goto done;
}
syslog(LOG_INFO, "Nodeid %s, Portnum %d, Keylen %d, Key %s\n",
whoami.nodeid, whoami.portnum,
whoami.key.keylen, whoami.key.key);
done:
close(clientsock);
}
close(tcpsock);
syslog(LOG_NOTICE, "daemon terminating");
exit(0);
}
/*
* DB stuff
*/
MYSQL_RES *
mydb_query(char *query, int ncols, ...)
{
MYSQL db;
MYSQL_RES *res;
char querybuf[2*BUFSIZ];
va_list ap;
int n;
va_start(ap, ncols);
n = vsnprintf(querybuf, sizeof(querybuf), query, ap);
if (n > sizeof(querybuf)) {
syslog(LOG_ERR, "query too long for buffer");
return (MYSQL_RES *) 0;
}
mysql_init(&db);
if (mysql_real_connect(&db, 0, 0, 0, dbname, 0, 0, 0) == 0) {
syslog(LOG_ERR, "%s: connect failed: %s",
dbname, mysql_error(&db));
return (MYSQL_RES *) 0;
}
if (mysql_real_query(&db, querybuf, n) != 0) {
syslog(LOG_ERR, "%s: query failed: %s",
dbname, mysql_error(&db));
mysql_close(&db);
return (MYSQL_RES *) 0;
}
res = mysql_store_result(&db);
if (res == 0) {
syslog(LOG_ERR, "%s: store_result failed: %s",
dbname, mysql_error(&db));
mysql_close(&db);
return (MYSQL_RES *) 0;
}
mysql_close(&db);
if (ncols && ncols != (int)mysql_num_fields(res)) {
syslog(LOG_ERR, "%s: Wrong number of fields returned "
"Wanted %d, Got %d",
dbname, ncols, (int)mysql_num_fields(res));
mysql_free_result(res);
return (MYSQL_RES *) 0;
}
return res;
}
int
mydb_update(char *query, ...)
{
MYSQL db;
char querybuf[BUFSIZ];
va_list ap;
int n;
va_start(ap, query);
n = vsnprintf(querybuf, sizeof(querybuf), query, ap);
if (n > sizeof(querybuf)) {
syslog(LOG_ERR, "query too long for buffer");
return 1;
}
mysql_init(&db);
if (mysql_real_connect(&db, 0, 0, 0, dbname, 0, 0, 0) == 0) {
syslog(LOG_ERR, "%s: connect failed: %s",
dbname, mysql_error(&db));
return 1;
}
if (mysql_real_query(&db, querybuf, n) != 0) {
syslog(LOG_ERR, "%s: query failed: %s",
dbname, mysql_error(&db));
mysql_close(&db);
return 1;
}
mysql_close(&db);
return 0;
}
This diff is collapsed.
......@@ -27,7 +27,7 @@ LIBEXEC_STUFF = mkprojdir rmproj mkacct-ctrl rmacct-ctrl \
assign_wrapper ptopgen
LIB_STUFF = libtbsetup.pm exitonwarn.pm libtestbed.pm snmpit_intel.pm \
snmpit_cisco.pm snmpit_lib.pm snmpit_apc.pm
snmpit_cisco.pm snmpit_lib.pm snmpit_apc.pm power_rpc27.pm
#
# Force dependencies on the scripts so that they will be rerun through
......
......@@ -17,18 +17,19 @@ my $TB = "@prefix@";
my $TESTMODE = "@TESTMODE@";
#
# Load the Testbed support stuff.
# Testbed Support libraries
#
push(@INC, "$TB/lib");
require libdb;
use lib "@prefix@/lib";
use libdb;
use libtestbed;
# Turn off line buffering on output
$| = 1;
my $SSH = "$TB/bin/sshtb -n -l root plastic.emulab.net";
my $SSH = "$TB/bin/sshtb -n -l root ";
my $PROG = "/usr/testbed/sbin/console_setup.proxy";
my $TBPID = "flux";
my $cmdargs = "";
my %cmdargs = ();
my @row;
# un-taint path
......@@ -73,7 +74,7 @@ foreach my $node (@nodes) {
# Need the project for the node since that is the group.
#
# HACK! If its a shark shelf, then need a wildcard query so we can
# the nodes.
# find the nodes.
#
if ($node =~ /sh\d+/) {
$db_result =
......@@ -91,14 +92,45 @@ foreach my $node (@nodes) {
else {
$pid = $TBPID;
}
$cmdargs = "$cmdargs $node $pid";
#
# Now we need to know *where* the tip line lives, since there are
# multiple tip servers. We want to group these still, so use an
# array of command arguments, indexed by the tip server.
#
$db_result =
DBQueryFatal("select server from tiplines where node_id='$node'");
if ($db_result->numrows == 0) {
print STDERR "*** No tip server defined for $node\n";
exit 1;
}
@row = $db_result->fetchrow_array();
$server = $row[0];
if (defined($cmdargs{$server})) {
$cmdargs{$server} = $cmdargs{$server} . " $node $pid";
}
else {
$cmdargs{$server} = "$node $pid ";
}
}
if ($TESTMODE) {
exit 0;
}
#
# Run the console setup program on the tip server nodes.
#
$UID = 0;
if (! $TESTMODE) {
system("$SSH $PROG $cmdargs") == 0 or
die("Failed: $SSH $PROG $cmdargs: $?");
foreach my $server (keys(%cmdargs)) {
my $args = $cmdargs{$server};
if (system("$SSH $server $PROG $args")) {
print STDERR "*** Failed: $SSH $server $PROG $args: $?\n";
exit 1;
}
}
exit 0;
#!/usr/bin/perl -wT
use English;
use strict;
use Socket;
use IO::Handle;
#
# A little perl module to power cycle something attached to an RPC27.
# Thats a serially controlled, power controller.
#
# XXX The little secretkey handshake is coded in here. If this changes
# in capture, you have to change it here too. Look for the "pack"
# statement below.
#
# Turn off line buffering on output
$| = 1;
# Set for more output.
my $debug = 1;
# RPC27 Prompt string
my $RPC27_PROMPT = "RPC27>";
my $RPC27_REBOOT = "reboot";
#
# Main routine.
#
# usage: power_rpc27(controller, outlet)
#
sub power_rpc27($$)
{
my($controller, $outlet) = @_;
my($TIP, $i);
#
# Form the connection to the controller via a "tip" line to the
# capture process. Once we have that, we can just talk to the
# controller directly.
#
if (! ($TIP = tipconnect($controller))) {
print STDERR "*** Could not form TIP connection to $controller\n";
return 1;
}
#
# Send a couple of newlines to get the command prompt, and then wait
# for it to print out the command prompt. This loop is set for a small
# number since if it cannot get the prompt quickly, then something has
# gone wrong/
#
print $TIP "\n";
for ($i = 0; $i < 5; $i++) {
my $line = <$TIP>;
if ($line =~ /^$RPC27_REBOOT/) {
last;
}
print $TIP "\n";
}
#
# Okay, got a prompt. Send it the string:
#
print $TIP "$RPC27_REBOOT $outlet\n";
if ($debug) {
print "power_rpc27: Cycled power to outlet $outlet on $controller.\n";
}
close($TIP);
}
#
# Connect up to the capture process. This should probably be in a library
# someplace.
#
sub tipconnect($)
{
my($controller) = $_[0];
my($server, $portnum, $keylen, $keydata);
my($inetaddr, $paddr, $proto);
my(%powerid_row);
local *TIP;
my $query_result =
DBQueryWarn("select * from tiplines where node_id='$controller'");
if ($query_result->numrows < 1) {
print STDERR "*** No such tipline: $controller\n";
return 1;
}
%powerid_row = $query_result->fetchhash();
$server = $powerid_row{'server'};
$portnum = $powerid_row{'portnum'};
$keylen = $powerid_row{'keylen'};
$keydata = $powerid_row{'keydata'};
if ($debug) {
print "tipconnect: $server $portnum $keylen $keydata\n";
}
#
# This stuff from the PERLIPC manpage.
#
if (! ($inetaddr = inet_aton($server))) {
print STDERR "*** Cannot map $server to IP address\n";
return 0;
}
$paddr = sockaddr_in($portnum, $inetaddr);
$proto = getprotobyname('tcp');
if (! socket(TIP, PF_INET, SOCK_STREAM, $proto)) {
print STDERR "*** Cannot create socket.\n";
return 0;
}
if (! connect(TIP, $paddr)) {
print STDERR
"*** Cannot connect to $controller on $server($portnum)\n";
return 0;
}
TIP->autoflush(1);
#
# Okay, we got a connection. We have to send over the key. This is a
# little hokey, since we have to make it look like the C struct.
#
my $secretkey = pack("iZ256", $keylen, $keydata);
if (! syswrite(TIP, $secretkey)) {
print STDERR
"*** Cannot write key to $controller on $server($portnum)\n";
return 0;
}
return(*TIP);
}
1;
......@@ -9,7 +9,7 @@ all: tip
include $(TESTBED_SRCDIR)/GNUmakerules
CC = gcc -g -O2 -DUSESOCKETS
CC = gcc -g -O2 -DUSESOCKETS -I$(TESTBED_SRCDIR)/capture
OBJS = cmds.o cmdtab.o hunt.o partab.o \
remote.o tip.o value.o vars.o getcap.o
......
......@@ -36,9 +36,19 @@
static char sccsid[] = "@(#)hunt.c 8.1 (Berkeley) 6/6/93";
#endif
static const char rcsid[] =
"$Id: hunt.c,v 1.4 2001-07-31 21:58:35 stoller Exp $";
"$Id: hunt.c,v 1.5 2001-08-09 18:40:43 stoller Exp $";
#endif /* not lint */
#ifdef USESOCKETS
#include <sys/param.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include "capdecls.h"
int socket_open(char *devname);
#endif
#include "tip.h"
#include <sys/types.h>
......@@ -47,13 +57,6 @@ static const char rcsid[] =
#include <libutil.h>
#endif
#ifdef USESOCKETS
#include <sys/socket.h>
#include <netinet/in.h>
int socket_open(char *devname);
#endif
extern char *getremote();
extern char *rindex();
......@@ -74,7 +77,7 @@ hunt(name)
register char *cp;
sig_t f;
int res;
f = signal(SIGALRM, dead);
while ((cp = getremote(name))) {
deadfl = 0;
......@@ -98,7 +101,7 @@ hunt(name)
if (setjmp(deadline) == 0) {
alarm(10);
#ifdef USESOCKETS
if ((FD = socket_open(cp)) >= 0) {
if ((FD = socket_open(name)) >= 0) {
HW = 0;
alarm(0);
signal(SIGALRM, SIG_DFL);
......@@ -143,35 +146,75 @@ hunt(name)
*
*/
int
socket_open(char *devname)
socket_open(char *tipname)
{
int sock;
struct sockaddr_in name;
char aclname[BUFSIZ], buf[BUFSIZ];
int aclbits[3];
char aclname[BUFSIZ];
char b1[256], b2[256];
secretkey_t key;
int port;
char hostname[MAXHOSTNAMELEN];
FILE *fp;
struct hostent *he;
(void) sprintf(aclname, "%s.acl", devname);
(void) sprintf(aclname, "%s/%s.acl", TIPPATH, tipname);
if ((fp = fopen(aclname, "r")) == NULL) {
return -1;
}
fscanf(fp, "%d %x %x %x", &port,
&aclbits[0], &aclbits[1], &aclbits[2]);
/* For sanity check below */
bzero(hostname, sizeof(hostname));
bzero(&key, sizeof(key));
port = 0;
while (fscanf(fp, "%s %s\n", &b1, &b2) != EOF) {
if (strcmp(b1, "host:") == 0) {
strcpy(hostname, b2);
}
else if (strcmp(b1, "port:") == 0) {
port = atoi(b2);
}
else if (strcmp(b1, "keylen:") == 0) {
key.keylen = atoi(b2);
}
else if (strcmp(b1, "key:") == 0) {
strcpy(key.key, b2);
}
else {
fprintf(stderr, "Unknown ACL: %s %s\n", b1, b2);
fclose(fp);
return -1;
}
}
fclose(fp);
/* Create socket from which to read. */
sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock < 0) {
return sock;
/* Make sure we got everything we need */
if (port == 0 || strlen(hostname) == 0 ||
key.keylen == 0 || strlen(key.key) == 0) {
fprintf(stderr, "Incomplete ACL\n");
return -1;
}
/* Create name. */
name.sin_family = AF_INET;
inet_aton("127.0.0.1", &name.sin_addr);
name.sin_port = htons(port);
he = gethostbyname(hostname);
if (! he) {
fprintf(stderr, "Unknown hostname %s: %s\n",
hostname, hstrerror(h_errno));
return -1;
}
memcpy ((char *)&name.sin_addr, he->h_addr, he->h_length);
/* Create socket from which to read. */
sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock < 0) {
return sock;
}
/* Caller picks up and displays error */
if (connect(sock, (struct sockaddr *) &name, sizeof(name)) < 0) {
close(sock);
......@@ -179,9 +222,9 @@ socket_open(char *devname)
}
/*
* Send the acl bits.
* Send the secretkey.
*/
if (write(sock, aclbits, sizeof(aclbits)) != sizeof(aclbits)) {
if (write(sock, &key, sizeof(key)) != sizeof(key)) {
close(sock);
return -1;
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment