Commit a2a896ab authored by Mike Hibler's avatar Mike Hibler

First crack at a frisbee "master server" for handling GET (download) requests.

There are a couple of new packet types in the frisbee protocol which are
exchanged via TCP with the master server: GETREQUEST and GETREPLY.  The
client passes to the master server an opaque imageid and a couple of options
and gets back the addr/port to use to actually download the image.  The
implementation of the master server is fragile and is more of a test
framework, Grant is working on a more robust master server.  I am mostly
doing a backend that communicates with the Emulab DB to do its authentication
and making the client changes.

The client now uses the -S option to specify the IP address of the master
server and the -F option to specify an imageid.  If no error is returned,
the image is downloaded using the returned addr/port.  If -Q is used in place
of -F, then the client makes a "status only" call getting back info about
whether the named image is accessible to the client and whether a server is
currently running.

On the server side, the new master server (mserver.c) has an Emulab
configuration "backend" that supports host-based authentication.
The IP address of the caller is mapped to a node_id/pid/gid/eid combo
that is used to determine access.  On a request, the specified imageid is
treated either as a pathname (if it starts with '/') or an image identifier
of the form "<pid>/<imagename>".  If it is a pathname, we check to make
sure that pathname (after running through "realpath") is contained in one
of the directories accessible to that node in its current experiment context;
i.e., /share, /proj/<pid>, /groups/<pid>/<gid>, or /users/<swapper-uid>.
If it is an image identifier, the DB is queried to ensure that access is
allowed to that image; i.e., it must be "global" or in the appropriate
project/group.

The master server forks a frisbeed for each valid request, if one is not
already running.  The multicast address selection is still based on the
emulab_indicies.frisbee_index field, but the address/port/server info is no
longer stored in the frisbee_blobs table (frisbee_pid, load_address,
load_busy are not set).

Note that this is not yet integrated in the os_load path.  Further work is
required to replace frisbeelauncher.
parent b1e4b205
......@@ -46,6 +46,8 @@
#undef TPM
#undef NOSHAREDFS
#undef NFSRACY
#undef FRISEBEEMCASTADDR
#undef FRISEBEEMCASTPORT
#undef HAVE_SRANDOMDEV
......
......@@ -1784,6 +1784,14 @@ cat >> confdefs.h <<EOF
#define BOSSEVENTPORT "$BOSSEVENTPORT"
EOF
cat >> confdefs.h <<EOF
#define FRISEBEEMCASTADDR "$FRISEBEEMCASTADDR"
EOF
cat >> confdefs.h <<EOF
#define FRISEBEEMCASTPORT "$FRISEBEEMCASTPORT"
EOF
if test $OPSDBSUPPORT -eq 1; then
cat >> confdefs.h <<EOF
......@@ -2222,17 +2230,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:2225: checking for $ac_hdr" >&5
echo "configure:2234: 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 2230 "configure"
#line 2239 "configure"
#include "confdefs.h"
#include <$ac_hdr>
EOF
ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
{ (eval echo configure:2235: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
{ (eval echo configure:2244: \"$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*
......@@ -2271,17 +2279,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:2274: checking for $ac_hdr" >&5
echo "configure:2283: 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 2279 "configure"
#line 2288 "configure"
#include "confdefs.h"
#include <$ac_hdr>
EOF
ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
{ (eval echo configure:2284: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
{ (eval echo configure:2293: \"$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*
......@@ -2314,7 +2322,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:2317: checking for $ac_word" >&5
echo "configure:2326: 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
......@@ -2371,7 +2379,7 @@ ac_link='${CXX-g++} -o conftest${ac_exeext} $CXXFLAGS $CPPFLAGS $LDFLAGS conftes
cross_compiling=$ac_cv_prog_cxx_cross
echo $ac_n "checking how to run the C++ preprocessor""... $ac_c" 1>&6
echo "configure:2374: checking how to run the C++ preprocessor" >&5
echo "configure:2383: checking how to run the C++ preprocessor" >&5
if test -z "$CXXCPP"; then
if eval "test \"`echo '$''{'ac_cv_prog_CXXCPP'+set}'`\" = set"; then
echo $ac_n "(cached) $ac_c" 1>&6
......@@ -2384,12 +2392,12 @@ ac_link='${CXX-g++} -o conftest${ac_exeext} $CXXFLAGS $CPPFLAGS $LDFLAGS conftes
cross_compiling=$ac_cv_prog_cxx_cross
CXXCPP="${CXX-g++} -E"
cat > conftest.$ac_ext <<EOF
#line 2387 "configure"
#line 2396 "configure"
#include "confdefs.h"
#include <stdlib.h>
EOF
ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
{ (eval echo configure:2392: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
{ (eval echo configure:2401: \"$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
:
......@@ -2415,17 +2423,17 @@ echo "$ac_t""$CXXCPP" 1>&6
ac_safe=`echo "xercesc/dom/DOM.hpp" | sed 'y%./+-%__p_%'`
echo $ac_n "checking for xercesc/dom/DOM.hpp""... $ac_c" 1>&6
echo "configure:2418: checking for xercesc/dom/DOM.hpp" >&5
echo "configure:2427: checking for xercesc/dom/DOM.hpp" >&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 2423 "configure"
#line 2432 "configure"
#include "confdefs.h"
#include <xercesc/dom/DOM.hpp>
EOF
ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
{ (eval echo configure:2428: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
{ (eval echo configure:2437: \"$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*
......@@ -2489,7 +2497,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:2492: checking for a BSD compatible install" >&5
echo "configure:2501: 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
......@@ -2550,7 +2558,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:2553: checking for $ac_word" >&5
echo "configure:2562: 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
......
......@@ -406,6 +406,8 @@ if test -n "$FSDIR_SCRATCH"; then
AC_DEFINE_UNQUOTED(FSDIR_SCRATCH, "$FSDIR_SCRATCH")
fi
AC_DEFINE_UNQUOTED(BOSSEVENTPORT, "$BOSSEVENTPORT")
AC_DEFINE_UNQUOTED(FRISEBEEMCASTADDR, "$FRISEBEEMCASTADDR")
AC_DEFINE_UNQUOTED(FRISEBEEMCASTPORT, "$FRISEBEEMCASTPORT")
if test $OPSDBSUPPORT -eq 1; then
AC_DEFINE_UNQUOTED(OPSDBSUPPORT, 1)
......
......@@ -175,6 +175,12 @@ mydb_update(char *query, ...)
return 1;
}
int
mydb_insertid()
{
return (int)mysql_insert_id(&db);
}
/*
* Map IP to node ID.
*/
......
......@@ -33,6 +33,7 @@ int mydb_seteventschedulerpid(char *pid, char *eid, int processid);
*/
MYSQL_RES *mydb_query(char *query, int ncols, ...);
int mydb_update(char *query, ...);
int mydb_insertid(void);
unsigned long mydb_escape_string(char *to, const char *from,
unsigned long length);
#
# EMULAB-COPYRIGHT
# Copyright (c) 2000-2009 University of Utah and the Flux Group.
# Copyright (c) 2000-2010 University of Utah and the Flux Group.
# All rights reserved.
#
......@@ -63,7 +63,13 @@ SERVERFLAGS = $(CFLAGS)
SERVERLIBS = $(PTHREADLIBS)
SERVEROBJS = server.o $(SHAREDOBJS)
CFLAGS = -O2 -g -Wall -fno-builtin-log $(LDSTATIC) $(PTHREADCFLAGS) -DSTATS
MYSQLCFLAGS = -I/usr/local/include
MYSQLLIBS = -L/usr/local/lib/mysql -lmysqlclient
MSERVERFLAGS = $(CFLAGS) $(MYSQLCFLAGS) -DUSE_EMULAB_CONFIG -I$(TESTBED_SRCDIR)/lib/libtb -I$(OBJDIR)
MSERVERLIBS = $(OBJDIR)/lib/libtb/libtb.a $(MYSQLLIBS) $(PTHREADLIBS)
MSERVEROBJS = mserver.o config.o config_emulab.o $(SHAREDOBJS)
CFLAGS = -O2 -g -Wall -fno-builtin-log $(LDSTATIC) $(PTHREADCFLAGS) -DSTATS -DMASTER_SERVER
LDFLAGS = $(LDSTATIC)
#
......@@ -110,6 +116,15 @@ frisbeed: $(SERVEROBJS)
cp frisbeed frisbeed.debug
strip frisbeed
mfrisbeed: $(MSERVEROBJS)
$(CC) $(LDFLAGS) $(MSERVERFLAGS) $(MSERVEROBJS) $(MSERVERLIBS) -o mfrisbeed
cp mfrisbeed mfrisbeed.debug
strip mfrisbeed
config.o: $(SRCDIR)/config.c configdefs.h log.h
$(CC) -c $(MSERVERFLAGS) $(SRCDIR)/config.c
config_emulab.o: $(SRCDIR)/config_emulab.c configdefs.h log.h
$(CC) -c $(MSERVERFLAGS) $(SRCDIR)/config_emulab.c
log.o: $(SRCDIR)/log.c decls.h log.h
$(CC) $(CFLAGS) -DLOG_TESTBED=$(LOG_TESTBED) -c $(SRCDIR)/log.c
......
......@@ -60,6 +60,8 @@ int zero = 0;
int portnum;
struct in_addr mcastaddr;
struct in_addr mcastif;
char *imageid;
int askonly;
static struct timeval stamp;
static struct in_addr serverip;
......@@ -137,18 +139,28 @@ ClientStats_t Stats;
#endif
char *usagestr =
"usage: frisbee [-drzbn] [-s #] <-p #> <-m ipaddr> <output filename>\n"
"usage: frisbee [-drzbnN] [-s #] <-m ipaddr> <-p #> <output filename>\n"
" or\n"
"usage: frisbee [-drzbnN] [-s #] <-S server> <-F fileid> <output filename>\n"
"\n"
" -d Turn on debugging. Multiple -d options increase output.\n"
" -r Randomly delay first request by up to one second.\n"
" -z Zero fill unused block ranges (default is to seek past).\n"
" -b Use broadcast instead of multicast\n"
" -n Do not use extra threads in diskwriter\n"
" -N Do not decompress the received data, just write to output.\n"
" -S server-IP Specify the IP address of the server to use.\n"
" -p portnum Specify a port number.\n"
" -m mcastaddr Specify a multicast address in dotted notation.\n"
" -i mcastif Specify a multicast interface in dotted notation.\n"
" -s slice Output to DOS slice (DOS numbering 1-4)\n"
" NOTE: Must specify a raw disk device for output filename.\n"
" -D DOS-type With -s, sets the DOS type of the slice in the MBR\n"
" -F file-ID Specify the ID of the file (image) to download.\n"
" Here -S specifies the 'master' server which will\n"
" return unicast/multicast info to use for image download.\n"
" -Q file-ID Ask the server (-S) about the indicated file (image).\n"
" Tells whether the image is accessible by this node/user.\n"
"\n"
"tuning options (if you don't know what they are, don't use em!):\n"
" -C MB Max MB of memory to use for network chunk buffering.\n"
......@@ -187,11 +199,11 @@ int
main(int argc, char **argv)
{
int ch, mem;
char *filename;
char *filename = NULL;
int dostype = -1;
int slice = 0;
while ((ch = getopt(argc, argv, "dhp:m:s:i:tbznT:r:E:D:C:W:S:M:R:I:ON")) != -1)
while ((ch = getopt(argc, argv, "dhp:m:s:i:tbznT:r:E:D:C:W:S:M:R:I:ONF:Q:")) != -1)
switch(ch) {
case 'd':
debug++;
......@@ -239,6 +251,15 @@ main(int argc, char **argv)
}
break;
case 'F':
imageid = optarg;
break;
case 'Q':
imageid = optarg;
askonly = 1;
break;
case 't':
tracing++;
break;
......@@ -314,14 +335,26 @@ main(int argc, char **argv)
argc -= optind;
argv += optind;
if (argc != 1)
usage();
filename = argv[0];
if (!askonly) {
if (argc != 1)
usage();
filename = argv[0];
}
if (!portnum || ! mcastaddr.s_addr)
if (!((imageid != NULL && serverip.s_addr != 0) ||
(mcastaddr.s_addr != 0 && portnum != 0)))
usage();
ClientLogInit();
#ifdef MASTER_SERVER
if (imageid && !ClientNetFindServer(&serverip, imageid, askonly, 5)) {
fprintf(stderr, "Could not get download info for '%s'\n",
imageid);
exit(1);
}
if (askonly)
exit(0);
#endif
ClientNetInit();
#ifdef DOEVENTS
......
/*
* Configuration file functions.
*/
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <assert.h>
#include "configdefs.h"
#include "log.h"
static struct config *myconfig;
/*
* Got a SIGHUP.
* Reread the config file.
*/
static void
config_signal(int sig)
{
void *savedconfig = NULL;
if (myconfig == NULL)
return;
savedconfig = myconfig->config_save();
if (myconfig->config_read() != 0) {
warning("WARNING: could not load new configuration, "
"restoring old config.");
if (savedconfig == NULL ||
myconfig->config_restore(savedconfig)) {
error("*** Could not restore old configuration; "
"aborting!");
abort();
}
} else
log("New configuration loaded.");
if (savedconfig)
myconfig->config_free(savedconfig);
}
int
config_init(int readit)
{
int rv;
#ifdef USE_EMULAB_CONFIG
extern struct config emulab_config;
myconfig = &emulab_config;
#else
extern struct config file_config;
myconfig = &file_config;
#endif
rv = myconfig->config_init();
if (rv)
return rv;
if (readit) {
rv = myconfig->config_read();
if (rv) {
myconfig->config_deinit();
return rv;
}
}
signal(SIGHUP, config_signal);
return 0;
}
int
config_read(void)
{
assert(myconfig != NULL);
return myconfig->config_read();
}
int
config_get_host_authinfo(struct in_addr *in, char *imageid,
struct config_host_authinfo **get,
struct config_host_authinfo **put)
{
assert(myconfig != NULL);
return myconfig->config_get_host_authinfo(in, imageid, get, put);
}
void
config_free_host_authinfo(struct config_host_authinfo *ai)
{
assert(myconfig != NULL);
return myconfig->config_free_host_authinfo(ai);
}
/*
* Authenticate access to an image based on the IP address of a host.
* Return zero and (optionally) a pointer to authentication information
* on success, an error code otherwise.
*/
int
config_auth_by_IP(struct in_addr *host, char *imageid,
struct config_host_authinfo **aip)
{
struct config_host_authinfo *ai;
if (config_get_host_authinfo(host, imageid, &ai, 0))
return CONFIG_ERR_HA_FAILED;
if (ai->hostid == NULL) {
config_free_host_authinfo(ai);
return CONFIG_ERR_HA_NOHOST;
}
if (ai->numimages == 0) {
config_free_host_authinfo(ai);
return CONFIG_ERR_HA_NOACCESS;
}
if (aip)
*aip = ai;
return 0;
}
int
config_get_server_address(struct config_host_authinfo *ai, int methods,
in_addr_t *addr, in_port_t *port, int *method)
{
assert(myconfig != NULL);
return myconfig->config_get_server_address(ai, methods,
addr, port, method);
}
char *
config_perror(int code)
{
switch (code) {
case CONFIG_ERR_HA_FAILED:
return "host authentication failed";
case CONFIG_ERR_HA_NOHOST:
return "unknown host";
case CONFIG_ERR_HA_NOIMAGE:
return "unknown image";
case CONFIG_ERR_HA_NOACCESS:
return "permission denied";
case CONFIG_ERR_HA_NOMETHOD:
return "invalid method";
default:
return "unknown error";
}
}
void
config_dump(FILE *fd)
{
signal(SIGHUP, SIG_IGN);
if (myconfig == NULL)
warning("config_dump: config file not yet read.");
else
myconfig->config_dump(fd);
signal(SIGHUP, config_signal);
}
/*
* Configuration "file" handling for Emulab.
*
* Uses info straight from the Emulab database.
*/
#ifdef USE_EMULAB_CONFIG
#include <sys/param.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <assert.h>
#include <mysql/mysql.h>
#include "log.h"
#include "configdefs.h"
/* Emulab includes */
#include "tbdb.h" /* the DB library */
#include "config.h" /* the defs-* defines */
/* Extra info associated with a image information entry */
struct emulab_ii_extra_info {
int DB_imageid;
};
/* Extra info associated with a host authentication entry */
struct emulab_ha_extra_info {
char *pid; /* project */
char *gid; /* group */
char *eid; /* experiment */
char *suid; /* swapper user name */
};
static char *MC_BASEADDR = FRISEBEEMCASTADDR;
static char *MC_BASEPORT = FRISEBEEMCASTPORT;
static char *SHAREDIR = SHAREROOT_DIR;
static char *PROJDIR = PROJROOT_DIR;
static char *GROUPSDIR = GROUPSROOT_DIR;
static char *USERSDIR = USERSROOT_DIR;
static char *SCRATCHDIR = SCRATCHROOT_DIR;
/* XXX should be autoconfiged as part of Emulab build */
static char *IMAGEDIR = "/usr/testbed/images";
/* Emit aliases when dumping the config info; makes it smaller */
static int dump_doaliases = 1;
/* Multicast address/port base info */
static int mc_a, mc_b, mc_c, mc_port;
/* Memory alloc functions that abort when no memory */
static void *mymalloc(size_t size);
static void *myrealloc(void *ptr, size_t size);
static char *mystrdup(const char *str);
static int
emulab_init(void)
{
static int called;
char pathbuf[PATH_MAX], *path;
if (!dbinit())
return -1;
if (called)
return 0;
called++;
/* One time parsing of MC address info */
if (sscanf(MC_BASEADDR, "%d.%d.%d", &mc_a, &mc_b, &mc_c) != 3) {
error("emulab_init: MC_BASEADDR '%s' not in A.B.C format!",
MC_BASEADDR);
return -1;
}
mc_port = atoi(MC_BASEPORT);
if ((path = realpath(SHAREROOT_DIR, pathbuf)) == NULL) {
error("emulab_init: could not resolve '%s'", SHAREROOT_DIR);
return -1;
}
SHAREDIR = mystrdup(path);
if ((path = realpath(PROJROOT_DIR, pathbuf)) == NULL) {
error("emulab_init: could not resolve '%s'", PROJROOT_DIR);
return -1;
}
PROJDIR = mystrdup(path);
if ((path = realpath(GROUPSROOT_DIR, pathbuf)) == NULL) {
error("emulab_init: could not resolve '%s'", GROUPSROOT_DIR);
return -1;
}
GROUPSDIR = mystrdup(path);
if ((path = realpath(USERSROOT_DIR, pathbuf)) == NULL) {
error("emulab_init: could not resolve '%s'", USERSROOT_DIR);
return -1;
}
USERSDIR = mystrdup(path);
if (strlen(SCRATCHROOT_DIR) > 0) {
if ((path = realpath(SCRATCHROOT_DIR, pathbuf)) == NULL) {
error("emulab_init: could not resolve '%s'",
SCRATCHROOT_DIR);
return -1;
}
SCRATCHDIR = mystrdup(path);
} else
SCRATCHDIR = NULL;
return 0;
}
static void
emulab_deinit(void)
{
dbclose();
}
static int
emulab_read(void)
{
/* "Reading" the config file is a no-op. */
return 0;
}
static void *
emulab_save(void)
{
static int dummy;
/* Just return non-zero value */
return (void *)&dummy;
}
static int
emulab_restore(void *state)
{
return 0;
}
static void
emulab_free(void *state)
{
}
/*
* Set the allowed GET methods.
* XXX for now, just multicast.
*/
void
set_get_methods(struct config_host_authinfo *ai, int ix)
{
ai->imageinfo[ix].get_methods = CONFIG_IMAGE_MCAST;
}
/*
* Set the GET options for a particular node/image.
* XXX right now these are implied by various bits of state in the DB:
*
* running in elabinelab: 54Mb/sec, keepalive of 15 sec.
* loading a "standard" image: 72Mb/sec
* any other image: 54Mb/sec
*/
void
set_get_options(struct config_host_authinfo *ai, int ix)
{
char str[256];
strcpy(str, "");
#ifdef ELABINELAB
strcat(str, " -W 54000000 -K 15");
#else
if (!strncmp(ai->imageinfo[ix].path, IMAGEDIR, strlen(IMAGEDIR)))
strcat(str, " -W 72000000");
else
strcat(str, " -W 54000000");
#endif
/*
* We use a small server inactive timeout since we no longer have
* to start up a frisbeed well in advance of the client(s).
*/
strcat(str, " -T 60");
ai->imageinfo[ix].get_options = mystrdup(str);
}
#define FREE(p) { if (p) free(p); }
/*
* Free the dynamically allocated host_authinfo struct.
*/
static void
emulab_free_host_authinfo(struct config_host_authinfo *ai)
{
int i;
if (ai == NULL)
return;
FREE(ai->hostid);
if (ai->imageinfo != NULL) {
for (i = 0; i < ai->numimages; i++) {
FREE(ai->imageinfo[i].imageid);
FREE(ai->imageinfo[i].path);
FREE(ai->imageinfo[i].get_options);
FREE(ai->imageinfo[i].put_options);
FREE(ai->imageinfo[i].extra);
}
free(ai->imageinfo);
}
if (ai->extra) {
struct emulab_ha_extra_info *ei = ai->extra;
FREE(ei->pid);
FREE(ei->gid);
FREE(ei->eid);
FREE(ei->suid);
free(ai->extra);
}
free(ai);
}
/*
* Return the IP address/port to be used by the server/clients for
* the image listed in ai->imageinfo[0]. Methods lists one or more transfer
* methods that the client can handle, we return the method chosen.
*
* For Emulab, we use the frisbee_index from the DB along with the base
* multicast address and port to compute a unique address/port. Uses the
* same logic that frisbeelauncher used to use.
* XXX right now we only do multicast.
*
* Return zero on success, non-zero otherwise.
*/
static int
emulab_get_server_address(struct config_host_authinfo *ai, int methods,
in_addr_t *addrp, in_port_t *portp, int *methp)
{
int idx;
int a, b, c, d;
assert(ai->numimages == 1 && ai->imageinfo != NULL);
if ((methods & CONFIG_IMAGE_MCAST) == 0) {
error("get_server_address: only support MCAST right now!");
return 1;
}
if (!mydb_update("UPDATE emulab_indicies "
" SET idx=LAST_INSERT_ID(idx+1) "
" WHERE name='frisbee_index'")) {
error("get_server_address: DB update error!");
return 1;
}
idx = mydb_insertid();
assert(idx > 0);
a = mc_a;
b = mc_b;
c = mc_c;
d = 1;
d += idx;
if (d > 254) {
c += (d / 254);
d = (d % 254) + 1;
}
if (c > 254) {
b += (c / 254);
c = (c % 254) + 1;
}
if (b > 254) {
error("get_server_address: ran out of MC addresses!");
return 1;
}
*methp = CONFIG_IMAGE_MCAST;
*addrp = (a << 24) | (b << 16) | (c << 8) | d;
*portp = mc_port + (((c << 8) | d) & 0x7FFFF);