tmcd.c 247 KB
Newer Older
Leigh B. Stoller's avatar
Leigh B. Stoller committed
1
2
/*
 * EMULAB-COPYRIGHT
3
 * Copyright (c) 2000-2012 University of Utah and the Flux Group.
Leigh B. Stoller's avatar
Leigh B. Stoller committed
4
5
6
 * All rights reserved.
 */

7
8
9
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
Mike Hibler's avatar
Mike Hibler committed
10
#include <arpa/inet.h>
11
#include <netdb.h>
Mike Hibler's avatar
Mike Hibler committed
12
#include <ctype.h>
13
#include <stdio.h>
Mike Hibler's avatar
Mike Hibler committed
14
#include <stdlib.h>
15
#include <errno.h>
Mike Hibler's avatar
Mike Hibler committed
16
17
#include <string.h>
#include <unistd.h>
18
19
20
21
#include <syslog.h>
#include <signal.h>
#include <stdarg.h>
#include <assert.h>
22
#include <sys/wait.h>
Leigh B. Stoller's avatar
Leigh B. Stoller committed
23
#include <sys/fcntl.h>
24
25
#include <sys/syscall.h>
#include <sys/stat.h>
Kirk Webb's avatar
   
Kirk Webb committed
26
#include <sys/param.h>
27
#include <paths.h>
Austin Clements's avatar
Austin Clements committed
28
#include <setjmp.h>
29
30
#include <pwd.h>
#include <grp.h>
31
#include <mysql/mysql.h>
32
#include "tmcd.h"
33
#include "config.h"
34
35
#include "ssl.h"
#include "log.h"
36
#include "tbdefs.h"
Leigh B. Stoller's avatar
Leigh B. Stoller committed
37
38
#include "bootwhat.h"
#include "bootinfo.h"
39

40
41
42
43
#ifdef EVENTSYS
#include "event.h"
#endif

44
45
46
/* XXX: Not sure this is okay! */
#include "tpm.h"

47
48
49
/*
 * XXX This needs to be localized!
 */
50
51
52
#define FSPROJDIR	FSNODE ":" FSDIR_PROJ
#define FSGROUPDIR	FSNODE ":" FSDIR_GROUPS
#define FSUSERDIR	FSNODE ":" FSDIR_USERS
53
54
55
#ifdef  FSDIR_SHARE
#define FSSHAREDIR	FSNODE ":" FSDIR_SHARE
#endif
56
57
58
59
60
61
62
63
#ifdef  FSDIR_SCRATCH
#define FSSCRATCHDIR	FSNODE ":" FSDIR_SCRATCH
#endif
#define PROJDIR		PROJROOT_DIR
#define GROUPDIR	GROUPSROOT_DIR
#define USERDIR		USERSROOT_DIR
#define SCRATCHDIR	SCRATCHROOT_DIR
#define SHAREDIR	SHAREROOT_DIR
64
#define NETBEDDIR	"/netbed"
Kirk Webb's avatar
   
Kirk Webb committed
65
#define PLISALIVELOGDIR "/usr/testbed/log/plabisalive"
66
67
#define RELOADPID	"emulab-ops"
#define RELOADEID	"reloading"
Austin Clements's avatar
Austin Clements committed
68
#define FSHOSTID	"/usr/testbed/etc/fshostid"
69
#define DOTSFS		".sfs"
70
71
#define RUNASUSER	"nobody"
#define RUNASGROUP	"nobody"
72
#define NTPSERVER       "ntp1"
73
#define PROTOUSER	"elabman"
74
75
76
77
#define PRIVKEY_LEN	128
#define URN_LEN		128
#define XSTRINGIFY(s)   STRINGIFY(s)
#define STRINGIFY(s)	#s
78

79
80
81
82
83
/* XXX backward compat */
#ifndef TBCOREDIR
#define	TBCOREDIR	TBROOT "/tmp"
#endif

84
85
86
87
/* socket read/write timeouts in ms */
#define READTIMO	3000
#define WRITETIMO	3000

88
#define TESTMODE
89
90
91
92
93
94
95
96
97
98
99
100
#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) */
101

102
103
104
#define DISKTYPE	"ad"
#define DISKNUM		0

105
106
/* Compiled in slothd parameters
 *
107
 * 1 - reg_interval  2 - agg_interval  3 - load_thresh
108
109
110
111
 * 4 - expt_thresh   5 - ctl_thresh
 */
#define SDPARAMS        "reg=300 agg=5 load=1 expt=5 ctl=1000"

112
113
/* Defined in configure and passed in via the makefile */
#define DBNAME_SIZE	64
Austin Clements's avatar
Austin Clements committed
114
#define HOSTID_SIZE	(32+64)
115
116
#define DEFAULT_DBNAME	TBDBNAME

Robert P Ricci's avatar
Robert P Ricci committed
117
118
119
120
/* For secure disk loading */
#define SECURELOAD_OPMODE "SECURELOAD"
#define SECURELOAD_STATE  "RELOADSETUP"

121
int		debug = 0;
122
static int	verbose = 0;
123
static int	insecure = 0;
124
static int	byteswritten = 0;
Leigh B Stoller's avatar
Leigh B Stoller committed
125
static char	pidfile[MAXPATHLEN];
126
static char     dbname[DBNAME_SIZE];
127
static struct in_addr myipaddr;
Austin Clements's avatar
Austin Clements committed
128
static char	fshostid[HOSTID_SIZE];
Leigh B. Stoller's avatar
Leigh B. Stoller committed
129
static int	nodeidtoexp(char *nodeid, char *pid, char *eid, char *gid);
130
131
static void	tcpserver(int sock, int portnum);
static void	udpserver(int sock, int portnum);
132
static int      handle_request(int, struct sockaddr_in *, char *, int);
133
static int      checkcerts(char*);
134
static int	makesockets(int portnum, int *udpsockp, int *tcpsockp);
Mike Hibler's avatar
Mike Hibler committed
135
136
int		client_writeback(int sock, void *buf, int len, int tcp);
void		client_writeback_done(int sock, struct sockaddr_in *client);
137
138
MYSQL_RES *	mydb_query(char *query, int ncols, ...);
int		mydb_update(char *query, ...);
139
static int	safesymlink(char *name1, char *name2);
140

141
142
143
144
/* socket timeouts */
static int	readtimo = READTIMO;
static int	writetimo = WRITETIMO;

145
/* thread support */
146
#define MAXCHILDREN	20
147
#define MINCHILDREN	8
148
static int	numchildren;
149
150
151
152
static int	maxchildren       = 13;
static int      num_udpservers    = 3;
static int      num_altudpservers = 1;
static int      num_alttcpservers = 1;
153
static int	mypid;
154
static volatile int killme;
155

156
157
158
159
160
/* Output macro to check for string overflow */
#define OUTPUT(buf, size, format...) \
({ \
	int __count__ = snprintf((buf), (size), ##format); \
        \
161
        if (__count__ >= (size)) { \
162
163
164
165
166
167
		error("Not enough room in output buffer! line %d.\n", __LINE__);\
		return 1; \
	} \
	__count__; \
})

168
169
170
171
172
/*
 * 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
173
	struct in_addr  client;
174
175
176
	int		allocated;
	int		jailflag;
	int		isvnode;
177
	int		issubnode;
178
	int		islocal;
179
	int		isdedicatedwa;
180
	int		iscontrol;
181
	int		isplabdslice;
182
	int		isplabsvc;
183
	int		elab_in_elab;
184
        int		singlenet;	  /* Modifier for elab_in_elab */
185
	int		update_accounts;
186
	int		exptidx;
187
188
189
	int		creator_idx;
	int		swapper_idx;
	int		swapper_isadmin;
190
        int		genisliver_idx;
191
        int		geniflags;
192
193
	char		nodeid[TBDB_FLEN_NODEID];
	char		vnodeid[TBDB_FLEN_NODEID];
Leigh B. Stoller's avatar
Leigh B. Stoller committed
194
	char		pnodeid[TBDB_FLEN_NODEID]; /* XXX */
195
196
197
198
199
	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];
200
	char		class[TBDB_FLEN_NODECLASS];
201
202
        char		ptype[TBDB_FLEN_NODETYPE];	/* Of physnode */
	char		pclass[TBDB_FLEN_NODECLASS];	/* Of physnode */
203
204
	char		creator[TBDB_FLEN_UID];
	char		swapper[TBDB_FLEN_UID];
205
	char		syncserver[TBDB_FLEN_VNAME];	/* The vname */
206
	char		keyhash[TBDB_FLEN_PRIVKEY];
207
	char		eventkey[TBDB_FLEN_PRIVKEY];
208
	char		sfshostid[TBDB_FLEN_SFSHOSTID];
209
	char		testdb[TBDB_FLEN_TINYTEXT];
210
	char		sharing_mode[TBDB_FLEN_TINYTEXT];
211
	char            privkey[PRIVKEY_LEN+1];
212
	char		nodeuuid[TBDB_FLEN_UUID];
213
214
        /* This key is a replacement for privkey, on protogeni resources */
	char            external_key[PRIVKEY_LEN+1];
215
} tmcdreq_t;
216
static int	iptonodeid(struct in_addr, tmcdreq_t *, char*);
217
218
static int	checkdbredirect(tmcdreq_t *);

219
220
221
222
223
#ifdef EVENTSYS
int			myevent_send(address_tuple_t address);
static event_handle_t	event_handle = NULL;
#endif

224
225
226
/*
 * Commands we support.
 */
227
228
#define COMMAND_PROTOTYPE(x) \
	static int \
229
	x(int sock, tmcdreq_t *reqp, char *rdata, int tcp, int vers)
230
231

COMMAND_PROTOTYPE(doreboot);
232
COMMAND_PROTOTYPE(donodeid);
233
COMMAND_PROTOTYPE(donodeuuid);
David Johnson's avatar
David Johnson committed
234
COMMAND_PROTOTYPE(domanifest);
235
236
237
COMMAND_PROTOTYPE(dostatus);
COMMAND_PROTOTYPE(doifconfig);
COMMAND_PROTOTYPE(doaccounts);
238
COMMAND_PROTOTYPE(dobridges);
239
COMMAND_PROTOTYPE(dodelay);
240
COMMAND_PROTOTYPE(dolinkdelay);
241
242
243
244
COMMAND_PROTOTYPE(dohosts);
COMMAND_PROTOTYPE(dorpms);
COMMAND_PROTOTYPE(dodeltas);
COMMAND_PROTOTYPE(dotarballs);
245
COMMAND_PROTOTYPE(doblobs);
246
247
248
249
250
COMMAND_PROTOTYPE(dostartcmd);
COMMAND_PROTOTYPE(dostartstat);
COMMAND_PROTOTYPE(doready);
COMMAND_PROTOTYPE(doreadycount);
COMMAND_PROTOTYPE(domounts);
Austin Clements's avatar
Austin Clements committed
251
COMMAND_PROTOTYPE(dosfshostid);
252
253
254
255
256
257
258
COMMAND_PROTOTYPE(doloadinfo);
COMMAND_PROTOTYPE(doreset);
COMMAND_PROTOTYPE(dorouting);
COMMAND_PROTOTYPE(dotrafgens);
COMMAND_PROTOTYPE(donseconfigs);
COMMAND_PROTOTYPE(dostate);
COMMAND_PROTOTYPE(docreator);
259
COMMAND_PROTOTYPE(dotunnels);
260
COMMAND_PROTOTYPE(dovnodelist);
261
COMMAND_PROTOTYPE(dosubnodelist);
262
COMMAND_PROTOTYPE(doisalive);
263
COMMAND_PROTOTYPE(doipodinfo);
264
265
COMMAND_PROTOTYPE(dontpinfo);
COMMAND_PROTOTYPE(dontpdrift);
266
COMMAND_PROTOTYPE(dojailconfig);
267
COMMAND_PROTOTYPE(doplabconfig);
268
269
COMMAND_PROTOTYPE(dosubconfig);
COMMAND_PROTOTYPE(doixpconfig);
270
COMMAND_PROTOTYPE(doslothdparams);
271
COMMAND_PROTOTYPE(doprogagents);
272
COMMAND_PROTOTYPE(dosyncserver);
273
COMMAND_PROTOTYPE(dokeyhash);
274
COMMAND_PROTOTYPE(doeventkey);
275
COMMAND_PROTOTYPE(dofullconfig);
276
277
COMMAND_PROTOTYPE(doroutelist);
COMMAND_PROTOTYPE(dorole);
278
COMMAND_PROTOTYPE(dorusage);
279
COMMAND_PROTOTYPE(dodoginfo);
Mike Hibler's avatar
Mike Hibler committed
280
COMMAND_PROTOTYPE(dohostkeys);
Mike Hibler's avatar
Mike Hibler committed
281
COMMAND_PROTOTYPE(dotmcctest);
282
COMMAND_PROTOTYPE(dofwinfo);
283
COMMAND_PROTOTYPE(dohostinfo);
284
COMMAND_PROTOTYPE(doemulabconfig);
285
COMMAND_PROTOTYPE(doeplabconfig);
286
COMMAND_PROTOTYPE(dolocalize);
287
COMMAND_PROTOTYPE(dorootpswd);
288
289
COMMAND_PROTOTYPE(dobooterrno);
COMMAND_PROTOTYPE(dobootlog);
Timothy Stack's avatar
   
Timothy Stack committed
290
COMMAND_PROTOTYPE(dobattery);
291
COMMAND_PROTOTYPE(dotopomap);
Timothy Stack's avatar
   
Timothy Stack committed
292
COMMAND_PROTOTYPE(douserenv);
Timothy Stack's avatar
   
Timothy Stack committed
293
294
COMMAND_PROTOTYPE(dotiptunnels);
COMMAND_PROTOTYPE(dorelayconfig);
295
COMMAND_PROTOTYPE(dotraceconfig);
296
COMMAND_PROTOTYPE(doltmap);
297
COMMAND_PROTOTYPE(doltpmap);
Kirk Webb's avatar
   
Kirk Webb committed
298
COMMAND_PROTOTYPE(doelvindport);
Kirk Webb's avatar
   
Kirk Webb committed
299
COMMAND_PROTOTYPE(doplabeventkeys);
300
COMMAND_PROTOTYPE(dointfcmap);
301
COMMAND_PROTOTYPE(domotelog);
302
COMMAND_PROTOTYPE(doportregister);
Leigh B. Stoller's avatar
Leigh B. Stoller committed
303
COMMAND_PROTOTYPE(dobootwhat);
304
305
COMMAND_PROTOTYPE(dotpmblob);
COMMAND_PROTOTYPE(dotpmpubkey);
306
COMMAND_PROTOTYPE(dotpmdummy);
307
COMMAND_PROTOTYPE(dodhcpdconf);
308
COMMAND_PROTOTYPE(dosecurestate);
309
COMMAND_PROTOTYPE(doquoteprep);
310
COMMAND_PROTOTYPE(doimagekey);
311
COMMAND_PROTOTYPE(donodeattributes);
312
COMMAND_PROTOTYPE(dodisks);
313

314
315
/*
 * The fullconfig slot determines what routines get called when pushing
316
 * out a full configuration. Physnodes get slightly different
317
 * than vnodes, and at some point we might want to distinguish different
318
319
320
321
322
 * types of vnodes (jailed, plab).
 */
#define FULLCONFIG_NONE		0x0
#define FULLCONFIG_PHYS		0x1
#define FULLCONFIG_VIRT		0x2
323
324
325
326
327
#define FULLCONFIG_ALL		(FULLCONFIG_PHYS|FULLCONFIG_VIRT)

/*
 * Flags encode a few other random properties of commands
 */
328
329
330
331
332
#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 */
333
#define F_REMREQSSL	0x20	/* remote nodes must connect with SSL */
334
#define F_REQTPM	0x40	/* require TPM on client */
335

336
337
struct command {
	char	*cmdname;
338
	int	fullconfig;
339
	int	flags;
340
	int    (*func)(int, tmcdreq_t *, char *, int, int);
341
} command_array[] = {
342
343
	{ "reboot",	  FULLCONFIG_NONE, 0, doreboot },
	{ "nodeid",	  FULLCONFIG_ALL,  0, donodeid },
344
	{ "nodeuuid",	  FULLCONFIG_ALL,  0, donodeuuid },
David Johnson's avatar
David Johnson committed
345
	{ "manifest",	  FULLCONFIG_ALL,  0, domanifest },
346
	{ "status",	  FULLCONFIG_NONE, 0, dostatus },
347
	{ "ifconfig",	  FULLCONFIG_ALL,  F_ALLOCATED, doifconfig },
348
	{ "accounts",	  FULLCONFIG_ALL,  F_REMREQSSL, doaccounts },
349
	{ "delay",	  FULLCONFIG_ALL,  F_ALLOCATED, dodelay },
350
	{ "bridges",	  FULLCONFIG_ALL,  F_ALLOCATED, dobridges },
351
352
353
354
355
	{ "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 },
356
	{ "blobs",	  FULLCONFIG_ALL,  F_ALLOCATED, doblobs },
357
358
	{ "startupcmd",	  FULLCONFIG_ALL,  F_ALLOCATED, dostartcmd },
	{ "startstatus",  FULLCONFIG_NONE, F_ALLOCATED, dostartstat }, /* Before startstat*/
359
	{ "startstat",	  FULLCONFIG_NONE, 0, dostartstat },
360
361
362
363
	{ "readycount",   FULLCONFIG_NONE, F_ALLOCATED, doreadycount },
	{ "ready",	  FULLCONFIG_NONE, F_ALLOCATED, doready },
	{ "mounts",	  FULLCONFIG_ALL,  F_ALLOCATED, domounts },
	{ "sfshostid",	  FULLCONFIG_NONE, F_ALLOCATED, dosfshostid },
364
365
	{ "loadinfo",	  FULLCONFIG_NONE, 0, doloadinfo},
	{ "reset",	  FULLCONFIG_NONE, 0, doreset},
366
367
368
369
	{ "routing",	  FULLCONFIG_ALL,  F_ALLOCATED, dorouting},
	{ "trafgens",	  FULLCONFIG_ALL,  F_ALLOCATED, dotrafgens},
	{ "nseconfigs",	  FULLCONFIG_ALL,  F_ALLOCATED, donseconfigs},
	{ "creator",	  FULLCONFIG_ALL,  F_ALLOCATED, docreator},
370
	{ "state",	  FULLCONFIG_NONE, 0, dostate},
371
	{ "tunnels",	  FULLCONFIG_ALL,  F_ALLOCATED, dotunnels},
372
	{ "vnodelist",	  FULLCONFIG_PHYS, 0, dovnodelist},
Timothy Stack's avatar
   
Timothy Stack committed
373
	{ "subnodelist",  FULLCONFIG_PHYS, 0, dosubnodelist},
374
	{ "isalive",	  FULLCONFIG_NONE, F_REMUDP|F_MINLOG, doisalive},
375
	{ "ipodinfo",	  FULLCONFIG_PHYS, 0, doipodinfo},
376
377
	{ "ntpinfo",	  FULLCONFIG_PHYS, 0, dontpinfo},
	{ "ntpdrift",	  FULLCONFIG_NONE, 0, dontpdrift},
378
379
	{ "jailconfig",	  FULLCONFIG_VIRT, F_ALLOCATED, dojailconfig},
	{ "plabconfig",	  FULLCONFIG_VIRT, F_ALLOCATED, doplabconfig},
Timothy Stack's avatar
   
Timothy Stack committed
380
	{ "subconfig",	  FULLCONFIG_NONE, 0, dosubconfig},
381
        { "sdparams",     FULLCONFIG_PHYS, 0, doslothdparams},
382
383
        { "programs",     FULLCONFIG_ALL,  F_ALLOCATED, doprogagents},
        { "syncserver",   FULLCONFIG_ALL,  F_ALLOCATED, dosyncserver},
384
        { "keyhash",      FULLCONFIG_ALL,  F_ALLOCATED|F_REMREQSSL, dokeyhash},
385
        { "eventkey",     FULLCONFIG_ALL,  F_ALLOCATED|F_REMREQSSL, doeventkey},
386
387
388
        { "fullconfig",   FULLCONFIG_NONE, F_ALLOCATED, dofullconfig},
        { "routelist",	  FULLCONFIG_PHYS, F_ALLOCATED, doroutelist},
        { "role",	  FULLCONFIG_PHYS, F_ALLOCATED, dorole},
389
        { "rusage",	  FULLCONFIG_NONE, F_REMUDP|F_MINLOG, dorusage},
390
        { "watchdoginfo", FULLCONFIG_ALL,  F_REMUDP|F_MINLOG, dodoginfo},
Mike Hibler's avatar
Mike Hibler committed
391
        { "hostkeys",     FULLCONFIG_NONE, 0, dohostkeys},
Mike Hibler's avatar
Mike Hibler committed
392
        { "tmcctest",     FULLCONFIG_NONE, F_MINLOG, dotmcctest},
393
        { "firewallinfo", FULLCONFIG_ALL,  0, dofwinfo},
394
        { "hostinfo",     FULLCONFIG_NONE, 0, dohostinfo},
395
	{ "emulabconfig", FULLCONFIG_NONE, F_ALLOCATED, doemulabconfig},
396
	{ "eplabconfig",  FULLCONFIG_NONE, F_ALLOCATED, doeplabconfig},
397
	{ "localization", FULLCONFIG_PHYS, 0, dolocalize},
398
	{ "rootpswd",     FULLCONFIG_NONE, F_REMREQSSL, dorootpswd},
399
400
	{ "booterrno",    FULLCONFIG_NONE, 0, dobooterrno},
	{ "bootlog",      FULLCONFIG_NONE, 0, dobootlog},
Timothy Stack's avatar
   
Timothy Stack committed
401
	{ "battery",      FULLCONFIG_NONE, F_REMUDP|F_MINLOG, dobattery},
402
	{ "topomap",      FULLCONFIG_NONE, F_MINLOG|F_ALLOCATED, dotopomap},
403
	{ "userenv",      FULLCONFIG_ALL,  F_ALLOCATED, douserenv},
Timothy Stack's avatar
   
Timothy Stack committed
404
	{ "tiptunnels",	  FULLCONFIG_ALL,  F_ALLOCATED, dotiptunnels},
405
	{ "traceinfo",	  FULLCONFIG_ALL,  F_ALLOCATED, dotraceconfig },
Kirk Webb's avatar
   
Kirk Webb committed
406
	{ "ltmap",        FULLCONFIG_NONE, F_MINLOG|F_ALLOCATED, doltmap},
407
	{ "ltpmap",       FULLCONFIG_NONE, F_MINLOG|F_ALLOCATED, doltpmap},
Kirk Webb's avatar
   
Kirk Webb committed
408
	{ "elvindport",   FULLCONFIG_NONE, 0, doelvindport},
409
	{ "plabeventkeys",FULLCONFIG_NONE, F_REMREQSSL, doplabeventkeys},
410
	{ "intfcmap",     FULLCONFIG_NONE, 0, dointfcmap},
411
412
	{ "motelog",      FULLCONFIG_ALL,  F_ALLOCATED, domotelog},
	{ "portregister", FULLCONFIG_NONE, F_REMNOSSL, doportregister},
Leigh B. Stoller's avatar
Leigh B. Stoller committed
413
	{ "bootwhat",	  FULLCONFIG_NONE, 0, dobootwhat },
414
415
	{ "tpmblob",	  FULLCONFIG_ALL, 0, dotpmblob },
	{ "tpmpubkey",	  FULLCONFIG_ALL, 0, dotpmpubkey },
416
	{ "tpmdummy",	  FULLCONFIG_ALL, F_REQTPM, dotpmdummy },
417
	{ "dhcpdconf",	  FULLCONFIG_ALL, 0, dodhcpdconf },
418
419
	{ "securestate",  FULLCONFIG_NONE, F_REMREQSSL, dosecurestate},
	{ "quoteprep",    FULLCONFIG_NONE, F_REMREQSSL, doquoteprep},
420
	{ "imagekey",     FULLCONFIG_NONE, F_REQTPM, doimagekey},
421
	{ "nodeattributes", FULLCONFIG_ALL, 0, donodeattributes},
422
	{ "disks",	  FULLCONFIG_ALL, 0, dodisks},
423
	
424
425
426
};
static int numcommands = sizeof(command_array)/sizeof(struct command);

427
char *usagestr =
428
429
430
 "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"
431
 " -c num	   Specify number of servers (must be %d <= x <= %d)\n"
432
 " -v              More verbose logging\n"
433
 " -i ipaddr       Sets the boss IP addr to return (for multi-homed servers)\n"
434
435
436
437
438
 "\n";

void
usage()
{
439
	fprintf(stderr, usagestr, MINCHILDREN, MAXCHILDREN);
440
441
442
	exit(1);
}

443
444
445
446
447
static void
cleanup()
{
	signal(SIGHUP, SIG_IGN);
	killme = 1;
448
	killpg(0, SIGHUP);
Leigh B Stoller's avatar
Leigh B Stoller committed
449
	unlink(pidfile);
450
451
}

452
453
454
455
static void
setverbose(int sig)
{
	signal(sig, SIG_IGN);
456

457
458
459
460
	if (sig == SIGUSR1)
		verbose = 1;
	else
		verbose = 0;
461
462
	info("verbose logging turned %s\n", verbose ? "on" : "off");

463
464
465
466
467
468
	/* Just the parent sends this */
	if (numchildren)
		killpg(0, sig);
	signal(sig, setverbose);
}

Mike Hibler's avatar
Mike Hibler committed
469
470
int
main(int argc, char **argv)
471
{
472
	int			tcpsock, udpsock, i, ch;
473
474
475
	int			alttcpsock, altudpsock;
	int			status, pid;
	int			portnum = TBSERVER_PORT;
476
477
	FILE			*fp;
	char			buf[BUFSIZ];
478
	struct hostent		*he;
479
	extern char		build_info[];
480
481
482
483
484
	int			server_counts[4]; /* udp,tcp,altudp,alttcp */
	struct {
		int	pid;
		int	which;
	} servers[MAXCHILDREN];
485

486
	while ((ch = getopt(argc, argv, "dp:c:Xvi:")) != -1)
487
488
489
490
491
492
		switch(ch) {
		case 'p':
			portnum = atoi(optarg);
			break;
		case 'd':
			debug++;
493
			break;
494
495
496
		case 'c':
			maxchildren = atoi(optarg);
			break;
497
498
499
		case 'X':
			insecure = 1;
			break;
500
501
502
		case 'v':
			verbose++;
			break;
503
504
505
506
507
508
509
		case 'i':
			if (inet_aton(optarg, &myipaddr) == 0) {
				fprintf(stderr, "invalid IP address %s\n",
					optarg);
				usage();
			}
			break;
510
511
512
513
514
515
516
517
518
519
		case 'h':
		case '?':
		default:
			usage();
		}
	argc -= optind;
	argv += optind;

	if (argc)
		usage();
520
521
	if (maxchildren < MINCHILDREN || maxchildren > MAXCHILDREN)
		usage();
522

523
524
525
526
527
528
#ifdef  WITHSSL
	if (tmcd_server_sslinit()) {
		error("SSL init failed!\n");
		exit(1);
	}
#endif
529
	if (debug)
530
531
532
		loginit(0, 0);
	else {
		/* Become a daemon */
533
534
535
536
537
		if (chdir(TBCOREDIR)) {
			daemon(0, 0);
		} else {
			daemon(1, 0);
		}
538
539
540
541
		loginit(1, "tmcd");
	}
	info("daemon starting (version %d)\n", CURRENT_VERSION);
	info("%s\n", build_info);
Mike Hibler's avatar
Mike Hibler committed
542

Austin Clements's avatar
Austin Clements committed
543
544
545
546
547
548
549
550
551
552
553
554
555
556
	/*
	 * 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;
557
558
559
				if (debug) {
				    info("fshostid: %s\n", fshostid);
				}
Austin Clements's avatar
Austin Clements committed
560
561
562
563
564
565
566
567
			}
			else {
				error("fshostid from %s may be corrupt: %s",
				      FSHOSTID, fshostid);
			}
			fclose(fp);
		}
	}
568

569
570
571
	/*
	 * Grab our IP for security check below.
	 */
572
	if (myipaddr.s_addr == 0) {
573
#ifdef	LBS
574
		strcpy(buf, BOSSNODE);
575
#else
576
577
		if (gethostname(buf, sizeof(buf)) < 0)
			pfatal("getting hostname");
578
#endif
579
580
581
582
583
584
		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);
585
586
	}

587
	/*
588
	 * If we were given a port on the command line, don't open the
589
590
591
592
	 * alternate ports
	 */
	if (portnum != TBSERVER_PORT) {
	    if (makesockets(portnum, &udpsock, &tcpsock) < 0) {
593
594
		error("Could not make sockets!");
		exit(1);
595
	    }
596
	    num_alttcpservers = num_altudpservers = 0;
597
598
599
600
601
602
	} else {
	    if (makesockets(portnum, &udpsock, &tcpsock) < 0 ||
		makesockets(TBSERVER_PORT2, &altudpsock, &alttcpsock) < 0) {
		    error("Could not make sockets!");
		    exit(1);
	    }
603
604
605
606
607
	}

	signal(SIGTERM, cleanup);
	signal(SIGINT, cleanup);
	signal(SIGHUP, cleanup);
608
609
	signal(SIGUSR1, setverbose);
	signal(SIGUSR2, setverbose);
610
611
612
613

	/*
	 * Stash the pid away.
	 */
614
	mypid = getpid();
Leigh B Stoller's avatar
Leigh B Stoller committed
615
616
	sprintf(pidfile, "%s/tmcd.pid", _PATH_VARRUN);
	fp = fopen(pidfile, "w");
617
	if (fp != NULL) {
618
		fprintf(fp, "%d\n", mypid);
619
620
621
		(void) fclose(fp);
	}

622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
	/*
	 * 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);
	}

655
656
	/*
	 * Now fork a set of children to handle requests. We keep the
657
658
659
660
661
662
663
	 * 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!
664
	 */
665
666
667
668
669
670
	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));
671

672
673
	while (1) {
		while (!killme && numchildren < maxchildren) {
674
			int which = 3;
675

676
677
678
679
680
681
682
683
			/*
			 * Find which kind of server is short one.
			 */
			for (i = 0; i < 4; i++) {
				if (server_counts[i]) {
					which = i;
					break;
				}
684
			}
685

686
687
688
689
690
			if ((pid = fork()) < 0) {
				errorc("forking server");
				goto done;
			}
			if (pid) {
691
692
693
694
695
696
697
698
699
700
				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;
701
702
703
				numchildren++;
				continue;
			}
704
705
706
			/* Poor way of knowing parent/child */
			numchildren = 0;
			mypid = getpid();
707

708
709
710
711
			/* Child does useful work! Never Returns! */
			signal(SIGTERM, SIG_DFL);
			signal(SIGINT, SIG_DFL);
			signal(SIGHUP, SIG_DFL);
712

713
			switch (which) {
714
			case 0: udpserver(udpsock, portnum);
715
				break;
716
			case 1: udpserver(altudpsock, TBSERVER_PORT2);
717
				break;
718
			case 2: tcpserver(alttcpsock, TBSERVER_PORT2);
719
				break;
720
			case 3: tcpserver(tcpsock, portnum);
721
722
723
724
725
726
727
728
729
730
731
732
				break;
			}
			exit(-1);
		}

		/*
		 * Parent waits.
		 */
		pid = waitpid(-1, &status, 0);
		if (pid < 0) {
			errorc("waitpid failed");
			continue;
733
		}
734
735
736
		if (WIFSIGNALED(status)) {
			error("server %d exited with signal %d!\n",
			      pid, WTERMSIG(status));
737
		}
738
739
		else if (WIFEXITED(status)) {
			error("server %d exited with status %d!\n",
740
			      pid, WEXITSTATUS(status));
741
		}
742
		numchildren--;
743
744
745
746
747
748
749
750
751
752

		/*
		 * 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;
			}
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
		}
		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
771
772
	socklen_t		length;
	int			i, udpsock, tcpsock;
773

Mike Hibler's avatar
Mike Hibler committed
774
	/*
775
	 * Setup TCP socket for incoming connections.
Mike Hibler's avatar
Mike Hibler committed
776
777
	 */

778
	/* Create socket from which to read. */
Mike Hibler's avatar
Mike Hibler committed
779
780
	tcpsock = socket(AF_INET, SOCK_STREAM, 0);
	if (tcpsock < 0) {
781
		pfatal("opening stream socket");
782
783
	}

784
	i = 1;
Mike Hibler's avatar
Mike Hibler committed
785
	if (setsockopt(tcpsock, SOL_SOCKET, SO_REUSEADDR,
786
		       (char *)&i, sizeof(i)) < 0)
787
		pwarning("setsockopt(SO_REUSEADDR)");;
788

789
790
791
	/* Create name. */
	name.sin_family = AF_INET;
	name.sin_addr.s_addr = INADDR_ANY;
792
	name.sin_port = htons((u_short) portnum);
Mike Hibler's avatar
Mike Hibler committed
793
	if (bind(tcpsock, (struct sockaddr *) &name, sizeof(name))) {
794
		pfatal("binding stream socket");
795
796
797
	}
	/* Find assigned port value and print it out. */
	length = sizeof(name);
Mike Hibler's avatar
Mike Hibler committed
798
	if (getsockname(tcpsock, (struct sockaddr *) &name, &length)) {
799
		pfatal("getsockname");
800
	}
801
	if (listen(tcpsock, 128) < 0) {
802
		pfatal("listen");
803
	}
804
	info("listening on TCP port %d\n", ntohs(name.sin_port));
805

Mike Hibler's avatar
Mike Hibler committed
806
807
808
809
810
811
812
	/*
	 * Setup UDP socket
	 */

	/* Create socket from which to read. */
	udpsock = socket(AF_INET, SOCK_DGRAM, 0);
	if (udpsock < 0) {
813
		pfatal("opening dgram socket");
Mike Hibler's avatar
Mike Hibler committed
814
815
816
817
818
	}

	i = 1;
	if (setsockopt(udpsock, SOL_SOCKET, SO_REUSEADDR,
		       (char *)&i, sizeof(i)) < 0)
819
		pwarning("setsockopt(SO_REUSEADDR)");;
820
821
822
823

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

Mike Hibler's avatar
Mike Hibler committed
825
826
827
	/* Create name. */
	name.sin_family = AF_INET;
	name.sin_addr.s_addr = INADDR_ANY;
828
	name.sin_port = htons((u_short) portnum);
Mike Hibler's avatar
Mike Hibler committed
829
	if (bind(udpsock, (struct sockaddr *) &name, sizeof(name))) {
830
		pfatal("binding dgram socket");
Mike Hibler's avatar
Mike Hibler committed
831
832
833
834
835
	}

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

840
841
842
	*tcpsockp = tcpsock;
	*udpsockp = udpsock;
	return 0;
843
}
844

845
/*
846
 * Listen for UDP requests. This is not a secure channel, and so this should
847
848
849
 * eventually be killed off.
 */
static void
850
udpserver(int sock, int portnum)
851
852
853
{
	char			buf[MYBUFSIZE];
	struct sockaddr_in	client;
Mike Hibler's avatar
lint    
Mike Hibler committed
854
855
	socklen_t		length;
	int			cc;
856
	unsigned int		nreq = 0;
857

858
859
	info("udpserver starting: pid=%d sock=%d portnum=%d\n",
	     mypid, sock, portnum);
860
861
862
863
864

	/*
	 * Wait for udp connections.
	 */
	while (1) {
865
		setproctitle("UDP %d: %u done", portnum, nreq);
866
		length = sizeof(client);
867
		cc = recvfrom(sock, buf, sizeof(buf) - 1,
868
869
870
			      0, (struct sockaddr *)&client, &length);
		if (cc <= 0) {
			if (cc < 0)
871
872
				errorc("Reading UDP request");
			error("UDP Connection aborted\n");
873
			continue;
874
		}
875
876
		buf[cc] = '\0';
		handle_request(sock, &client, buf, 0);
877
		nreq++;
878
879
880
881
	}
	exit(1);
}

882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
int
tmcd_accept(int sock, struct sockaddr *addr, socklen_t *addrlen, int ms)
{
	int	newsock;

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

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

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

	return newsock;
}

908
/*
909
 * Listen for TCP requests.
910
911
 */
static void
912
tcpserver(int sock, int portnum)
913
{
914
	char			buf[MAXTMCDPACKET];
915
	struct sockaddr_in	client;
Mike Hibler's avatar
lint    
Mike Hibler committed
916
917
	socklen_t		length;
	int			cc, newsock;
918
	unsigned int		nreq = 0;
919
	struct timeval		tv;
920

921
922
	info("tcpserver starting: pid=%d sock=%d portnum=%d\n",
	     mypid, sock, portnum);
923
924
925
926
927

	/*
	 * Wait for TCP connections.
	 */
	while (1) {
928
		setproctitle("TCP %d: %u done", portnum, nreq);
929
		length  = sizeof(client);
930
931
		newsock = ACCEPT(sock, (struct sockaddr *)&client, &length,
				 readtimo);
932
		if (newsock < 0) {
933
			errorc("accepting TCP connection");
934
			continue;
935
		}