tmcd.c 347 KB
Newer Older
Leigh B. Stoller's avatar
Leigh B. Stoller committed
1
/*
2
 * Copyright (c) 2000-2016 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 B. Stoller's avatar
Leigh B. 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 B. Stoller's avatar
Leigh B. 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"
Austin Clements's avatar
Austin Clements committed
88
#define FSHOSTID	"/usr/testbed/etc/fshostid"
89
#define DOTSFS		".sfs"
90 91
#define RUNASUSER	"nobody"
#define RUNASGROUP	"nobody"
Mike Hibler's avatar
Mike Hibler committed
92
#ifndef NTPSERVER
93
#define NTPSERVER       "ntp1"
Mike Hibler's avatar
Mike Hibler committed
94
#endif
95
#define PROTOUSER	"elabman"
96 97 98 99
#define PRIVKEY_LEN	128
#define URN_LEN		128
#define XSTRINGIFY(s)   STRINGIFY(s)
#define STRINGIFY(s)	#s
100

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

106 107 108 109
/* socket read/write timeouts in ms */
#define READTIMO	3000
#define WRITETIMO	3000

110
#define TESTMODE
111 112 113 114 115 116 117 118 119 120 121 122
#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) */
123

124 125 126
#define DISKTYPE	"ad"
#define DISKNUM		0

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

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

144 145 146 147 148 149
#ifdef IMAGEPROVENANCE
#define WITHPROVENANCE	1
#else
#define WITHPROVENANCE	0
#endif

150 151 152 153 154 155 156
#ifdef WITHZFS
#undef WITHZFS
#define WITHZFS	1
#else
#define WITHZFS	0
#endif

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

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

166 167 168
/* Our answer to "geni-get --version" */
#define GENI_VERSION "1"

169 170 171 172 173 174 175 176
/* 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)

177 178 179 180 181 182 183
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;

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

208 209 210 211
/* socket timeouts */
static int	readtimo = READTIMO;
static int	writetimo = WRITETIMO;

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

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

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

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

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

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

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

/*
 * Flags encode a few other random properties of commands
 */
429 430 431 432 433
#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 */
434
#define F_REMREQSSL	0x20	/* remote nodes must connect with SSL */
435
#define F_REQTPM	0x40	/* require TPM on client */
436

437 438
struct command {
	char	*cmdname;
439
	int	fullconfig;
440
	int	flags;
441
	int    (*func)(int, tmcdreq_t *, char *, int, int);
442
} command_array[] = {
443 444
	{ "reboot",	  FULLCONFIG_NONE, 0, doreboot },
	{ "nodeid",	  FULLCONFIG_ALL,  0, donodeid },
445
	{ "nodeuuid",	  FULLCONFIG_ALL,  0, donodeuuid },
David Johnson's avatar
David Johnson committed
446
	{ "manifest",	  FULLCONFIG_ALL,  0, domanifest },
447
	{ "status",	  FULLCONFIG_NONE, 0, dostatus },
448
	{ "ifconfig",	  FULLCONFIG_ALL,  F_ALLOCATED, doifconfig },
449
	{ "accounts",	  FULLCONFIG_ALL,  F_REMREQSSL, doaccounts },
450
	{ "delay",	  FULLCONFIG_ALL,  F_ALLOCATED, dodelay },
451
	{ "bridges",	  FULLCONFIG_ALL,  F_ALLOCATED, dobridges },
452 453 454 455 456
	{ "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 },
457
	{ "blobs",	  FULLCONFIG_ALL,  F_ALLOCATED, doblobs },
458 459
	{ "startupcmd",	  FULLCONFIG_ALL,  F_ALLOCATED, dostartcmd },
	{ "startstatus",  FULLCONFIG_NONE, F_ALLOCATED, dostartstat }, /* Before startstat*/
460
	{ "startstat",	  FULLCONFIG_NONE, 0, dostartstat },
461 462
	{ "readycount",   FULLCONFIG_NONE, F_ALLOCATED, doreadycount },
	{ "ready",	  FULLCONFIG_NONE, F_ALLOCATED, doready },
463
	{ "storageconfig", FULLCONFIG_ALL, F_ALLOCATED, dostorageconfig},
464 465
	{ "mounts",	  FULLCONFIG_ALL,  F_ALLOCATED, domounts },
	{ "sfshostid",	  FULLCONFIG_NONE, F_ALLOCATED, dosfshostid },
466 467
	{ "loadinfo",	  FULLCONFIG_NONE, 0, doloadinfo},
	{ "reset",	  FULLCONFIG_NONE, 0, doreset},
468 469 470 471
	{ "routing",	  FULLCONFIG_ALL,  F_ALLOCATED, dorouting},
	{ "trafgens",	  FULLCONFIG_ALL,  F_ALLOCATED, dotrafgens},
	{ "nseconfigs",	  FULLCONFIG_ALL,  F_ALLOCATED, donseconfigs},
	{ "creator",	  FULLCONFIG_ALL,  F_ALLOCATED, docreator},
472
	{ "state",	  FULLCONFIG_NONE, 0, dostate},
473
	{ "tunnels",	  FULLCONFIG_ALL,  F_ALLOCATED, dotunnels},
474
	{ "vnodelist",	  FULLCONFIG_PHYS, 0, dovnodelist},
475
	{ "subnodelist",  FULLCONFIG_PHYS, 0, dosubnodelist},
476
	{ "isalive",	  FULLCONFIG_NONE, F_REMUDP|F_MINLOG, doisalive},
477
	{ "ipodinfo",	  FULLCONFIG_PHYS, 0, doipodinfo},
478 479
	{ "ntpinfo",	  FULLCONFIG_PHYS, 0, dontpinfo},
	{ "ntpdrift",	  FULLCONFIG_NONE, 0, dontpdrift},
480 481
	{ "jailconfig",	  FULLCONFIG_VIRT, F_ALLOCATED, dojailconfig},
	{ "plabconfig",	  FULLCONFIG_VIRT, F_ALLOCATED, doplabconfig},
482
	{ "subconfig",	  FULLCONFIG_NONE, 0, dosubconfig},
483
        { "sdparams",     FULLCONFIG_PHYS, 0, doslothdparams},
484 485
        { "programs",     FULLCONFIG_ALL,  F_ALLOCATED, doprogagents},
        { "syncserver",   FULLCONFIG_ALL,  F_ALLOCATED, dosyncserver},
486
        { "keyhash",      FULLCONFIG_ALL,  F_ALLOCATED|F_REMREQSSL, dokeyhash},
487
        { "eventkey",     FULLCONFIG_ALL,  F_ALLOCATED|F_REMREQSSL, doeventkey},
488 489 490
        { "fullconfig",   FULLCONFIG_NONE, F_ALLOCATED, dofullconfig},
        { "routelist",	  FULLCONFIG_PHYS, F_ALLOCATED, doroutelist},
        { "role",	  FULLCONFIG_PHYS, F_ALLOCATED, dorole},
491
        { "rusage",	  FULLCONFIG_NONE, F_REMUDP|F_MINLOG, dorusage},
492
        { "watchdoginfo", FULLCONFIG_ALL,  F_REMUDP|F_MINLOG, dodoginfo},
493
        { "hostkeys",     FULLCONFIG_NONE, 0, dohostkeys},
Mike Hibler's avatar
Mike Hibler committed
494
        { "tmcctest",     FULLCONFIG_NONE, F_MINLOG, dotmcctest},
495
        { "firewallinfo", FULLCONFIG_ALL,  0, dofwinfo},
496
        { "hostinfo",     FULLCONFIG_NONE, 0, dohostinfo},
497
	{ "emulabconfig", FULLCONFIG_NONE, F_ALLOCATED, doemulabconfig},
498
	{ "eplabconfig",  FULLCONFIG_NONE, F_ALLOCATED, doeplabconfig},
499
	{ "localization", FULLCONFIG_PHYS, 0, dolocalize},
500
	{ "rootpswd",     FULLCONFIG_NONE, F_REMREQSSL, dorootpswd},
501 502
	{ "booterrno",    FULLCONFIG_NONE, 0, dobooterrno},
	{ "bootlog",      FULLCONFIG_NONE, 0, dobootlog},
503
	{ "battery",      FULLCONFIG_NONE, F_REMUDP|F_MINLOG, dobattery},
504
	{ "topomap",      FULLCONFIG_NONE, F_MINLOG|F_ALLOCATED, dotopomap},
505
	{ "userenv",      FULLCONFIG_ALL,  F_ALLOCATED, douserenv},
506
	{ "tiptunnels",	  FULLCONFIG_ALL,  F_ALLOCATED, dotiptunnels},
507
	{ "traceinfo",	  FULLCONFIG_ALL,  F_ALLOCATED, dotraceconfig },
508
	{ "ltmap",        FULLCONFIG_NONE, F_MINLOG|F_ALLOCATED, doltmap},
509
	{ "ltpmap",       FULLCONFIG_NONE, F_MINLOG|F_ALLOCATED, doltpmap},
510
	{ "elvindport",   FULLCONFIG_NONE, 0, doelvindport},
511
	{ "plabeventkeys",FULLCONFIG_NONE, F_REMREQSSL, doplabeventkeys},
512
	{ "intfcmap",     FULLCONFIG_NONE, 0, dointfcmap},
513 514
	{ "motelog",      FULLCONFIG_ALL,  F_ALLOCATED, domotelog},
	{ "portregister", FULLCONFIG_NONE, F_REMNOSSL, doportregister},
Leigh B. Stoller's avatar
Leigh B. Stoller committed
515
	{ "bootwhat",	  FULLCONFIG_NONE, 0, dobootwhat },
516 517
	{ "tpmblob",	  FULLCONFIG_ALL, 0, dotpmblob },
	{ "tpmpubkey",	  FULLCONFIG_ALL, 0, dotpmpubkey },
518
	{ "tpmdummy",	  FULLCONFIG_ALL, F_REQTPM, dotpmdummy },
519
	{ "dhcpdconf",	  FULLCONFIG_ALL, 0, dodhcpdconf },
520 521
	{ "securestate",  FULLCONFIG_NONE, F_REMREQSSL, dosecurestate},
	{ "quoteprep",    FULLCONFIG_NONE, F_REMREQSSL, doquoteprep},
522
	{ "imagekey",     FULLCONFIG_NONE, F_REQTPM, doimagekey},
523
	{ "nodeattributes", FULLCONFIG_ALL, 0, donodeattributes},
524
	{ "disks",	  FULLCONFIG_ALL, 0, dodisks},
525
	{ "arpinfo",	  FULLCONFIG_NONE, 0, doarpinfo},
Mike Hibler's avatar
Mike Hibler committed
526
	{ "hwinfo",	  FULLCONFIG_NONE, 0, dohwinfo},
527
	{ "tiplineinfo",  FULLCONFIG_NONE,  F_ALLOCATED, dotiplineinfo},
528
	{ "imageinfo",      FULLCONFIG_NONE,  F_ALLOCATED, doimageid},
529
	{ "imagesize",   FULLCONFIG_NONE,  F_ALLOCATED, doimagesize},
Kirk Webb's avatar
Kirk Webb committed
530
	{ "pnetnodeattrs", FULLCONFIG_NONE, F_ALLOCATED, dopnetnodeattrs},
531 532 533 534 535 536 537 538 539
#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 },
540 541
	{ "geni_certificate", FULLCONFIG_NONE, 0, dogenicert },
	{ "geni_key", FULLCONFIG_NONE, 0, dogenikey },
542 543 544 545
	{ "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 },
546 547
	{ "geni_status", FULLCONFIG_NONE, 0, dogenistatus },
	{ "geni_commands", FULLCONFIG_NONE, 0, dogenicommands },
548
	{ "geni_all",     FULLCONFIG_NONE, 0, dogeniall },
549
	{ "geni_param",   FULLCONFIG_NONE, 0, dogeniparam },
550 551 552
	/* 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 }
553
#endif
554 555 556
};
static int numcommands = sizeof(command_array)/sizeof(struct command);

557
char *usagestr =
558 559 560
 "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"
561
 " -c num	   Specify number of servers (must be %d <= x <= %d)\n"
562
 " -v              More verbose logging\n"
563
 " -i ipaddr       Sets the boss IP addr to return (for multi-homed servers)\n"
564 565 566 567 568
 "\n";

void
usage()
{
569
	fprintf(stderr, usagestr, MINCHILDREN, MAXCHILDREN);
570 571 572
	exit(1);
}

573 574 575 576 577
static void
cleanup()
{
	signal(SIGHUP, SIG_IGN);
	killme = 1;
578
	killpg(0, SIGHUP);
Leigh B Stoller's avatar
Leigh B Stoller committed
579
	unlink(pidfile);
580 581
}

582 583 584 585
static void
setverbose(int sig)
{
	signal(sig, SIG_IGN);
586

587 588 589 590
	if (sig == SIGUSR1)
		verbose = 1;
	else
		verbose = 0;
591 592
	info("verbose logging turned %s\n", verbose ? "on" : "off");

593 594 595 596 597 598
	/* Just the parent sends this */
	if (numchildren)
		killpg(0, sig);
	signal(sig, setverbose);
}

Mike Hibler's avatar
Mike Hibler committed
599 600
int
main(int argc, char **argv)
601
{
602
	int			tcpsock, udpsock, i, ch;
603 604 605
	int			alttcpsock, altudpsock;
	int			status, pid;
	int			portnum = TBSERVER_PORT;
606 607
	FILE			*fp;
	char			buf[BUFSIZ];
608
	struct hostent		*he;
609
	extern char		build_info[];
610 611 612 613 614
	int			server_counts[4]; /* udp,tcp,altudp,alttcp */
	struct {
		int	pid;
		int	which;
	} servers[MAXCHILDREN];
615

616
	while ((ch = getopt(argc, argv, "dp:c:Xvi:")) != -1)
617 618 619 620 621 622
		switch(ch) {
		case 'p':
			portnum = atoi(optarg);
			break;
		case 'd':
			debug++;
623
			break;
624 625 626
		case 'c':
			maxchildren = atoi(optarg);
			break;
627 628 629
		case 'X':
			insecure = 1;
			break;
630 631 632
		case 'v':
			verbose++;
			break;
633 634 635 636 637 638 639
		case 'i':
			if (inet_aton(optarg, &myipaddr) == 0) {
				fprintf(stderr, "invalid IP address %s\n",
					optarg);
				usage();
			}
			break;
640 641 642 643 644 645 646 647 648 649
		case 'h':
		case '?':
		default:
			usage();
		}
	argc -= optind;
	argv += optind;

	if (argc)
		usage();
650 651
	if (maxchildren < MINCHILDREN || maxchildren > MAXCHILDREN)
		usage();
652

653 654 655 656 657 658
#ifdef  WITHSSL
	if (tmcd_server_sslinit()) {
		error("SSL init failed!\n");
		exit(1);
	}
#endif
659
	if (debug)
660 661 662
		loginit(0, 0);
	else {
		/* Become a daemon */
663 664 665 666 667
		if (chdir(TBCOREDIR)) {
			daemon(0, 0);
		} else {
			daemon(1, 0);
		}
668 669 670 671
		loginit(1, "tmcd");
	}
	info("daemon starting (version %d)\n", CURRENT_VERSION);
	info("%s\n", build_info);
Mike Hibler's avatar
Mike Hibler committed
672

Austin Clements's avatar
Austin Clements committed
673 674 675 676 677 678 679 680 681 682 683 684 685 686
	/*
	 * 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;
687 688 689
				if (debug) {
				    info("fshostid: %s\n", fshostid);
				}
Austin Clements's avatar
Austin Clements committed
690 691 692 693 694 695 696 697
			}
			else {
				error("fshostid from %s may be corrupt: %s",
				      FSHOSTID, fshostid);
			}
			fclose(fp);
		}
	}
698

699 700 701
	/*
	 * Grab our IP for security check below.
	 */
702
	if (myipaddr.s_addr == 0) {
703
#ifdef	LBS
704
		strcpy(buf, BOSSNODE);
705
#else
706 707
		if (gethostname(buf, sizeof(buf)) < 0)
			pfatal("getting hostname");
708
#endif
709 710 711 712 713 714
		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);
715 716
	}

717
	/*
718
	 * If we were given a port on the command line, don't open the
719 720 721 722
	 * alternate ports
	 */
	if (portnum != TBSERVER_PORT) {
	    if (makesockets(portnum, &udpsock, &tcpsock) < 0) {
723 724
		error("Could not make sockets!");
		exit(1);
725
	    }
726
	    num_alttcpservers = num_altudpservers = 0;
727 728 729 730 731 732
	} else {
	    if (makesockets(portnum, &udpsock, &tcpsock) < 0 ||
		makesockets(TBSERVER_PORT2, &altudpsock, &alttcpsock) < 0) {
		    error("Could not make sockets!");
		    exit(1);
	    }
733 734
	}

Mike Hibler's avatar
Mike Hibler committed
735 736 737 738 739 740 741 742 743 744 745 746 747
	/*
	 * 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;

748 749 750
	signal(SIGTERM, cleanup);
	signal(SIGINT, cleanup);
	signal(SIGHUP, cleanup);
751 752
	signal(SIGUSR1, setverbose);
	signal(SIGUSR2, setverbose);
753 754 755 756

	/*
	 * Stash the pid away.
	 */
757
	mypid = getpid();
Leigh B Stoller's avatar
Leigh B Stoller committed
758 759
	sprintf(pidfile, "%s/tmcd.pid", _PATH_VARRUN);
	fp = fopen(pidfile, "w");
760
	if (fp != NULL) {
761
		fprintf(fp, "%d\n", mypid);
762 763 764
		(void) fclose(fp);
	}

765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797
	/*
	 * 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);
	}

798 799
	/*
	 * Now fork a set of children to handle requests. We keep the
800 801 802 803 804 805 806
	 * 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!
807
	 */
808 809 810 811 812 813
	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));
814

815 816
	while (1) {
		while (!killme && numchildren < maxchildren) {
817
			int which = 3;
818

819 820 821 822 823 824 825 826
			/*
			 * Find which kind of server is short one.
			 */
			for (i = 0; i < 4; i++) {
				if (server_counts[i]) {
					which = i;
					break;
				}
827
			}
828

829 830 831 832 833
			if ((pid = fork()) < 0) {
				errorc("forking server");
				goto done;
			}
			if (pid) {
834 835 836 837 838 839 840 841 842 843
				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;
844 845 846
				numchildren++;
				continue;
			}
847 848 849
			/* Poor way of knowing parent/child */
			numchildren = 0;
			mypid = getpid();
850

851 852 853 854
			/* Child does useful work! Never Returns! */
			signal(SIGTERM, SIG_DFL);
			signal(SIGINT, SIG_DFL);
			signal(SIGHUP, SIG_DFL);
855

856
			switch (which) {
857
			case 0: udpserver(udpsock, portnum);
858
				break;
859
			case 1: udpserver(altudpsock, TBSERVER_PORT2);
860
				break;
861
			case 2: tcpserver(alttcpsock, TBSERVER_PORT2);
862
				break;
863
			case 3: tcpserver(tcpsock, portnum);
864 865 866 867 868 869 870 871 872 873 874 875
				break;
			}
			exit(-1);
		}

		/*
		 * Parent waits.
		 */
		pid = waitpid(-1, &status, 0);
		if (pid < 0) {
			errorc("waitpid failed");
			continue;
876
		}
877 878 879
		if (WIFSIGNALED(status)) {
			error("server %d exited with signal %d!\n",
			      pid, WTERMSIG(status));
880
		}
881 882
		else if (WIFEXITED(status)) {
			error("server %d exited with status %d!\n",
883
			      pid, WEXITSTATUS(status));
884
		}
885
		numchildren--;
886 887 888 889 890 891 892 893 894 895

		/*
		 * 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;
			}
896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913
		}
		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
lint  
Mike Hibler committed
914 915
	socklen_t		length;
	int			i, udpsock, tcpsock;
916

Mike Hibler's avatar
Mike Hibler committed
917
	/*
918
	 * Setup TCP socket for incoming connections.
Mike Hibler's avatar
Mike Hibler committed
919 920
	 */

921
	/* Create socket from which to read. */
Mike Hibler's avatar
Mike Hibler committed
922 923
	tcpsock = socket(AF_INET, SOCK_STREAM, 0);
	if (tcpsock < 0) {
924
		pfatal("opening stream socket");
925 926
	}

927
	i = 1;
Mike Hibler's avatar
Mike Hibler committed
928
	if (setsockopt(tcpsock, SOL_SOCKET, SO_REUSEADDR,
929
		       (char *)&i, sizeof(i)) < 0)
930
		pwarning("setsockopt(SO_REUSEADDR)");;
931

932 933 934
	/* Create name. */
	name.sin_family = AF_INET;
	name.sin_addr.s_addr = INADDR_ANY;
935
	name.sin_port = htons((u_short) portnum);
Mike Hibler's avatar
Mike Hibler committed
936
	if (bind(tcpsock, (struct sockaddr *) &name, sizeof(name))) {
937
		pfatal("binding stream socket");
938 939 940
	}
	/* Find assigned port value and print it out. */
	length = sizeof(name);
Mike Hibler's avatar
Mike Hibler committed
941
	if (getsockname(tcpsock, (struct sockaddr *) &name, &length)) {
942
		pfatal("getsockname");
943
	}
944
	if (listen(tcpsock, 128) < 0) {
945
		pfatal("listen");
946
	}
947
	info("listening on TCP port %d\n", ntohs(name.sin_port));
948

Mike Hibler's avatar
Mike Hibler committed
949 950 951 952 953 954 955
	/*
	 * Setup UDP socket
	 */

	/* Create socket from which to read. */
	udpsock = socket(AF_INET, SOCK_DGRAM, 0);
	if (udpsock < 0) {
956
		pfatal("opening dgram socket");
Mike Hibler's avatar
Mike Hibler committed
957 958 959 960 961
	}

	i = 1;
	if (setsockopt(udpsock, SOL_SOCKET, SO_REUSEADDR,
		       (char *)&i, sizeof(i)) < 0)
962
		pwarning("setsockopt(SO_REUSEADDR)");;
963 964 965 966

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

Mike Hibler's avatar
Mike Hibler committed
968 969 970
	/* Create name. */
	name.sin_family = AF_INET;
	name.sin_addr.s_addr = INADDR_ANY;
971
	name.sin_port = htons((u_short) portnum);
Mike Hibler's avatar
Mike Hibler committed
972
	if (bind(udpsock, (struct sockaddr *) &name, sizeof(name))) {
973
		pfatal("binding dgram socket");
Mike Hibler's avatar
Mike Hibler committed
974 975 976 977 978
	}

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

983 984 985
	*tcpsockp = tcpsock;
	*udpsockp = udpsock;
	return 0;
986
}
987

988
/*
989
 * Listen for UDP requests. This is not a secure channel, and so this should
990 991 992
 * eventually be killed off.
 */
static void