tmcd.c 315 KB
Newer Older
Leigh B. Stoller's avatar
Leigh B. 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 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>
Leigh B. Stoller's avatar
Leigh B. Stoller committed
40
#include <sys/fcntl.h>
41 42
#include <sys/syscall.h>
#include <sys/stat.h>
Kirk Webb's avatar
 
Kirk Webb committed
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"
Kirk Webb's avatar
 
Kirk Webb committed
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"
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
/* Defined in configure and passed in via the makefile */
#define DBNAME_SIZE	64
Austin Clements's avatar
Austin Clements committed
144
#define HOSTID_SIZE	(32+64)
145 146
#define DEFAULT_DBNAME	TBDBNAME

Robert P Ricci's avatar
Robert P Ricci committed
147 148 149 150
/* For secure disk loading */
#define SECURELOAD_OPMODE "SECURELOAD"
#define SECURELOAD_STATE  "RELOADSETUP"

151 152 153
/* Our answer to "geni-get --version" */
#define GENI_VERSION "1"

154
int		debug = 0;
155
static int	verbose = 0;
156
static int	insecure = 0;
157
static int	byteswritten = 0;
Leigh B Stoller's avatar
Leigh B Stoller committed
158
static char	pidfile[MAXPATHLEN];
159
static char     dbname[DBNAME_SIZE];
160
static struct in_addr myipaddr;
Mike Hibler's avatar
Mike Hibler committed
161
static struct in_addr cnet, cmask, jnet, jmask;
Austin Clements's avatar
Austin Clements committed
162
static char	fshostid[HOSTID_SIZE];
Leigh B. Stoller's avatar
Leigh B. Stoller committed
163
static int	nodeidtoexp(char *nodeid, char *pid, char *eid, char *gid);
164 165
static void	tcpserver(int sock, int portnum);
static void	udpserver(int sock, int portnum);
166
static int      handle_request(int, struct sockaddr_in *, char *, int);
167
static int      checkcerts(char*);
168
static int	makesockets(int portnum, int *udpsockp, int *tcpsockp);
Mike Hibler's avatar
Mike Hibler committed
169 170
int		client_writeback(int sock, void *buf, int len, int tcp);
void		client_writeback_done(int sock, struct sockaddr_in *client);
171 172
MYSQL_RES *	mydb_query(char *query, int ncols, ...);
int		mydb_update(char *query, ...);
173
static int	safesymlink(char *name1, char *name2);
174 175
static int      getImageInfo(char *path, char *nodeid, char *pid, char *imagename,
			     unsigned int *mtime, unsigned int *chunks);
176

177 178 179 180
/* socket timeouts */
static int	readtimo = READTIMO;
static int	writetimo = WRITETIMO;

181
/* thread support */
182
#define MAXCHILDREN	20
183
#define MINCHILDREN	8
184
static int	numchildren;
185 186 187 188
static int	maxchildren       = 13;
static int      num_udpservers    = 3;
static int      num_altudpservers = 1;
static int      num_alttcpservers = 1;
189
static int	mypid;
190
static volatile int killme;
191

192 193 194 195 196
/* Output macro to check for string overflow */
#define OUTPUT(buf, size, format...) \
({ \
	int __count__ = snprintf((buf), (size), ##format); \
        \
197
        if (__count__ >= (size)) { \
198 199 200 201 202 203
		error("Not enough room in output buffer! line %d.\n", __LINE__);\
		return 1; \
	} \
	__count__; \
})

204 205 206 207 208
/*
 * 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
209
	struct in_addr  client;
210 211 212
	int		allocated;
	int		jailflag;
	int		isvnode;
213
	int		asvnode;
214
	int		issubnode;
215
	int		islocal;
216
	int		isdedicatedwa;
217
	int		iscontrol;
218
	int		isplabdslice;
219
	int		isplabsvc;
220
	int		elab_in_elab;
221
        int		singlenet;	  /* Modifier for elab_in_elab */
222
	int		update_accounts;
223
	int		exptidx;
224 225 226
	int		creator_idx;
	int		swapper_idx;
	int		swapper_isadmin;
227
        int		genisliver_idx;
228
        int		geniflags;
229
        int		nonfsmounts;
230 231
	char		nodeid[TBDB_FLEN_NODEID];
	char		vnodeid[TBDB_FLEN_NODEID];
Leigh B. Stoller's avatar
Leigh B. Stoller committed
232
	char		pnodeid[TBDB_FLEN_NODEID]; /* XXX */
233 234 235 236 237
	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];
238
	char		class[TBDB_FLEN_NODECLASS];
239 240
        char		ptype[TBDB_FLEN_NODETYPE];	/* Of physnode */
	char		pclass[TBDB_FLEN_NODECLASS];	/* Of physnode */
241 242
	char		creator[TBDB_FLEN_UID];
	char		swapper[TBDB_FLEN_UID];
243
	char		syncserver[TBDB_FLEN_VNAME];	/* The vname */
244
	char		keyhash[TBDB_FLEN_PRIVKEY];
245
	char		eventkey[TBDB_FLEN_PRIVKEY];
246
	char		sfshostid[TBDB_FLEN_SFSHOSTID];
247
	char		testdb[TBDB_FLEN_TINYTEXT];
248
	char		sharing_mode[TBDB_FLEN_TINYTEXT];
249
	char            privkey[PRIVKEY_LEN+1];
250
	char		nodeuuid[TBDB_FLEN_UUID];
251 252
        /* This key is a replacement for privkey, on protogeni resources */
	char            external_key[PRIVKEY_LEN+1];
253
} tmcdreq_t;
254
static int	iptonodeid(struct in_addr, tmcdreq_t *, char*);
255
static int	checkdbredirect(tmcdreq_t *);
256
static int      sendstoreconf(int sock, int tcp, tmcdreq_t *reqp, char *bscmd, 
257
			      char *vname, int dopersist);
258

259 260 261 262 263
#ifdef EVENTSYS
int			myevent_send(address_tuple_t address);
static event_handle_t	event_handle = NULL;
#endif

264 265 266
/*
 * Commands we support.
 */
267 268
#define COMMAND_PROTOTYPE(x) \
	static int \
269
	x(int sock, tmcdreq_t *reqp, char *rdata, int tcp, int vers)
270 271

COMMAND_PROTOTYPE(doreboot);
272
COMMAND_PROTOTYPE(donodeid);
273
COMMAND_PROTOTYPE(donodeuuid);
David Johnson's avatar
David Johnson committed
274
COMMAND_PROTOTYPE(domanifest);
275 276 277
COMMAND_PROTOTYPE(dostatus);
COMMAND_PROTOTYPE(doifconfig);
COMMAND_PROTOTYPE(doaccounts);
278
COMMAND_PROTOTYPE(dobridges);
279
COMMAND_PROTOTYPE(dodelay);
280
COMMAND_PROTOTYPE(dolinkdelay);
281 282 283 284
COMMAND_PROTOTYPE(dohosts);
COMMAND_PROTOTYPE(dorpms);
COMMAND_PROTOTYPE(dodeltas);
COMMAND_PROTOTYPE(dotarballs);
285
COMMAND_PROTOTYPE(doblobs);
286 287 288 289
COMMAND_PROTOTYPE(dostartcmd);
COMMAND_PROTOTYPE(dostartstat);
COMMAND_PROTOTYPE(doready);
COMMAND_PROTOTYPE(doreadycount);
290
COMMAND_PROTOTYPE(dostorageconfig);
291
COMMAND_PROTOTYPE(domounts);
Austin Clements's avatar
Austin Clements committed
292
COMMAND_PROTOTYPE(dosfshostid);
293 294 295 296 297 298 299
COMMAND_PROTOTYPE(doloadinfo);
COMMAND_PROTOTYPE(doreset);
COMMAND_PROTOTYPE(dorouting);
COMMAND_PROTOTYPE(dotrafgens);
COMMAND_PROTOTYPE(donseconfigs);
COMMAND_PROTOTYPE(dostate);
COMMAND_PROTOTYPE(docreator);
300
COMMAND_PROTOTYPE(dotunnels);
301
COMMAND_PROTOTYPE(dovnodelist);
302
COMMAND_PROTOTYPE(dosubnodelist);
303
COMMAND_PROTOTYPE(doisalive);
304
COMMAND_PROTOTYPE(doipodinfo);
305 306
COMMAND_PROTOTYPE(dontpinfo);
COMMAND_PROTOTYPE(dontpdrift);
307
COMMAND_PROTOTYPE(dojailconfig);
308
COMMAND_PROTOTYPE(doplabconfig);
309 310
COMMAND_PROTOTYPE(dosubconfig);
COMMAND_PROTOTYPE(doixpconfig);
311
COMMAND_PROTOTYPE(doslothdparams);
312
COMMAND_PROTOTYPE(doprogagents);
313
COMMAND_PROTOTYPE(dosyncserver);
314
COMMAND_PROTOTYPE(dokeyhash);
315
COMMAND_PROTOTYPE(doeventkey);
316
COMMAND_PROTOTYPE(dofullconfig);
317 318
COMMAND_PROTOTYPE(doroutelist);
COMMAND_PROTOTYPE(dorole);
319
COMMAND_PROTOTYPE(dorusage);
320
COMMAND_PROTOTYPE(dodoginfo);
Mike Hibler's avatar
Mike Hibler committed
321
COMMAND_PROTOTYPE(dohostkeys);
Mike Hibler's avatar
Mike Hibler committed
322
COMMAND_PROTOTYPE(dotmcctest);
323
COMMAND_PROTOTYPE(dofwinfo);
324
COMMAND_PROTOTYPE(dohostinfo);
325
COMMAND_PROTOTYPE(doemulabconfig);
326
COMMAND_PROTOTYPE(doeplabconfig);
327
COMMAND_PROTOTYPE(dolocalize);
328
COMMAND_PROTOTYPE(dorootpswd);
329 330
COMMAND_PROTOTYPE(dobooterrno);
COMMAND_PROTOTYPE(dobootlog);
Timothy Stack's avatar
 
Timothy Stack committed
331
COMMAND_PROTOTYPE(dobattery);
332
COMMAND_PROTOTYPE(dotopomap);
Timothy Stack's avatar
 
Timothy Stack committed
333
COMMAND_PROTOTYPE(douserenv);
Timothy Stack's avatar
 
Timothy Stack committed
334 335
COMMAND_PROTOTYPE(dotiptunnels);
COMMAND_PROTOTYPE(dorelayconfig);
336
COMMAND_PROTOTYPE(dotraceconfig);
337
COMMAND_PROTOTYPE(doltmap);
338
COMMAND_PROTOTYPE(doltpmap);
Kirk Webb's avatar
 
Kirk Webb committed
339
COMMAND_PROTOTYPE(doelvindport);
Kirk Webb's avatar
 
Kirk Webb committed
340
COMMAND_PROTOTYPE(doplabeventkeys);
341
COMMAND_PROTOTYPE(dointfcmap);
342
COMMAND_PROTOTYPE(domotelog);
343
COMMAND_PROTOTYPE(doportregister);
Leigh B. Stoller's avatar
Leigh B. Stoller committed
344
COMMAND_PROTOTYPE(dobootwhat);
345 346
COMMAND_PROTOTYPE(dotpmblob);
COMMAND_PROTOTYPE(dotpmpubkey);
347
COMMAND_PROTOTYPE(dotpmdummy);
348
COMMAND_PROTOTYPE(dodhcpdconf);
349
COMMAND_PROTOTYPE(dosecurestate);
350
COMMAND_PROTOTYPE(doquoteprep);
351
COMMAND_PROTOTYPE(doimagekey);
352
COMMAND_PROTOTYPE(donodeattributes);
353
COMMAND_PROTOTYPE(dodisks);
354
COMMAND_PROTOTYPE(doarpinfo);
Mike Hibler's avatar
Mike Hibler committed
355
COMMAND_PROTOTYPE(dohwinfo);
356
COMMAND_PROTOTYPE(dotiplineinfo);
357 358 359 360 361 362 363 364 365 366 367 368
#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);
369 370
COMMAND_PROTOTYPE(dogenistatus);
COMMAND_PROTOTYPE(dogenicommands);
371
COMMAND_PROTOTYPE(dogeniall);
372
COMMAND_PROTOTYPE(dogeniinvalid);
373
#endif
374

375 376
/*
 * The fullconfig slot determines what routines get called when pushing
377
 * out a full configuration. Physnodes get slightly different
378
 * than vnodes, and at some point we might want to distinguish different
379 380 381 382 383
 * types of vnodes (jailed, plab).
 */
#define FULLCONFIG_NONE		0x0
#define FULLCONFIG_PHYS		0x1
#define FULLCONFIG_VIRT		0x2
384 385 386 387 388
#define FULLCONFIG_ALL		(FULLCONFIG_PHYS|FULLCONFIG_VIRT)

/*
 * Flags encode a few other random properties of commands
 */
389 390 391 392 393
#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 */
394
#define F_REMREQSSL	0x20	/* remote nodes must connect with SSL */
395
#define F_REQTPM	0x40	/* require TPM on client */
396

397 398
struct command {
	char	*cmdname;
399
	int	fullconfig;
400
	int	flags;
401
	int    (*func)(int, tmcdreq_t *, char *, int, int);
402
} command_array[] = {
403 404
	{ "reboot",	  FULLCONFIG_NONE, 0, doreboot },
	{ "nodeid",	  FULLCONFIG_ALL,  0, donodeid },
405
	{ "nodeuuid",	  FULLCONFIG_ALL,  0, donodeuuid },
David Johnson's avatar
David Johnson committed
406
	{ "manifest",	  FULLCONFIG_ALL,  0, domanifest },
407
	{ "status",	  FULLCONFIG_NONE, 0, dostatus },
408
	{ "ifconfig",	  FULLCONFIG_ALL,  F_ALLOCATED, doifconfig },
409
	{ "accounts",	  FULLCONFIG_ALL,  F_REMREQSSL, doaccounts },
410
	{ "delay",	  FULLCONFIG_ALL,  F_ALLOCATED, dodelay },
411
	{ "bridges",	  FULLCONFIG_ALL,  F_ALLOCATED, dobridges },
412 413 414 415 416
	{ "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 },
417
	{ "blobs",	  FULLCONFIG_ALL,  F_ALLOCATED, doblobs },
418 419
	{ "startupcmd",	  FULLCONFIG_ALL,  F_ALLOCATED, dostartcmd },
	{ "startstatus",  FULLCONFIG_NONE, F_ALLOCATED, dostartstat }, /* Before startstat*/
420
	{ "startstat",	  FULLCONFIG_NONE, 0, dostartstat },
421 422
	{ "readycount",   FULLCONFIG_NONE, F_ALLOCATED, doreadycount },
	{ "ready",	  FULLCONFIG_NONE, F_ALLOCATED, doready },
423
	{ "storageconfig", FULLCONFIG_ALL, F_ALLOCATED, dostorageconfig},
424 425
	{ "mounts",	  FULLCONFIG_ALL,  F_ALLOCATED, domounts },
	{ "sfshostid",	  FULLCONFIG_NONE, F_ALLOCATED, dosfshostid },
426 427
	{ "loadinfo",	  FULLCONFIG_NONE, 0, doloadinfo},
	{ "reset",	  FULLCONFIG_NONE, 0, doreset},
428 429 430 431
	{ "routing",	  FULLCONFIG_ALL,  F_ALLOCATED, dorouting},
	{ "trafgens",	  FULLCONFIG_ALL,  F_ALLOCATED, dotrafgens},
	{ "nseconfigs",	  FULLCONFIG_ALL,  F_ALLOCATED, donseconfigs},
	{ "creator",	  FULLCONFIG_ALL,  F_ALLOCATED, docreator},
432
	{ "state",	  FULLCONFIG_NONE, 0, dostate},
433
	{ "tunnels",	  FULLCONFIG_ALL,  F_ALLOCATED, dotunnels},
434
	{ "vnodelist",	  FULLCONFIG_PHYS, 0, dovnodelist},
Timothy Stack's avatar
 
Timothy Stack committed
435
	{ "subnodelist",  FULLCONFIG_PHYS, 0, dosubnodelist},
436
	{ "isalive",	  FULLCONFIG_NONE, F_REMUDP|F_MINLOG, doisalive},
437
	{ "ipodinfo",	  FULLCONFIG_PHYS, 0, doipodinfo},
438 439
	{ "ntpinfo",	  FULLCONFIG_PHYS, 0, dontpinfo},
	{ "ntpdrift",	  FULLCONFIG_NONE, 0, dontpdrift},
440 441
	{ "jailconfig",	  FULLCONFIG_VIRT, F_ALLOCATED, dojailconfig},
	{ "plabconfig",	  FULLCONFIG_VIRT, F_ALLOCATED, doplabconfig},
Timothy Stack's avatar
 
Timothy Stack committed
442
	{ "subconfig",	  FULLCONFIG_NONE, 0, dosubconfig},
443
        { "sdparams",     FULLCONFIG_PHYS, 0, doslothdparams},
444 445
        { "programs",     FULLCONFIG_ALL,  F_ALLOCATED, doprogagents},
        { "syncserver",   FULLCONFIG_ALL,  F_ALLOCATED, dosyncserver},
446
        { "keyhash",      FULLCONFIG_ALL,  F_ALLOCATED|F_REMREQSSL, dokeyhash},
447
        { "eventkey",     FULLCONFIG_ALL,  F_ALLOCATED|F_REMREQSSL, doeventkey},
448 449 450
        { "fullconfig",   FULLCONFIG_NONE, F_ALLOCATED, dofullconfig},
        { "routelist",	  FULLCONFIG_PHYS, F_ALLOCATED, doroutelist},
        { "role",	  FULLCONFIG_PHYS, F_ALLOCATED, dorole},
451
        { "rusage",	  FULLCONFIG_NONE, F_REMUDP|F_MINLOG, dorusage},
452
        { "watchdoginfo", FULLCONFIG_ALL,  F_REMUDP|F_MINLOG, dodoginfo},
Mike Hibler's avatar
Mike Hibler committed
453
        { "hostkeys",     FULLCONFIG_NONE, 0, dohostkeys},
Mike Hibler's avatar
Mike Hibler committed
454
        { "tmcctest",     FULLCONFIG_NONE, F_MINLOG, dotmcctest},
455
        { "firewallinfo", FULLCONFIG_ALL,  0, dofwinfo},
456
        { "hostinfo",     FULLCONFIG_NONE, 0, dohostinfo},
457
	{ "emulabconfig", FULLCONFIG_NONE, F_ALLOCATED, doemulabconfig},
458
	{ "eplabconfig",  FULLCONFIG_NONE, F_ALLOCATED, doeplabconfig},
459
	{ "localization", FULLCONFIG_PHYS, 0, dolocalize},
460
	{ "rootpswd",     FULLCONFIG_NONE, F_REMREQSSL, dorootpswd},
461 462
	{ "booterrno",    FULLCONFIG_NONE, 0, dobooterrno},
	{ "bootlog",      FULLCONFIG_NONE, 0, dobootlog},
Timothy Stack's avatar
 
Timothy Stack committed
463
	{ "battery",      FULLCONFIG_NONE, F_REMUDP|F_MINLOG, dobattery},
464
	{ "topomap",      FULLCONFIG_NONE, F_MINLOG|F_ALLOCATED, dotopomap},
465
	{ "userenv",      FULLCONFIG_ALL,  F_ALLOCATED, douserenv},
Timothy Stack's avatar
 
Timothy Stack committed
466
	{ "tiptunnels",	  FULLCONFIG_ALL,  F_ALLOCATED, dotiptunnels},
467
	{ "traceinfo",	  FULLCONFIG_ALL,  F_ALLOCATED, dotraceconfig },
Kirk Webb's avatar
 
Kirk Webb committed
468
	{ "ltmap",        FULLCONFIG_NONE, F_MINLOG|F_ALLOCATED, doltmap},
469
	{ "ltpmap",       FULLCONFIG_NONE, F_MINLOG|F_ALLOCATED, doltpmap},
Kirk Webb's avatar
 
Kirk Webb committed
470
	{ "elvindport",   FULLCONFIG_NONE, 0, doelvindport},
471
	{ "plabeventkeys",FULLCONFIG_NONE, F_REMREQSSL, doplabeventkeys},
472
	{ "intfcmap",     FULLCONFIG_NONE, 0, dointfcmap},
473 474
	{ "motelog",      FULLCONFIG_ALL,  F_ALLOCATED, domotelog},
	{ "portregister", FULLCONFIG_NONE, F_REMNOSSL, doportregister},
Leigh B. Stoller's avatar
Leigh B. Stoller committed
475
	{ "bootwhat",	  FULLCONFIG_NONE, 0, dobootwhat },
476 477
	{ "tpmblob",	  FULLCONFIG_ALL, 0, dotpmblob },
	{ "tpmpubkey",	  FULLCONFIG_ALL, 0, dotpmpubkey },
478
	{ "tpmdummy",	  FULLCONFIG_ALL, F_REQTPM, dotpmdummy },
479
	{ "dhcpdconf",	  FULLCONFIG_ALL, 0, dodhcpdconf },
480 481
	{ "securestate",  FULLCONFIG_NONE, F_REMREQSSL, dosecurestate},
	{ "quoteprep",    FULLCONFIG_NONE, F_REMREQSSL, doquoteprep},
482
	{ "imagekey",     FULLCONFIG_NONE, F_REQTPM, doimagekey},
483
	{ "nodeattributes", FULLCONFIG_ALL, 0, donodeattributes},
484
	{ "disks",	  FULLCONFIG_ALL, 0, dodisks},
485
	{ "arpinfo",	  FULLCONFIG_NONE, 0, doarpinfo},
Mike Hibler's avatar
Mike Hibler committed
486
	{ "hwinfo",	  FULLCONFIG_NONE, 0, dohwinfo},
487
	{ "tiplineinfo",  FULLCONFIG_NONE,  F_ALLOCATED, dotiplineinfo},
488 489 490 491 492 493 494 495 496 497 498 499 500
#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 },
501 502
	{ "geni_status", FULLCONFIG_NONE, 0, dogenistatus },
	{ "geni_commands", FULLCONFIG_NONE, 0, dogenicommands },
503
	{ "geni_all",     FULLCONFIG_NONE, 0, dogeniall },
504 505 506
	/* 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 }
507
#endif
508 509 510
};
static int numcommands = sizeof(command_array)/sizeof(struct command);

511
char *usagestr =
512 513 514
 "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"
515
 " -c num	   Specify number of servers (must be %d <= x <= %d)\n"
516
 " -v              More verbose logging\n"
517
 " -i ipaddr       Sets the boss IP addr to return (for multi-homed servers)\n"
518 519 520 521 522
 "\n";

void
usage()
{
523
	fprintf(stderr, usagestr, MINCHILDREN, MAXCHILDREN);
524 525 526
	exit(1);
}

527 528 529 530 531
static void
cleanup()
{
	signal(SIGHUP, SIG_IGN);
	killme = 1;
532
	killpg(0, SIGHUP);
Leigh B Stoller's avatar
Leigh B Stoller committed
533
	unlink(pidfile);
534 535
}

536 537 538 539
static void
setverbose(int sig)
{
	signal(sig, SIG_IGN);
540

541 542 543 544
	if (sig == SIGUSR1)
		verbose = 1;
	else
		verbose = 0;
545 546
	info("verbose logging turned %s\n", verbose ? "on" : "off");

547 548 549 550 551 552
	/* Just the parent sends this */
	if (numchildren)
		killpg(0, sig);
	signal(sig, setverbose);
}

Mike Hibler's avatar
Mike Hibler committed
553 554
int
main(int argc, char **argv)
555
{
556
	int			tcpsock, udpsock, i, ch;
557 558 559
	int			alttcpsock, altudpsock;
	int			status, pid;
	int			portnum = TBSERVER_PORT;
560 561
	FILE			*fp;
	char			buf[BUFSIZ];
562
	struct hostent		*he;
563
	extern char		build_info[];
564 565 566 567 568
	int			server_counts[4]; /* udp,tcp,altudp,alttcp */
	struct {
		int	pid;
		int	which;
	} servers[MAXCHILDREN];
569

570
	while ((ch = getopt(argc, argv, "dp:c:Xvi:")) != -1)
571 572 573 574 575 576
		switch(ch) {
		case 'p':
			portnum = atoi(optarg);
			break;
		case 'd':
			debug++;
577
			break;
578 579 580
		case 'c':
			maxchildren = atoi(optarg);
			break;
581 582 583
		case 'X':
			insecure = 1;
			break;
584 585 586
		case 'v':
			verbose++;
			break;
587 588 589 590 591 592 593
		case 'i':
			if (inet_aton(optarg, &myipaddr) == 0) {
				fprintf(stderr, "invalid IP address %s\n",
					optarg);
				usage();
			}
			break;
594 595 596 597 598 599 600 601 602 603
		case 'h':
		case '?':
		default:
			usage();
		}
	argc -= optind;
	argv += optind;

	if (argc)
		usage();
604 605
	if (maxchildren < MINCHILDREN || maxchildren > MAXCHILDREN)
		usage();
606

607 608 609 610 611 612
#ifdef  WITHSSL
	if (tmcd_server_sslinit()) {
		error("SSL init failed!\n");
		exit(1);
	}
#endif
613
	if (debug)
614 615 616
		loginit(0, 0);
	else {
		/* Become a daemon */
617 618 619 620 621
		if (chdir(TBCOREDIR)) {
			daemon(0, 0);
		} else {
			daemon(1, 0);
		}
622 623 624 625
		loginit(1, "tmcd");
	}
	info("daemon starting (version %d)\n", CURRENT_VERSION);
	info("%s\n", build_info);
Mike Hibler's avatar
Mike Hibler committed
626

Austin Clements's avatar
Austin Clements committed
627 628 629 630 631 632 633 634 635 636 637 638 639 640
	/*
	 * 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;
641 642 643
				if (debug) {
				    info("fshostid: %s\n", fshostid);
				}
Austin Clements's avatar
Austin Clements committed
644 645 646 647 648 649 650 651
			}
			else {
				error("fshostid from %s may be corrupt: %s",
				      FSHOSTID, fshostid);
			}
			fclose(fp);
		}
	}
652

653 654 655
	/*
	 * Grab our IP for security check below.
	 */
656
	if (myipaddr.s_addr == 0) {
657
#ifdef	LBS
658
		strcpy(buf, BOSSNODE);
659
#else
660 661
		if (gethostname(buf, sizeof(buf)) < 0)
			pfatal("getting hostname");
662
#endif
663 664 665 666 667 668
		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);
669 670
	}

671
	/*
672
	 * If we were given a port on the command line, don't open the
673 674 675 676
	 * alternate ports
	 */
	if (portnum != TBSERVER_PORT) {
	    if (makesockets(portnum, &udpsock, &tcpsock) < 0) {
677 678
		error("Could not make sockets!");
		exit(1);
679
	    }
680
	    num_alttcpservers = num_altudpservers = 0;
681 682 683 684 685 686
	} else {
	    if (makesockets(portnum, &udpsock, &tcpsock) < 0 ||
		makesockets(TBSERVER_PORT2, &altudpsock, &alttcpsock) < 0) {
		    error("Could not make sockets!");
		    exit(1);
	    }
687 688
	}

Mike Hibler's avatar
Mike Hibler committed
689 690 691 692 693 694 695 696 697 698 699 700 701
	/*
	 * 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;

702 703 704
	signal(SIGTERM, cleanup);
	signal(SIGINT, cleanup);
	signal(SIGHUP, cleanup);
705 706
	signal(SIGUSR1, setverbose);
	signal(SIGUSR2, setverbose);
707 708 709 710

	/*
	 * Stash the pid away.
	 */
711
	mypid = getpid();
Leigh B Stoller's avatar
Leigh B Stoller committed
712 713
	sprintf(pidfile, "%s/tmcd.pid", _PATH_VARRUN);
	fp = fopen(pidfile, "w");
714
	if (fp != NULL) {
715
		fprintf(fp, "%d\n", mypid);
716 717 718
		(void) fclose(fp);
	}

719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751
	/*
	 * 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);
	}

752 753
	/*
	 * Now fork a set of children to handle requests. We keep the
754 755 756 757 758 759 760
	 * 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!
761
	 */
762 763 764 765 766 767
	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));
768

769 770
	while (1) {
		while (!killme && numchildren < maxchildren) {
771
			int which = 3;
772

773 774 775 776 777 778 779 780
			/*
			 * Find which kind of server is short one.
			 */
			for (i = 0; i < 4; i++) {
				if (server_counts[i]) {
					which = i;
					break;
				}
781
			}
782

783 784 785 786 787
			if ((pid = fork()) < 0) {
				errorc("forking server");
				goto done;
			}
			if (pid) {
788 789 790 791 792 793 794 795 796 797
				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;
798 799 800
				numchildren++;
				continue;
			}
801 802 803
			/* Poor way of knowing parent/child */
			numchildren = 0;
			mypid = getpid();
804

805 806 807 808
			/* Child does useful work! Never Returns! */
			signal(SIGTERM, SIG_DFL);
			signal(SIGINT, SIG_DFL);
			signal(SIGHUP, SIG_DFL);
809

810
			switch (which) {
811
			case 0: udpserver(udpsock, portnum);
812
				break;
813
			case 1: udpserver(altudpsock, TBSERVER_PORT2);
814
				break;
815
			case 2: tcpserver(alttcpsock, TBSERVER_PORT2);
816
				break;
817
			case 3: tcpserver(tcpsock, portnum);
818 819 820 821 822 823 824 825 826 827 828 829
				break;
			}
			exit(-1);
		}

		/*
		 * Parent waits.
		 */
		pid = waitpid(-1, &status, 0);
		if (pid < 0) {
			errorc("waitpid failed");
			continue;
830
		}
831 832 833
		if (WIFSIGNALED(status)) {
			error("server %d exited with signal %d!\n",
			      pid, WTERMSIG(status));
834
		}
835 836
		else if (WIFEXITED(status)) {
			error("server %d exited with status %d!\n",
837
			      pid, WEXITSTATUS(status));
838
		}
839
		numchildren--;
840 841 842 843 844 845 846 847 848 849

		/*
		 * 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;
			}
850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867
		}
		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
868 869
	socklen_t		length;
	int			i, udpsock, tcpsock;
870

Mike Hibler's avatar
Mike Hibler committed
871
	/*
872
	 * Setup TCP socket for incoming connections.
Mike Hibler's avatar
Mike Hibler committed
873 874
	 */

875
	/* Create socket from which to read. */
Mike Hibler's avatar
Mike Hibler committed
876 877
	tcpsock = socket(AF_INET, SOCK_STREAM, 0);
	if (tcpsock < 0) {
878
		pfatal("opening stream socket");
879 880
	}

881
	i = 1;
Mike Hibler's avatar
Mike Hibler committed
882
	if (setsockopt(tcpsock, SOL_SOCKET, SO_REUSEADDR,
883
		       (char *)&i, sizeof(i)) < 0)
884
		pwarning("setsockopt(SO_REUSEADDR)");;
885

886 887 888
	/* Create name. */
	name.sin_family = AF_INET;
	name.sin_addr.s_addr = INADDR_ANY;
889
	name.sin_port = htons((u_short) portnum);
Mike Hibler's avatar
Mike Hibler committed
890
	if (bind(tcpsock, (struct sockaddr *) &name, sizeof(name))) {
891
		pfatal("binding stream socket");
892 893 894
	}
	/* Find assigned port value and print it out. */
	length = sizeof(name);
Mike Hibler's avatar
Mike Hibler committed
895
	if (getsockname(tcpsock, (struct sockaddr *) &name, &length)) {
896
		pfatal("getsockname");
897
	}
898
	if (listen(tcpsock, 128) < 0) {
899
		pfatal("listen");
900
	}
901
	info("listening on TCP port %d\n", ntohs(name.sin_port));
902

Mike Hibler's avatar
Mike Hibler committed
903 904 905 906 907 908 909
	/*
	 * Setup UDP socket
	 */

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

	i = 1;
	if (setsockopt(udpsock, SOL_SOCKET, SO_REUSEADDR,
		       (char *)&i, sizeof(i)) < 0)
916
		pwarning("setsockopt(SO_REUSEADDR)");;
917 918 919 920

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

Mike Hibler's avatar
Mike Hibler committed
922 923 924
	/* Create name. */
	name.sin_family = AF_INET;
	name.sin_addr.s_addr = INADDR_ANY;
925
	name.sin_port = htons((u_short) portnum);
Mike Hibler's avatar
Mike Hibler committed
926
	if (bind(udpsock, (struct sockaddr *) &name, sizeof(name))) {
927
		pfatal("binding dgram socket");
Mike Hibler's avatar
Mike Hibler committed
928 929 930 931 932
	}

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

937 938 939
	*tcpsockp = tcpsock;
	*udpsockp = udpsock;
	return 0;
940
}
941

942
/*
Leigh B. Stoller's avatar