tmcd.c 121 KB
Newer Older
Leigh B. Stoller's avatar
Leigh B. Stoller committed
1
2
/*
 * EMULAB-COPYRIGHT
3
 * Copyright (c) 2000-2004 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
15
16
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
17
18
19
20
#include <syslog.h>
#include <signal.h>
#include <stdarg.h>
#include <assert.h>
21
#include <sys/wait.h>
Leigh B. Stoller's avatar
Leigh B. Stoller committed
22
#include <sys/fcntl.h>
23
24
#include <sys/syscall.h>
#include <sys/stat.h>
25
#include <paths.h>
Austin Clements's avatar
Austin Clements committed
26
#include <setjmp.h>
27
28
#include <pwd.h>
#include <grp.h>
29
30
#include <mysql/mysql.h>
#include "decls.h"
31
#include "config.h"
32
33
#include "ssl.h"
#include "log.h"
34
#include "tbdefs.h"
35

36
37
38
39
#ifdef EVENTSYS
#include "event.h"
#endif

40
41
42
/*
 * XXX This needs to be localized!
 */
43
44
45
#define FSPROJDIR	FSNODE ":" FSDIR_PROJ
#define FSGROUPDIR	FSNODE ":" FSDIR_GROUPS
#define FSUSERDIR	FSNODE ":" FSDIR_USERS
46
47
48
#ifdef  FSDIR_SHARE
#define FSSHAREDIR	FSNODE ":" FSDIR_SHARE
#endif
49
#define PROJDIR		"/proj"
50
#define GROUPDIR	"/groups"
51
#define USERDIR		"/users"
52
#define NETBEDDIR	"/netbed"
53
#define SHAREDIR	"/share"
54
55
#define RELOADPID	"emulab-ops"
#define RELOADEID	"reloading"
Austin Clements's avatar
Austin Clements committed
56
#define FSHOSTID	"/usr/testbed/etc/fshostid"
57
#define DOTSFS		".sfs"
58
59
#define RUNASUSER	"nobody"
#define RUNASGROUP	"nobody"
60

61
#define TESTMODE
62
63
64
65
66
67
68
69
70
71
72
73
#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) */
74

75
76
77
#define DISKTYPE	"ad"
#define DISKNUM		0

78
79
80
81
82
83
84
/* Compiled in slothd parameters
 *
 * 1 - reg_interval  2 - agg_interval  3 - load_thresh  
 * 4 - expt_thresh   5 - ctl_thresh
 */
#define SDPARAMS        "reg=300 agg=5 load=1 expt=5 ctl=1000"

85
86
/* Defined in configure and passed in via the makefile */
#define DBNAME_SIZE	64
Austin Clements's avatar
Austin Clements committed
87
#define HOSTID_SIZE	(32+64)
88
89
#define DEFAULT_DBNAME	TBDBNAME

90
int		debug = 0;
91
static int	verbose = 0;
92
static int	insecure = 0;
93
static int	byteswritten = 0;
94
static char     dbname[DBNAME_SIZE];
95
static struct in_addr myipaddr;
Austin Clements's avatar
Austin Clements committed
96
static char	fshostid[HOSTID_SIZE];
Leigh B. Stoller's avatar
Leigh B. Stoller committed
97
static int	nodeidtoexp(char *nodeid, char *pid, char *eid, char *gid);
98
static int	checkprivkey(struct in_addr, char *);
99
100
static void	tcpserver(int sock, int portnum);
static void	udpserver(int sock, int portnum);
101
static int      handle_request(int, struct sockaddr_in *, char *, int);
102
static int	makesockets(int portnum, int *udpsockp, int *tcpsockp);
Mike Hibler's avatar
Mike Hibler committed
103
104
int		client_writeback(int sock, void *buf, int len, int tcp);
void		client_writeback_done(int sock, struct sockaddr_in *client);
105
106
MYSQL_RES *	mydb_query(char *query, int ncols, ...);
int		mydb_update(char *query, ...);
107
static int	safesymlink(char *name1, char *name2);
108

109
/* thread support */
110
#define MAXCHILDREN	20
111
112
#define MINCHILDREN	5
static int	numchildren;
113
static int	maxchildren = 10;
114
static int	mypid;
115
static volatile int killme;
116

117
118
119
120
121
/* Output macro to check for string overflow */
#define OUTPUT(buf, size, format...) \
({ \
	int __count__ = snprintf((buf), (size), ##format); \
        \
122
        if (__count__ >= (size)) { \
123
124
125
126
127
128
		error("Not enough room in output buffer! line %d.\n", __LINE__);\
		return 1; \
	} \
	__count__; \
})

129
130
131
132
133
134
135
136
/*
 * 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 {
	int		allocated;
	int		jailflag;
	int		isvnode;
137
	int		issubnode;
138
	int		islocal;
139
	int		iscontrol;
140
	int		isplabdslice;
141
	int		isplabsvc;
142
	int		update_accounts;
143
144
	char		nodeid[TBDB_FLEN_NODEID];
	char		vnodeid[TBDB_FLEN_NODEID];
Leigh B. Stoller's avatar
Leigh B. Stoller committed
145
	char		pnodeid[TBDB_FLEN_NODEID]; /* XXX */
146
147
148
149
150
	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];
151
	char		class[TBDB_FLEN_NODECLASS];
152
153
        char		ptype[TBDB_FLEN_NODETYPE];	/* Of physnode */
	char		pclass[TBDB_FLEN_NODECLASS];	/* Of physnode */
154
155
	char		creator[TBDB_FLEN_UID];
	char		swapper[TBDB_FLEN_UID];
156
	char		syncserver[TBDB_FLEN_VNAME];	/* The vname */
157
	char		keyhash[TBDB_FLEN_PRIVKEY];
158
	char		eventkey[TBDB_FLEN_PRIVKEY];
159
	char		sfshostid[TBDB_FLEN_SFSHOSTID];
160
	char		testdb[256];
161
	int		veth_encapsulate;
162
163
164
165
} tmcdreq_t;
static int	iptonodeid(struct in_addr, tmcdreq_t *);
static int	checkdbredirect(tmcdreq_t *);

166
167
168
169
170
#ifdef EVENTSYS
int			myevent_send(address_tuple_t address);
static event_handle_t	event_handle = NULL;
#endif

171
172
173
/*
 * Commands we support.
 */
174
175
#define COMMAND_PROTOTYPE(x) \
	static int \
176
	x(int sock, tmcdreq_t *reqp, char *rdata, int tcp, int vers)
177
178

COMMAND_PROTOTYPE(doreboot);
179
COMMAND_PROTOTYPE(donodeid);
180
181
182
183
COMMAND_PROTOTYPE(dostatus);
COMMAND_PROTOTYPE(doifconfig);
COMMAND_PROTOTYPE(doaccounts);
COMMAND_PROTOTYPE(dodelay);
184
COMMAND_PROTOTYPE(dolinkdelay);
185
186
187
188
189
190
191
192
193
194
COMMAND_PROTOTYPE(dohosts);
COMMAND_PROTOTYPE(dohostsV2);
COMMAND_PROTOTYPE(dorpms);
COMMAND_PROTOTYPE(dodeltas);
COMMAND_PROTOTYPE(dotarballs);
COMMAND_PROTOTYPE(dostartcmd);
COMMAND_PROTOTYPE(dostartstat);
COMMAND_PROTOTYPE(doready);
COMMAND_PROTOTYPE(doreadycount);
COMMAND_PROTOTYPE(domounts);
Austin Clements's avatar
Austin Clements committed
195
COMMAND_PROTOTYPE(dosfshostid);
196
197
198
199
200
201
202
COMMAND_PROTOTYPE(doloadinfo);
COMMAND_PROTOTYPE(doreset);
COMMAND_PROTOTYPE(dorouting);
COMMAND_PROTOTYPE(dotrafgens);
COMMAND_PROTOTYPE(donseconfigs);
COMMAND_PROTOTYPE(dostate);
COMMAND_PROTOTYPE(docreator);
203
COMMAND_PROTOTYPE(dotunnels);
204
COMMAND_PROTOTYPE(dovnodelist);
205
COMMAND_PROTOTYPE(dosubnodelist);
206
COMMAND_PROTOTYPE(doisalive);
207
COMMAND_PROTOTYPE(doipodinfo);
208
209
COMMAND_PROTOTYPE(dontpinfo);
COMMAND_PROTOTYPE(dontpdrift);
210
COMMAND_PROTOTYPE(dojailconfig);
211
COMMAND_PROTOTYPE(doplabconfig);
212
213
COMMAND_PROTOTYPE(dosubconfig);
COMMAND_PROTOTYPE(doixpconfig);
214
COMMAND_PROTOTYPE(doslothdparams);
215
COMMAND_PROTOTYPE(doprogagents);
216
COMMAND_PROTOTYPE(dosyncserver);
217
COMMAND_PROTOTYPE(dokeyhash);
218
COMMAND_PROTOTYPE(doeventkey);
219
COMMAND_PROTOTYPE(dofullconfig);
220
221
COMMAND_PROTOTYPE(doroutelist);
COMMAND_PROTOTYPE(dorole);
222
COMMAND_PROTOTYPE(dorusage);
223
COMMAND_PROTOTYPE(dodoginfo);
Mike Hibler's avatar
Mike Hibler committed
224
COMMAND_PROTOTYPE(dohostkeys);
225

226
227
/*
 * The fullconfig slot determines what routines get called when pushing
228
 * out a full configuration. Physnodes get slightly different
229
230
231
232
233
234
 * then vnodes, and at some point we might want to distinguish different
 * types of vnodes (jailed, plab).
 */
#define FULLCONFIG_NONE		0x0
#define FULLCONFIG_PHYS		0x1
#define FULLCONFIG_VIRT		0x2
235
236
237
238
239
240
#define FULLCONFIG_ALL		(FULLCONFIG_PHYS|FULLCONFIG_VIRT)

/*
 * Flags encode a few other random properties of commands
 */
#define F_REMUDP	0x1	/* remote nodes can request using UDP */
241
242
#define F_MINLOG	0x2	/* record minimal logging info normally */
#define F_MAXLOG	0x4	/* record maximal logging info normally */
243

244
245
struct command {
	char	*cmdname;
246
	int	fullconfig;	
247
	int	flags;
248
	int    (*func)(int, tmcdreq_t *, char *, int, int);
249
} command_array[] = {
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
	{ "reboot",	  FULLCONFIG_NONE, 0, doreboot },
	{ "nodeid",	  FULLCONFIG_ALL,  0, donodeid },
	{ "status",	  FULLCONFIG_NONE, 0, dostatus },
	{ "ifconfig",	  FULLCONFIG_ALL,  0, doifconfig },
	{ "accounts",	  FULLCONFIG_ALL,  0, doaccounts },
	{ "delay",	  FULLCONFIG_ALL,  0, dodelay },
	{ "linkdelay",	  FULLCONFIG_ALL,  0, dolinkdelay },
	{ "hostnamesV2",  FULLCONFIG_NONE, 0, dohostsV2 },   /* Will go away */
	{ "hostnames",	  FULLCONFIG_ALL,  0, dohosts },
	{ "rpms",	  FULLCONFIG_ALL,  0, dorpms },
	{ "deltas",	  FULLCONFIG_NONE, 0, dodeltas },
	{ "tarballs",	  FULLCONFIG_ALL,  0, dotarballs },
	{ "startupcmd",	  FULLCONFIG_ALL,  0, dostartcmd },
	{ "startstatus",  FULLCONFIG_NONE, 0, dostartstat }, /* Before startstat*/
	{ "startstat",	  FULLCONFIG_NONE, 0, dostartstat },
	{ "readycount",   FULLCONFIG_NONE, 0, doreadycount },
	{ "ready",	  FULLCONFIG_NONE, 0, doready },
	{ "mounts",	  FULLCONFIG_ALL,  0, domounts },
	{ "sfshostid",	  FULLCONFIG_NONE, 0, dosfshostid },
	{ "loadinfo",	  FULLCONFIG_NONE, 0, doloadinfo},
	{ "reset",	  FULLCONFIG_NONE, 0, doreset},
	{ "routing",	  FULLCONFIG_ALL,  0, dorouting},
	{ "trafgens",	  FULLCONFIG_ALL,  0, dotrafgens},
	{ "nseconfigs",	  FULLCONFIG_ALL,  0, donseconfigs},
	{ "creator",	  FULLCONFIG_ALL,  0, docreator},
	{ "state",	  FULLCONFIG_NONE, 0, dostate},
	{ "tunnels",	  FULLCONFIG_ALL,  0, dotunnels},
	{ "vnodelist",	  FULLCONFIG_PHYS, 0, dovnodelist},
	{ "subnodelist",  FULLCONFIG_PHYS, 0, dosubnodelist},
	{ "isalive",	  FULLCONFIG_NONE, F_REMUDP|F_MINLOG, doisalive},
	{ "ipodinfo",	  FULLCONFIG_NONE, 0, doipodinfo},
	{ "ntpinfo",	  FULLCONFIG_PHYS, 0, dontpinfo},
	{ "ntpdrift",	  FULLCONFIG_NONE, 0, dontpdrift},
	{ "jailconfig",	  FULLCONFIG_VIRT, 0, dojailconfig},
	{ "plabconfig",	  FULLCONFIG_VIRT, 0, doplabconfig},
	{ "subconfig",	  FULLCONFIG_NONE, 0, dosubconfig},
        { "sdparams",     FULLCONFIG_PHYS, 0, doslothdparams},
        { "programs",     FULLCONFIG_ALL,  0, doprogagents},
        { "syncserver",   FULLCONFIG_ALL,  0, dosyncserver},
        { "keyhash",      FULLCONFIG_ALL,  0, dokeyhash},
        { "eventkey",     FULLCONFIG_ALL,  0, doeventkey},
        { "fullconfig",   FULLCONFIG_NONE, 0, dofullconfig},
        { "routelist",	  FULLCONFIG_PHYS, 0, doroutelist},
        { "role",	  FULLCONFIG_PHYS, 0, dorole},
        { "rusage",	  FULLCONFIG_NONE, F_REMUDP|F_MINLOG, dorusage},
295
        { "watchdoginfo", FULLCONFIG_ALL,  F_REMUDP|F_MINLOG, dodoginfo},
Mike Hibler's avatar
Mike Hibler committed
296
        { "hostkeys",     FULLCONFIG_NONE, 0, dohostkeys},
297
298
299
};
static int numcommands = sizeof(command_array)/sizeof(struct command);

300
301
302
303
char *usagestr = 
 "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"
304
 " -c num	   Specify number of servers (must be %d <= x <= %d)\n"
305
 " -v              More verbose logging\n"
306
 " -i ipaddr       Sets the boss IP addr to return (for multi-homed servers)\n"
307
308
309
310
311
 "\n";

void
usage()
{
312
	fprintf(stderr, usagestr, MINCHILDREN, MAXCHILDREN);
313
314
315
	exit(1);
}

316
317
318
319
320
static void
cleanup()
{
	signal(SIGHUP, SIG_IGN);
	killme = 1;
321
	killpg(0, SIGHUP);
322
323
}

324
325
326
327
328
329
330
331
332
static void
setverbose(int sig)
{
	signal(sig, SIG_IGN);
	
	if (sig == SIGUSR1)
		verbose = 1;
	else
		verbose = 0;
333
334
	info("verbose logging turned %s\n", verbose ? "on" : "off");

335
336
337
338
339
340
	/* Just the parent sends this */
	if (numchildren)
		killpg(0, sig);
	signal(sig, setverbose);
}

Mike Hibler's avatar
Mike Hibler committed
341
342
int
main(int argc, char **argv)
343
{
344
345
346
347
	int			tcpsock, udpsock, i, ch, foo[4];
	int			alttcpsock, altudpsock;
	int			status, pid;
	int			portnum = TBSERVER_PORT;
348
349
	FILE			*fp;
	char			buf[BUFSIZ];
350
	struct hostent		*he;
351
	extern char		build_info[];
352

353
	while ((ch = getopt(argc, argv, "dp:c:Xvi:")) != -1)
354
355
356
357
358
359
		switch(ch) {
		case 'p':
			portnum = atoi(optarg);
			break;
		case 'd':
			debug++;
360
			break;
361
362
363
		case 'c':
			maxchildren = atoi(optarg);
			break;
364
365
366
		case 'X':
			insecure = 1;
			break;
367
368
369
		case 'v':
			verbose++;
			break;
370
371
372
373
374
375
376
		case 'i':
			if (inet_aton(optarg, &myipaddr) == 0) {
				fprintf(stderr, "invalid IP address %s\n",
					optarg);
				usage();
			}
			break;
377
378
379
380
381
382
383
384
385
386
		case 'h':
		case '?':
		default:
			usage();
		}
	argc -= optind;
	argv += optind;

	if (argc)
		usage();
387
388
	if (maxchildren < MINCHILDREN || maxchildren > MAXCHILDREN)
		usage();
389

390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
#ifdef  WITHSSL
	if (tmcd_server_sslinit()) {
		error("SSL init failed!\n");
		exit(1);
	}
#endif
	if (debug) 
		loginit(0, 0);
	else {
		/* Become a daemon */
		daemon(0, 0);
		loginit(1, "tmcd");
	}
	info("daemon starting (version %d)\n", CURRENT_VERSION);
	info("%s\n", build_info);
Mike Hibler's avatar
Mike Hibler committed
405

Austin Clements's avatar
Austin Clements committed
406
407
408
409
410
411
412
413
414
415
416
417
418
419
	/*
	 * 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;
420
421
422
				if (debug) {
				    info("fshostid: %s\n", fshostid);
				}
Austin Clements's avatar
Austin Clements committed
423
424
425
426
427
428
429
430
431
			}
			else {
				error("fshostid from %s may be corrupt: %s",
				      FSHOSTID, fshostid);
			}
			fclose(fp);
		}
	}
	
432
433
434
	/*
	 * Grab our IP for security check below.
	 */
435
	if (myipaddr.s_addr == 0) {
436
#ifdef	LBS
437
		strcpy(buf, BOSSNODE);
438
#else
439
440
		if (gethostname(buf, sizeof(buf)) < 0)
			pfatal("getting hostname");
441
#endif
442
443
444
445
446
447
		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);
448
449
	}

450
451
452
453
454
455
	/*
	 * If we were given a port on the command line, don't open the 
	 * alternate ports
	 */
	if (portnum != TBSERVER_PORT) {
	    if (makesockets(portnum, &udpsock, &tcpsock) < 0) {
456
457
		error("Could not make sockets!");
		exit(1);
458
459
460
461
462
463
464
	    }
	} else {
	    if (makesockets(portnum, &udpsock, &tcpsock) < 0 ||
		makesockets(TBSERVER_PORT2, &altudpsock, &alttcpsock) < 0) {
		    error("Could not make sockets!");
		    exit(1);
	    }
465
466
467
468
469
	}

	signal(SIGTERM, cleanup);
	signal(SIGINT, cleanup);
	signal(SIGHUP, cleanup);
470
471
	signal(SIGUSR1, setverbose);
	signal(SIGUSR2, setverbose);
472
473
474
475

	/*
	 * Stash the pid away.
	 */
476
	mypid = getpid();
477
478
479
	sprintf(buf, "%s/tmcd.pid", _PATH_VARRUN);
	fp = fopen(buf, "w");
	if (fp != NULL) {
480
		fprintf(fp, "%d\n", mypid);
481
482
483
		(void) fclose(fp);
	}

484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
	/*
	 * 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);
	}

517
518
519
520
521
522
523
524
525
526
527
	/*
	 * Now fork a set of children to handle requests. We keep the
	 * pool at a set level. No need to get too fancy at this point,
	 * although this approach *is* rather bogus. 
	 */
	bzero(foo, sizeof(foo));
	while (1) {
		while (!killme && numchildren < maxchildren) {
			int which = 0;
			if (!foo[1])
				which = 1;
528
529
530
531
532
533
			else if (!debug && portnum == TBSERVER_PORT) {
				if (!foo[2])
					which = 2;
				else if (!foo[3])
					which = 3;
			}
534

535
536
537
538
539
540
541
542
543
			if ((pid = fork()) < 0) {
				errorc("forking server");
				goto done;
			}
			if (pid) {
				foo[which] = pid;
				numchildren++;
				continue;
			}
544
545
546
547
			/* Poor way of knowing parent/child */
			numchildren = 0;
			mypid = getpid();
			
548
549
550
551
552
553
			/* Child does useful work! Never Returns! */
			signal(SIGTERM, SIG_DFL);
			signal(SIGINT, SIG_DFL);
			signal(SIGHUP, SIG_DFL);
			
			switch (which) {
554
			case 0: tcpserver(tcpsock, portnum);
555
				break;
556
			case 1: udpserver(udpsock, portnum);
557
				break;
558
			case 2: udpserver(altudpsock, TBSERVER_PORT2);
559
				break;
560
			case 3: tcpserver(alttcpsock, TBSERVER_PORT2);
561
562
563
564
565
566
567
568
569
570
571
572
				break;
			}
			exit(-1);
		}

		/*
		 * Parent waits.
		 */
		pid = waitpid(-1, &status, 0);
		if (pid < 0) {
			errorc("waitpid failed");
			continue;
573
574
575
576
		} 
		if (WIFSIGNALED(status)) {
			error("server %d exited with signal %d!\n",
			      pid, WTERMSIG(status));
577
		}
578
579
580
		else if (WIFEXITED(status)) {
			error("server %d exited with status %d!\n",
			      pid, WEXITSTATUS(status));	  
581
		}
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
		numchildren--;
		for (i = 0; i < (sizeof(foo)/sizeof(int)); i++) {
			if (foo[i] == pid)
				foo[i] = 0;
		}
		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;
	int			length, i, udpsock, tcpsock;

Mike Hibler's avatar
Mike Hibler committed
606
	/*
607
	 * Setup TCP socket for incoming connections.
Mike Hibler's avatar
Mike Hibler committed
608
609
	 */

610
	/* Create socket from which to read. */
Mike Hibler's avatar
Mike Hibler committed
611
612
	tcpsock = socket(AF_INET, SOCK_STREAM, 0);
	if (tcpsock < 0) {
613
		pfatal("opening stream socket");
614
615
	}

616
	i = 1;
Mike Hibler's avatar
Mike Hibler committed
617
	if (setsockopt(tcpsock, SOL_SOCKET, SO_REUSEADDR,
618
		       (char *)&i, sizeof(i)) < 0)
619
		pwarning("setsockopt(SO_REUSEADDR)");;
620
621
622
623
	
	/* Create name. */
	name.sin_family = AF_INET;
	name.sin_addr.s_addr = INADDR_ANY;
624
	name.sin_port = htons((u_short) portnum);
Mike Hibler's avatar
Mike Hibler committed
625
	if (bind(tcpsock, (struct sockaddr *) &name, sizeof(name))) {
626
		pfatal("binding stream socket");
627
628
629
	}
	/* Find assigned port value and print it out. */
	length = sizeof(name);
Mike Hibler's avatar
Mike Hibler committed
630
	if (getsockname(tcpsock, (struct sockaddr *) &name, &length)) {
631
		pfatal("getsockname");
632
	}
633
	if (listen(tcpsock, 128) < 0) {
634
		pfatal("listen");
635
	}
636
637
	info("listening on TCP port %d\n", ntohs(name.sin_port));
	
Mike Hibler's avatar
Mike Hibler committed
638
639
640
641
642
643
644
	/*
	 * Setup UDP socket
	 */

	/* Create socket from which to read. */
	udpsock = socket(AF_INET, SOCK_DGRAM, 0);
	if (udpsock < 0) {
645
		pfatal("opening dgram socket");
Mike Hibler's avatar
Mike Hibler committed
646
647
648
649
650
	}

	i = 1;
	if (setsockopt(udpsock, SOL_SOCKET, SO_REUSEADDR,
		       (char *)&i, sizeof(i)) < 0)
651
		pwarning("setsockopt(SO_REUSEADDR)");;
Mike Hibler's avatar
Mike Hibler committed
652
653
654
655
	
	/* Create name. */
	name.sin_family = AF_INET;
	name.sin_addr.s_addr = INADDR_ANY;
656
	name.sin_port = htons((u_short) portnum);
Mike Hibler's avatar
Mike Hibler committed
657
	if (bind(udpsock, (struct sockaddr *) &name, sizeof(name))) {
658
		pfatal("binding dgram socket");
Mike Hibler's avatar
Mike Hibler committed
659
660
661
662
663
	}

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

668
669
670
	*tcpsockp = tcpsock;
	*udpsockp = udpsock;
	return 0;
671
}
672

673
/*
674
 * Listen for UDP requests. This is not a secure channel, and so this should
675
676
677
 * eventually be killed off.
 */
static void
678
udpserver(int sock, int portnum)
679
680
681
682
{
	char			buf[MYBUFSIZE];
	struct sockaddr_in	client;
	int			length, cc;
683
	unsigned int		nreq = 0;
684
	
685
	info("udpserver starting: pid=%d sock=%d\n", mypid, sock);
686
687
688
689
690

	/*
	 * Wait for udp connections.
	 */
	while (1) {
691
		setproctitle("UDP %d: %u done", portnum, nreq);
692
		length = sizeof(client);		
693
		cc = recvfrom(sock, buf, sizeof(buf) - 1,
694
695
696
			      0, (struct sockaddr *)&client, &length);
		if (cc <= 0) {
			if (cc < 0)
697
698
				errorc("Reading UDP request");
			error("UDP Connection aborted\n");
699
			continue;
700
		}
701
702
		buf[cc] = '\0';
		handle_request(sock, &client, buf, 0);
703
		nreq++;
704
705
706
707
708
	}
	exit(1);
}

/*
709
 * Listen for TCP requests.
710
711
 */
static void
712
tcpserver(int sock, int portnum)
713
714
715
716
{
	char			buf[MYBUFSIZE];
	struct sockaddr_in	client;
	int			length, cc, newsock;
717
	unsigned int		nreq = 0;
718
	
719
	info("tcpserver starting: pid=%d sock=%d\n", mypid, sock);
720
721
722
723
724

	/*
	 * Wait for TCP connections.
	 */
	while (1) {
725
		setproctitle("TCP %d: %u done", portnum, nreq);
726
		length  = sizeof(client);
727
		newsock = ACCEPT(sock, (struct sockaddr *)&client, &length);
728
		if (newsock < 0) {
729
			errorc("accepting TCP connection");
730
			continue;
731
		}
Mike Hibler's avatar
Mike Hibler committed
732
733

		/*
734
		 * Read in the command request.
Mike Hibler's avatar
Mike Hibler committed
735
		 */
736
		if ((cc = READ(newsock, buf, sizeof(buf) - 1)) <= 0) {
737
			if (cc < 0)
738
739
740
				errorc("Reading TCP request");
			error("TCP connection aborted\n");
			CLOSE(newsock);
741
			continue;
742
		}
743
744
		buf[cc] = '\0';
		handle_request(newsock, &client, buf, 1);
745
		CLOSE(newsock);
746
		nreq++;
747
748
749
	}
	exit(1);
}
Mike Hibler's avatar
Mike Hibler committed
750

751
752
753
754
static int
handle_request(int sock, struct sockaddr_in *client, char *rdata, int istcp)
{
	struct sockaddr_in redirect_client;
755
	int		   redirect = 0, havekey = 0;
756
	char		   buf[BUFSIZ], *bp, *cp;
757
	char		   privkey[TBDB_FLEN_PRIVKEY];
758
	int		   i, overbose = 0, err = 0;
759
	int		   version = DEFAULT_VERSION;
760
761
	tmcdreq_t	   tmcdreq, *reqp = &tmcdreq;

762
	byteswritten = 0;
763
764
765
766
767
768
#ifdef	WITHSSL
	cp = (istcp ? (isssl ? "SSL" : "TCP") : "UDP");
#else
	cp = (istcp ? "TCP" : "UDP");
#endif
	setproctitle("%s: %s", reqp->nodeid, cp);
769

770
771
772
773
	/*
	 * Init the req structure.
	 */
	bzero(reqp, sizeof(*reqp));
Mike Hibler's avatar
Mike Hibler committed
774

775
776
777
	/*
	 * Look for special tags. 
	 */
778
	bp = rdata;
779
	while ((bp = strsep(&rdata, " ")) != NULL) {
780
781
782
783
784
785
786
787
788
789
790
791
792
793
		/*
		 * Look for PRIVKEY. 
		 */
		if (sscanf(bp, "PRIVKEY=%64s", buf)) {
			havekey = 1;
			strncpy(privkey, buf, sizeof(privkey));

			if (debug) {
				info("PRIVKEY %s\n", buf);
			}
			continue;
		}


794
795
796
797
798
799
800
		/*
		 * Look for VERSION. 
		 */
		if (sscanf(bp, "VERSION=%d", &i) == 1) {
			version = i;
			continue;
		}
Mike Hibler's avatar
Mike Hibler committed
801

802
803
804
805
806
807
808
809
810
811
		/*
		 * Look for REDIRECT, which is a proxy request for a
		 * client other than the one making the request. Good
		 * for testing. Might become a general tmcd redirect at
		 * some point, so that we can test new tmcds.
		 */
		if (sscanf(bp, "REDIRECT=%30s", buf)) {
			redirect_client = *client;
			redirect        = 1;
			inet_aton(buf, &client->sin_addr);
812

813
814
			info("REDIRECTED from %s to %s\n",
			     inet_ntoa(redirect_client.sin_addr), buf);
815

816
817
818
819
820
821
822
823
824
825
826
			continue;
		}
		
		/*
		 * Look for VNODE. This is used for virtual nodes.
		 * It indicates which of the virtual nodes (on the physical
		 * node) is talking to us. Currently no perm checking.
		 * Very temporary approach; should be done via a per-vnode
		 * cert or a key.
		 */
		if (sscanf(bp, "VNODEID=%30s", buf)) {
827
828
			reqp->isvnode = 1;
			strncpy(reqp->vnodeid, buf, sizeof(reqp->vnodeid));
829

830
831
832
833
834
			if (debug) {
				info("VNODEID %s\n", buf);
			}
			continue;
		}
835

836
837
838
839
840
		/*
		 * An empty token (two delimiters next to each other)
		 * is indicated by a null string. If nothing matched,
		 * and its not an empty token, it must be the actual
		 * command and arguments. Break out.
Leigh B. Stoller's avatar
Leigh B. Stoller committed
841
842
843
		 *
		 * Note that rdata will point to any text after the command.
		 *
844
845
846
847
848
		 */
		if (*bp) {
			break;
		}
	}
849

850
851
852
	/* Start with default DB */
	strcpy(dbname, DEFAULT_DBNAME);

853
	/*
854
	 * Map the ip to a nodeid.
855
	 */
856
	if ((err = iptonodeid(client->sin_addr, reqp))) {
857
858
859
		if (reqp->isvnode) {
			error("No such vnode %s associated with %s\n",
			      reqp->vnodeid, inet_ntoa(client->sin_addr));
860
861
862
863
864
		}
		else {
			error("No such node: %s\n",
			      inet_ntoa(client->sin_addr));
		}
865
866
867
868
869
870
871
872
		goto skipit;
	}

	/*
	 * Redirect is allowed from the local host only!
	 * I use this for testing. See below where I test redirect
	 * if the verification fails. 
	 */
873
	if (!insecure && redirect &&
874
	    redirect_client.sin_addr.s_addr != myipaddr.s_addr) {
875
876
877
878
		char	buf1[32], buf2[32];
		
		strcpy(buf1, inet_ntoa(redirect_client.sin_addr));
		strcpy(buf2, inet_ntoa(client->sin_addr));
879
880
881

		if (verbose)
			info("%s INVALID REDIRECT: %s\n", buf1, buf2);
882
883
		goto skipit;
	}
884
885
886
887
888
889

#ifdef  WITHSSL
	/*
	 * If the connection is not SSL, then it must be a local node.
	 */
	if (isssl) {
890
891
		if (tmcd_sslverify_client(reqp->nodeid, reqp->pclass,
					  reqp->ptype,  reqp->islocal)) {
892
			error("%s: SSL verification failure\n", reqp->nodeid);
893
894
895
896
			if (! redirect)
				goto skipit;
		}
	}
897
	else if (!reqp->islocal) {
898
899
900
		if (!istcp)
			goto execute;
		
901
		error("%s: Remote node connected without SSL!\n",reqp->nodeid);
902
903
		if (!insecure)
			goto skipit;
904
	}
905
906
907
908
909
910
911
912
913
	else if (reqp->iscontrol) {
		if (!istcp)
			goto execute;

		error("%s: Control node connection without SSL!\n",
		      reqp->nodeid);
		if (!insecure)
			goto skipit;
	}
914
915
916
917
#else
	/*
	 * When not compiled for ssl, do not allow remote connections.
	 */
918
	if (!reqp->islocal) {
919
920
		error("%s: Remote node connection not allowed (Define SSL)!\n",
		      reqp->nodeid);
921
922
		if (!insecure)
			goto skipit;
923
	}
924
925
926
927
928
929
	if (reqp->iscontrol) {
		error("%s: Control node connection not allowed "
		      "(Define SSL)!\n", reqp->nodeid);
		if (!insecure)
			goto skipit;
	}
930
931
932
933
#endif
	/*
	 * Check for a redirect using the default DB. This allows
	 * for a simple redirect to a secondary DB for testing.
934
	 * Upon return, the dbname has been changed if redirected.
935
	 */
936
	if (checkdbredirect(reqp)) {
937
938
939
		/* Something went wrong */
		goto skipit;
	}
940

941
942
943
944
945
	/*
	 * Do private key check. widearea nodes must report a private key
	 * It comes over ssl of course. At present we skip this check for
	 * ron nodes. 
	 */
946
	if (!reqp->islocal) {
947
		if (!havekey) {
948
			error("%s: No privkey sent!\n", reqp->nodeid);
949
950
951
952
953
954
955
			/*
			 * Skip. Okay, the problem is that the nodes out
			 * there are not reporting the key!
			goto skipit;
			 */
		}
		else if (checkprivkey(client->sin_addr, privkey)) {
956
957
			error("%s: privkey mismatch: %s!\n",
			      reqp->nodeid, privkey);
958
959
960
961
			goto skipit;
		}
	}

962
963
964
	/*
	 * Figure out what command was given.
	 */
965
 execute:
966
	for (i = 0; i < numcommands; i++)
967
		if (strncmp(bp, command_array[i].cmdname,
968
969
			    strlen(command_array[i].cmdname)) == 0)
			break;
Mike Hibler's avatar
Mike Hibler committed
970

971
	if (i == numcommands) {
972
		info("%s: INVALID REQUEST: %.8s\n", reqp->nodeid, bp);
973
974
		goto skipit;
	}
975

976
	/*
977
978
	 * If this is a UDP request from a remote node,
	 * make sure it is allowed.
979
	 */
980
	if (!istcp && !reqp->islocal &&
981
	    (command_array[i].flags & F_REMUDP) == 0) {
982
983
984
985
986
		error("%s: Invalid request (%s) from remote node using UDP!\n",
		      reqp->nodeid, command_array[i].cmdname);
		goto skipit;
	}

987
988
989
	/*
	 * Execute it.
	 */
990
991
992
993
	if ((command_array[i].flags & F_MAXLOG) != 0) {
		overbose = verbose;
		verbose = 1;
	}
994
	if (verbose || (command_array[i].flags & F_MINLOG) == 0)
995
996
		info("%s: vers:%d %s %s\n", reqp->nodeid,
		     version, cp, command_array[i].cmdname);
997
	setproctitle("%s: %s %s", reqp->nodeid, cp, command_array[i].cmdname);
998

999
	err = command_array[i].func(sock, reqp, rdata, istcp, version);
1000
1001
1002

	if (err)
		info("%s: %s: returned %d\n",
1003
		     reqp->nodeid, command_array[i].cmdname, err);
1004
1005
	if ((command_array[i].flags & F_MAXLOG) != 0)
		verbose = overbose;
1006

1007
 skipit:
1008
1009
1010
	if (!istcp) 
		client_writeback_done(sock,
				      redirect ? &redirect_client : client);
1011

1012
	if (byteswritten && (command_array[i].flags & F_MINLOG) == 0)
1013
1014
		info("%s: %s wrote %d bytes\n",
		     reqp->nodeid, command_array[i].cmdname,
1015
1016
		     byteswritten);

1017
	return 0;
1018
1019
1020
1021
1022
}

/*
 * Accept notification of reboot. 
 */
1023
COMMAND_PROTOTYPE(doreboot)
1024
{
1025
	/*
1026
1027
	 * This is now a no-op. The things this used to do are now
	 * done by stated when we hit RELOAD/RELOADDONE state
1028
	 */
1029
1030
	return 0;
}
1031

1032
1033
1034
1035
1036
1037
1038
/*
 * Return emulab nodeid (not the experimental name).
 */
COMMAND_PROTOTYPE(donodeid)
{
	char		buf[MYBUFSIZE];

1039
	OUTPUT(buf, sizeof(buf), "%s\n", reqp->nodeid);
1040
	client_writeback(sock, buf, strlen(buf), tcp);
1041
1042
1043
1044
1045
1046
	return 0;
}

/*
 * Return status of node. Is it allocated to an experiment, or free.
 */
1047
COMMAND_PROTOTYPE(dostatus)
1048
{
Mike Hibler's avatar
Mike Hibler committed
1049
	char		buf[MYBUFSIZE];
1050
1051
1052
1053

	/*
	 * Now check reserved table
	 */
1054
1055
	if (! reqp->allocated) {
		info("STATUS: %s: Node is free\n", reqp->nodeid);
1056
1057
1058
		strcpy(buf, "FREE\n");
		client_writeback(sock, buf, strlen(buf), tcp);
		return 0;
1059
1060
	}

1061
1062
	OUTPUT(buf, sizeof(buf), "ALLOCATED=%s/%s NICKNAME=%s\n",
	       reqp->pid, reqp->eid, reqp->nickname);
1063
1064
	client_writeback(sock, buf, strlen(buf), tcp);

1065
1066
	if (verbose)
		info("STATUS: %s: %s", reqp->nodeid, buf);
1067
1068
1069
1070
1071
1072
	return 0;
}

/*
 * Return ifconfig information to client.
 */