Commit 77dbad39 authored by Mike Hibler's avatar Mike Hibler

Support image PUT (aka, "upload") and assorted minor changes.

1. Support for PUT.

The big change is support for uploading via the master server, based heavily
on the prototype that Grant did. Currently only host-based (IP-based)
authentication is done as is the case with download. Grant's SSL-based
authentication code is "integrated" but has not even been compiled in.

The PUT protocol allows for assorted gewgaws, like specifying a maximum size,
setting a timeout value, returning size and signature info, etc.

There is a new, awkwardly-named client utility "frisupload" which, like the
download client, takes an "image ID" as an argument and requests to upload
(PUT) that image via the master server. As with download, the image ID can
be either of the form "<pid>/<emulab-image-name>", to upload/update an actual
Emulab image or it can start with a "/" in which case it is considered to
be a pathname on the server.

On the server side, the master server takes PUT requests, verifies permission
to upload the image, fires up a separate instance of an upload daemon (with
the even catchier moniker "frisuploadd"), and returns the unicast addr/port
info to the client which then begins the upload. The master server also acts
as a traffic cop to make sure that downloads and uploads (or uploads and
uploads) don't overlap.

This has been integrated into the Emulab "create image" process in a
backward-compatible way (i.e., so old admin MFSes will continue to work).
Boy, was that fun. One not-so-desirable effect of this integration is that
images now traverse our network twice, once to upload from node to boss and
once for boss to write out the image file across NFS to ops. This is not
really something that should be "fixed" in frisbee, it is only "undesirable"
because we have a crappy NFS server.

What has NOT been done includes: support of hierarchical PUT operations
(we don't need it for either the elabinelab or subboss case), support for
uploading standard images stored on boss (we really want something better
than host-based authentication here), and the aforementioned support of
SSL-based authentication.

2. Other tidbits that got mixed in with PUT support:

Added two new site variables:
    images/frisbee/maxrate_std
    images/frisbee/maxrate_usr
which replace the hardwired (in mfrisbeed and frisbeelauncher before that)
bandwidth limits for image download. mfrisbeed reads these (and the
images/create/* variables) when it starts up or receives a HUP signal.
These could be read from the DB on every GET/PUT, but they really don't change
much and I needed something to test the reread-the-config-on-a-HUP code!

Fixed avoidance of "problematic multicast addresses" so it would actually
work as intended.

Lots of internal "refactoring" to make up for things I did wrong the first
time and to give the general impression that "Wow, Mike did a LOT!"
parent 3ba294d9
......@@ -192,6 +192,18 @@ while (my ($uid,$uid_idx,$admin,$status) = $users_result->fetchrow_array()) {
mysystem("$TB/sbin/genelists -a");
mysystem("$TB/sbin/exports_setup");
#
# Set some sitevars that are different for inner elabs
#
if (TBSiteVarExists("images/frisbee/maxrate_std")) {
# XXX traditional value for elabinelab
TBSetSiteVar("images/frisbee/maxrate_std", "54000000");
}
if (TBSiteVarExists("images/frisbee/maxrate_usr")) {
# XXX traditional value for elabinelab
TBSetSiteVar("images/frisbee/maxrate_usr", "54000000");
}
#
# Fixup pxeboot and MFS info.
#
......
......@@ -2,7 +2,7 @@
#
# EMULAB-COPYRIGHT
# Copyright (c) 2000-2004, 2007 University of Utah and the Flux Group.
# Copyright (c) 2000-2011 University of Utah and the Flux Group.
# All rights reserved.
#
......@@ -10,14 +10,14 @@ use English;
use Getopt::Std;
#
# Create a disk image. Caller must have sudo permission!
# Client-side to create a disk image. Caller must have sudo permission!
#
sub usage()
{
print STDOUT "Usage: create-image [-s slice] <device file> <filename>\n";
print STDOUT "Usage: create-image [-S image-server] [-F imageid] [-s slice] <device file> <filename>\n";
exit(-1);
}
my $optlist = "rs:";
my $optlist = "F:S:s:";
#
# Turn off line buffering on output
......@@ -35,10 +35,15 @@ delete @ENV{'IFS', 'CDPATH', 'ENV', 'BASH_ENV'};
#
my $sudo = "/usr/local/bin/sudo";
my $zipper = "/usr/local/bin/imagezip";
my $uploader = "/usr/local/bin/frisupload";
my $slice = "";
my $device;
my $filename;
# Frisbee master server params
my $iserver = "boss"; # XXX
my $imageid;
#
# Parse command arguments. Once we return from getopts, all that should be
# left are the required arguments.
......@@ -51,6 +56,23 @@ if (@ARGV != 2) {
usage();
}
if (defined($options{"S"})) {
$iserver = $options{"S"};
if ($iserver =~ /^([-\w\.]+)$/) {
$iserver = $1;
} else {
die("Bad -S hostname: '$iserver'");
}
}
if (defined($options{"F"})) {
$imageid = $options{"F"};
if ($imageid =~ /^(\S+)$/) {
$imageid = $1;
} else {
die("Bad -F imageid: '$imageid'");
}
}
if (defined($options{"s"})) {
my $num = $options{"s"};
......@@ -68,7 +90,11 @@ if (defined($options{"s"})) {
$slice = "-N $slice";
}
$device = $ARGV[0];
$filename = $ARGV[1];
if (defined($imageid)) {
$filename = "-";
} else {
$filename = $ARGV[1];
}
#
# Untaint the arguments.
......@@ -87,13 +113,22 @@ else {
die("Tainted output filename: $filename");
}
#
# If imageid is defined, we use the frisbee uploader.
#
my $cmd = "$zipper $slice $device $filename";
if (defined($imageid)) {
$cmd .= " | $uploader -S $iserver -F $imageid -";
}
#
# Run the command using sudo, since by definition only testbed users
# with proper trust should be able to zip up a disk. sudo will fail
# if the user is not in the proper group.
#
if (system("$sudo $zipper $slice $device $filename")) {
if (system("$sudo $cmd")) {
print STDERR "*** Failed to create image!\n";
print STDERR " command: '$sudo $cmd'\n";
exit 1;
}
......
......@@ -38,7 +38,7 @@ WITH_MSERVER_EMULAB = 1
include $(OBJDIR)/Makeconf
all: frisbee frisbeed mfrisbeed
all: frisbee frisbeed mfrisbeed frisupload frisuploadd
include $(TESTBED_SRCDIR)/GNUmakerules
......@@ -74,6 +74,16 @@ MSERVERFLAGS = -DUSE_NULL_CONFIG $(CFLAGS) -I$(OBJDIR)
MSERVEROBJS = mserver.o $(SHAREDOBJS) config.o config_null.o
MSERVERLIBS =
# Master server based image uploader client
UPLOADFLAGS = $(CFLAGS)
UPLOADLIBS =
UPLOADOBJS = upload.o uploadio.o log.o network.o utils.o
# Master server based image uploader server
UPLOADDFLAGS = $(CFLAGS)
UPLOADDLIBS =
UPLOADDOBJS = frisuploader.o uploadio.o log.o
ifeq ($(WITH_MSERVER_EMULAB),1)
# Emulab master server config
MYSQLCFLAGS = -I/usr/local/include
......@@ -130,11 +140,27 @@ frisbeed-debug: $(SERVEROBJS)
# cp frisbeed frisbeed.debug
# strip frisbeed
frisupload-debug: $(UPLOADOBJS)
$(CC) $(LDFLAGS) $(UPLOADFLAGS) $(UPLOADOBJS) $(UPLOADLIBS) -o $@
# cp frisupload frisupload.debug
# strip frisupload
frisuploadd-debug: $(UPLOADDOBJS)
$(CC) $(LDFLAGS) $(UPLOADDFLAGS) $(UPLOADDOBJS) $(UPLOADDLIBS) -o $@
# cp frisuploadd frisuploadd.debug
# strip frisuploadd
mfrisbeed: $(MSERVEROBJS)
$(CC) $(LDFLAGS) $(MSERVERFLAGS) $(MSERVEROBJS) $(MSERVERLIBS) -o mfrisbeed
cp mfrisbeed mfrisbeed.debug
strip mfrisbeed
frisuploader.o: $(SRCDIR)/frisuploader.c decls.h uploadio.h
$(CC) -c $(UPLOADDFLAGS) $(SRCDIR)/frisuploader.c
upload.o: $(SRCDIR)/upload.c decls.h utils.h uploadio.h
$(CC) -c $(UPLOADFLAGS) $(SRCDIR)/upload.c
uploadio.o: $(SRCDIR)/uploadio.c decls.h utils.h uploadio.h
$(CC) -c $(UPLOADFLAGS) $(SRCDIR)/uploadio.c
mserver.o: $(SRCDIR)/mserver.c decls.h configdefs.h utils.h
$(CC) -c $(MSERVERFLAGS) $(SRCDIR)/mserver.c
config.o: $(SRCDIR)/config.c configdefs.h log.h
......@@ -164,12 +190,13 @@ log.o: decls.h log.h
network.o: decls.h utils.h
trace.o: decls.h trace.h log.h
install: $(INSTALL_SBINDIR)/mfrisbeed $(INSTALL_SBINDIR)/frisbeed $(INSTALL_SBINDIR)/frisbee
install: $(INSTALL_SBINDIR)/mfrisbeed $(INSTALL_SBINDIR)/frisbeed $(INSTALL_SBINDIR)/frisbee $(INSTALL_SBINDIR)/frisuploadd
client: frisbee
client: frisbee frisupload
client-install: client
$(INSTALL_PROGRAM) frisbee $(DESTDIR)$(CLIENT_BINDIR)
$(INSTALL_PROGRAM) frisupload $(DESTDIR)$(CLIENT_BINDIR)
clean:
/bin/rm -f *.o *.a frisbee frisbeed mfrisbeed *.debug
......
......@@ -11,7 +11,9 @@
#include <stdlib.h>
#include <signal.h>
#include <string.h>
#include <errno.h>
#include <assert.h>
#include <sys/stat.h>
#include "decls.h"
#include "configdefs.h"
#include "log.h"
......@@ -30,6 +32,7 @@ config_signal(int sig)
if (myconfig == NULL)
return;
log("Reading new configuration.");
savedconfig = myconfig->config_save();
if (myconfig->config_read() != 0) {
warning("WARNING: could not load new configuration, "
......@@ -154,12 +157,13 @@ config_free_host_authinfo(struct config_host_authinfo *ai)
* on success, an error code otherwise.
*/
int
config_auth_by_IP(struct in_addr *reqip, struct in_addr *hostip, char *imageid,
struct config_host_authinfo **aip)
config_auth_by_IP(int isget, struct in_addr *reqip, struct in_addr *hostip,
char *imageid, struct config_host_authinfo **aip)
{
struct config_host_authinfo *ai;
if (config_get_host_authinfo(reqip, hostip, imageid, &ai, 0))
if (config_get_host_authinfo(reqip, hostip, imageid,
isget ? &ai : 0, isget ? 0 : &ai))
return MS_ERROR_FAILED;
if (ai->hostid == NULL) {
config_free_host_authinfo(ai);
......@@ -199,3 +203,324 @@ config_dump(FILE *fd)
myconfig->config_dump(fd);
signal(SIGHUP, config_signal);
}
/*
* Utility functions for all configurations.
*/
/*
* Return 'dir' if 'path' is in 'dir'; i.e., is 'dir' a prefix of 'path'?
* Return NULL if 'path' is not in 'dir'.
* Note: returns NULL if path == dir.
* Note: assumes realpath has been run on both.
*/
char *
isindir(char *dir, char *path)
{
int len = dir ? strlen(dir) : 0;
char *rv = dir;
if (dir == NULL || path == NULL || *dir == '\0' || *path == '\0' ||
strncmp(dir, path, len) || path[len] != '/')
rv = NULL;
#if 0
fprintf(stderr, "isindir(dir=%s, path=%s) => %s\n",
dir ? dir : "", path ? path : "", rv ? rv : "NULL");
#endif
return rv;
}
/*
* Account for differences between BSD and Linux realpath.
*
* BSD realpath() apparently doesn't even test the final component.
* It will return success even if the final component doesn't exist.
* Settle on the Linux behavior, which does return an error if the
* final component does not exist.
*
* BSD realpath() is documented to, on error, return the canonicalized
* version of the path up to and including the component that caused the
* error. While this behavior is not documented in the Linux version,
* it is the observed behavior, so we assume it (making only a feeble
* check to verify).
*
* Returns a pointer to the 'rpath' buffer if 'path' fully resolves,
* and 'rpath' is filled with the complete canonicalized path.
*
* Returns NULL if there was an error resolving any component of 'path'
* with errno set to the cause of the failure (e.g., ENOENT if a component
* is missing) and 'rpath' is filled with the canonicalized path up to and
* including the component that caused the error.
*/
char *
myrealpath(char *path, char rpath[PATH_MAX])
{
char *rv;
assert(path[0] != '\0');
rpath[0] = '\0';
rv = realpath(path, rpath);
assert(rpath[0] != '\0');
#ifndef linux
if (rv != NULL) {
struct stat sb;
/* also sets errno correctly */
if (stat(path, &sb) < 0)
rv = NULL;
}
#endif
return rv;
}
#define INDIR(d, dl, p) ((p)[dl] == '/' && strncmp(p, d, dl) == 0)
/*
* "Resolve" a pathname.
*
* Makes sure that 'path' falls within the 'dir' after application of
* realpath to resolve "..", symlinks, etc. If 'create' is set, it will
* create missing intermediate directories. In detail:
*
* The path (and dir) must be absolute, or it is an error.
* If 'create' is zero, then all components of the path must exist
* or it is an error.
* If 'create' is non-zero, then missing components except the last
* will be created.
* If 'dir' is non-null, the existing part of the path must resolve
* within this directory or it is an error.
* If 'dir' is null, then the path is resolved as far as it exists
* ('create' is ignored and assumed to be zero).
*
* Returns NULL on an error, a malloc'ed pointer to a canonicalized
* path otherwise.
*/
char *
resolvepath(char *path, char *dir, int create)
{
char rpath[PATH_MAX], *next, *ep, *pathcopy;
struct stat sb;
int pathlen = strlen(path);
int dirlen = 0;
int rpathlen;
mode_t omask, cmask;
char *npath;
if (debug > 1)
info("resolvepath '%s' in dir '%s'", path, dir);
/* validate path */
if (path == NULL) {
if (debug > 1)
info(" null path");
return NULL;
}
if (path[0] != '/') {
if (debug > 1)
info(" path is not absolute");
return NULL;
}
if (pathlen >= sizeof rpath) {
if (debug > 1)
info(" path is too long");
return NULL;
}
/* validate dir */
if (dir) {
dirlen = strlen(dir);
if (dir[0] != '/') {
if (debug > 1)
info(" path and dir (%s) must be absolute",
dir);
return NULL;
}
/* XXX make dir=='/' work */
if (dirlen == 1)
dirlen = 0;
} else {
create = 0;
}
/*
* If the full path resolves, make sure it falls in dir (if given)
* and is a regular file.
*/
if (myrealpath(path, rpath) != NULL) {
if (dir && !INDIR(dir, dirlen, rpath)) {
if (debug > 1)
info(" resolved path (%s) not in dir (%s)",
rpath, dir);
return NULL;
}
if (stat(rpath, &sb) < 0 || !S_ISREG(sb.st_mode)) {
if (debug > 1)
info(" not a regular file");
return NULL;
}
return strdup(rpath);
}
if (debug > 1) {
int oerrno = errno;
info(" partially resolved to '%s' (errno %d)", rpath, errno);
errno = oerrno;
}
/*
* If create is not set or we failed for any reason other than
* a non-existent component, we return an error.
*/
if (!create || errno != ENOENT) {
if (debug > 1)
info(" realpath failed at %s with %d",
rpath, errno);
return NULL;
}
/*
* Need to create intermediate directories.
*
* First, check that what does exist of the path falls within
* the directory given.
*/
assert(dir != NULL);
if (!INDIR(dir, dirlen, rpath)) {
if (debug > 1)
info(" resolved path (%s) not in dir (%s)",
rpath, dir);
return NULL;
}
/*
* Establish permission (mode) for new directories.
* Start with permission of 'dir'; this will get updated with
* every component that resolves, so that new directories will
* get created with the permission of the most recent ancestor.
*/
if (stat(dir, &sb) < 0) {
if (debug > 1)
info(" stat failed on dir (%s)?!", dir);
return NULL;
}
cmask = (sb.st_mode & (S_IRWXU|S_IRWXG|S_IRWXO)) | S_IRWXU;
omask = umask(0);
if (debug > 1)
info(" umask=0 (was 0%o), initial cmask=0%o", omask, cmask);
/*
* Find the first component of the original path that does not
* exist (i.e., what part of the original path maps to what realpath
* returned) and create everything from there on.
*
* Note that if what realpath returned is a prefix of the original
* path then nothing was translated and we are already at the first
* component that needs creation. So we find the appropriate location
* in the original string and go from there.
*/
npath = NULL;
if ((pathcopy = strdup(path)) == NULL)
goto done;
rpathlen = strlen(rpath);
assert(rpathlen <= pathlen);
if (rpathlen > 1 && strncmp(pathcopy, rpath, rpathlen) == 0 &&
(pathcopy[rpathlen] == '\0' || pathcopy[rpathlen] == '/')) {
/* same string, start at last slash in path */
if (pathcopy[rpathlen] == '\0') {
next = rindex(pathcopy, '/');
assert(next != NULL);
}
/* rpath is a prefix, start at last slash in that prefix */
else {
next = rindex(rpath, '/');
assert(next != NULL);
next = &pathcopy[next-rpath];
}
}
/* rpath is not a prefix, must scan entire path */
else {
next = pathcopy;
}
assert(next >= pathcopy && next < &pathcopy[pathlen]);
assert(*next == '/');
next++;
if (debug > 1)
info(" pathscan: path='%s', rpath='%s', start at '%s'",
pathcopy, rpath, next);
while ((ep = index(next, '/')) != NULL) {
*ep = '\0';
if (debug > 1)
info(" testing: %s", pathcopy);
/*
* We use realpath here instead of just stat to make
* sure someone isn't actively tweaking the filesystem
* and messing with our head.
*
* If realpath fails, it should fail with ENOENT and
* the realpath-ified part should fall in our directory.
* Otherwise, someone really is playing games.
*/
if (myrealpath(pathcopy, rpath) == NULL) {
if (errno != ENOENT ||
(dir && !INDIR(dir, dirlen, rpath))) {
if (debug > 1)
info(" resolves bad (%s)\n",
rpath);
goto done;
}
/*
* We have hit a missing component.
* Create the component and carry on.
*/
if (mkdir(rpath, cmask) < 0) {
if (debug > 1)
info(" create failed (%s)\n",
rpath);
goto done;
}
if (debug > 1)
info(" created (%s)", rpath);
} else {
if (debug > 1)
info(" exists (%s)", rpath);
/*
* Update the creation permission; see comment above.
*/
if (stat(rpath, &sb) < 0) {
if (debug > 1)
info(" stat failed (%s)?!", rpath);
goto done;
}
cmask = (sb.st_mode & (S_IRWXU|S_IRWXG|S_IRWXO)) |
S_IRWXU;
}
*ep = '/';
next = ep+1;
}
/*
* We are down to the final component of the original path.
* It should either exist and be a regular file or not
* exist at all.
*/
if (myrealpath(pathcopy, rpath) == NULL &&
(errno != ENOENT || !INDIR(dir, dirlen, rpath))) {
if (debug > 1)
info(" final resolved path (%s) bad (%d)",
rpath, errno);
goto done;
}
/*
* We are in our happy place. rpath is the canonicalized path.
*/
npath = strdup(rpath);
if (debug > 1)
info("resolvepath: '%s' resolved to '%s'", path, npath);
done:
umask(omask);
if (pathcopy)
free(pathcopy);
return npath;
}
This diff is collapsed.
This diff is collapsed.
......@@ -7,14 +7,19 @@
*/
struct config_imageinfo {
char *imageid; /* unique name of image */
char *dir; /* directory to which path must resolve */
char *path; /* path where image is stored */
void *sig; /* signature of image */
int flags; /* */
char *get_options; /* options for GET operation */
int uid; /* UID to run server process as */
int gid; /* GID to run server process as */
char *get_options; /* command line options for GET server */
int get_methods; /* allowed GET transfer mechanisms */
int get_uid; /* UID to run frisbee server as */
int get_gid; /* GID to run frisbee server as */
char *put_options; /* options for PUT operation */
int get_timeout; /* max time to allow GET server to run */
char *put_options; /* command line options for PUT server */
uint64_t put_maxsize; /* maximum size for this image */
int put_timeout; /* max time to allow PUT server to run */
char *put_oldversion; /* where to save the old version */
void *extra; /* config-type specific info */
};
......@@ -23,9 +28,11 @@ struct config_imageinfo {
#define CONFIG_PATH_ISDIR 0x2 /* path is a directory */
#define CONFIG_PATH_ISGLOB 0x4 /* path is a file glob */
#define CONFIG_PATH_ISRE 0x8 /* path is a perl RE */
#define CONFIG_SIG_ISMTIME 0x10 /* sig is path mtime */
#define CONFIG_SIG_ISMD5 0x20 /* sig is MD5 hash of path */
#define CONFIG_SIG_ISSHA1 0x40 /* sig is SHA1 hash of path */
#define CONFIG_PATH_RESOLVE 0x10 /* path needs resolution at use */
#define CONFIG_PATH_EXISTS 0x20 /* imaged named by path arg exists */
#define CONFIG_SIG_ISMTIME 0x1000 /* sig is path mtime */
#define CONFIG_SIG_ISMD5 0x2000 /* sig is MD5 hash of path */
#define CONFIG_SIG_ISSHA1 0x4000 /* sig is SHA1 hash of path */
/* methods */
#define CONFIG_IMAGE_UNKNOWN 0x0
......@@ -69,10 +76,16 @@ extern int config_get_host_authinfo(struct in_addr *,
struct config_host_authinfo **);
extern void config_dump_host_authinfo(struct config_host_authinfo *);
extern void config_free_host_authinfo(struct config_host_authinfo *);
extern int config_auth_by_IP(struct in_addr *, struct in_addr *, char *,
struct config_host_authinfo **);
extern int config_auth_by_IP(int, struct in_addr *, struct in_addr *,
char *, struct config_host_authinfo **);
extern int config_get_server_address(struct config_imageinfo *, int, int,
in_addr_t *, in_port_t *, int *);
extern void * config_save(void);
extern int config_restore(void *);
extern void config_dump(FILE *);
/* Common utility functions */
extern char * isindir(char *dir, char *path);
extern char * myrealpath(char *path, char rpath[PATH_MAX]);
extern char * resolvepath(char *path, char *dir, int create);
......@@ -350,6 +350,30 @@ typedef struct {
uint32_t losize;
} __attribute__((__packed__)) GetReply;
typedef struct {
uint32_t hostip;
uint8_t status;
uint16_t idlen;
uint8_t imageid[MS_MAXIDLEN];
uint32_t hisize;
uint32_t losize;
uint32_t mtime;
uint32_t timeout;
} __attribute__((__packed__)) PutRequest;
typedef struct {
uint16_t error;
uint32_t addr;
uint16_t port;
uint8_t exists;
uint16_t sigtype;
uint8_t signature[MS_MAXSIGLEN];
uint32_t hisize;
uint32_t losize;
uint32_t himaxsize;
uint32_t lomaxsize;
} __attribute__((__packed__)) PutReply;
typedef struct {
struct {
int8_t version[4];
......@@ -358,6 +382,8 @@ typedef struct {
union {
GetRequest getrequest;
GetReply getreply;
PutRequest putrequest;
PutReply putreply;
} body;
} MasterMsg_t;
......@@ -386,6 +412,9 @@ typedef struct {
#define MS_ERROR_NOMETHOD 5 /* not avail to host via method */
#define MS_ERROR_INVALID 6 /* invalid argument */
#define MS_ERROR_TRYAGAIN 7 /* try again later */
#define MS_ERROR_TOOBIG 8 /* attempted PUT is too large */
#define MS_ERROR_BADMTIME 9 /* attempt to set bad mtime */
#define MS_ERROR_NOTIMPL 10 /* operation not implemented */
#endif
/*
......
#!/usr/bin/perl -wT
#
# EMULAB-COPYRIGHT
# Copyright (c) 2000-2011 University of Utah and the Flux Group.
# All rights reserved.
#
#
# XXX this is an Emulab-only script to optimize the upload process.
# Since the master server runs on boss, but most images live on ops,
# we wind up writing images across the network twice: once uploading to
# boss and then again writing the image across NFS to ops.
#
# Note that this same problem exists for frisbeed and multicasting images,
# so this should really be generalized to handle that as well.
#
use English;
use Getopt::Std;
/*
* EMULAB-COPYRIGHT
* Copyright (c) 2010-2011 University of Utah and the Flux Group.
* All rights reserved.
*/
/*
* Server-side for uploading of frisbee images.
* Invoked by the frisbee master server.
*/
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <signal.h>
#include <fcntl.h>
#include <sys/time.h>
#include <setjmp.h>
#include "decls.h"
#include "uploadio.h"
static struct in_addr clientip;
static struct in_addr clientif;
static char *path;
static uint64_t maxsize = 0;
static int bufsize = (64 * 1024);;
static int timeout = 0;
static int sock = -1;
/* Globals */
int debug = 0;
int portnum;
static void usage(void);
static void parse_args(int argc, char **argv);
static void net_init(void);
static int recv_file(void);
int
main(int argc, char **argv)
{
int rv;
UploadLogInit();
parse_args(argc, argv);
net_init();
log("%s: listening on port %d for image data from %s (max of %llu bytes)",
path, portnum, inet_ntoa(clientip), maxsize);
rv = recv_file();
close(sock);
exit(rv);
}