tmcd.c 326 KB
Newer Older
Leigh Stoller's avatar
Leigh Stoller committed
1
/*
2
 * Copyright (c) 2000-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/>.
 * 
 * }}}
Leigh Stoller's avatar
Leigh Stoller committed
22 23
 */

24 25 26
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
Mike Hibler's avatar
Mike Hibler committed
27
#include <arpa/inet.h>
28
#include <netdb.h>
Mike Hibler's avatar
Mike Hibler committed
29
#include <ctype.h>
30
#include <stdio.h>
Mike Hibler's avatar
Mike Hibler committed
31
#include <stdlib.h>
32
#include <errno.h>
Mike Hibler's avatar
Mike Hibler committed
33 34
#include <string.h>
#include <unistd.h>
35 36 37 38
#include <syslog.h>
#include <signal.h>
#include <stdarg.h>
#include <assert.h>
39
#include <sys/wait.h>
40
#include <sys/fcntl.h>
41 42
#include <sys/syscall.h>
#include <sys/stat.h>
43
#include <sys/param.h>
44
#include <paths.h>
Austin Clements's avatar
Austin Clements committed
45
#include <setjmp.h>
46 47
#include <pwd.h>
#include <grp.h>
48
#include <mysql/mysql.h>
49
#include "tmcd.h"
50
#include "config.h"
51 52
#include "ssl.h"
#include "log.h"
53
#include "tbdefs.h"
54
#include "bsdefs.h"
Leigh Stoller's avatar
Leigh Stoller committed
55 56
#include "bootwhat.h"
#include "bootinfo.h"
57

58 59 60 61
#ifdef EVENTSYS
#include "event.h"
#endif

62 63 64
/* XXX: Not sure this is okay! */
#include "tpm.h"

65 66 67
/*
 * XXX This needs to be localized!
 */
68 69 70
#define FSPROJDIR	FSNODE ":" FSDIR_PROJ
#define FSGROUPDIR	FSNODE ":" FSDIR_GROUPS
#define FSUSERDIR	FSNODE ":" FSDIR_USERS
71 72 73
#ifdef  FSDIR_SHARE
#define FSSHAREDIR	FSNODE ":" FSDIR_SHARE
#endif
74 75 76
#ifdef  FSDIR_SCRATCH
#define FSSCRATCHDIR	FSNODE ":" FSDIR_SCRATCH
#endif
77 78
/* XXX InstaGeni Rack Hack. Hack until I decide on a better way */
#define FSJAILIP       "172.17.253.254"
79 80 81 82 83
#define PROJDIR		PROJROOT_DIR
#define GROUPDIR	GROUPSROOT_DIR
#define USERDIR		USERSROOT_DIR
#define SCRATCHDIR	SCRATCHROOT_DIR
#define SHAREDIR	SHAREROOT_DIR
84
#define NETBEDDIR	"/netbed"
85
#define PLISALIVELOGDIR "/usr/testbed/log/plabisalive"
86 87
#define RELOADPID	"emulab-ops"
#define RELOADEID	"reloading"
88
#define FSHOSTID	"/usr/testbed/etc/fshostid"
89
#define DOTSFS		".sfs"
90 91
#define RUNASUSER	"nobody"
#define RUNASGROUP	"nobody"
92
#define NTPSERVER       "ntp1"
93
#define PROTOUSER	"elabman"
94 95 96 97
#define PRIVKEY_LEN	128
#define URN_LEN		128
#define XSTRINGIFY(s)   STRINGIFY(s)
#define STRINGIFY(s)	#s
98

99 100 101 102 103
/* XXX backward compat */
#ifndef TBCOREDIR
#define	TBCOREDIR	TBROOT "/tmp"
#endif

104 105 106 107
/* socket read/write timeouts in ms */
#define READTIMO	3000
#define WRITETIMO	3000

108
#define TESTMODE
109 110 111 112 113 114 115 116 117 118 119 120
#define DEFAULTNETMASK	"255.255.255.0"
/* This can be tossed once all the changes are in place */
static char *
CHECKMASK(char *arg)
{
	if (arg && arg[0])
		return arg;

	error("No netmask defined!\n");
	return DEFAULTNETMASK;
}
/* #define CHECKMASK(arg)  ((arg) && (arg[0]) ? (arg) : DEFAULTNETMASK) */
121

122 123 124
#define DISKTYPE	"ad"
#define DISKNUM		0

125 126
/* Compiled in slothd parameters
 *
127
 * 1 - reg_interval  2 - agg_interval  3 - load_thresh
128 129 130 131
 * 4 - expt_thresh   5 - ctl_thresh
 */
#define SDPARAMS        "reg=300 agg=5 load=1 expt=5 ctl=1000"

132 133 134 135 136 137 138 139 140 141
/*
 * Compiled in tarball/rpm param.
 * Should come out of the DB.
 */
#ifdef SPEWFROMOPS
#define TARRPM_SERVER	USERNODE
#else
#define TARRPM_SERVER	BOSSNODE
#endif

142 143 144 145 146 147
#ifdef IMAGEPROVENANCE
#define WITHPROVENANCE	1
#else
#define WITHPROVENANCE	0
#endif

148 149 150 151 152 153 154
#ifdef WITHZFS
#undef WITHZFS
#define WITHZFS	1
#else
#define WITHZFS	0
#endif

155 156
/* Defined in configure and passed in via the makefile */
#define DBNAME_SIZE	64
Austin Clements's avatar
Austin Clements committed
157
#define HOSTID_SIZE	(32+64)
158 159
#define DEFAULT_DBNAME	TBDBNAME

Robert P Ricci's avatar
Robert P Ricci committed
160 161 162 163
/* For secure disk loading */
#define SECURELOAD_OPMODE "SECURELOAD"
#define SECURELOAD_STATE  "RELOADSETUP"

164 165 166
/* Our answer to "geni-get --version" */
#define GENI_VERSION "1"

167 168 169 170 171 172 173 174
/* Taint support */
#define TB_TAINTSTATE_BLACKBOX  1
#define TB_TAINTSTATE_USERONLY  2
#define TB_TAINTSTATE_DANGEROUS 4
#define HAS_ANY_TAINTS(tset, tcheck) (tset & tcheck)
#define HAS_ALL_TAINTS(tset, tcheck) ((tset & tcheck) == tcheck)
#define HAS_TAINT(tset, tcheck) HAS_ALL_TAINTS(tset, tcheck)

175 176 177 178 179 180 181
typedef struct {
	char pid[TBDB_FLEN_PID];
	char gid[TBDB_FLEN_GID];
	char name[TBDB_FLEN_IMAGENAME];
	char version[11]; /* Max size of a 32-bit int in string form. */
} imstrings_t;

182
int		debug = 0;
183
static int	verbose = 0;
184
static int	insecure = 0;
185
static int	byteswritten = 0;
Leigh Stoller's avatar
Leigh Stoller committed
186
static char	pidfile[MAXPATHLEN];
187
static char     dbname[DBNAME_SIZE];
188
static struct in_addr myipaddr;
189
static struct in_addr cnet, cmask, jnet, jmask;
Austin Clements's avatar
Austin Clements committed
190
static char	fshostid[HOSTID_SIZE];
Leigh Stoller's avatar
Leigh Stoller committed
191
static int	nodeidtoexp(char *nodeid, char *pid, char *eid, char *gid);
192 193
static void	tcpserver(int sock, int portnum);
static void	udpserver(int sock, int portnum);
194
static int      handle_request(int, struct sockaddr_in *, char *, int);
195
static int      checkcerts(char*);
196
static int	makesockets(int portnum, int *udpsockp, int *tcpsockp);
Mike Hibler's avatar
Mike Hibler committed
197 198
int		client_writeback(int sock, void *buf, int len, int tcp);
void		client_writeback_done(int sock, struct sockaddr_in *client);
199 200
MYSQL_RES *	mydb_query(char *query, int ncols, ...);
int		mydb_update(char *query, ...);
201
static int	safesymlink(char *name1, char *name2);
202
static int      getImageInfo(char *path, char *nodeid, char *pid, char *imagename,
203
			     unsigned int *mtime, off_t *isize);
204
static int	getrandomchars(char *buf, int len);
205

206 207 208 209
/* socket timeouts */
static int	readtimo = READTIMO;
static int	writetimo = WRITETIMO;

210
/* thread support */
211
#define MAXCHILDREN	20
212
#define MINCHILDREN	8
213
static int	numchildren;
214 215 216 217
static int	maxchildren       = 13;
static int      num_udpservers    = 3;
static int      num_altudpservers = 1;
static int      num_alttcpservers = 1;
218
static int	mypid;
219
static volatile int killme;
220

221 222 223 224 225
/* Output macro to check for string overflow */
#define OUTPUT(buf, size, format...) \
({ \
	int __count__ = snprintf((buf), (size), ##format); \
        \
226
        if (__count__ >= (size)) { \
227 228 229 230 231 232
		error("Not enough room in output buffer! line %d.\n", __LINE__);\
		return 1; \
	} \
	__count__; \
})

233 234 235 236 237
/*
 * This structure is passed to each request function. The intent is to
 * reduce the number of DB queries per request to a minimum.
 */
typedef struct {
Leigh Stoller's avatar
Leigh Stoller committed
238
	struct in_addr  client;
239 240 241
	int		allocated;
	int		jailflag;
	int		isvnode;
242
	int		asvnode;
243
	int		issubnode;
244
	int		islocal;
245
	int		isdedicatedwa;
246
	int		iscontrol;
247
	int		isplabdslice;
248
	int		isplabsvc;
249
	int		elab_in_elab;
250
        int		singlenet;	  /* Modifier for elab_in_elab */
251
	int		update_accounts;
252
	int		exptidx;
253 254 255
	int		creator_idx;
	int		swapper_idx;
	int		swapper_isadmin;
256
        int		genisliver_idx;
257
        int		geniflags;
258
        int		nonfsmounts;
259
	unsigned int    taintstates;
260 261
	char		nodeid[TBDB_FLEN_NODEID];
	char		vnodeid[TBDB_FLEN_NODEID];
Leigh Stoller's avatar
Leigh Stoller committed
262
	char		pnodeid[TBDB_FLEN_NODEID]; /* XXX */
263 264 265 266 267
	char		pid[TBDB_FLEN_PID];
	char		eid[TBDB_FLEN_EID];
	char		gid[TBDB_FLEN_GID];
	char		nickname[TBDB_FLEN_VNAME];
	char		type[TBDB_FLEN_NODETYPE];
268
	char		class[TBDB_FLEN_NODECLASS];
269 270
        char		ptype[TBDB_FLEN_NODETYPE];	/* Of physnode */
	char		pclass[TBDB_FLEN_NODECLASS];	/* Of physnode */
271 272
	char		creator[TBDB_FLEN_UID];
	char		swapper[TBDB_FLEN_UID];
273
	char		syncserver[TBDB_FLEN_VNAME];	/* The vname */
274
	char		keyhash[TBDB_FLEN_PRIVKEY];
275
	char		eventkey[TBDB_FLEN_PRIVKEY];
276
	char		sfshostid[TBDB_FLEN_SFSHOSTID];
277
	char		testdb[TBDB_FLEN_TINYTEXT];
278
	char		sharing_mode[TBDB_FLEN_TINYTEXT];
279
	char		erole[TBDB_FLEN_TINYTEXT];
280
	char            privkey[PRIVKEY_LEN+1];
281
	char		nodeuuid[TBDB_FLEN_UUID];
282 283
        /* This key is a replacement for privkey, on protogeni resources */
	char            external_key[PRIVKEY_LEN+1];
284
} tmcdreq_t;
285
static int	iptonodeid(struct in_addr, tmcdreq_t *, char*);
286
static int	checkdbredirect(tmcdreq_t *);
287
static int      sendstoreconf(int sock, int tcp, tmcdreq_t *reqp, char *bscmd, 
288
			      char *vname, int dopersist);
289
static int      get_imagestrings(tmcdreq_t *reqp, imstrings_t *imstrings);
290

291 292 293 294 295
#ifdef EVENTSYS
int			myevent_send(address_tuple_t address);
static event_handle_t	event_handle = NULL;
#endif

296 297 298
/*
 * Commands we support.
 */
299 300
#define COMMAND_PROTOTYPE(x) \
	static int \
301
	x(int sock, tmcdreq_t *reqp, char *rdata, int tcp, int vers)
302 303

COMMAND_PROTOTYPE(doreboot);
304
COMMAND_PROTOTYPE(donodeid);
305
COMMAND_PROTOTYPE(donodeuuid);
David Johnson's avatar
David Johnson committed
306
COMMAND_PROTOTYPE(domanifest);
307 308 309
COMMAND_PROTOTYPE(dostatus);
COMMAND_PROTOTYPE(doifconfig);
COMMAND_PROTOTYPE(doaccounts);
310
COMMAND_PROTOTYPE(dobridges);
311
COMMAND_PROTOTYPE(dodelay);
312
COMMAND_PROTOTYPE(dolinkdelay);
313 314 315 316
COMMAND_PROTOTYPE(dohosts);
COMMAND_PROTOTYPE(dorpms);
COMMAND_PROTOTYPE(dodeltas);
COMMAND_PROTOTYPE(dotarballs);
317
COMMAND_PROTOTYPE(doblobs);
318 319 320 321
COMMAND_PROTOTYPE(dostartcmd);
COMMAND_PROTOTYPE(dostartstat);
COMMAND_PROTOTYPE(doready);
COMMAND_PROTOTYPE(doreadycount);
322
COMMAND_PROTOTYPE(dostorageconfig);
323
COMMAND_PROTOTYPE(domounts);
Austin Clements's avatar
Austin Clements committed
324
COMMAND_PROTOTYPE(dosfshostid);
325 326 327 328 329 330 331
COMMAND_PROTOTYPE(doloadinfo);
COMMAND_PROTOTYPE(doreset);
COMMAND_PROTOTYPE(dorouting);
COMMAND_PROTOTYPE(dotrafgens);
COMMAND_PROTOTYPE(donseconfigs);
COMMAND_PROTOTYPE(dostate);
COMMAND_PROTOTYPE(docreator);
332
COMMAND_PROTOTYPE(dotunnels);
333
COMMAND_PROTOTYPE(dovnodelist);
334
COMMAND_PROTOTYPE(dosubnodelist);
335
COMMAND_PROTOTYPE(doisalive);
336
COMMAND_PROTOTYPE(doipodinfo);
337 338
COMMAND_PROTOTYPE(dontpinfo);
COMMAND_PROTOTYPE(dontpdrift);
339
COMMAND_PROTOTYPE(dojailconfig);
340
COMMAND_PROTOTYPE(doplabconfig);
341 342
COMMAND_PROTOTYPE(dosubconfig);
COMMAND_PROTOTYPE(doixpconfig);
343
COMMAND_PROTOTYPE(doslothdparams);
344
COMMAND_PROTOTYPE(doprogagents);
345
COMMAND_PROTOTYPE(dosyncserver);
346
COMMAND_PROTOTYPE(dokeyhash);
347
COMMAND_PROTOTYPE(doeventkey);
348
COMMAND_PROTOTYPE(dofullconfig);
349 350
COMMAND_PROTOTYPE(doroutelist);
COMMAND_PROTOTYPE(dorole);
351
COMMAND_PROTOTYPE(dorusage);
352
COMMAND_PROTOTYPE(dodoginfo);
353
COMMAND_PROTOTYPE(dohostkeys);
Mike Hibler's avatar
Mike Hibler committed
354
COMMAND_PROTOTYPE(dotmcctest);
355
COMMAND_PROTOTYPE(dofwinfo);
356
COMMAND_PROTOTYPE(dohostinfo);
357
COMMAND_PROTOTYPE(doemulabconfig);
358
COMMAND_PROTOTYPE(doeplabconfig);
359
COMMAND_PROTOTYPE(dolocalize);
360
COMMAND_PROTOTYPE(dorootpswd);
361 362
COMMAND_PROTOTYPE(dobooterrno);
COMMAND_PROTOTYPE(dobootlog);
363
COMMAND_PROTOTYPE(dobattery);
364
COMMAND_PROTOTYPE(dotopomap);
365
COMMAND_PROTOTYPE(douserenv);
366 367
COMMAND_PROTOTYPE(dotiptunnels);
COMMAND_PROTOTYPE(dorelayconfig);
368
COMMAND_PROTOTYPE(dotraceconfig);
369
COMMAND_PROTOTYPE(doltmap);
370
COMMAND_PROTOTYPE(doltpmap);
371
COMMAND_PROTOTYPE(doelvindport);
372
COMMAND_PROTOTYPE(doplabeventkeys);
373
COMMAND_PROTOTYPE(dointfcmap);
374
COMMAND_PROTOTYPE(domotelog);
375
COMMAND_PROTOTYPE(doportregister);
Leigh Stoller's avatar
Leigh Stoller committed
376
COMMAND_PROTOTYPE(dobootwhat);
377 378
COMMAND_PROTOTYPE(dotpmblob);
COMMAND_PROTOTYPE(dotpmpubkey);
379
COMMAND_PROTOTYPE(dotpmdummy);
380
COMMAND_PROTOTYPE(dodhcpdconf);
381
COMMAND_PROTOTYPE(dosecurestate);
382
COMMAND_PROTOTYPE(doquoteprep);
383
COMMAND_PROTOTYPE(doimagekey);
384
COMMAND_PROTOTYPE(donodeattributes);
385
COMMAND_PROTOTYPE(dodisks);
386
COMMAND_PROTOTYPE(doarpinfo);
387
COMMAND_PROTOTYPE(dohwinfo);
388
COMMAND_PROTOTYPE(dotiplineinfo);
389
COMMAND_PROTOTYPE(doimageid);
390 391 392 393 394 395 396 397 398 399 400 401
#if PROTOGENI_SUPPORT
COMMAND_PROTOTYPE(dogeniclientid);
COMMAND_PROTOTYPE(dogenisliceurn);
COMMAND_PROTOTYPE(dogenisliceemail);
COMMAND_PROTOTYPE(dogeniuserurn);
COMMAND_PROTOTYPE(dogeniuseremail);
COMMAND_PROTOTYPE(dogenigeniuser);
COMMAND_PROTOTYPE(dogenimanifest);
COMMAND_PROTOTYPE(dogenicontrolmac);
COMMAND_PROTOTYPE(dogeniversion);
COMMAND_PROTOTYPE(dogenigetversion);
COMMAND_PROTOTYPE(dogenisliverstatus);
402 403
COMMAND_PROTOTYPE(dogenistatus);
COMMAND_PROTOTYPE(dogenicommands);
404
COMMAND_PROTOTYPE(dogeniall);
405
COMMAND_PROTOTYPE(dogeniinvalid);
406
#endif
407

408 409
/*
 * The fullconfig slot determines what routines get called when pushing
410
 * out a full configuration. Physnodes get slightly different
411
 * than vnodes, and at some point we might want to distinguish different
412 413 414 415 416
 * types of vnodes (jailed, plab).
 */
#define FULLCONFIG_NONE		0x0
#define FULLCONFIG_PHYS		0x1
#define FULLCONFIG_VIRT		0x2
417 418 419 420 421
#define FULLCONFIG_ALL		(FULLCONFIG_PHYS|FULLCONFIG_VIRT)

/*
 * Flags encode a few other random properties of commands
 */
422 423 424 425 426
#define F_REMUDP	0x01	/* remote nodes can request using UDP */
#define F_MINLOG	0x02	/* record minimal logging info normally */
#define F_MAXLOG	0x04	/* record maximal logging info normally */
#define F_ALLOCATED	0x08	/* node must be allocated to make call */
#define F_REMNOSSL	0x10	/* remote nodes can request without SSL */
427
#define F_REMREQSSL	0x20	/* remote nodes must connect with SSL */
428
#define F_REQTPM	0x40	/* require TPM on client */
429

430 431
struct command {
	char	*cmdname;
432
	int	fullconfig;
433
	int	flags;
434
	int    (*func)(int, tmcdreq_t *, char *, int, int);
435
} command_array[] = {
436 437
	{ "reboot",	  FULLCONFIG_NONE, 0, doreboot },
	{ "nodeid",	  FULLCONFIG_ALL,  0, donodeid },
438
	{ "nodeuuid",	  FULLCONFIG_ALL,  0, donodeuuid },
David Johnson's avatar
David Johnson committed
439
	{ "manifest",	  FULLCONFIG_ALL,  0, domanifest },
440
	{ "status",	  FULLCONFIG_NONE, 0, dostatus },
441
	{ "ifconfig",	  FULLCONFIG_ALL,  F_ALLOCATED, doifconfig },
442
	{ "accounts",	  FULLCONFIG_ALL,  F_REMREQSSL, doaccounts },
443
	{ "delay",	  FULLCONFIG_ALL,  F_ALLOCATED, dodelay },
444
	{ "bridges",	  FULLCONFIG_ALL,  F_ALLOCATED, dobridges },
445 446 447 448 449
	{ "linkdelay",	  FULLCONFIG_ALL,  F_ALLOCATED, dolinkdelay },
	{ "hostnames",	  FULLCONFIG_NONE, F_ALLOCATED, dohosts },
	{ "rpms",	  FULLCONFIG_ALL,  F_ALLOCATED, dorpms },
	{ "deltas",	  FULLCONFIG_NONE, F_ALLOCATED, dodeltas },
	{ "tarballs",	  FULLCONFIG_ALL,  F_ALLOCATED, dotarballs },
450
	{ "blobs",	  FULLCONFIG_ALL,  F_ALLOCATED, doblobs },
451 452
	{ "startupcmd",	  FULLCONFIG_ALL,  F_ALLOCATED, dostartcmd },
	{ "startstatus",  FULLCONFIG_NONE, F_ALLOCATED, dostartstat }, /* Before startstat*/
453
	{ "startstat",	  FULLCONFIG_NONE, 0, dostartstat },
454 455
	{ "readycount",   FULLCONFIG_NONE, F_ALLOCATED, doreadycount },
	{ "ready",	  FULLCONFIG_NONE, F_ALLOCATED, doready },
456
	{ "storageconfig", FULLCONFIG_ALL, F_ALLOCATED, dostorageconfig},
457 458
	{ "mounts",	  FULLCONFIG_ALL,  F_ALLOCATED, domounts },
	{ "sfshostid",	  FULLCONFIG_NONE, F_ALLOCATED, dosfshostid },
459 460
	{ "loadinfo",	  FULLCONFIG_NONE, 0, doloadinfo},
	{ "reset",	  FULLCONFIG_NONE, 0, doreset},
461 462 463 464
	{ "routing",	  FULLCONFIG_ALL,  F_ALLOCATED, dorouting},
	{ "trafgens",	  FULLCONFIG_ALL,  F_ALLOCATED, dotrafgens},
	{ "nseconfigs",	  FULLCONFIG_ALL,  F_ALLOCATED, donseconfigs},
	{ "creator",	  FULLCONFIG_ALL,  F_ALLOCATED, docreator},
465
	{ "state",	  FULLCONFIG_NONE, 0, dostate},
466
	{ "tunnels",	  FULLCONFIG_ALL,  F_ALLOCATED, dotunnels},
467
	{ "vnodelist",	  FULLCONFIG_PHYS, 0, dovnodelist},
468
	{ "subnodelist",  FULLCONFIG_PHYS, 0, dosubnodelist},
469
	{ "isalive",	  FULLCONFIG_NONE, F_REMUDP|F_MINLOG, doisalive},
470
	{ "ipodinfo",	  FULLCONFIG_PHYS, 0, doipodinfo},
471 472
	{ "ntpinfo",	  FULLCONFIG_PHYS, 0, dontpinfo},
	{ "ntpdrift",	  FULLCONFIG_NONE, 0, dontpdrift},
473 474
	{ "jailconfig",	  FULLCONFIG_VIRT, F_ALLOCATED, dojailconfig},
	{ "plabconfig",	  FULLCONFIG_VIRT, F_ALLOCATED, doplabconfig},
475
	{ "subconfig",	  FULLCONFIG_NONE, 0, dosubconfig},
476
        { "sdparams",     FULLCONFIG_PHYS, 0, doslothdparams},
477 478
        { "programs",     FULLCONFIG_ALL,  F_ALLOCATED, doprogagents},
        { "syncserver",   FULLCONFIG_ALL,  F_ALLOCATED, dosyncserver},
479
        { "keyhash",      FULLCONFIG_ALL,  F_ALLOCATED|F_REMREQSSL, dokeyhash},
480
        { "eventkey",     FULLCONFIG_ALL,  F_ALLOCATED|F_REMREQSSL, doeventkey},
481 482 483
        { "fullconfig",   FULLCONFIG_NONE, F_ALLOCATED, dofullconfig},
        { "routelist",	  FULLCONFIG_PHYS, F_ALLOCATED, doroutelist},
        { "role",	  FULLCONFIG_PHYS, F_ALLOCATED, dorole},
484
        { "rusage",	  FULLCONFIG_NONE, F_REMUDP|F_MINLOG, dorusage},
485
        { "watchdoginfo", FULLCONFIG_ALL,  F_REMUDP|F_MINLOG, dodoginfo},
486
        { "hostkeys",     FULLCONFIG_NONE, 0, dohostkeys},
Mike Hibler's avatar
Mike Hibler committed
487
        { "tmcctest",     FULLCONFIG_NONE, F_MINLOG, dotmcctest},
488
        { "firewallinfo", FULLCONFIG_ALL,  0, dofwinfo},
489
        { "hostinfo",     FULLCONFIG_NONE, 0, dohostinfo},
490
	{ "emulabconfig", FULLCONFIG_NONE, F_ALLOCATED, doemulabconfig},
491
	{ "eplabconfig",  FULLCONFIG_NONE, F_ALLOCATED, doeplabconfig},
492
	{ "localization", FULLCONFIG_PHYS, 0, dolocalize},
493
	{ "rootpswd",     FULLCONFIG_NONE, F_REMREQSSL, dorootpswd},
494 495
	{ "booterrno",    FULLCONFIG_NONE, 0, dobooterrno},
	{ "bootlog",      FULLCONFIG_NONE, 0, dobootlog},
496
	{ "battery",      FULLCONFIG_NONE, F_REMUDP|F_MINLOG, dobattery},
497
	{ "topomap",      FULLCONFIG_NONE, F_MINLOG|F_ALLOCATED, dotopomap},
498
	{ "userenv",      FULLCONFIG_ALL,  F_ALLOCATED, douserenv},
499
	{ "tiptunnels",	  FULLCONFIG_ALL,  F_ALLOCATED, dotiptunnels},
500
	{ "traceinfo",	  FULLCONFIG_ALL,  F_ALLOCATED, dotraceconfig },
501
	{ "ltmap",        FULLCONFIG_NONE, F_MINLOG|F_ALLOCATED, doltmap},
502
	{ "ltpmap",       FULLCONFIG_NONE, F_MINLOG|F_ALLOCATED, doltpmap},
503
	{ "elvindport",   FULLCONFIG_NONE, 0, doelvindport},
504
	{ "plabeventkeys",FULLCONFIG_NONE, F_REMREQSSL, doplabeventkeys},
505
	{ "intfcmap",     FULLCONFIG_NONE, 0, dointfcmap},
506 507
	{ "motelog",      FULLCONFIG_ALL,  F_ALLOCATED, domotelog},
	{ "portregister", FULLCONFIG_NONE, F_REMNOSSL, doportregister},
Leigh Stoller's avatar
Leigh Stoller committed
508
	{ "bootwhat",	  FULLCONFIG_NONE, 0, dobootwhat },
509 510
	{ "tpmblob",	  FULLCONFIG_ALL, 0, dotpmblob },
	{ "tpmpubkey",	  FULLCONFIG_ALL, 0, dotpmpubkey },
511
	{ "tpmdummy",	  FULLCONFIG_ALL, F_REQTPM, dotpmdummy },
512
	{ "dhcpdconf",	  FULLCONFIG_ALL, 0, dodhcpdconf },
513 514
	{ "securestate",  FULLCONFIG_NONE, F_REMREQSSL, dosecurestate},
	{ "quoteprep",    FULLCONFIG_NONE, F_REMREQSSL, doquoteprep},
515
	{ "imagekey",     FULLCONFIG_NONE, F_REQTPM, doimagekey},
516
	{ "nodeattributes", FULLCONFIG_ALL, 0, donodeattributes},
517
	{ "disks",	  FULLCONFIG_ALL, 0, dodisks},
518
	{ "arpinfo",	  FULLCONFIG_NONE, 0, doarpinfo},
519
	{ "hwinfo",	  FULLCONFIG_NONE, 0, dohwinfo},
520
	{ "tiplineinfo",  FULLCONFIG_NONE,  F_ALLOCATED, dotiplineinfo},
521
	{ "imageinfo",      FULLCONFIG_NONE,  F_ALLOCATED, doimageid},
522 523 524 525 526 527 528 529 530 531 532 533 534
#if PROTOGENI_SUPPORT
	{ "geni_client_id", FULLCONFIG_NONE, 0, dogeniclientid },
	{ "geni_slice_urn", FULLCONFIG_NONE, 0, dogenisliceurn },
	{ "geni_slice_email", FULLCONFIG_NONE, 0, dogenisliceemail },
	{ "geni_user_urn", FULLCONFIG_NONE, 0, dogeniuserurn },
	{ "geni_user_email", FULLCONFIG_NONE, 0, dogeniuseremail },
	/* Yes, "geni_user" is a stupid name.  Wasn't my idea. */
	{ "geni_geni_user", FULLCONFIG_NONE, 0, dogenigeniuser },
	{ "geni_manifest", FULLCONFIG_NONE, 0, dogenimanifest },
	{ "geni_control_mac", FULLCONFIG_NONE, 0, dogenicontrolmac },
	{ "geni_version", FULLCONFIG_NONE, 0, dogeniversion },
	{ "geni_getversion", FULLCONFIG_NONE, 0, dogenigetversion },
	{ "geni_sliverstatus", FULLCONFIG_NONE, 0, dogenisliverstatus },
535 536
	{ "geni_status", FULLCONFIG_NONE, 0, dogenistatus },
	{ "geni_commands", FULLCONFIG_NONE, 0, dogenicommands },
537
	{ "geni_all",     FULLCONFIG_NONE, 0, dogeniall },
538 539 540
	/* A rather ugly hack to avoid making error handling a special case.
	   THIS MUST BE THE LAST ENTRY IN THE ARRAY! */
	{ "geni_invalid", FULLCONFIG_NONE, 0, dogeniinvalid }
541
#endif
542 543 544
};
static int numcommands = sizeof(command_array)/sizeof(struct command);

545
char *usagestr =
546 547 548
 "usage: tmcd [-d] [-p #]\n"
 " -d              Turn on debugging. Multiple -d options increase output\n"
 " -p portnum	   Specify a port number to listen on\n"
549
 " -c num	   Specify number of servers (must be %d <= x <= %d)\n"
550
 " -v              More verbose logging\n"
551
 " -i ipaddr       Sets the boss IP addr to return (for multi-homed servers)\n"
552 553 554 555 556
 "\n";

void
usage()
{
557
	fprintf(stderr, usagestr, MINCHILDREN, MAXCHILDREN);
558 559 560
	exit(1);
}

561 562 563 564 565
static void
cleanup()
{
	signal(SIGHUP, SIG_IGN);
	killme = 1;
566
	killpg(0, SIGHUP);
Leigh Stoller's avatar
Leigh Stoller committed
567
	unlink(pidfile);
568 569
}

570 571 572 573
static void
setverbose(int sig)
{
	signal(sig, SIG_IGN);
574

575 576 577 578
	if (sig == SIGUSR1)
		verbose = 1;
	else
		verbose = 0;
579 580
	info("verbose logging turned %s\n", verbose ? "on" : "off");

581 582 583 584 585 586
	/* Just the parent sends this */
	if (numchildren)
		killpg(0, sig);
	signal(sig, setverbose);
}

Mike Hibler's avatar
Mike Hibler committed
587 588
int
main(int argc, char **argv)
589
{
590
	int			tcpsock, udpsock, i, ch;
591 592 593
	int			alttcpsock, altudpsock;
	int			status, pid;
	int			portnum = TBSERVER_PORT;
594 595
	FILE			*fp;
	char			buf[BUFSIZ];
596
	struct hostent		*he;
597
	extern char		build_info[];
598 599 600 601 602
	int			server_counts[4]; /* udp,tcp,altudp,alttcp */
	struct {
		int	pid;
		int	which;
	} servers[MAXCHILDREN];
603

604
	while ((ch = getopt(argc, argv, "dp:c:Xvi:")) != -1)
605 606 607 608 609 610
		switch(ch) {
		case 'p':
			portnum = atoi(optarg);
			break;
		case 'd':
			debug++;
611
			break;
612 613 614
		case 'c':
			maxchildren = atoi(optarg);
			break;
615 616 617
		case 'X':
			insecure = 1;
			break;
618 619 620
		case 'v':
			verbose++;
			break;
621 622 623 624 625 626 627
		case 'i':
			if (inet_aton(optarg, &myipaddr) == 0) {
				fprintf(stderr, "invalid IP address %s\n",
					optarg);
				usage();
			}
			break;
628 629 630 631 632 633 634 635 636 637
		case 'h':
		case '?':
		default:
			usage();
		}
	argc -= optind;
	argv += optind;

	if (argc)
		usage();
638 639
	if (maxchildren < MINCHILDREN || maxchildren > MAXCHILDREN)
		usage();
640

641 642 643 644 645 646
#ifdef  WITHSSL
	if (tmcd_server_sslinit()) {
		error("SSL init failed!\n");
		exit(1);
	}
#endif
647
	if (debug)
648 649 650
		loginit(0, 0);
	else {
		/* Become a daemon */
651 652 653 654 655
		if (chdir(TBCOREDIR)) {
			daemon(0, 0);
		} else {
			daemon(1, 0);
		}
656 657 658 659
		loginit(1, "tmcd");
	}
	info("daemon starting (version %d)\n", CURRENT_VERSION);
	info("%s\n", build_info);
Mike Hibler's avatar
Mike Hibler committed
660

Austin Clements's avatar
Austin Clements committed
661 662 663 664 665 666 667 668 669 670 671 672 673 674
	/*
	 * Get FS's SFS hostid
	 * XXX This approach is somewhat kludgy
	 */
	strcpy(fshostid, "");
	if (access(FSHOSTID,R_OK) == 0) {
		fp = fopen(FSHOSTID, "r");
		if (!fp) {
			error("Failed to get FS's hostid");
		}
		else {
			fgets(fshostid, HOSTID_SIZE, fp);
			if (rindex(fshostid, '\n')) {
				*rindex(fshostid, '\n') = 0;
675 676 677
				if (debug) {
				    info("fshostid: %s\n", fshostid);
				}
Austin Clements's avatar
Austin Clements committed
678 679 680 681 682 683 684 685
			}
			else {
				error("fshostid from %s may be corrupt: %s",
				      FSHOSTID, fshostid);
			}
			fclose(fp);
		}
	}
686

687 688 689
	/*
	 * Grab our IP for security check below.
	 */
690
	if (myipaddr.s_addr == 0) {
691
#ifdef	LBS
692
		strcpy(buf, BOSSNODE);
693
#else
694 695
		if (gethostname(buf, sizeof(buf)) < 0)
			pfatal("getting hostname");
696
#endif
697 698 699 700 701 702
		if ((he = gethostbyname(buf)) == NULL) {
			error("Could not get IP (%s) - %s\n",
			      buf, hstrerror(h_errno));
			exit(1);
		}
		memcpy((char *)&myipaddr, he->h_addr, he->h_length);
703 704
	}

705
	/*
706
	 * If we were given a port on the command line, don't open the
707 708 709 710
	 * alternate ports
	 */
	if (portnum != TBSERVER_PORT) {
	    if (makesockets(portnum, &udpsock, &tcpsock) < 0) {
711 712
		error("Could not make sockets!");
		exit(1);
713
	    }
714
	    num_alttcpservers = num_altudpservers = 0;
715 716 717 718 719 720
	} else {
	    if (makesockets(portnum, &udpsock, &tcpsock) < 0 ||
		makesockets(TBSERVER_PORT2, &altudpsock, &alttcpsock) < 0) {
		    error("Could not make sockets!");
		    exit(1);
	    }
721 722
	}

723 724 725 726 727 728 729 730 731 732 733 734 735
	/*
	 * Get control net info into a usable form.
	 */
	if (!inet_aton(CONTROL_NETWORK, &cnet) ||
	    !inet_aton(CONTROL_NETMASK, &cmask) ||
	    !inet_aton(JAILIPBASE, &jnet) ||
	    !inet_aton(JAILIPMASK, &jmask)) {
		error("Could not convert control net addrs/masks");
		exit(1);
	}
	cnet.s_addr &= cmask.s_addr;
	jnet.s_addr &= jmask.s_addr;

736 737 738
	signal(SIGTERM, cleanup);
	signal(SIGINT, cleanup);
	signal(SIGHUP, cleanup);
739 740
	signal(SIGUSR1, setverbose);
	signal(SIGUSR2, setverbose);
741 742 743 744

	/*
	 * Stash the pid away.
	 */
745
	mypid = getpid();
Leigh Stoller's avatar
Leigh Stoller committed
746 747
	sprintf(pidfile, "%s/tmcd.pid", _PATH_VARRUN);
	fp = fopen(pidfile, "w");
748
	if (fp != NULL) {
749
		fprintf(fp, "%d\n", mypid);
750 751 752
		(void) fclose(fp);
	}

753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785
	/*
	 * Change to non-root user!
	 */
	if (geteuid() == 0) {
		struct passwd	*pw;
		uid_t		uid;
		gid_t		gid;

		/*
		 * Must be a valid user of course.
		 */
		if ((pw = getpwnam(RUNASUSER)) == NULL) {
			error("invalid user: %s", RUNASUSER);
			exit(1);
		}
		uid = pw->pw_uid;
		gid = pw->pw_gid;

		if (setgroups(1, &gid)) {
			errorc("setgroups");
			exit(1);
		}
		if (setgid(gid)) {
			errorc("setgid");
			exit(1);
		}
		if (setuid(uid)) {
			errorc("setuid");
			exit(1);
		}
		info("Flipped to user/group %d/%d\n", uid, gid);
	}

786 787
	/*
	 * Now fork a set of children to handle requests. We keep the
788 789 790 791 792 793 794
	 * pool at a set level. There are 4 types of servers, each getting
	 * a different number of servers. We do it this cause otherwise
	 * we have to deal with the select storm problem; a bunch of processes
	 * select on the same set of file descriptors, and all get woken up
	 * when something comes in, then all read from the socket but only
	 * one gets it and the others go back to sleep. There are various ways
	 * to deal with this problem, but all of them are a lot more code!
795
	 */
796 797 798 799 800 801
	server_counts[0] = num_udpservers;
	server_counts[1] = num_altudpservers;
	server_counts[2] = num_alttcpservers;
	server_counts[3] = maxchildren -
		(num_udpservers + num_altudpservers + num_altudpservers);
	bzero(servers, sizeof(servers));
802

803 804
	while (1) {
		while (!killme && numchildren < maxchildren) {
805
			int which = 3;
806

807 808 809 810 811 812 813 814
			/*
			 * Find which kind of server is short one.
			 */
			for (i = 0; i < 4; i++) {
				if (server_counts[i]) {
					which = i;
					break;
				}
815
			}
816

817 818 819 820 821
			if ((pid = fork()) < 0) {
				errorc("forking server");
				goto done;
			}
			if (pid) {
822 823 824 825 826 827 828 829 830 831
				server_counts[which]--;
				/*
				 * Find free slot
				 */
				for (i = 0; i < maxchildren; i++) {
					if (!servers[i].pid)
						break;
				}
				servers[i].pid   = pid;
				servers[i].which = which;
832 833 834
				numchildren++;
				continue;
			}
835 836 837
			/* Poor way of knowing parent/child */
			numchildren = 0;
			mypid = getpid();
838

839 840 841 842
			/* Child does useful work! Never Returns! */
			signal(SIGTERM, SIG_DFL);
			signal(SIGINT, SIG_DFL);
			signal(SIGHUP, SIG_DFL);
843

844
			switch (which) {
845
			case 0: udpserver(udpsock, portnum);
846
				break;
847
			case 1: udpserver(altudpsock, TBSERVER_PORT2);
848
				break;
849
			case 2: tcpserver(alttcpsock, TBSERVER_PORT2);
850
				break;
851
			case 3: tcpserver(tcpsock, portnum);
852 853 854 855 856 857 858 859 860 861 862 863
				break;
			}
			exit(-1);
		}

		/*
		 * Parent waits.
		 */
		pid = waitpid(-1, &status, 0);
		if (pid < 0) {
			errorc("waitpid failed");
			continue;
864
		}
865 866 867
		if (WIFSIGNALED(status)) {
			error("server %d exited with signal %d!\n",
			      pid, WTERMSIG(status));
868
		}
869 870
		else if (WIFEXITED(status)) {
			error("server %d exited with status %d!\n",
871
			      pid, WEXITSTATUS(status));
872
		}
873
		numchildren--;
874 875 876 877 878 879 880 881 882 883

		/*
		 * Figure out which and what kind of server it was that died.
		 */
		for (i = 0; i < maxchildren; i++) {
			if (servers[i].pid == pid) {
				servers[i].pid = 0;
				server_counts[servers[i].which]++;
				break;
			}
884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901
		}
		if (killme && !numchildren)
			break;
	}
 done:
	CLOSE(tcpsock);
	close(udpsock);
	info("daemon terminating\n");
	exit(0);
}

/*
 * Create sockets on specified port.
 */
static int
makesockets(int portnum, int *udpsockp, int *tcpsockp)
{
	struct sockaddr_in	name;
Mike Hibler's avatar
Mike Hibler committed
902 903
	socklen_t		length;
	int			i, udpsock, tcpsock;
904

Mike Hibler's avatar
Mike Hibler committed
905
	/*
906
	 * Setup TCP socket for incoming connections.
Mike Hibler's avatar
Mike Hibler committed
907 908
	 */

909
	/* Create socket from which to read. */
Mike Hibler's avatar
Mike Hibler committed
910 911
	tcpsock = socket(AF_INET, SOCK_STREAM, 0);
	if (tcpsock < 0) {
912
		pfatal("opening stream socket");
913 914
	}

915
	i = 1;
Mike Hibler's avatar
Mike Hibler committed
916
	if (setsockopt(tcpsock, SOL_SOCKET, SO_REUSEADDR,
917
		       (char *)&i, sizeof(i)) < 0)
918
		pwarning("setsockopt(SO_REUSEADDR)");;
919

920 921 922
	/* Create name. */
	name.sin_family = AF_INET;
	name.sin_addr.s_addr = INADDR_ANY;
923
	name.sin_port = htons((u_short) portnum);
Mike Hibler's avatar
Mike Hibler committed
924
	if (bind(tcpsock, (struct sockaddr *) &name, sizeof(name))) {
925
		pfatal("binding stream socket");
926 927 928
	}
	/* Find assigned port value and print it out. */
	length = sizeof(name);
Mike Hibler's avatar
Mike Hibler committed
929
	if (getsockname(tcpsock, (struct sockaddr *) &name, &length)) {
930
		pfatal("getsockname");
931
	}
932
	if (listen(tcpsock, 128) < 0) {
933
		pfatal("listen");
934
	}
935
	info("listening on TCP port %d\n", ntohs(name.sin_port));
936

Mike Hibler's avatar
Mike Hibler committed
937 938 939 940 941 942 943
	/*
	 * Setup UDP socket
	 */

	/* Create socket from which to read. */
	udpsock = socket(AF_INET, SOCK_DGRAM, 0);
	if (udpsock < 0) {
944
		pfatal("opening dgram socket");
Mike Hibler's avatar
Mike Hibler committed
945 946 947 948 949
	}

	i = 1;
	if (setsockopt(udpsock, SOL_SOCKET, SO_REUSEADDR,
		       (char *)&i, sizeof(i)) < 0)
950
		pwarning("setsockopt(SO_REUSEADDR)");;
951 952 953 954

	i = 128 * 1024;
	if (setsockopt(udpsock, SOL_SOCKET, SO_RCVBUF, &i, sizeof(i)) < 0)
		pwarning("setsockopt(SO_RCVBUF)");
955

Mike Hibler's avatar
Mike Hibler committed
956 957 958
	/* Create name. */
	name.sin_family = AF_INET;
	name.sin_addr.s_addr = INADDR_ANY;
959
	name.sin_port = htons((u_short) portnum);
Mike Hibler's avatar
Mike Hibler committed
960
	if (bind(udpsock, (struct sockaddr *) &name, sizeof(name))) {
961
		pfatal("binding dgram socket");
Mike Hibler's avatar
Mike Hibler committed
962 963 964 965 966
	}

	/* Find assigned port value and print it out. */
	length = sizeof(name);
	if (getsockname(udpsock, (struct sockaddr *) &name, &length)) {
967
		pfatal("getsockname");
Mike Hibler's avatar
Mike Hibler committed
968
	}
969
	info("listening on UDP port %d\n", ntohs(name.sin_port));
970

971 972 973
	*tcpsockp = tcpsock;
	*udpsockp = udpsock;
	return 0;
974
}
975

976
/*
977
 * Listen for UDP requests. This is not a secure channel, and so this should
978 979 980
 * eventually be killed off.
 */
static void
981
udpserver(int sock, int portnum)
982 983 984
{
	char			buf[MYBUFSIZE];
	struct sockaddr_in	client;
Mike Hibler's avatar
Mike Hibler committed
985 986
	socklen_t		length;
	int			cc;
987
	unsigned int		nreq = 0;
988

989 990
	info("udpserver starting: pid=%d sock=%d portnum=%d\n",
	     mypid, sock, portnum);
991 992 993 994 995

	/*
	 * Wait for udp connections.
	 */
	while (1) {
996
		setproctitle("UDP %d: %u done", portnum, nreq);
997
		length = sizeof(client);
998
		cc = recvfrom(sock, buf, sizeof(buf) - 1,
999 1000 1001
			      0, (struct sockaddr *)&client, &length);
		if (cc <= 0) {
			if (cc < 0)
1002 1003
				errorc("Reading UDP request");
			error("UDP Connection aborted\n");
1004
			continue;
1005
		}
1006 1007
		buf[cc] = '\0';
		handle_request(sock, &client, buf, 0);
1008
		nreq++;
1009 1010 1011 1012
	}
	exit(1);
}

1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038
int
tmcd_accept(int sock, struct sockaddr *addr, socklen_t *addrlen, int ms)
{
	int	newsock;

	if ((newsock = accept(sock, addr, addrlen)) < 0)
		return -1;

	/*
	 * Set timeout value to keep us from hanging due to a
	 * malfunctioning or malicious client.
	 */
	if (ms > 0) {
		struct timeval tv;

		tv.tv_sec = ms / 1000;
		tv.tv_usec = (ms % 1000) * 1000;
		if (setsockopt(newsock, SOL_SOCKET, SO_RCVTIMEO,
			       &tv, sizeof(tv)) < 0) {
			errorc("setting SO_RCVTIMEO");
		}
	}

	return newsock;
}

1039
/*
1040
 * Listen for TCP requests.
1041 1042
 */
static void
1043
tcpserver(int sock, int portnum)
1044
{
1045
	char			buf[MAXTMCDPACKET];
1046
	struct sockaddr_in	client;
Mike Hibler's avatar
Mike Hibler committed
1047 1048
	socklen_t		length;
	int			cc, newsock;
1049
	unsigned int		nreq = 0;
1050
	struct timeval		tv;
1051

1052 1053
	info("tcpserver starting: pid=%d sock=%d portnum=%d\n",
	     mypid, sock, portnum);
1054 1055 1056 1057 1058

	/*
	 * Wait for TCP connections.
	 */
	while (1) {
1059
		setproctitle("TCP %d: %u done", portnum, nreq);
1060
		length  = sizeof(client);
1061 1062
		newsock = ACCEPT(sock, (struct sockaddr *)&client, &length,
				 readtimo);
1063
		if (newsock < 0) {
1064
			errorc("accepting TCP connection");
1065
			continue;
1066
		}
Mike Hibler's avatar
Mike Hibler committed
1067

1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081
		/*
		 * Set write timeout value to keep us from hanging due to a
		 * malfunctioning or malicious client.
		 * NOTE: ACCEPT function sets read timeout.
		 */
		tv.tv_sec = writetimo / 1000;
		tv.tv_usec = (writetimo % 1000) * 1000;
		if (setsockopt(newsock, SOL_SOCKET, SO_SNDTIMEO,
			       &tv, sizeof(tv)) < 0) {
			errorc("setting SO_SNDTIMEO");
			CLOSE(newsock);
			continue;
		}

Mike Hibler's avatar
Mike Hibler committed
1082
		/*
1083
		 * Read in the command request.
Mike Hibler's avatar
Mike Hibler committed
1084
		 */
1085
		if ((cc = READ(newsock, buf, sizeof(buf) - 1)) <= 0) {
1086 1087 1088 1089 1090 1091
			if (cc < 0) {
				if (errno == EWOULDBLOCK)
					errorc("Timeout reading TCP request");
				else
					errorc("Error reading TCP request");
			}
1092 1093
			error("TCP connection aborted\n");
			CLOSE(newsock);
1094
			continue;
1095
		}
1096 1097
		buf[cc] = '\0';
		handle_request(newsock, &client, buf, 1);
1098
		CLOSE(newsock);
1099
		nreq++;
1100 1101 1102
	}
	exit(1);
}
Mike Hibler's avatar
Mike Hibler committed
1103

1104 1105 1106
//#define error(x...)	fprintf(stderr, ##x)
//#define info(x...)	fprintf(stderr, ##x)

1107 1108 1109 1110
static int
handle_request(int sock, struct sockaddr_in *client, char *rdata, int istcp)
{
	struct sockaddr_in redirect_client;
1111
	int		   redirect = 0;
1112
	char		   buf[BUFSIZ], *bp, *cp;
1113 1114
	char		   privkeybuf[PRIVKEY_LEN];
	char		   *privkey = (char *) NULL;
1115
	int		   i, overbose = 0, err = 0;
1116
	int		   version = DEFAULT_VERSION;
1117 1118
	tmcdreq_t	   tmcdreq, *reqp = &tmcdreq;

1119
	byteswritten = 0;
1120 1121 1122 1123 1124
#ifdef	WITHSSL
	cp = (istcp ? (isssl ? "SSL" : "TCP") : "UDP");
#else
	cp = (istcp ? "TCP" : "UDP");
#endif
1125
	setproctitle("%s: %s", inet_ntoa(client->sin_addr), cp);
1126

1127 1128 1129 1130
	/*
	 * Init the req structure.
	 */
	bzero(reqp, sizeof(*reqp));
Mike Hibler's avatar
Mike Hibler committed
1131

1132
	/*
1133
	 * Look for special tags.
1134
	 */
1135
	bp = rdata;
1136
	while ((bp = strsep(&rdata, " ")) != NULL) {
1137
		/*
1138
		 * Look for PRIVKEY.
1139
		 */
1140 1141 1142 1143 1144
		if (sscanf(bp, "PRIVKEY=%" XSTRINGIFY(PRIVKEY_LEN) "s", buf)) {
			if (strlen(buf) < 16) {
				info("tmcd client provided short privkey");
				goto skipit;
			}
1145 1146 1147 1148 1149 1150 1151
			for (i = 0; i < strlen(buf); i++){
				if (! isxdigit(buf[i])) {
					info("tmcd client provided invalid "
					     "characters in privkey");
					goto skipit;
				}
			}
1152 1153
			strncpy(privkeybuf, buf, sizeof(privkeybuf));
			privkey = privkeybuf;
1154 1155

			if (debug) {
1156
				info("%s: PRIVKEY %s\n", reqp->nodeid, buf);
1157 1158 1159 1160 1161
			}
			continue;
		}


1162
		/*
1163
		 * Look for VERSION.
1164 1165
		 * Check for clients that are newer than the server
		 * and complain.
1166 1167 1168
		 */
		if (sscanf(bp, "VERSION=%d", &i) == 1) {
			version = i;
1169
			if (version > CURRENT_VERSION) {
Mike Hibler's avatar
Mike Hibler committed
1170 1171
				error("version skew on request from %s: "
				      "server=%d, request=%d, "
1172
				      "old TMCD installed?\n",
Mike Hibler's avatar
Mike Hibler committed
1173
				      inet_ntoa(client->sin_addr),
1174 1175
				      CURRENT_VERSION, version);
			}
1176 1177
			continue;
		}
Mike Hibler's avatar
Mike Hibler committed
1178

1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207
		/*
		 * Look for PROXYFOR, which tells us the client making the
		 * request is doing it on behalf of a container. This is
		 * now used from the XEN dom0 so we can tailor the results
		 * and should eventually replace the VNODEID below so that
		 * we can tell the difference between the host asking for
		 * the clients info and tmcc acting as a proxy (or even the
		 * container asking itself, which can happen too now that
		 * allow routable IPs for containers). 
		 */
		if (sscanf(bp, "PROXYFOR=%30s", buf)) {
			for (i = 0; i < strlen(buf); i++){
				if (! (isalnum(buf[i]) ||
				       buf[i] == '_' || buf[i] == '-')) {
					info("tmcd client provided invalid "
					     "characters in vnodeid");
					goto skipit;
				}
			}
			reqp->isvnode = 1;
			reqp->asvnode = 1;
			strncpy(reqp->vnodeid, buf, sizeof(reqp->vnodeid));

			if (