tmcd.c 322 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
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
int		debug = 0;
168
static int	verbose = 0;
169
static int	insecure = 0;
170
static int	byteswritten = 0;
Leigh B Stoller's avatar
Leigh B Stoller committed
171
static char	pidfile[MAXPATHLEN];
172
static char     dbname[DBNAME_SIZE];
173
static struct in_addr myipaddr;
Mike Hibler's avatar
Mike Hibler committed
174
static struct in_addr cnet, cmask, jnet, jmask;
Austin Clements's avatar
Austin Clements committed
175
static char	fshostid[HOSTID_SIZE];
Leigh B. Stoller's avatar
Leigh B. Stoller committed
176
static int	nodeidtoexp(char *nodeid, char *pid, char *eid, char *gid);
177
178
static void	tcpserver(int sock, int portnum);
static void	udpserver(int sock, int portnum);
179
static int      handle_request(int, struct sockaddr_in *, char *, int);
180
static int      checkcerts(char*);
181
static int	makesockets(int portnum, int *udpsockp, int *tcpsockp);
Mike Hibler's avatar
Mike Hibler committed
182
183
int		client_writeback(int sock, void *buf, int len, int tcp);
void		client_writeback_done(int sock, struct sockaddr_in *client);
184
185
MYSQL_RES *	mydb_query(char *query, int ncols, ...);
int		mydb_update(char *query, ...);
186
static int	safesymlink(char *name1, char *name2);
187
static int      getImageInfo(char *path, char *nodeid, char *pid, char *imagename,
188
			     unsigned int *mtime, off_t *isize);
189
static int	getrandomchars(char *buf, int len);
190

191
192
193
194
/* socket timeouts */
static int	readtimo = READTIMO;
static int	writetimo = WRITETIMO;

195
/* thread support */
196
#define MAXCHILDREN	20
197
#define MINCHILDREN	8
198
static int	numchildren;
199
200
201
202
static int	maxchildren       = 13;
static int      num_udpservers    = 3;
static int      num_altudpservers = 1;
static int      num_alttcpservers = 1;
203
static int	mypid;
204
static volatile int killme;
205

206
207
208
209
210
/* Output macro to check for string overflow */
#define OUTPUT(buf, size, format...) \
({ \
	int __count__ = snprintf((buf), (size), ##format); \
        \
211
        if (__count__ >= (size)) { \
212
213
214
215
216
217
		error("Not enough room in output buffer! line %d.\n", __LINE__);\
		return 1; \
	} \
	__count__; \
})

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

274
275
276
277
278
#ifdef EVENTSYS
int			myevent_send(address_tuple_t address);
static event_handle_t	event_handle = NULL;
#endif

279
280
281
/*
 * Commands we support.
 */
282
283
#define COMMAND_PROTOTYPE(x) \
	static int \
284
	x(int sock, tmcdreq_t *reqp, char *rdata, int tcp, int vers)
285
286

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

390
391
/*
 * The fullconfig slot determines what routines get called when pushing
392
 * out a full configuration. Physnodes get slightly different
393
 * than vnodes, and at some point we might want to distinguish different
394
395
396
397
398
 * types of vnodes (jailed, plab).
 */
#define FULLCONFIG_NONE		0x0
#define FULLCONFIG_PHYS		0x1
#define FULLCONFIG_VIRT		0x2
399
400
401
402
403
#define FULLCONFIG_ALL		(FULLCONFIG_PHYS|FULLCONFIG_VIRT)

/*
 * Flags encode a few other random properties of commands
 */
404
405
406
407
408
#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 */
409
#define F_REMREQSSL	0x20	/* remote nodes must connect with SSL */
410
#define F_REQTPM	0x40	/* require TPM on client */
411

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

526
char *usagestr =
527
528
529
 "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"
530
 " -c num	   Specify number of servers (must be %d <= x <= %d)\n"
531
 " -v              More verbose logging\n"
532
 " -i ipaddr       Sets the boss IP addr to return (for multi-homed servers)\n"
533
534
535
536
537
 "\n";

void
usage()
{
538
	fprintf(stderr, usagestr, MINCHILDREN, MAXCHILDREN);
539
540
541
	exit(1);
}

542
543
544
545
546
static void
cleanup()
{
	signal(SIGHUP, SIG_IGN);
	killme = 1;
547
	killpg(0, SIGHUP);
Leigh B Stoller's avatar
Leigh B Stoller committed
548
	unlink(pidfile);
549
550
}

551
552
553
554
static void
setverbose(int sig)
{
	signal(sig, SIG_IGN);
555

556
557
558
559
	if (sig == SIGUSR1)
		verbose = 1;
	else
		verbose = 0;
560
561
	info("verbose logging turned %s\n", verbose ? "on" : "off");

562
563
564
565
566
567
	/* Just the parent sends this */
	if (numchildren)
		killpg(0, sig);
	signal(sig, setverbose);
}

Mike Hibler's avatar
Mike Hibler committed
568
569
int
main(int argc, char **argv)
570
{
571
	int			tcpsock, udpsock, i, ch;
572
573
574
	int			alttcpsock, altudpsock;
	int			status, pid;
	int			portnum = TBSERVER_PORT;
575
576
	FILE			*fp;
	char			buf[BUFSIZ];
577
	struct hostent		*he;
578
	extern char		build_info[];
579
580
581
582
583
	int			server_counts[4]; /* udp,tcp,altudp,alttcp */
	struct {
		int	pid;
		int	which;
	} servers[MAXCHILDREN];
584

585
	while ((ch = getopt(argc, argv, "dp:c:Xvi:")) != -1)
586
587
588
589
590
591
		switch(ch) {
		case 'p':
			portnum = atoi(optarg);
			break;
		case 'd':
			debug++;
592
			break;
593
594
595
		case 'c':
			maxchildren = atoi(optarg);
			break;
596
597
598
		case 'X':
			insecure = 1;
			break;
599
600
601
		case 'v':
			verbose++;
			break;
602
603
604
605
606
607
608
		case 'i':
			if (inet_aton(optarg, &myipaddr) == 0) {
				fprintf(stderr, "invalid IP address %s\n",
					optarg);
				usage();
			}
			break;
609
610
611
612
613
614
615
616
617
618
		case 'h':
		case '?':
		default:
			usage();
		}
	argc -= optind;
	argv += optind;

	if (argc)
		usage();
619
620
	if (maxchildren < MINCHILDREN || maxchildren > MAXCHILDREN)
		usage();
621

622
623
624
625
626
627
#ifdef  WITHSSL
	if (tmcd_server_sslinit()) {
		error("SSL init failed!\n");
		exit(1);
	}
#endif
628
	if (debug)
629
630
631
		loginit(0, 0);
	else {
		/* Become a daemon */
632
633
634
635
636
		if (chdir(TBCOREDIR)) {
			daemon(0, 0);
		} else {
			daemon(1, 0);
		}
637
638
639
640
		loginit(1, "tmcd");
	}
	info("daemon starting (version %d)\n", CURRENT_VERSION);
	info("%s\n", build_info);
Mike Hibler's avatar
Mike Hibler committed
641

Austin Clements's avatar
Austin Clements committed
642
643
644
645
646
647
648
649
650
651
652
653
654
655
	/*
	 * 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;
656
657
658
				if (debug) {
				    info("fshostid: %s\n", fshostid);
				}
Austin Clements's avatar
Austin Clements committed
659
660
661
662
663
664
665
666
			}
			else {
				error("fshostid from %s may be corrupt: %s",
				      FSHOSTID, fshostid);
			}
			fclose(fp);
		}
	}
667

668
669
670
	/*
	 * Grab our IP for security check below.
	 */
671
	if (myipaddr.s_addr == 0) {
672
#ifdef	LBS
673
		strcpy(buf, BOSSNODE);
674
#else
675
676
		if (gethostname(buf, sizeof(buf)) < 0)
			pfatal("getting hostname");
677
#endif
678
679
680
681
682
683
		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);
684
685
	}

686
	/*
687
	 * If we were given a port on the command line, don't open the
688
689
690
691
	 * alternate ports
	 */
	if (portnum != TBSERVER_PORT) {
	    if (makesockets(portnum, &udpsock, &tcpsock) < 0) {
692
693
		error("Could not make sockets!");
		exit(1);
694
	    }
695
	    num_alttcpservers = num_altudpservers = 0;
696
697
698
699
700
701
	} else {
	    if (makesockets(portnum, &udpsock, &tcpsock) < 0 ||
		makesockets(TBSERVER_PORT2, &altudpsock, &alttcpsock) < 0) {
		    error("Could not make sockets!");
		    exit(1);
	    }
702
703
	}

Mike Hibler's avatar
Mike Hibler committed
704
705
706
707
708
709
710
711
712
713
714
715
716
	/*
	 * 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;

717
718
719
	signal(SIGTERM, cleanup);
	signal(SIGINT, cleanup);
	signal(SIGHUP, cleanup);
720
721
	signal(SIGUSR1, setverbose);
	signal(SIGUSR2, setverbose);
722
723
724
725

	/*
	 * Stash the pid away.
	 */
726
	mypid = getpid();
Leigh B Stoller's avatar
Leigh B Stoller committed
727
728
	sprintf(pidfile, "%s/tmcd.pid", _PATH_VARRUN);
	fp = fopen(pidfile, "w");
729
	if (fp != NULL) {
730
		fprintf(fp, "%d\n", mypid);
731
732
733
		(void) fclose(fp);
	}

734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
	/*
	 * 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);
	}

767
768
	/*
	 * Now fork a set of children to handle requests. We keep the
769
770
771
772
773
774
775
	 * 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!
776
	 */
777
778
779
780
781
782
	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));
783

784
785
	while (1) {
		while (!killme && numchildren < maxchildren) {
786
			int which = 3;
787

788
789
790
791
792
793
794
795
			/*
			 * Find which kind of server is short one.
			 */
			for (i = 0; i < 4; i++) {
				if (server_counts[i]) {
					which = i;
					break;
				}
796
			}
797

798
799
800
801
802
			if ((pid = fork()) < 0) {
				errorc("forking server");
				goto done;
			}
			if (pid) {
803
804
805
806
807
808
809
810
811
812
				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;
813
814
815
				numchildren++;
				continue;
			}
816
817
818
			/* Poor way of knowing parent/child */
			numchildren = 0;
			mypid = getpid();
819

820
821
822
823
			/* Child does useful work! Never Returns! */
			signal(SIGTERM, SIG_DFL);
			signal(SIGINT, SIG_DFL);
			signal(SIGHUP, SIG_DFL);
824

825
			switch (which) {
826
			case 0: udpserver(udpsock, portnum);
827
				break;
828
			case 1: udpserver(altudpsock, TBSERVER_PORT2);
829
				break;
830
			case 2: tcpserver(alttcpsock, TBSERVER_PORT2);
831
				break;
832
			case 3: tcpserver(tcpsock, portnum);
833
834
835
836
837
838
839
840
841
842
843
844
				break;
			}
			exit(-1);
		}

		/*
		 * Parent waits.
		 */
		pid = waitpid(-1, &status, 0);
		if (pid < 0) {
			errorc("waitpid failed");
			continue;
845
		}
846
847
848
		if (WIFSIGNALED(status)) {
			error("server %d exited with signal %d!\n",
			      pid, WTERMSIG(status));
849
		}
850
851
		else if (WIFEXITED(status)) {
			error("server %d exited with status %d!\n",
852
			      pid, WEXITSTATUS(status));
853
		}
854
		numchildren--;
855
856
857
858
859
860
861
862
863
864

		/*
		 * 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;
			}
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
		}
		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
883
884
	socklen_t		length;
	int			i, udpsock, tcpsock;
885

Mike Hibler's avatar
Mike Hibler committed
886
	/*
887
	 * Setup TCP socket for incoming connections.
Mike Hibler's avatar
Mike Hibler committed
888
889
	 */

890
	/* Create socket from which to read. */
Mike Hibler's avatar
Mike Hibler committed
891
892
	tcpsock = socket(AF_INET, SOCK_STREAM, 0);
	if (tcpsock < 0) {
893
		pfatal("opening stream socket");
894
895
	}

896
	i = 1;
Mike Hibler's avatar
Mike Hibler committed
897
	if (setsockopt(tcpsock, SOL_SOCKET, SO_REUSEADDR,
898
		       (char *)&i, sizeof(i)) < 0)
899
		pwarning("setsockopt(SO_REUSEADDR)");;
900

901
902
903
	/* Create name. */
	name.sin_family = AF_INET;
	name.sin_addr.s_addr = INADDR_ANY;
904
	name.sin_port = htons((u_short) portnum);
Mike Hibler's avatar
Mike Hibler committed
905
	if (bind(tcpsock, (struct sockaddr *) &name, sizeof(name))) {
906
		pfatal("binding stream socket");
907
908
909
	}
	/* Find assigned port value and print it out. */
	length = sizeof(name);
Mike Hibler's avatar
Mike Hibler committed
910
	if (getsockname(tcpsock, (struct sockaddr *) &name, &length)) {
911
		pfatal("getsockname");
912
	}
913
	if (listen(tcpsock, 128) < 0) {
914
		pfatal("listen");
915
	}
916
	info("listening on TCP port %d\n", ntohs(name.sin_port));
917

Mike Hibler's avatar
Mike Hibler committed
918
919
920
921
922
923
924
	/*
	 * Setup UDP socket
	 */

	/* Create socket from which to read. */
	udpsock = socket(AF_INET, SOCK_DGRAM, 0);
	if (udpsock < 0) {
925
		pfatal("opening dgram socket");
Mike Hibler's avatar
Mike Hibler committed
926
927
928
929
930
	}

	i = 1;
	if (setsockopt(udpsock, SOL_SOCKET, SO_REUSEADDR,
		       (char *)&i, sizeof(i)) < 0)
931
		pwarning("setsockopt(SO_REUSEADDR)");;
932
933
934
935

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

Mike Hibler's avatar
Mike Hibler committed
937
938
939
	/* Create name. */
	name.sin_family = AF_INET;
	name.sin_addr.s_addr = INADDR_ANY;
940
	name.sin_port = htons((u_short) portnum);
Mike Hibler's avatar
Mike Hibler committed
941
	if (bind(udpsock, (struct sockaddr *) &name, sizeof(name))) {
942
		pfatal("binding dgram socket");
Mike Hibler's avatar
Mike Hibler committed
943
944
945
946
947
	}

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

952
953
954
	*tcpsockp = tcpsock;
	*udpsockp = udpsock;
	return 0;
955
}
956

957
/*
958
 * Listen for UDP requests. This is not a secure channel, and so this should
959
960
961
 * eventually be killed off.
 */
static void
962
udpserver(int sock, int portnum)
963
964
965
{
	char			buf[MYBUFSIZE];
	struct sockaddr_in	client;
Mike Hibler's avatar
lint    
Mike Hibler committed
966
967
	socklen_t		length;
	int			cc;
968
	unsigned int		nreq = 0;
969

970
971
	info("udpserver starting: pid=%d sock=%d portnum=%d\n",
	     mypid, sock, portnum);
972
973
974
975
976

	/*
	 * Wait for udp connections.
	 */
	while (1) {
977
		setproctitle("UDP %d: %u done", portnum, nreq);
978
		length = sizeof(client);
979
		cc = recvfrom(sock, buf, sizeof(buf) - 1,
980
981
982
			      0, (struct sockaddr *)&client, &length);
		if (cc <= 0) {
			if (cc < 0)
983
984
				errorc("Reading UDP request");
			error("UDP Connection aborted\n");
985
			continue;
986
		}
987
988
		buf[cc] = '\0';
		handle_request(sock, &client, buf, 0);
989
		nreq++;
990
991
992
993
	}
	exit(1);
}

994
995
996
997
998
999
1000
int
tmcd_accept(int sock, struct sockaddr *addr, socklen_t *addrlen, int ms)
{
	int	newsock;

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