mserver.c 59.4 KB
Newer Older
1
/*
2
 * Copyright (c) 2010-2014 University of Utah and the Flux Group.
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
 * 
 * {{{EMULAB-LICENSE
 * 
 * This file is part of the Emulab network testbed software.
 * 
 * This file is free software: you can redistribute it and/or modify it
 * under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or (at
 * your option) any later version.
 * 
 * This file is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Affero General Public
 * License for more details.
 * 
 * You should have received a copy of the GNU Affero General Public License
 * along with this file.  If not, see <http://www.gnu.org/licenses/>.
 * 
 * }}}
22 23 24 25 26
 */

/*
 * Frisbee master server.  Listen for GET/PUT requests and fork off
 * handler processes.
27 28 29
 * 
 * TODO:
 * - timeouts for child frisbee processes
30 31 32
 * - record the state of running frisbeeds in persistant store so that
 *   they can be restarted if we die and restart
 * - related: make sure we don't leave orphans when we die!
33
 * - handle signals: INT/TERM should kill all frisbeeds and remove our pid
34 35
 */
#include <paths.h>
36 37
#include <sys/stat.h>
#include <sys/select.h>
38 39 40 41 42 43 44 45 46 47 48 49
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <syslog.h>
#include <sys/wait.h>
#include <errno.h>
#include <assert.h>
50 51 52
#ifdef WITH_IGMP
#include <sys/time.h>
#endif
53
#include "decls.h"
54
#include "utils.h"
55 56
#include "configdefs.h"

57 58
#define FRISBEE_SERVER	"/usr/testbed/sbin/frisbeed"
#define FRISBEE_CLIENT	"/usr/testbed/sbin/frisbee"
59
#define FRISBEE_UPLOAD	"/usr/testbed/sbin/frisuploadd"
60
#define FRISBEE_RETRIES	5
61

62
static void	get_options(int argc, char **argv);
63
static int	makesocket(int portnum, struct in_addr *ifip, int *tcpsockp);
64
static void	handle_request(int sock);
65
static int	reapchildren(int apid, int *status);
66 67 68
#ifdef WITH_IGMP
static void	handle_igmp(void);
#endif
69
static char *	gidstr(int ngids, gid_t gids[]);
70 71 72 73

static int	daemonize = 1;
int		debug = 0;
static int	dumpconfig = 0;
74
static int	onlymethods = (MS_METHOD_UNICAST|MS_METHOD_MULTICAST);
75
static int	parentmethods = (MS_METHOD_UNICAST|MS_METHOD_MULTICAST);
76 77
static int	myuid = NOUID;
static int	mygid = NOUID;
78 79 80 81 82 83 84 85

/*
 * For recursively GETing images:
 *   parentip/parentport  address of a parent master server (-S,-P)
 *   fetchfromabove       true if a parent has been specified
 *   canredirect          if true, redirect our clients to our parent (-R)
 *   usechildauth         if true, pass client's authinfo to our parent (-A)
 */
86 87
static struct in_addr parentip;
static int	parentport = MS_PORTNUM;
88 89 90
static int	fetchfromabove = 0;
static int	canredirect = 0;
static int	usechildauth = 0;
91
static int	mirrormode = 0;
92
static char     *configstyle = "null";
93
static char	*configopts = "";
94
static struct in_addr ifaceip;
95
static int	igmpqueryinterval = 0;
96 97

/* XXX the following just keep network.c happy */
98
int		portnum = MS_PORTNUM;
99
int		sockbufsize = SOCKBUFSIZE;
100 101 102 103 104 105 106 107 108 109
struct in_addr	mcastaddr;
struct in_addr	mcastif;

int
main(int argc, char **argv)
{
	int			tcpsock;
	FILE			*fp;
	char			buf[BUFSIZ];
	char			*pidfile = (char *) NULL;
110 111
	struct timeval		tv;
	fd_set			ready;
112 113 114
#ifdef USE_LOCALHOST_PROXY
	int			localsock = -1;
#endif
115 116 117

	get_options(argc, argv);

118 119 120
	myuid = geteuid();
	mygid = getegid();

121 122 123 124 125 126 127
	if (daemonize && debug) {
		int odebug = debug;
		debug = 0;
		MasterServerLogInit();
		debug = odebug;
	} else
		MasterServerLogInit();
128

129 130
	FrisLog("mfrisbeed daemon starting as %d/%d, methods=%s (debug level %d)",
		myuid, mygid, GetMSMethods(onlymethods), debug);
131
	if (fetchfromabove)
132 133 134 135
		FrisLog("  using parent %s:%d%s, methods=%s",
			inet_ntoa(parentip), parentport,
			mirrormode ? " in mirror mode" : "",
			GetMSMethods(parentmethods));
136 137
#ifdef WITH_IGMP
	if (igmpqueryinterval)
138 139
		FrisLog("  acting as IGMP querier on %s with %d second interval",
			inet_ntoa(ifaceip), igmpqueryinterval);
140
#endif
141
	config_init(configstyle, 1, configopts);
142 143 144 145 146 147 148 149 150 151

	/* Just dump the config to stdout in human readable form and exit. */
	if (dumpconfig) {
		config_dump(stdout);
		exit(0);
	}

	/*
	 * Create TCP server.
	 */
152
	if (makesocket(portnum, &ifaceip, &tcpsock) < 0) {
153
		FrisError("Could not make primary tcp socket!");
154 155
		exit(1);
	}
156 157 158 159 160 161 162 163 164
#ifdef USE_LOCALHOST_PROXY
	/*
	 * Listen on localhost too for proxy requests.
	 */
	if (ifaceip.s_addr != htonl(INADDR_ANY) &&
	    ifaceip.s_addr != htonl(INADDR_LOOPBACK)) {
		struct in_addr localip;
		localip.s_addr = htonl(INADDR_LOOPBACK);
		if (makesocket(portnum, &localip, &localsock) < 0) {
165
			FrisError("Could not create localhost tcp socket!");
166 167 168 169
			exit(1);
		}
	}
#endif
170 171 172 173 174
#ifdef WITH_IGMP
	if (igmpqueryinterval)
		IGMPInit(&ifaceip, NULL);
#endif

175 176 177 178 179 180 181 182 183
	/* Now become a daemon */
	if (daemonize)
		daemon(0, 0);

	/*
	 * Stash the pid away.
	 */
	if (!geteuid()) {
		if (!pidfile) {
184
			sprintf(buf, "%s/mfrisbeed.pid", _PATH_VARRUN);
185 186 187 188 189 190 191 192 193 194 195 196
			pidfile = buf;
		}
		fp = fopen(pidfile, "w");
		if (fp != NULL) {
			fprintf(fp, "%d\n", getpid());
			(void) fclose(fp);
		}
	}

	/*
	 * Handle connections
	 */
197
	FD_ZERO(&ready);
198 199 200
	while (1) {
		struct sockaddr_in client;
		socklen_t length;
201
		int newsock, rv, maxsock = 0;
202

203 204 205 206 207 208
#ifdef WITH_IGMP
		/* Do the querier thing */
		if (igmpqueryinterval)
			handle_igmp();
#endif

209
		FD_SET(tcpsock, &ready);
210 211 212 213 214 215 216 217
		maxsock = tcpsock + 1;
#ifdef USE_LOCALHOST_PROXY
		if (localsock >= 0) {
			FD_SET(localsock, &ready);
			if (localsock > tcpsock)
				maxsock = localsock + 1;
		}
#endif
218 219
		tv.tv_sec = 0;
		tv.tv_usec = 100000;
220
		rv = select(maxsock, &ready, NULL, NULL, &tv);
221 222 223
		if (rv < 0) {
			if (errno == EINTR)
				continue;
224
			FrisPfatal("select failed");
225 226
		}
		if (rv) {
227 228 229 230 231
			int sock = tcpsock;
#ifdef USE_LOCALHOST_PROXY
			if (localsock >= 0 && FD_ISSET(localsock, &ready))
				sock = localsock;
#endif
232
			length  = sizeof(client);
233
			newsock = accept(sock, (struct sockaddr *)&client,
234 235
					 &length);
			if (newsock < 0)
236
				FrisPwarning("accepting TCP connection");
237 238 239 240 241 242
			else {
				fcntl(newsock, F_SETFD, FD_CLOEXEC);
				handle_request(newsock);
				close(newsock);
			}
		}
243
		(void) reapchildren(0, NULL);
244 245
	}
	close(tcpsock);
246 247 248 249
#ifdef USE_LOCALHOST_PROXY
	if (localsock >= 0)
		close(localsock);
#endif
250
	FrisLog("daemon terminating");
251 252 253 254 255
	exit(0);
}

struct childinfo {
	struct childinfo *next;
256
	struct config_imageinfo *imageinfo;
257
	int ptype;
258 259
	int method;
	int pid;
260
	char *pidfile;
261
	int uid;		/* UID to run child as */
262 263
	gid_t gids[MAXGIDS];	/* GID to run child as */
	int ngids;		/* number of GIDs */
264 265
	int retries;		/* # times to try starting up child */
	int timeout;		/* max runtime (sec) for child */
266 267 268 269
	in_addr_t servaddr;	/* -S arg */
	in_addr_t ifaceaddr;	/* -i arg */
	in_addr_t addr;		/* -m arg */
	in_port_t port;		/* -p arg */
270 271
	void (*done)(struct childinfo *, int);
	void *extra;
272
};
273 274
#define PTYPE_CLIENT	1
#define PTYPE_SERVER	2
275
#define PTYPE_UPLOADER	4
276

277 278
struct clientextra {
	char *realname;
279
	char *resolvedname;
280 281 282 283 284 285 286
	/* info from our parent */
	uint16_t sigtype;
	uint8_t signature[MS_MAXSIGLEN];
	uint32_t hisize;
	uint32_t losize;
};

287 288 289 290
struct uploadextra {
	char *realname;
	uint64_t isize;
	uint32_t mtime;
291
	int itimeout;
292 293
};

294
static struct childinfo *findchild(char *, int, int);
295
static int startchild(struct childinfo *);
296
static struct childinfo *startserver(struct config_imageinfo *,
297
				     in_addr_t, in_addr_t, int, int *);
298
static struct childinfo *startclient(struct config_imageinfo *,
299 300
				     in_addr_t, in_addr_t, in_addr_t,
				     in_port_t, int, int *);
301 302 303
static struct childinfo *startuploader(struct config_imageinfo *,
				       in_addr_t, in_addr_t, uint64_t,
				       uint32_t, int, int *);
304 305 306 307 308 309 310

static void
free_imageinfo(struct config_imageinfo *ii)
{
	if (ii) {
		if (ii->imageid)
			free(ii->imageid);
311 312
		if (ii->dir)
			free(ii->dir);
313 314 315 316 317 318
		if (ii->path)
			free(ii->path);
		if (ii->sig)
			free(ii->sig);
		if (ii->get_options)
			free(ii->get_options);
319 320 321 322
		if (ii->put_oldversion)
			free(ii->put_oldversion);
		if (ii->put_options)
			free(ii->put_options);
323 324 325 326 327 328 329 330 331 332 333 334
		free(ii);
	}
}

/*
 * (Deep) copy an imageinfo structure.
 * Returns pointer or null on error.
 */
static struct config_imageinfo *
copy_imageinfo(struct config_imageinfo *ii)
{
	struct config_imageinfo *nii;
335
	int i;
336 337 338 339 340

	if ((nii = calloc(1, sizeof *nii)) == NULL)
		goto fail;
	if (ii->imageid && (nii->imageid = strdup(ii->imageid)) == NULL)
		goto fail;
341 342
	if (ii->dir && (nii->dir = strdup(ii->dir)) == NULL)
		goto fail;
343 344
	if (ii->path && (nii->path = strdup(ii->path)) == NULL)
		goto fail;
345 346 347 348 349 350 351 352 353 354 355
	if (ii->sig) {
		int sz = 0;
		if (ii->flags & CONFIG_SIG_ISMTIME)
			sz = sizeof(time_t);
		if (sz) {
			if ((nii->sig = malloc(sz)) == NULL)
				goto fail;
		}
		if (nii->sig)
			memcpy(nii->sig, ii->sig, sz);
	}
356
	nii->flags = ii->flags;
357
	nii->uid = ii->uid;
358 359 360
	for (i = 0; i < ii->ngids; i++)
		nii->gids[i] = ii->gids[i];
	nii->ngids = ii->ngids;
361 362 363 364
	if (ii->get_options &&
	    (nii->get_options = strdup(ii->get_options)) == NULL)
		goto fail;
	nii->get_methods = ii->get_methods;
365 366 367 368 369 370 371 372 373
	nii->get_timeout = ii->get_timeout;
	if (ii->put_oldversion &&
	    (nii->put_oldversion = strdup(ii->put_oldversion)) == NULL)
		goto fail;
	if (ii->put_options &&
	    (nii->put_options = strdup(ii->put_options)) == NULL)
		goto fail;
	nii->put_maxsize = ii->put_maxsize;
	nii->put_timeout = ii->put_timeout;
374
	nii->put_itimeout = ii->put_itimeout;
375
	/* XXX don't care about extra right now */
376 377 378 379 380 381
	return nii;

 fail:
	free_imageinfo(nii);
	return NULL;
}
382 383

/*
384 385 386 387 388 389
 * Fetch an image from our parent by getting image info from it and firing
 * off a frisbee.  If canredirect is non-zero, we also hook our child up
 * directly with our parent so that it can fetch in parallel.  If statusonly
 * is non-zero, we just request info from our parent and return that.
 * Returns zero if Reply struct contains the desired info, TRYAGAIN if we
 * have started up a frisbee to fetch from our parent, or an error otherwise.
390
 * Status is returned in host order.
391 392
 */
int
393 394 395
fetch_parent(struct in_addr *myip, struct in_addr *hostip,
	     struct in_addr *pip, in_port_t pport,
	     struct config_imageinfo *ii, int statusonly, GetReply *replyp)
396
{
397
	struct childinfo *ci;
398
	struct clientextra *ce;
399
	struct in_addr pif;
400
	in_addr_t authip;
401
	GetReply reply;
402
	int rv, methods;
403

404 405 406 407 408 409
	/*
	 * If usechildauth is set, we pass the child host IP to our parent
	 * for authentication, otherwise we use our own.
	 */
	authip = usechildauth ? ntohl(hostip->s_addr) : 0;

410 411 412 413 414 415 416
	/*
	 * Allowed image methods are constrained by any parent methods
	 */
	methods = ii->get_methods & parentmethods;
	if (methods == 0)
		return MS_ERROR_NOMETHOD;

417
	/*
418 419 420 421
	 * See if a fetch is already in progress.
	 * If so we will either return "try again later" or point them to
	 * our parent.
	 */
422
	ci = findchild(ii->imageid, PTYPE_CLIENT, methods);
423 424
	if (ci != NULL) {
		if (debug)
425 426
			FrisInfo("%s: fetch from %s in progress",
				 ii->imageid, inet_ntoa(*pip));
427 428 429 430 431 432 433 434 435 436 437 438 439 440

		/*
		 * Since a download is in progress we don't normally need
		 * to revalidate since we wouldn't be downloading the image
		 * if we didn't have access.
		 *
		 * However, when acting for a child, we do need to validate
		 * every time since the child making the current request
		 * might not have the same access as the child we are
		 * currently downloading for.
		 */
		if (authip) {
			if (!ClientNetFindServer(ntohl(pip->s_addr),
						 pport, authip, ii->imageid,
441
						 methods, 1, 5,
442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459
						 &reply, &pif))
				return MS_ERROR_NOIMAGE;
			if (reply.error)
				return reply.error;
		}
		/*
		 * We return the info we got from our parent.
		 */
		assert(ci->extra != NULL);
		ce = ci->extra;

		memset(replyp, 0, sizeof *replyp);
		replyp->sigtype = ce->sigtype;
		memcpy(replyp->signature, ce->signature, MS_MAXSIGLEN);
		replyp->hisize = ce->hisize;
		replyp->losize = ce->losize;
		if (statusonly) {
			if (debug)
460 461 462 463 464
				FrisInfo("%s: parent status: "
					 "sigtype=%d, sig=0x%08x..., size=%x/%x",
					 ii->imageid,
					 ce->sigtype, *(uint32_t *)ce->signature,
					 ce->hisize, ce->losize);
465 466 467 468 469 470 471 472 473 474 475 476
			return 0;
		}

		/*
		 * A real get request.
		 * We either tell them to try again later or redirect them
		 * to our parent.
		 */
		goto done;
	}

	if (debug)
477 478 479
		FrisInfo("%s: requesting %simage from %s:%d",
			 ii->imageid, (statusonly ? "status of ": ""),
			 inet_ntoa(*pip), pport);
480 481 482

	/*
	 * Image fetch is not in progress.
483 484 485
	 * Send our parent a GET request and see what it says.
	 */
	if (!ClientNetFindServer(ntohl(pip->s_addr), pport, authip,
486
				 ii->imageid, methods, statusonly, 5,
487 488 489 490 491 492
				 &reply, &pif))
		return MS_ERROR_NOIMAGE;
	if (reply.error)
		return reply.error;

	/*
493
	 * Return the image size and signature from our parent
494
	 */
495 496 497 498 499
	memset(replyp, 0, sizeof *replyp);
	replyp->sigtype = reply.sigtype;
	memcpy(replyp->signature, reply.signature, MS_MAXSIGLEN);
	replyp->hisize = reply.hisize;
	replyp->losize = reply.losize;
500
	if (statusonly) {
501
		if (debug)
502 503 504 505 506
			FrisInfo("%s: parent status: "
				 "sigtype=%d, sig=0x%08x..., size=%x/%x",
				 ii->imageid,
				 reply.sigtype, *(uint32_t *)reply.signature,
				 reply.hisize, reply.losize);
507 508 509
		return 0;
	}

510
	/*
511
	 * Parent has started up a frisbeed, spawn a frisbee to capture it.
512 513 514 515 516 517 518
	 *
	 * Note that servaddr from the reply might not be our parent (pip),
	 * if our parent in turn had to ask its parent and redirect was in
	 * effect.  Unfortunately, in this case, 'pif' might not be correct
	 * since it is always the interface from which our parent responded.
	 * It will work as long as our parent's ancestor(s) reach us via
	 * the same interface.
519
	 */
520 521
	ci = startclient(ii, ntohl(pif.s_addr), reply.servaddr,
			 reply.addr, reply.port, reply.method, &rv);
522 523 524
	if (ci == NULL)
		return rv;

525
	/*
526 527
	 * Cache the size/signature info for use in future calls when
	 * we are busy (the case above).
528
	 */
529 530 531 532 533 534 535
	assert(ci->extra != NULL);
	ce = ci->extra;
	ce->sigtype = replyp->sigtype;
	memcpy(ce->signature, replyp->signature, MS_MAXSIGLEN);
	ce->hisize = replyp->hisize;
	ce->losize = replyp->losize;
	if (debug)
536 537 538 539 540
		FrisInfo("%s cache status: "
			 "sigtype=%d, sig=0x%08x..., size=%x/%x",
			 ii->imageid,
			 ce->sigtype, *(uint32_t *)ce->signature,
			 ce->hisize, ce->losize);
541

542 543 544 545
 done:
	if (!canredirect)
		return MS_ERROR_TRYAGAIN;

546 547 548 549 550 551 552
	/*
	 * XXX more unicast "fer now" hackary...if we are talking unicast with
	 * our parent, we cannot redirect the child there as well.
	 */
	if (ci->method == MS_METHOD_UNICAST)
		return MS_ERROR_TRYAGAIN;

553 554 555 556 557
	memset(replyp, 0, sizeof *replyp);
	replyp->method = ci->method;
	replyp->servaddr = ci->servaddr;
	replyp->addr = ci->addr;
	replyp->port = ci->port;
558
	return 0;
559
}
560

561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582
/*
 * Handle a GET request.
 *
 * This can be either a request to get the actual image (status==0)
 * or a request to get just the status of the image (status==1).
 *
 * A request for the actual image causes us to start up a frisbee server
 * for the image if one is not already running.  If we are configured with
 * a parent, we may need to first fetch the image from our parent with a
 * GET request.  In this case, we may return a TRYAGAIN status while the
 * image transfer is in progress. Alternatively, if we are configured to
 * allow redirection, we could just point our client to our parent and allow
 * them to download the image along side us.
 *
 * A request for status will not fire up a server, but may require a status
 * call on our parent.  A status call will never return TRYAGAIN, it will
 * always return the most recent status for an image or some other error.
 * If we have a parent, we will always get its status for the image and
 * return that if "newer" than ours. Thus status is a synchronous call that
 * will return the attributes of the image that will ultimately be transferred
 * to the client upon a GET request.
 */
583 584 585 586
void
handle_get(int sock, struct sockaddr_in *sip, struct sockaddr_in *cip,
	   MasterMsg_t *msg)
{
587
	struct in_addr host;
588
	char imageid[MS_MAXIDLEN+1], *cimageid;
589
	char clientip[sizeof("XXX.XXX.XXX.XXX")+1];
590
	int len;
591 592
	struct config_host_authinfo *ai;
	struct config_imageinfo *ii = NULL;
593
	struct childinfo *ci;
594
	struct stat sb;
595
	uint64_t isize;
596
	int rv, methods, wantstatus;
597
	int getfromparent;
598
	GetReply reply;
599 600
	char *op;

601 602
	/*
	 * If an explicit host was listed, use that as the host we are
603 604 605
	 * authenticating, otherwise use the caller's IP.  config_auth_by_IP
	 * will reject the former if the caller is not allowed to proxy for
	 * the node in question.
606 607 608 609 610 611 612
	 */
	if (msg->body.getrequest.hostip)
		host.s_addr = msg->body.getrequest.hostip;
	else
		host.s_addr = cip->sin_addr.s_addr;

	len = ntohs(msg->body.getrequest.idlen);
613 614
	memcpy(imageid, msg->body.getrequest.imageid, len);
	imageid[len] = '\0';
615 616 617 618 619
	cimageid = config_canonicalize_imageid(imageid);
	if (cimageid != NULL && strcmp(cimageid, imageid) == 0) {
		free(cimageid);
		cimageid = NULL;
	}
620 621
	methods = msg->body.getrequest.methods;
	wantstatus = msg->body.getrequest.status;
622
	op = wantstatus ? "GETSTATUS" : "GET";
623 624

	strncpy(clientip, inet_ntoa(cip->sin_addr), sizeof clientip);
625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641
	if (cimageid == NULL) {
		if (host.s_addr != cip->sin_addr.s_addr)
			FrisLog("%s: %s from %s (for %s), methods: 0x%x",
				imageid, op, clientip, inet_ntoa(host),
				methods);
		else
			FrisLog("%s: %s from %s, (methods: 0x%x)",
				imageid, op, clientip, methods);
	} else {
		if (host.s_addr != cip->sin_addr.s_addr)
			FrisLog("%s (%s): %s from %s (for %s), methods: 0x%x",
				imageid, cimageid, op, clientip,
				inet_ntoa(host), methods);
		else
			FrisLog("%s (%s): %s from %s, (methods: 0x%x)",
				imageid, cimageid, op, clientip, methods);
	}
642
	memset(msg, 0, sizeof *msg);
643 644 645
	msg->hdr.type = htonl(MS_MSGTYPE_GETREPLY);
	strncpy((char *)msg->hdr.version, MS_MSGVERS_1,
		sizeof(msg->hdr.version));
646

647 648
	/*
	 * If they request a method we don't support, reject them before
649 650
	 * we do any other work.  XXX maybe the method should not matter
	 * for a status-only call?
651 652 653 654 655
	 */
	methods &= onlymethods;
	if (methods == 0)
		goto badmethod;

656 657 658 659 660 661 662 663 664
	/*
	 * Use the canonical name from here on out.
	 */
	if (cimageid != NULL) {
		strcpy(imageid, cimageid);
		free(cimageid);
		cimageid = NULL;
	}

665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680
	/*
	 * In mirrormode, we first validate access with our parent
	 * before doing anything locally.
	 */
	if (mirrormode) {
		struct in_addr pif;
		in_addr_t authip;

		authip = usechildauth ? ntohl(host.s_addr) : 0;
		if (!ClientNetFindServer(ntohl(parentip.s_addr),
					 parentport, authip, imageid,
					 methods, 1, 5,
					 &reply, &pif))
			reply.error = MS_ERROR_NOIMAGE;
		if (reply.error) {
			msg->body.getreply.error = reply.error;
681 682
			FrisLog("%s: client %s authentication with parent failed: %s",
				imageid, clientip, GetMSError(reply.error));
683 684
			goto reply;
		}
685
	}
686

687
#ifdef USE_LOCALHOST_PROXY
688
	/*
689
	 * XXX this is really an Emulab special case for a boss node.
690
	 *
691 692 693
	 * We allow localhost access to any image that has a daemon
	 * currently running.  This is a hack to allow localhost to get
	 * info for a running daemon so it can kill it.
694
	 *
695 696 697
	 * Perhaps localhost should be able to access ANY valid image
	 * or image directory (running or not), but I don't want to go
	 * there right now...
698 699 700 701 702
	 */
	if (wantstatus &&
	    cip->sin_addr.s_addr == htonl(INADDR_LOOPBACK) &&
	    host.s_addr == htonl(INADDR_LOOPBACK) &&
	    (ci = findchild(imageid, PTYPE_SERVER, methods)) != NULL) {
703
		/* XXX only fill in the info that the caller cares about */
704 705 706 707 708 709 710 711
		msg->body.getreply.method = ci->method;
		msg->body.getreply.isrunning = 1;
		msg->body.getreply.addr = htonl(ci->addr);
		msg->body.getreply.port = htons(ci->port);
		goto reply;
	}
#endif

712 713 714 715
	/*
	 * See if node has access to the image.
	 * If not, return an error code immediately.
	 */
716
	rv = config_auth_by_IP(1, &cip->sin_addr, &host, imageid, &ai);
717
	if (rv) {
718 719
		FrisWarning("%s: client %s %s failed: %s",
			    imageid, clientip, op, GetMSError(rv));
720 721 722 723 724 725 726
		msg->body.getreply.error = rv;
		goto reply;
	}
	if (debug > 1)
		config_dump_host_authinfo(ai);
	if (ai->numimages > 1) {
		rv = MS_ERROR_INVALID;
727 728 729
		FrisWarning("%s: client %s %s failed: "
			    "lookup returned multiple (%d) images",
			    imageid, clientip, op, ai->numimages);
730 731 732 733 734
		msg->body.getreply.error = rv;
		goto reply;
	}
	ii = copy_imageinfo(&ai->imageinfo[0]);
	config_free_host_authinfo(ai);		
735 736
	assert((ii->flags & CONFIG_PATH_ISFILE) != 0);

737 738 739 740 741
	/*
	 * If the image is currently being uploaded, return TRYAGAIN.
	 */
	if (findchild(ii->imageid, PTYPE_UPLOADER, MS_METHOD_UNICAST)) {
		rv = MS_ERROR_TRYAGAIN;
742
		FrisLog("%s: %s currently being uploaded", imageid, op);
743 744 745 746
		msg->body.getreply.error = rv;
		goto reply;
	}

747 748 749 750 751
	/*
	 * See if image actually exists.
	 *
	 * If the file exists but is not a regular file, we return an error.
	 *
752 753 754 755
	 * If the file does not exist (it is possible to request an image
	 * that doesn't exist if the authentication check allows access to
	 * the containing directory) and we have a parent, we request it
	 * from the parent.
756
	 */
757 758
	isize = 0;
	getfromparent = 0;
759 760
	if ((ii->flags & CONFIG_PATH_EXISTS) != 0 &&
	    stat(ii->path, &sb) == 0) {
761 762
		if (!S_ISREG(sb.st_mode)) {
			rv = MS_ERROR_INVALID;
763 764 765
			FrisWarning("%s: client %s %s failed: "
				    "not a regular file",
				    imageid, clientip, op);
766 767 768
			msg->body.getreply.error = rv;
			goto reply;
		}
769 770 771 772 773
		isize = sb.st_size;

		/*
		 * If the file exists and we have a parent, get the signature
		 * from the parent and see if we need to update our copy.
774
		 * We only do this for mirror mode.
775
		 */
776
		if (mirrormode) {
777
			FrisLog("%s: have local copy", imageid);
778 779

			/*
780 781 782
			 * See if the signature is out of date.
			 * Since this is mirror mode, we can use the status
			 * info we got from the earlier call.
783
			 *
784 785 786 787
			 * Note that we actually check for a different date,
			 * not just a newer date. People often roll back
			 * faulty images to previous versions.
			 *
788 789
			 * XXX need checks for other signature types.
			 */
790
			if ((reply.sigtype == MS_SIGTYPE_MTIME &&
791
			     *(time_t *)reply.signature != sb.st_mtime)) {
792
				uint32_t mt = *(uint32_t *)reply.signature;
793

794 795
				msg->body.getreply.sigtype =
					htons(reply.sigtype);
796
				if (reply.sigtype == MS_SIGTYPE_MTIME)
797 798
					*(uint32_t *)reply.signature =
						htonl(mt);
799
				memcpy(msg->body.getreply.signature,
800 801 802 803 804
				       reply.signature, MS_MAXSIGLEN);
				msg->body.getreply.hisize =
					htonl(reply.hisize);
				msg->body.getreply.losize =
					htonl(reply.losize);
805 806 807 808

				if (wantstatus)
					goto reply;

809 810 811
				FrisLog("%s: local copy (sig=%x) "
					"is out of date (sig=%x), GET from parent",
					imageid, sb.st_mtime, mt);
812 813
				getfromparent = 1;
			}
814
		}
815
	} else if (fetchfromabove) {
816
		FrisLog("%s: no local copy, %s from parent", imageid, op);
817 818 819

		/*
		 * We don't have the image, but we have a parent,
820 821 822 823 824
		 * request the status as we did above. Again, in mirrormode,
		 * we have already fetched the status so no additional call
		 * is needed.
		 *
		 * Any error is reflected to our caller.
825
		 */
826 827 828 829
		if (!mirrormode) {
			rv = fetch_parent(&sip->sin_addr, &host, &parentip,
					  parentport, ii, 1, &reply);
			if (rv) {
830 831 832
				FrisLog("%s: failed getting parent status: %s, "
					"failing",
					imageid, GetMSError(rv));
833 834 835
				msg->body.getreply.error = rv;
				goto reply;
			}
836 837 838 839 840
		}
		/*
		 * And we must always fetch from the parent.
		 * Return the attributes we got via the check above.
		 */
841 842
		msg->body.getreply.sigtype = htons(reply.sigtype);
		if (reply.sigtype == MS_SIGTYPE_MTIME) {
843
			uint32_t mt;
844 845
			mt = *(uint32_t *)reply.signature;
			*(uint32_t *)reply.signature = htonl(mt);
846
		}
847
		memcpy(msg->body.getreply.signature, reply.signature,
848
		       MS_MAXSIGLEN);
849 850
		msg->body.getreply.hisize = htonl(reply.hisize);
		msg->body.getreply.losize = htonl(reply.losize);
851 852 853

		if (wantstatus)
			goto reply;
854 855
		getfromparent = 1;
	} else {
856 857 858
		/*
		 * No image and no parent, just flat fail.
		 */
859
		rv = (errno == ENOENT) ? MS_ERROR_NOIMAGE : MS_ERROR_INVALID;
860 861
		FrisWarning("%s: client %s %s failed: %s",
			    imageid, clientip, op, GetMSError(rv));
862 863 864 865
		msg->body.getreply.error = rv;
		goto reply;
	}

866 867 868 869 870 871
	/*
	 * Either we did not have the image or our copy is out of date,
	 * attempt to fetch from our parent.
	 */
	if (getfromparent) {
		rv = fetch_parent(&sip->sin_addr, &host,
872
				  &parentip, parentport, ii, 0, &reply);
873
		/*
874 875 876
		 * Redirecting to parent.
		 * Can only do this if our parents method is compatible
		 * with the client's request.
877 878
		 */
		if (rv == 0) {
879 880
			if ((reply.method & methods) != 0) {
				msg->body.getreply.method = reply.method;
881 882
				msg->body.getreply.isrunning = 1;
				msg->body.getreply.servaddr =
883
					htonl(reply.servaddr);
884
				msg->body.getreply.addr =
885
					htonl(reply.addr);
886
				msg->body.getreply.port =
887
					htons(reply.port);
888 889
				FrisLog("%s: redirecting %s to our parent",
					imageid, clientip);
890 891
				goto reply;
			}
892 893 894
			FrisLog("%s: cannot redirect %s to parent; "
				"incompatible transfer methods",
				imageid, clientip);
895
			rv = MS_ERROR_TRYAGAIN;
896 897 898 899 900 901
		}
		/*
		 * If parent callout failed, but we have a copy
		 * use our stale version.
		 */
		if (rv != MS_ERROR_TRYAGAIN && isize > 0) {
902 903 904
			FrisWarning("%s: client %s %s from parent failed: %s, "
				    "using our stale copy",
				    imageid, clientip, op, GetMSError(rv));
905 906 907 908 909
		}
		/*
		 * Otherwise, we are busy fetching the new copy (TRYAGAIN),
		 * or we had a real failure and we don't have a copy.
		 */
910
		else {
911 912
			FrisWarning("%s: client %s %s from parent failed: %s",
				    imageid, clientip, op, GetMSError(rv));
913 914 915
			msg->body.getreply.error = rv;
			goto reply;
		}
916 917
	}

918 919 920 921
	/*
	 * Figure out what transfer method to use.
	 */
	if (debug)
922 923
		FrisInfo("  request methods: 0x%x, image methods: 0x%x",
			 methods, ii->get_methods);
924
	methods &= ii->get_methods;
925
	if (methods == 0) {
926
	badmethod:
927
		rv = MS_ERROR_NOMETHOD;
928 929
		FrisWarning("%s: client %s %s failed: %s",
			    imageid, clientip, op, GetMSError(rv));
930 931 932 933 934 935 936 937
		msg->body.getreply.error = rv;
		goto reply;
	}

	/*
	 * Otherwise see if there is a frisbeed already running, starting
	 * one if not.  Then construct a reply with the available info.
	 */
938
	ci = findchild(ii->imageid, PTYPE_SERVER, methods);
939 940 941 942 943 944

	/*
	 * XXX right now frisbeed doesn't support mutiple clients
	 * on the same unicast address.  We could do multiple servers
	 * for the same image, but we don't.
	 */
945
	if (!wantstatus && ci != NULL && ci->method == MS_METHOD_UNICAST) {
946 947 948
		FrisWarning("%s: client %s %s failed: "
			    "unicast server already running",
			    imageid, clientip, op);
949 950 951 952
		msg->body.getreply.error = MS_ERROR_TRYAGAIN;
		goto reply;
	}

953 954
	if (ci == NULL) {
		struct in_addr in;
955
		in_addr_t myaddr;
956
		int stat;
957

958 959 960 961 962 963 964
		if (ii->sig != NULL) {
			int32_t mt = *(int32_t *)ii->sig;
			assert((ii->flags & CONFIG_SIG_ISMTIME) != 0);

			msg->body.getreply.sigtype = htons(MS_SIGTYPE_MTIME);
			*(int32_t *)msg->body.getreply.signature = htonl(mt);
		}
965 966 967
		if (wantstatus) {
			msg->body.getreply.method = methods;
			msg->body.getreply.isrunning = 0;
968 969
			msg->body.getreply.hisize = htonl(isize >> 32);
			msg->body.getreply.losize = htonl(isize);
970
			FrisLog("%s: STATUS is not running", imageid);
971 972
			goto reply;
		}
973 974 975 976 977 978 979 980 981 982 983
		myaddr = ntohl(sip->sin_addr.s_addr);
#ifdef USE_LOCALHOST_PROXY
		/*
		 * If this was a proxy request from localhost,
		 * we need to start the server on the real interface
		 * instead.  If none was specified, we don't know what
		 * interface to use, so just fail.
		 */
		if (myaddr == INADDR_LOOPBACK) {
			myaddr = ntohl(ifaceip.s_addr);
			if (myaddr == INADDR_ANY) {
984 985
				FrisWarning("%s: cannot start server on behalf "
					    "of %s", imageid, clientip);
986 987 988 989 990 991
				msg->body.getreply.error = MS_ERROR_FAILED;
				goto reply;
			}
		}
#endif
		ci = startserver(ii, myaddr, ntohl(cip->sin_addr.s_addr),
992
				 methods, &rv);
993 994 995 996
		if (ci == NULL) {
			msg->body.getreply.error = rv;
			goto reply;
		}
997

998
		in.s_addr = htonl(ci->addr);
999 1000 1001
		FrisLog("%s: started %s server on %s:%d (pid %d, timo %ds)",
			imageid, GetMSMethods(ci->method),
			inet_ntoa(in), ci->port, ci->pid, ci->timeout);
1002
		if (debug) {
1003 1004
			FrisLog("  uid: %d, gids: %s",
				ci->uid, gidstr(ci->ngids, ci->gids));
1005
		}
1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016

		/*
		 * Watch for an immediate death so we don't tell our client
		 * a server is running when it really isn't.
		 *
		 * XXX what is the right response? Right now we just tell
		 * them to try again and hope the problem is transient.
		 */
		sleep(2);
		if (reapchildren(ci->pid, &stat)) {
			msg->body.getreply.error = MS_ERROR_TRYAGAIN;
1017 1018 1019
			FrisLog("%s: server immediately exited (stat=0x%x), "
				"telling client to try again!",
				imageid, stat);
1020 1021
			goto reply;
		}
1022 1023 1024
	} else {
		struct in_addr in;

1025
		if (ii->sig != NULL) {
1026 1027 1028 1029 1030 1031
			int32_t mt = *(int32_t *)ii->sig;
			assert((ii->flags & CONFIG_SIG_ISMTIME) != 0);

			msg->body.getreply.sigtype = htons(MS_SIGTYPE_MTIME);
			*(int32_t *)msg->body.getreply.signature = htonl(mt);
		}
1032 1033
		in.s_addr = htonl(ci->addr);
		if (wantstatus)
1034 1035 1036
			FrisLog("%s: STATUS is running %s on %s:%d (pid %d)",
				imageid, GetMSMethods(ci->method),
				inet_ntoa(in), ci->port, ci->pid);
1037
		else
1038 1039 1040
			FrisLog("%s: %s server already running on %s:%d (pid %d)",
				imageid, GetMSMethods(ci->method),
				inet_ntoa(in), ci->port, ci->pid);
1041
		if (debug)
1042 1043
			FrisLog("  uid: %d, gids: %s",
				ci->uid, gidstr(ci->ngids, ci->gids));
1044 1045
	}

1046 1047
	msg->body.getreply.hisize = htonl(isize >> 32);
	msg->body.getreply.losize = htonl(isize);
1048 1049
	msg->body.getreply.method = ci->method;
	msg->body.getreply.isrunning = 1;
1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063
	msg->body.getreply.servaddr = htonl(ci->servaddr);
	/*
	 * XXX tmp hack.  Currently, if we are unicasting, startserver
	 * returns the addr field set to the address of the client
	 * (see the XXX there for details).  However, we need to return our
	 * address to the client.
	 *
	 * When frisbeed is changed to support more than one unicast client,
	 * this will change.
	 */
	if (ci->method == MS_METHOD_UNICAST)
		msg->body.getreply.addr = msg->body.getreply.servaddr;
	else
		msg->body.getreply.addr = htonl(ci->addr);
1064 1065
	msg->body.getreply.port = htons(ci->port);

1066
 reply:
1067
	msg->body.getreply.error = htons(msg->body.getreply.error);
1068
	if (debug) {
1069 1070 1071 1072 1073
		FrisInfo("%s reply: sigtype=%d, sig=0x%08x..., size=%x/%x",
			 op, ntohs(msg->body.getreply.sigtype),
			 ntohl(*(uint32_t *)msg->body.getreply.signature),
			 ntohl(msg->body.getreply.hisize),
			 ntohl(msg->body.getreply.losize));
1074 1075
	}
	len = sizeof msg->hdr + sizeof msg->body.getreply;
1076
	if (!MsgSend(sock, msg, len, 10))
1077 1078
		FrisError("%s: could not send reply",
			  inet_ntoa(cip->sin_addr));
1079 1080
	if (ii)
		free_imageinfo(ii);
1081 1082
}

1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123
void
handle_put(int sock, struct sockaddr_in *sip, struct sockaddr_in *cip,
	   MasterMsg_t *msg)
{
	struct in_addr host, in;
	in_addr_t myaddr;
	char imageid[MS_MAXIDLEN+1];
	char clientip[sizeof("XXX.XXX.XXX.XXX")+1];
	int len;
	struct config_host_authinfo *ai;
	struct config_imageinfo *ii = NULL;
	struct childinfo *ci;
	struct stat sb;
	uint64_t isize;
	uint32_t mtime, timo;
	int rv, wantstatus;
	char *op;

	/*
	 * If an explicit host was listed, use that as the host we are
	 * authenticating, otherwise use the caller's IP.  config_auth_by_IP
	 * will reject the former if the caller is not allowed to proxy for
	 * the node in question.
	 */
	if (msg->body.putrequest.hostip)
		host.s_addr = msg->body.putrequest.hostip;
	else
		host.s_addr = cip->sin_addr.s_addr;

	len = ntohs(msg->body.putrequest.idlen);
	memcpy(imageid, msg->body.putrequest.imageid, len);
	imageid[len] = '\0';
	wantstatus = msg->body.putrequest.status;
	op = wantstatus ? "PUTSTATUS" : "PUT";
	isize = ((uint64_t)ntohl(msg->body.putrequest.hisize) << 32) |
		ntohl(msg->body.putrequest.losize);
	mtime = ntohl(msg->body.putrequest.mtime);
	timo = ntohl(msg->body.putrequest.timeout);

	strncpy(clientip, inet_ntoa(cip->sin_addr), sizeof clientip);
	if (host.s_addr != cip->sin_addr.s_addr)
1124 1125
		FrisLog("%s: %s from %s (for %s), size=%llu",
			imageid, op, clientip, inet_ntoa(host), isize);
1126
	else
1127 1128
		FrisLog("%s: %s from %s, size=%llu",
			imageid, op, clientip, isize);
1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139

	memset(msg, 0, sizeof *msg);
	msg->hdr.type = htonl(MS_MSGTYPE_PUTREPLY);
	strncpy((char *)msg->hdr.version, MS_MSGVERS_1,
		sizeof(msg->hdr.version));

	/*
	 * XXX we don't handle mirror mode right now.
	 */
	if (mirrormode) {
		rv = MS_ERROR_NOTIMPL;
1140 1141 1142
		FrisWarning("%s: client %s %s failed: "
			    "upload not supported in mirror mode",
			    imageid, clientip, op);
1143 1144 1145 1146 1147 1148 1149 1150 1151 1152
		msg->body.putreply.error = rv;
		goto reply;
	}

	/*
	 * See if node has access to the image.
	 * If not, return an error code immediately.
	 */
	rv = config_auth_by_IP(0, &cip->sin_addr, &host, imageid, &ai);
	if (rv) {
1153 1154
		FrisWarning("%s: client %s %s failed: %s",
			    imageid, clientip, op, GetMSError(rv));
1155 1156 1157 1158 1159 1160 1161
		msg->body.putreply.error = rv;
		goto reply;
	}
	if (debug > 1)
		config_dump_host_authinfo(ai);
	if (ai->numimages > 1) {
		rv = MS_ERROR_INVALID;
1162 1163 1164
		FrisWarning("%s: client %s %s failed: "
			    "lookup returned multiple (%d) images",
			    imageid, clientip, op, ai->numimages);
1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178
		msg->body.putreply.error = rv;
		goto reply;
	}
	ii = copy_imageinfo(&ai->imageinfo[0]);
	config_free_host_authinfo(ai);		
	assert((ii->flags & CONFIG_PATH_ISFILE) != 0);

	/*
	 * If they gave us a size and it exceeds the maxsize, return an error.
	 * We do this even for a status-only request; they can specify a size
	 * of zero if they want to get all the image attributes.
	 */
	if (isize > ii->put_maxsize) {
		rv = MS_ERROR_TOOBIG;
1179 1180 1181
		FrisWarning("%s: client %s %s failed: "
			    "upload size (%llu) exceeds maximum for image (%llu)",
			    imageid, clientip, op, isize, ii->put_maxsize);
1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196

		/*
		 * We return the max size here along with the error so that
		 * the client doesn't have to make a second, wantstatus call.
		 */
		msg->body.putreply.himaxsize = htonl(ii->put_maxsize >> 32);
		msg->body.putreply.lomaxsize = htonl(ii->put_maxsize);

		msg->body.putreply.error = rv;
		goto reply;
	}

	/*
	 * They gave us an mtime and it is "bad", return an error.
	 * XXX somewhat arbitrary: cannot set a time in the future.
1197
	 * XXX cut them some slack on the future time thing, up to 10s okay.
1198 1199 1200 1201 1202
	 */
	if (mtime) {
		struct timeval now;

		gettimeofday(&now, NULL);
1203
		if (mtime > (now.tv_sec + 10)) {
1204
			rv = MS_ERROR_BADMTIME;
1205
			FrisWarning("%s: client %s %s failed: "
1206 1207 1208
				    "attempt to set mtime in the future "
				    "(%u > %u)\n",
				    imageid, clientip, op, mtime, now.tv_sec);
1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221
			msg->body.putreply.error = rv;
			goto reply;
		}
	}

	/*
	 * If the image is being served, fetched from our parent,
	 * or uploaded--return TRYAGAIN.
	 */
	if ((ci = findchild(ii->imageid, PTYPE_SERVER, onlymethods)) ||
	    (ci = findchild(ii->imageid, PTYPE_CLIENT, onlymethods)) ||
	    (ci = findchild(ii->imageid, PTYPE_UPLOADER, MS_METHOD_UNICAST))) {
		msg->body.putreply.error = MS_ERROR_TRYAGAIN;
1222 1223 1224 1225
		FrisLog("%s: %s currently being %s", imageid, op,
			(ci->ptype == PTYPE_SERVER) ? "served" :
			(ci->ptype == PTYPE_CLIENT) ? "downloaded from parent" :
			"uploaded");
1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243
		goto reply;
	}

	/*
	 * See if image actually exists.
	 *
	 * If the file exists but is not a regular file, we return an error.
	 * (maybe we should just remove it?)
	 *
	 * If it is a regular file, we return its current signature.
	 *
	 * If the file does not exist, that is okay (the authentication check
	 * has verified that the node can create the image).
	 */
	if ((ii->flags & CONFIG_PATH_EXISTS) != 0 &&
	    stat(ii->path, &sb) == 0) {
		if (!S_ISREG(sb.st_mode)) {
			rv = MS_ERROR_INVALID;
1244 1245 1246
			FrisWarning("%s: client %s %s failed: "
				    "existing target is not a regular file",
				    imageid, clientip, op);
1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277
			msg->body.putreply.error = rv;
			goto reply;
		}
		msg->body.putreply.exists = 1;

		/*
		 * Return the current size and signature info
		 */
		msg->body.putreply.hisize = htonl((uint64_t)sb.st_size >> 32);
		msg->body.putreply.losize = htonl(sb.st_size);

		if (ii->sig != NULL) {
			int32_t mt = *(int32_t *)ii->sig;

			assert((ii->flags & CONFIG_SIG_ISMTIME) != 0);
			msg->body.putreply.sigtype = htons(MS_SIGTYPE_MTIME);
			*(int32_t *)msg->body.putreply.signature = htonl(mt);
		}
	} else {
		msg->body.putreply.exists = 0;
	}

	/*
	 * At this point we know that there is no conflicting use of the
	 * image in progress, so either return status or fire off an uploader
	 * and return the contact info to the client.
	 */
	msg->body.putreply.himaxsize = htonl(ii->put_maxsize >> 32);
	msg->body.putreply.lomaxsize = htonl(ii->put_maxsize);

	if (wantstatus) {
1278
		FrisLog("%s: PUTSTATUS upload allowed", imageid);
1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289
		goto reply;
	}
	myaddr = ntohl(sip->sin_addr.s_addr);
	ci = startuploader(ii, myaddr, ntohl(cip->sin_addr.s_addr),
			   isize, mtime, (int)timo, &rv);
	if (ci == NULL) {
		msg->body.putreply.error = rv;
		goto reply;
	}

	in.s_addr = htonl(ci->addr);
1290 1291
	FrisLog("%s: started uploader on %s:%d (pid %d, timo %ds)",
		imageid, inet_ntoa(in), ci->port, ci->pid, ci->timeout);
1292
	if (debug)
1293 1294
		FrisLog("  uid: %d, gids: %s",
			ci->uid, gidstr(ci->ngids, ci->gids));
1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305

	/*
	 * Watch for an immediate death so we don't tell our client
	 * an uploader is running when it really isn't.
	 *
	 * XXX what is the right response? Right now we just tell
	 * them to try again and hope the problem is transient.
	 */
	sleep(2);
	if (reapchildren(ci->pid, &rv)) {
		msg->body.putreply.error = MS_ERROR_TRYAGAIN;
1306 1307 1308
		FrisLog("%s: uploader immediately exited (stat=0x%x), "
			"telling client to try again!",
			imageid, rv);
1309 1310 1311 1312 1313 1314 1315 1316 1317
		goto reply;
	}

	msg->body.putreply.addr = htonl(ci->servaddr);
	msg->body.putreply.port = htons(ci->port);

 reply:
	msg->body.putreply.error = htons(msg->body.putreply.error);
	if (debug) {
1318 1319 1320 1321 1322 1323 1324 1325
		FrisInfo("%s reply: sigtype=%d, sig=0x%08x..., "
			 "size=%x/%x, maxsize=%x/%x",
			 op, ntohs(msg->body.putreply.sigtype),
			 ntohl(*(uint32_t *)msg->body.putreply.signature),
			 ntohl(msg->body.putreply.hisize),
			 ntohl(msg->body.putreply.losize),
			 ntohl(msg->body.putreply.himaxsize),
			 ntohl(msg->body.putreply.lomaxsize));
1326 1327 1328
	}
	len = sizeof msg->hdr + sizeof msg->body.putreply;
	if (!MsgSend(sock, msg, len, 10))
1329 1330
		FrisError("%s: could not send reply",
			  inet_ntoa(cip->sin_addr));
1331 1332 1333 1334
	if (ii)
		free_imageinfo(ii);
}

1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354
static void
handle_request(int sock)
{
	MasterMsg_t msg;
	int cc;
	struct sockaddr_in me, you;
	socklen_t len;

	len = sizeof me;
	if (getsockname(sock, (struct sockaddr *)&me, &len) < 0) {
		perror("getsockname");
		return;
	}
	len = sizeof you;
	if (getpeername(sock, (struct sockaddr *)&you, &len) < 0) {
		perror("getpeername");
		return;
	}

	cc = read(sock, &msg, sizeof msg);
1355
	if (cc < sizeof msg.hdr) {
1356 1357 1358
		if (cc < 0)
			perror("request message failed");
		else
1359
			FrisError("request message too small");
1360 1361
		return;
	}
1362
	switch (ntohl(msg.hdr.type)) {
1363
	case MS_MSGTYPE_GETREQUEST:
1364
		if (cc < sizeof msg.hdr + sizeof msg.body.getrequest)
1365
			FrisError("GET request message too small");
1366 1367 1368
		else
			handle_get(sock, &me, &you, &msg);
		break;
1369 1370
	case MS_MSGTYPE_PUTREQUEST:
		if (cc < sizeof msg.hdr + sizeof msg.body.putrequest)
1371
			FrisError("PUT request message too small");
1372 1373 1374
		else
			handle_put(sock, &me, &you, &msg);
		break;
1375
	default:
1376
		FrisError("unrecognized message type %d", ntohl(msg.hdr.type));
1377 1378 1379 1380 1381 1382 1383
		break;
	}
}

static void
usage(void)
{
1384
	fprintf(stderr, "mfrisbeed [-ADRd] [-X method] [-I imagedir] [-S parentIP] [-P parentport] [-p port]\n");
1385
	fprintf(stderr, "Basic:\n");
1386
	fprintf(stderr, "  -C <style>  configuration style: emulab, file, or null\n");
1387
	fprintf(stderr, "  -O <str>    configuration options, style-specific\n");
1388
	fprintf(stderr, "  -I <dir>    default directory where images are stored\n");
1389 1390 1391
	fprintf(stderr, "  -x <methods> transfer methods to allow from clients: ucast, mcast, bcast or any\n");
	fprintf(stderr, "  -X <method> transfer method to request from parent\n");
	fprintf(stderr, "  -p <port>   port to listen on\n");
1392 1393
	fprintf(stderr, "Debug:\n");
	fprintf(stderr, "  -d          debug mode; does not daemonize\n");
1394
	fprintf(stderr, "  -D          force daemonizing even with debug\n");
1395 1396 1397 1398 1399 1400
	fprintf(stderr, "Proxying:\n");
	fprintf(stderr, "  -S <parent> parent name or IP\n");
	fprintf(stderr, "  -P <pport>  parent port to contact\n");
	fprintf(stderr, "  -A          pass on authentication info from our child to parent\n");
	fprintf(stderr, "  -M          act as a strict mirror for our parent\n");
	fprintf(stderr, "  -R          redirect child to parent if local image not available\n");
1401 1402 1403 1404 1405 1406 1407
	exit(-1);
}

static void
get_options(int argc, char **argv)
{
	int ch;
1408
	int forcedaemonize = 0;
1409

1410
	while ((ch = getopt(argc, argv, "AC:O:DI:MRX:x:S:P:p:i:dhQ:")) != -1)
1411
		switch(ch) {
1412 1413 1414
		case 'A':
			usechildauth = 1;
			break;
1415
		case 'C':
1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426
			if (strcmp(optarg, "emulab") == 0 ||
			    strcmp(optarg, "file") == 0 ||
			    strcmp(optarg, "null") == 0)
				configstyle = optarg;
			else {
				fprintf(stderr,
					"-C should specify one: "
					"'emulab', 'file', 'null'\n");
				exit(1);
			}
			break;
1427 1428 1429
		case 'O':
			configopts = optarg;
			break;
1430
		case 'x':
1431
		case 'X':
1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449
		{
			char *ostr, *str, *cp;
			int nm = 0;

			str = ostr = strdup(optarg);
			while ((cp = strsep(&str, ",")) != NULL) {
				if (strcmp(cp, "ucast") == 0)
					nm |= MS_METHOD_UNICAST;
				else if (strcmp(cp, "mcast") == 0)
					nm |= MS_METHOD_MULTICAST;
				else if (strcmp(cp, "bcast") == 0)
					nm |= MS_METHOD_BROADCAST;
				else if (strcmp(cp, "any") == 0)
					nm = MS_METHOD_ANY;
			}
			free(ostr);
			if (nm == 0) {
				fprintf(stderr,
1450 1451 1452
					"-%c should specify one or more of: "
					"'ucast', 'mcast', 'bcast', 'any'\n",
					ch);
1453 1454
				exit(1);
			}
1455 1456 1457 1458
			if (ch == 'x')
				onlymethods = nm;
			else
				parentmethods = nm;