Commit 8d80301e authored by Mike Hibler's avatar Mike Hibler
Browse files

More work toward getting this working on subboss.

More work on the hierarchical configuration for subboss. When doing host-based
authentication, allow client to pass an explicit host (IP) to the mserver.
If the mserver is configured to allow it, that IP is used for authenticating
the request instead of the caller's IP. Add a default ("null") configuration
so the mserver can operate out-of-the-box with no config file. The goal of
these two changes is for an mserver instance with the default config and a
proxy option to serve the needs of a subboss node (i.e., so no explicit
configuration will be needed).
parent c5b7cceb
......@@ -190,7 +190,7 @@ if ($SCRATCHDIR) {
my @LOGFILES = ("$LOGDIR/bootinfo.log", "$LOGDIR/tmcd.log",
"$LOGDIR/capture.log", "$LOGDIR/dhcpd.log", "$LOGDIR/capserver.log",
"$LOGDIR/frisbeed.log", "$LOGDIR/proxydhcpd.log",
"$LOGDIR/frisbeed.log", "$LOGDIR/mfrisbeed.log", "$LOGDIR/proxydhcpd.log",
"$LOGDIR/stated.log", "$LOGDIR/osselect.log",
"$LOGDIR/tftpd.log", "$LOGDIR/sdcollectd.log", "$LOGDIR/genlastlog.log",
"$LOGDIR/sshxmlrpc.log", "$LOGDIR/plabgetfree.log", "$LOGDIR/xmlrpcbag.log",
......@@ -828,6 +828,7 @@ Phase "syslog", "Setting up syslog", sub {
"!tftpd", "*.*\t\t\t\t\t\t$LOGDIR/tftpd.log",
"!capserver", "*.*\t\t\t\t\t\t$LOGDIR/capserver.log",
"!frisbeed", "*.*\t\t\t\t\t\t$LOGDIR/frisbeed.log",
"!mfrisbeed", "*.*\t\t\t\t\t\t$LOGDIR/mfrisbeed.log",
"!pubsubd", "*.*\t\t\t\t\t\t$LOGDIR/pubsubd.log",
"!osselect", "*.*\t\t\t\t\t\t$LOGDIR/osselect.log",
"!genlastlog","*.*\t\t\t\t\t\t$LOGDIR/genlastlog.log",
......@@ -876,6 +877,7 @@ Phase "syslog", "Setting up syslog", sub {
"$LOGDIR/osselect.log 640 9 300 * Z",
"$LOGDIR/power.log 640 7 300 * Z",
"$LOGDIR/frisbeed.log 640 7 300 * Z",
"$LOGDIR/mfrisbeed.log 640 7 300 * Z",
"$LOGDIR/tftpd.log 640 7 200 * Z",
"$LOGDIR/dhcpd.log 640 7 200 * Z",
"$LOGDIR/bootinfo.log 640 7 200 * Z",
......
......@@ -33,6 +33,9 @@ endif
endif
endif
WITH_MSERVER_NULL = 1
WITH_MSERVER_EMULAB = 0
include $(OBJDIR)/Makeconf
all: frisbee frisbeed mfrisbeed
......@@ -63,11 +66,22 @@ SERVERFLAGS = $(CFLAGS)
SERVERLIBS = $(PTHREADLIBS)
SERVEROBJS = server.o $(SHAREDOBJS)
#
# Master server configuration
#
# Default master server config
MSERVERFLAGS = -DUSE_NULL_CONFIG $(CFLAGS) -I$(OBJDIR)
MSERVEROBJS = mserver.o $(SHAREDOBJS) config.o config_null.o
MSERVERLIBS =
ifeq ($(WITH_MSERVER_EMULAB),1)
# Emulab master server config
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)
MSERVEROBJS = mserver.o config.o config_emulab.o $(SHAREDOBJS)
MSERVERFLAGS += -DUSE_EMULAB_CONFIG $(MYSQLCFLAGS) -I$(TESTBED_SRCDIR)/lib/libtb
MSERVEROBJS += config_emulab.o
MSERVERLIBS += $(OBJDIR)/lib/libtb/libtb.a $(MYSQLLIBS)
endif
CFLAGS = -O2 -g -Wall -fno-builtin-log $(LDSTATIC) $(PTHREADCFLAGS) -DSTATS -DMASTER_SERVER
LDFLAGS = $(LDSTATIC)
......@@ -125,6 +139,8 @@ 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
config_null.o: $(SRCDIR)/config_null.c configdefs.h log.h
$(CC) -c $(MSERVERFLAGS) $(SRCDIR)/config_null.c
log.o: $(SRCDIR)/log.c decls.h log.h
$(CC) $(CFLAGS) -DLOG_TESTBED=$(LOG_TESTBED) -c $(SRCDIR)/log.c
......
......@@ -45,15 +45,17 @@ 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;
extern struct config *emulab_init();
if (myconfig == NULL)
myconfig = emulab_init();
#endif
#ifdef USE_NULL_CONFIG
extern struct config *null_init();
if (myconfig == NULL)
myconfig = null_init();
#endif
rv = myconfig->config_init();
if (rv)
return rv;
if (myconfig == NULL)
return -1;
if (readit) {
rv = myconfig->config_read();
......
......@@ -56,65 +56,6 @@ 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;
log("Read Emulab configuration");
return 0;
}
static void
emulab_deinit(void)
{
......@@ -152,7 +93,7 @@ emulab_free(void *state)
* Set the allowed GET methods.
* XXX for now, just multicast.
*/
void
static void
set_get_methods(struct config_host_authinfo *ai, int ix)
{
ai->imageinfo[ix].get_methods = CONFIG_IMAGE_MCAST;
......@@ -169,7 +110,7 @@ set_get_methods(struct config_host_authinfo *ai, int ix)
* loading a "standard" image: 72Mb/sec
* any other image: 54Mb/sec
*/
void
static void
set_get_options(struct config_host_authinfo *ai, int ix)
{
char str[256];
......@@ -1017,7 +958,7 @@ dump_image_aliases(FILE *fd)
" GROUP by e.pid,e.gid", 2);
assert(res != NULL);
lastpid = strdup("NONE");
lastpid = mystrdup("NONE");
nrows = mysql_num_rows(res);
for (i = 0; i < nrows; i++) {
MYSQL_RES *res2;
......@@ -1033,7 +974,7 @@ dump_image_aliases(FILE *fd)
/* New pid, put out shared project image alias */
if (strcmp(lastpid, row[0])) {
free(lastpid);
lastpid = strdup(row[0]);
lastpid = mystrdup(row[0]);
res2 = mydb_query("SELECT imagename"
" FROM images"
......@@ -1134,7 +1075,6 @@ emulab_dump(FILE *fd)
}
struct config emulab_config = {
emulab_init,
emulab_deinit,
emulab_read,
emulab_get_host_authinfo,
......@@ -1146,6 +1086,65 @@ struct config emulab_config = {
emulab_dump
};
struct config *
emulab_init(void)
{
static int called;
char pathbuf[PATH_MAX], *path;
if (!dbinit())
return NULL;
if (called)
return &emulab_config;
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 NULL;
}
mc_port = atoi(MC_BASEPORT);
if ((path = realpath(SHAREROOT_DIR, pathbuf)) == NULL) {
error("emulab_init: could not resolve '%s'", SHAREROOT_DIR);
return NULL;
}
SHAREDIR = mystrdup(path);
if ((path = realpath(PROJROOT_DIR, pathbuf)) == NULL) {
error("emulab_init: could not resolve '%s'", PROJROOT_DIR);
return NULL;
}
PROJDIR = mystrdup(path);
if ((path = realpath(GROUPSROOT_DIR, pathbuf)) == NULL) {
error("emulab_init: could not resolve '%s'", GROUPSROOT_DIR);
return NULL;
}
GROUPSDIR = mystrdup(path);
if ((path = realpath(USERSROOT_DIR, pathbuf)) == NULL) {
error("emulab_init: could not resolve '%s'", USERSROOT_DIR);
return NULL;
}
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 NULL;
}
SCRATCHDIR = mystrdup(path);
} else
SCRATCHDIR = NULL;
log("Read Emulab configuration");
return &emulab_config;
}
/*
* XXX memory allocation functions that either return memory or abort.
* We shouldn't run out of memory and don't want to check every return values.
......@@ -1219,4 +1218,11 @@ warning(const char *fmt, ...)
}
#endif
#else
struct config *
emulab_init(void)
{
return NULL;
}
#endif
/*
* Configuration "file" handling for Emulab.
*
* Uses info straight from the Emulab database.
*/
#ifdef USE_NULL_CONFIG
#include <sys/param.h>
#include <sys/stat.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <assert.h>
#include "log.h"
#include "configdefs.h"
static char *DEFAULT_IMAGEDIR = "/usr/local/images";
static char *DEFAULT_MCADDR = "239.192.1";
static char *DEFAULT_MCPORT = "1025";
static char *indexfile;
char *imagedir = NULL;
static char *rimagedir;
/* 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 void
null_deinit(void)
{
}
static int
null_read(void)
{
/* "Reading" the config file is a no-op. */
return 0;
}
static void *
null_save(void)
{
static int dummy;
/* Just return non-zero value */
return (void *)&dummy;
}
static int
null_restore(void *state)
{
return 0;
}
static void
null_free(void *state)
{
}
/*
* Set the allowed GET methods.
* XXX for now, just unicast and multicast.
*/
static void
set_get_methods(struct config_host_authinfo *ai, int ix)
{
ai->imageinfo[ix].get_methods = CONFIG_IMAGE_MCAST;
#if 1
ai->imageinfo[ix].get_methods |= CONFIG_IMAGE_UCAST;
#endif
}
/*
* Set the GET options for a particular node/image.
*/
static void
set_get_options(struct config_host_authinfo *ai, int ix)
{
char str[256];
strcpy(str, "");
strcat(str, " -W 54000000 -K 15");
/*
* 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
null_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].sig);
FREE(ai->imageinfo[i].get_options);
FREE(ai->imageinfo[i].put_options);
FREE(ai->imageinfo[i].extra);
}
free(ai->imageinfo);
}
assert(ai->extra == NULL);
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.
* If first is non-zero, then we need to return a "new" address and *addrp
* and *portp are uninitialized. If non-zero, then our last choice failed
* (probably due to a port conflict) and we need to choose a new address
* to try, possibly based on the existing info in *addrp and *portp.
*
* 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. For retries (first==0),
* we choose a whole new addr/port for multicast.
*
* For unicast, we use the index as well, just to produce a unique port
* number.
*
* Return zero on success, non-zero otherwise.
*/
static int
null_get_server_address(struct config_imageinfo *ii, int methods, int first,
in_addr_t *addrp, in_port_t *portp, int *methp)
{
int a, b, c, d, idx;
FILE *fd;
if ((methods & (CONFIG_IMAGE_MCAST|CONFIG_IMAGE_UCAST)) == 0) {
error("get_server_address: only support UCAST/MCAST");
return 1;
}
if ((fd = fopen(indexfile, "r+")) == NULL) {
error("get_server_address: could not open index file '%s'!",
indexfile);
return 1;
}
if (fscanf(fd, "%d", &idx) != 1 || idx < 0) {
error("get_server_address: bogus index in '%s'!",
indexfile);
fclose(fd);
return 1;
}
if (fseek(fd, 0L, SEEK_SET) != 0 || fprintf(fd, "%d\n", idx+1) < 0) {
error("get_server_address: cannot update index in '%s'!",
indexfile);
fclose(fd);
return 1;
}
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;
}
if (methods & CONFIG_IMAGE_MCAST) {
*methp = CONFIG_IMAGE_MCAST;
*addrp = (a << 24) | (b << 16) | (c << 8) | d;
} else if (methods & CONFIG_IMAGE_UCAST) {
*methp = CONFIG_IMAGE_UCAST;
*addrp = 0;
}
*portp = mc_port + (((c << 8) | d) & 0x7FFF);
return 0;
}
/*
* Return one if 'path' is in 'dir'; i.e., is 'dir' a prefix of 'path'?
* Note: returns zero if path == dir.
* Note: assumes realpath has been run on both.
*/
static int
isindir(char *dir, char *path)
{
int len = dir ? strlen(dir) : 0;
int rv = 1;
if (dir == NULL || path == NULL || *dir == '\0' || *path == '\0' ||
strncmp(dir, path, len) || path[len] != '/')
rv = 0;
if (0)
fprintf(stderr, "isindir(dir=%s, path=%s) => %d\n",
dir ? dir : "", path ? path : "", rv);
return rv;
}
/*
* Just return imagedir for GET and PUT.
*/
static void
allow_stddirs(char *imageid,
struct config_host_authinfo *get,
struct config_host_authinfo *put)
{
struct config_imageinfo *ci;
struct stat sb;
if (get == NULL && put == NULL)
return;
/*
* No image specified, just return info about the directories
* that are accessible.
*/
if (imageid == NULL) {
int ni, i;
size_t ns;
char *dirs[8];
/*
* Right now, allow PUT to imagedir.
*/
if (put != NULL) {
dirs[0] = imagedir;
ni = put->numimages + 1;
ns = ni * sizeof(struct config_imageinfo);
if (put->imageinfo)
put->imageinfo = myrealloc(put->imageinfo, ns);
else
put->imageinfo = mymalloc(ns);
for (i = put->numimages; i < ni; i++) {
ci = &put->imageinfo[i];
ci->imageid = NULL;
ci->path = mystrdup(dirs[i - put->numimages]);
ci->flags = CONFIG_PATH_ISDIR;
if (stat(ci->path, &sb) == 0) {
ci->sig = mymalloc(sizeof(time_t));
*(time_t *)ci->sig = sb.st_mtime;
ci->flags |= CONFIG_SIG_ISMTIME;
} else
ci->sig = NULL;
ci->get_options = NULL;
ci->get_methods = 0;
ci->put_options = NULL;
ci->extra = NULL;
}
put->numimages = ni;
}
/*
* and GETs as well.
*/
if (get != NULL) {
dirs[0] = imagedir;
ni = get->numimages + 1;
ns = ni * sizeof(struct config_imageinfo);
if (get->imageinfo)
get->imageinfo = myrealloc(get->imageinfo, ns);
else
get->imageinfo = mymalloc(ns);
for (i = get->numimages; i < ni; i++) {
ci = &get->imageinfo[i];
ci->imageid = NULL;
ci->path = mystrdup(dirs[i - get->numimages]);
ci->flags = CONFIG_PATH_ISDIR;
if (stat(ci->path, &sb) == 0) {
ci->sig = mymalloc(sizeof(time_t));
*(time_t *)ci->sig = sb.st_mtime;
ci->flags |= CONFIG_SIG_ISMTIME;
} else
ci->sig = NULL;
set_get_options(get, i);
set_get_methods(get, i);
ci->put_options = NULL;
ci->extra = NULL;
}
get->numimages = ni;
}
goto done;
}
/*
* Image was specified; find the real path for the targetted file.
* Don't want users symlinking to files outside their allowed space.
*/
assert(imageid == NULL);
done:
return;
}
/*
* Find all images (imageid==NULL) or a specific image (imageid!=NULL)
* that a particular node can access for GET/PUT. Pretty simple default:
* any node can read/write any image within the default image directory.
*
* For a single image this will either return a single image or no image.
* The desired image must be one of the images that would be returned in
* the all images case.
*
* Imageids should be a path to which we prepend the imagedir if it is
* not already there.
*