tmcd.c 110 KB
Newer Older
Leigh B. Stoller's avatar
Leigh B. Stoller committed
1
2
/*
 * EMULAB-COPYRIGHT
3
 * Copyright (c) 2000-2003 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 TESTMODE
60
61
62
63
64
65
66
67
68
69
70
71
#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) */
72

73
74
75
#define DISKTYPE	"ad"
#define DISKNUM		0

76
77
78
79
80
81
82
/* 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"

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

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

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

115
116
117
118
119
120
121
122
/*
 * 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;
123
	int		issubnode;
124
	int		islocal;
125
	int		iscontrol;
126
	int		update_accounts;
127
128
	char		nodeid[TBDB_FLEN_NODEID];
	char		vnodeid[TBDB_FLEN_NODEID];
Leigh B. Stoller's avatar
Leigh B. Stoller committed
129
	char		pnodeid[TBDB_FLEN_NODEID]; /* XXX */
130
131
132
133
134
	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];
135
	char		class[TBDB_FLEN_NODECLASS];
136
137
        char		ptype[TBDB_FLEN_NODETYPE];	/* Of physnode */
	char		pclass[TBDB_FLEN_NODECLASS];	/* Of physnode */
138
139
	char		creator[TBDB_FLEN_UID];
	char		swapper[TBDB_FLEN_UID];
140
	char		syncserver[TBDB_FLEN_VNAME];	/* The vname */
141
	char		keyhash[TBDB_FLEN_PRIVKEY];
142
143
144
145
146
	char		testdb[256];
} tmcdreq_t;
static int	iptonodeid(struct in_addr, tmcdreq_t *);
static int	checkdbredirect(tmcdreq_t *);

147
148
149
150
151
#ifdef EVENTSYS
int			myevent_send(address_tuple_t address);
static event_handle_t	event_handle = NULL;
#endif

152
153
154
/*
 * Commands we support.
 */
155
156
#define COMMAND_PROTOTYPE(x) \
	static int \
157
	x(int sock, tmcdreq_t *reqp, char *rdata, int tcp, int vers)
158
159

COMMAND_PROTOTYPE(doreboot);
160
COMMAND_PROTOTYPE(donodeid);
161
162
163
164
COMMAND_PROTOTYPE(dostatus);
COMMAND_PROTOTYPE(doifconfig);
COMMAND_PROTOTYPE(doaccounts);
COMMAND_PROTOTYPE(dodelay);
165
COMMAND_PROTOTYPE(dolinkdelay);
166
167
168
169
170
171
172
173
174
175
176
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(dolog);
COMMAND_PROTOTYPE(domounts);
Austin Clements's avatar
Austin Clements committed
177
COMMAND_PROTOTYPE(dosfshostid);
178
179
180
181
182
183
184
COMMAND_PROTOTYPE(doloadinfo);
COMMAND_PROTOTYPE(doreset);
COMMAND_PROTOTYPE(dorouting);
COMMAND_PROTOTYPE(dotrafgens);
COMMAND_PROTOTYPE(donseconfigs);
COMMAND_PROTOTYPE(dostate);
COMMAND_PROTOTYPE(docreator);
185
COMMAND_PROTOTYPE(dotunnels);
186
COMMAND_PROTOTYPE(dovnodelist);
187
COMMAND_PROTOTYPE(dosubnodelist);
188
COMMAND_PROTOTYPE(doisalive);
189
COMMAND_PROTOTYPE(doipodinfo);
190
COMMAND_PROTOTYPE(doatarball);
191
COMMAND_PROTOTYPE(doanrpm);
192
193
COMMAND_PROTOTYPE(dontpinfo);
COMMAND_PROTOTYPE(dontpdrift);
194
COMMAND_PROTOTYPE(dojailconfig);
195
COMMAND_PROTOTYPE(doplabconfig);
196
197
COMMAND_PROTOTYPE(dosubconfig);
COMMAND_PROTOTYPE(doixpconfig);
198
COMMAND_PROTOTYPE(doslothdparams);
199
COMMAND_PROTOTYPE(doprogagents);
200
COMMAND_PROTOTYPE(dosyncserver);
201
COMMAND_PROTOTYPE(dokeyhash);
202
203
204

struct command {
	char	*cmdname;
205
	int    (*func)(int, tmcdreq_t *, char *, int, int);
206
} command_array[] = {
207
	{ "reboot",	doreboot },
208
	{ "nodeid",	donodeid },
209
210
211
212
	{ "status",	dostatus },
	{ "ifconfig",	doifconfig },
	{ "accounts",	doaccounts },
	{ "delay",	dodelay },
213
	{ "linkdelay",	dolinkdelay },
214
215
	{ "hostnamesV2",dohostsV2 },	/* This will go away */
	{ "hostnames",	dohosts },
216
	{ "rpms",	dorpms },
217
	{ "deltas",	dodeltas },
218
	{ "tarballs",	dotarballs },
219
	{ "startupcmd",	dostartcmd },
Mike Hibler's avatar
Mike Hibler committed
220
	{ "startstatus",dostartstat }, /* Leave this before "startstat" */
221
	{ "startstat",	dostartstat },
222
223
	{ "readycount", doreadycount },
	{ "ready",	doready },
Mike Hibler's avatar
Mike Hibler committed
224
	{ "log",	dolog },
225
	{ "mounts",	domounts },
Austin Clements's avatar
Austin Clements committed
226
	{ "sfshostid",	dosfshostid },
227
	{ "loadinfo",	doloadinfo},
Robert Ricci's avatar
Robert Ricci committed
228
	{ "reset",	doreset},
229
	{ "routing",	dorouting},
230
	{ "trafgens",	dotrafgens},
231
	{ "nseconfigs",	donseconfigs},
232
	{ "creator",	docreator},
233
	{ "state",	dostate},
234
	{ "tunnels",	dotunnels},
235
	{ "vnodelist",	dovnodelist},
236
	{ "subnodelist",dosubnodelist},
237
	{ "isalive",	doisalive},
238
	{ "ipodinfo",	doipodinfo},
239
240
241
	{ "ntpinfo",	dontpinfo},
	{ "ntpdrift",	dontpdrift},
	{ "tarball",	doatarball},
242
	{ "rpm",	doanrpm},
243
	{ "jailconfig",	dojailconfig},
244
	{ "plabconfig",	doplabconfig},
245
	{ "subconfig",	dosubconfig},
246
        { "sdparams",   doslothdparams},
247
        { "programs",   doprogagents},
248
        { "syncserver", dosyncserver},
249
        { "keyhash",    dokeyhash},
250
251
252
};
static int numcommands = sizeof(command_array)/sizeof(struct command);

253
254
255
256
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"
257
 " -c num	   Specify number of servers (must be %d <= x <= %d)\n"
258
259
260
261
262
 "\n";

void
usage()
{
263
	fprintf(stderr, usagestr, MINCHILDREN, MAXCHILDREN);
264
265
266
	exit(1);
}

267
268
269
270
271
static void
cleanup()
{
	signal(SIGHUP, SIG_IGN);
	killme = 1;
272
	killpg(0, SIGHUP);
273
274
}

275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
static void
setverbose(int sig)
{
	signal(sig, SIG_IGN);
	
	if (sig == SIGUSR1)
		verbose = 1;
	else
		verbose = 0;
	/* Just the parent sends this */
	if (numchildren)
		killpg(0, sig);
	signal(sig, setverbose);
}

Mike Hibler's avatar
Mike Hibler committed
290
291
int
main(int argc, char **argv)
292
{
293
294
295
296
	int			tcpsock, udpsock, i, ch, foo[4];
	int			alttcpsock, altudpsock;
	int			status, pid;
	int			portnum = TBSERVER_PORT;
297
298
	FILE			*fp;
	char			buf[BUFSIZ];
299
	struct hostent		*he;
300
	extern char		build_info[];
301

302
	while ((ch = getopt(argc, argv, "dp:c:Xv")) != -1)
303
304
305
306
307
308
		switch(ch) {
		case 'p':
			portnum = atoi(optarg);
			break;
		case 'd':
			debug++;
309
			break;
310
311
312
		case 'c':
			maxchildren = atoi(optarg);
			break;
313
314
315
		case 'X':
			insecure = 1;
			break;
316
317
318
		case 'v':
			verbose++;
			break;
319
320
321
322
323
324
325
326
327
328
		case 'h':
		case '?':
		default:
			usage();
		}
	argc -= optind;
	argv += optind;

	if (argc)
		usage();
329
330
	if (maxchildren < MINCHILDREN || maxchildren > MAXCHILDREN)
		usage();
331

332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
#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
347

Austin Clements's avatar
Austin Clements committed
348
349
350
351
352
353
354
355
356
357
358
359
360
361
	/*
	 * 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;
362
363
364
				if (debug) {
				    info("fshostid: %s\n", fshostid);
				}
Austin Clements's avatar
Austin Clements committed
365
366
367
368
369
370
371
372
373
			}
			else {
				error("fshostid from %s may be corrupt: %s",
				      FSHOSTID, fshostid);
			}
			fclose(fp);
		}
	}
	
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
	/*
	 * Grab our IP for security check below.
	 */
#ifdef	LBS
	strcpy(buf, BOSSNODE);
#else
	if (gethostname(buf, sizeof(buf)) < 0)
		pfatal("getting hostname");
#endif
	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);

389
390
391
392
393
394
	/*
	 * 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) {
395
396
		error("Could not make sockets!");
		exit(1);
397
398
399
400
401
402
403
	    }
	} else {
	    if (makesockets(portnum, &udpsock, &tcpsock) < 0 ||
		makesockets(TBSERVER_PORT2, &altudpsock, &alttcpsock) < 0) {
		    error("Could not make sockets!");
		    exit(1);
	    }
404
405
406
407
408
	}

	signal(SIGTERM, cleanup);
	signal(SIGINT, cleanup);
	signal(SIGHUP, cleanup);
409
410
	signal(SIGUSR1, setverbose);
	signal(SIGUSR2, setverbose);
411
412
413
414

	/*
	 * Stash the pid away.
	 */
415
	mypid = getpid();
416
417
418
	sprintf(buf, "%s/tmcd.pid", _PATH_VARRUN);
	fp = fopen(buf, "w");
	if (fp != NULL) {
419
		fprintf(fp, "%d\n", mypid);
420
421
422
423
424
425
426
427
428
429
430
431
432
433
		(void) fclose(fp);
	}

	/*
	 * 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;
434
			else if (!debug && !foo[2])
435
				which = 2;
436
			else if (!debug && !foo[3])
437
				which = 3;
438

439
440
441
442
443
444
445
446
447
			if ((pid = fork()) < 0) {
				errorc("forking server");
				goto done;
			}
			if (pid) {
				foo[which] = pid;
				numchildren++;
				continue;
			}
448
449
450
451
			/* Poor way of knowing parent/child */
			numchildren = 0;
			mypid = getpid();
			
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
			/* Child does useful work! Never Returns! */
			signal(SIGTERM, SIG_DFL);
			signal(SIGINT, SIG_DFL);
			signal(SIGHUP, SIG_DFL);
			
			switch (which) {
			case 0: tcpserver(tcpsock);
				break;
			case 1: udpserver(udpsock);
				break;
			case 2: udpserver(altudpsock);
				break;
			case 3: tcpserver(alttcpsock);
				break;
			}
			exit(-1);
		}

		/*
		 * Parent waits.
		 */
		pid = waitpid(-1, &status, 0);
		if (pid < 0) {
			errorc("waitpid failed");
			continue;
477
478
479
480
		} 
		if (WIFSIGNALED(status)) {
			error("server %d exited with signal %d!\n",
			      pid, WTERMSIG(status));
481
		}
482
483
484
		else if (WIFEXITED(status)) {
			error("server %d exited with status %d!\n",
			      pid, WEXITSTATUS(status));	  
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
		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
510
	/*
511
	 * Setup TCP socket for incoming connections.
Mike Hibler's avatar
Mike Hibler committed
512
513
	 */

514
	/* Create socket from which to read. */
Mike Hibler's avatar
Mike Hibler committed
515
516
	tcpsock = socket(AF_INET, SOCK_STREAM, 0);
	if (tcpsock < 0) {
517
		pfatal("opening stream socket");
518
519
	}

520
	i = 1;
Mike Hibler's avatar
Mike Hibler committed
521
	if (setsockopt(tcpsock, SOL_SOCKET, SO_REUSEADDR,
522
		       (char *)&i, sizeof(i)) < 0)
523
		pwarning("setsockopt(SO_REUSEADDR)");;
524
525
526
527
	
	/* Create name. */
	name.sin_family = AF_INET;
	name.sin_addr.s_addr = INADDR_ANY;
528
	name.sin_port = htons((u_short) portnum);
Mike Hibler's avatar
Mike Hibler committed
529
	if (bind(tcpsock, (struct sockaddr *) &name, sizeof(name))) {
530
		pfatal("binding stream socket");
531
532
533
	}
	/* Find assigned port value and print it out. */
	length = sizeof(name);
Mike Hibler's avatar
Mike Hibler committed
534
	if (getsockname(tcpsock, (struct sockaddr *) &name, &length)) {
535
		pfatal("getsockname");
536
	}
537
	if (listen(tcpsock, 128) < 0) {
538
		pfatal("listen");
539
	}
540
541
	info("listening on TCP port %d\n", ntohs(name.sin_port));
	
Mike Hibler's avatar
Mike Hibler committed
542
543
544
545
546
547
548
	/*
	 * Setup UDP socket
	 */

	/* Create socket from which to read. */
	udpsock = socket(AF_INET, SOCK_DGRAM, 0);
	if (udpsock < 0) {
549
		pfatal("opening dgram socket");
Mike Hibler's avatar
Mike Hibler committed
550
551
552
553
554
	}

	i = 1;
	if (setsockopt(udpsock, SOL_SOCKET, SO_REUSEADDR,
		       (char *)&i, sizeof(i)) < 0)
555
		pwarning("setsockopt(SO_REUSEADDR)");;
Mike Hibler's avatar
Mike Hibler committed
556
557
558
559
	
	/* Create name. */
	name.sin_family = AF_INET;
	name.sin_addr.s_addr = INADDR_ANY;
560
	name.sin_port = htons((u_short) portnum);
Mike Hibler's avatar
Mike Hibler committed
561
	if (bind(udpsock, (struct sockaddr *) &name, sizeof(name))) {
562
		pfatal("binding dgram socket");
Mike Hibler's avatar
Mike Hibler committed
563
564
565
566
567
	}

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

572
573
574
	*tcpsockp = tcpsock;
	*udpsockp = udpsock;
	return 0;
575
}
576

577
/*
578
 * Listen for UDP requests. This is not a secure channel, and so this should
579
580
581
582
583
584
585
586
587
 * eventually be killed off.
 */
static void
udpserver(int sock)
{
	char			buf[MYBUFSIZE];
	struct sockaddr_in	client;
	int			length, cc;
	
588
	info("udpserver starting: pid=%d sock=%d\n", mypid, sock);
589
590
591
592
593
594

	/*
	 * Wait for udp connections.
	 */
	while (1) {
		length = sizeof(client);		
595
		cc = recvfrom(sock, buf, sizeof(buf) - 1,
596
597
598
			      0, (struct sockaddr *)&client, &length);
		if (cc <= 0) {
			if (cc < 0)
599
600
				errorc("Reading UDP request");
			error("UDP Connection aborted\n");
601
			continue;
602
		}
603
604
605
606
607
608
609
		buf[cc] = '\0';
		handle_request(sock, &client, buf, 0);
	}
	exit(1);
}

/*
610
 * Listen for TCP requests.
611
612
613
614
615
616
617
618
 */
static void
tcpserver(int sock)
{
	char			buf[MYBUFSIZE];
	struct sockaddr_in	client;
	int			length, cc, newsock;
	
619
	info("tcpserver starting: pid=%d sock=%d\n", mypid, sock);
620
621
622
623
624
625

	/*
	 * Wait for TCP connections.
	 */
	while (1) {
		length  = sizeof(client);
626
		newsock = ACCEPT(sock, (struct sockaddr *)&client, &length);
627
		if (newsock < 0) {
628
			errorc("accepting TCP connection");
629
			continue;
630
		}
Mike Hibler's avatar
Mike Hibler committed
631
632

		/*
633
		 * Read in the command request.
Mike Hibler's avatar
Mike Hibler committed
634
		 */
635
		if ((cc = READ(newsock, buf, sizeof(buf) - 1)) <= 0) {
636
			if (cc < 0)
637
638
639
				errorc("Reading TCP request");
			error("TCP connection aborted\n");
			CLOSE(newsock);
640
			continue;
641
		}
642
643
		buf[cc] = '\0';
		handle_request(newsock, &client, buf, 1);
644
		CLOSE(newsock);
645
646
647
	}
	exit(1);
}
Mike Hibler's avatar
Mike Hibler committed
648

649
650
651
652
static int
handle_request(int sock, struct sockaddr_in *client, char *rdata, int istcp)
{
	struct sockaddr_in redirect_client;
653
	int		   redirect = 0, havekey = 0;
654
	char		   buf[BUFSIZ], *bp, *cp;
655
	char		   privkey[TBDB_FLEN_PRIVKEY];
656
	int		   i, err = 0;
657
	int		   version = DEFAULT_VERSION;
658
659
	tmcdreq_t	   tmcdreq, *reqp = &tmcdreq;

660
661
	byteswritten = 0;

662
663
664
665
	/*
	 * Init the req structure.
	 */
	bzero(reqp, sizeof(*reqp));
Mike Hibler's avatar
Mike Hibler committed
666

667
668
669
	/*
	 * Look for special tags. 
	 */
670
	bp = rdata;
671
	while ((bp = strsep(&rdata, " ")) != NULL) {
672
673
674
675
676
677
678
679
680
681
682
683
684
685
		/*
		 * Look for PRIVKEY. 
		 */
		if (sscanf(bp, "PRIVKEY=%64s", buf)) {
			havekey = 1;
			strncpy(privkey, buf, sizeof(privkey));

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


686
687
688
689
690
691
692
		/*
		 * Look for VERSION. 
		 */
		if (sscanf(bp, "VERSION=%d", &i) == 1) {
			version = i;
			continue;
		}
Mike Hibler's avatar
Mike Hibler committed
693

694
695
696
697
698
699
700
701
702
703
		/*
		 * 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);
704

705
706
			info("REDIRECTED from %s to %s\n",
			     inet_ntoa(redirect_client.sin_addr), buf);
707

708
709
710
711
712
713
714
715
716
717
718
			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)) {
719
720
			reqp->isvnode = 1;
			strncpy(reqp->vnodeid, buf, sizeof(reqp->vnodeid));
721

722
723
724
725
726
			if (debug) {
				info("VNODEID %s\n", buf);
			}
			continue;
		}
727

728
729
730
731
732
		/*
		 * 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
733
734
735
		 *
		 * Note that rdata will point to any text after the command.
		 *
736
737
738
739
740
		 */
		if (*bp) {
			break;
		}
	}
741

742
743
744
	/* Start with default DB */
	strcpy(dbname, DEFAULT_DBNAME);

745
	/*
746
	 * Map the ip to a nodeid.
747
	 */
748
	if ((err = iptonodeid(client->sin_addr, reqp))) {
749
750
		if (err == 2) {
			error("No such node vnode mapping %s on %s\n",
751
			      reqp->vnodeid, reqp->nodeid);
752
753
754
755
756
		}
		else {
			error("No such node: %s\n",
			      inet_ntoa(client->sin_addr));
		}
757
758
759
760
761
762
763
764
		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. 
	 */
765
	if (!insecure && redirect &&
766
	    redirect_client.sin_addr.s_addr != myipaddr.s_addr) {
767
768
769
770
		char	buf1[32], buf2[32];
		
		strcpy(buf1, inet_ntoa(redirect_client.sin_addr));
		strcpy(buf2, inet_ntoa(client->sin_addr));
771
772
773

		if (verbose)
			info("%s INVALID REDIRECT: %s\n", buf1, buf2);
774
775
		goto skipit;
	}
776
777
778
779
780
781

#ifdef  WITHSSL
	/*
	 * If the connection is not SSL, then it must be a local node.
	 */
	if (isssl) {
782
783
		if (tmcd_sslverify_client(reqp->nodeid, reqp->pclass,
					  reqp->ptype,  reqp->islocal)) {
784
			error("%s: SSL verification failure\n", reqp->nodeid);
785
786
787
788
			if (! redirect)
				goto skipit;
		}
	}
789
	else if (!reqp->islocal) {
790
791
792
		if (!istcp)
			goto execute;
		
793
		error("%s: Remote node connected without SSL!\n",reqp->nodeid);
794
795
		if (!insecure)
			goto skipit;
796
	}
797
798
799
800
801
802
803
804
805
	else if (reqp->iscontrol) {
		if (!istcp)
			goto execute;

		error("%s: Control node connection without SSL!\n",
		      reqp->nodeid);
		if (!insecure)
			goto skipit;
	}
806
807
808
809
#else
	/*
	 * When not compiled for ssl, do not allow remote connections.
	 */
810
	if (!reqp->islocal) {
811
812
		error("%s: Remote node connection not allowed (Define SSL)!\n",
		      reqp->nodeid);
813
814
		if (!insecure)
			goto skipit;
815
	}
816
817
818
819
820
821
	if (reqp->iscontrol) {
		error("%s: Control node connection not allowed "
		      "(Define SSL)!\n", reqp->nodeid);
		if (!insecure)
			goto skipit;
	}
822
823
824
825
#endif
	/*
	 * Check for a redirect using the default DB. This allows
	 * for a simple redirect to a secondary DB for testing.
826
	 * Upon return, the dbname has been changed if redirected.
827
	 */
828
	if (checkdbredirect(reqp)) {
829
830
831
		/* Something went wrong */
		goto skipit;
	}
832

833
834
835
836
837
	/*
	 * 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. 
	 */
838
	if (!reqp->islocal) {
839
		if (!havekey) {
840
			error("%s: No privkey sent!\n", reqp->nodeid);
841
842
843
844
845
846
847
			/*
			 * Skip. Okay, the problem is that the nodes out
			 * there are not reporting the key!
			goto skipit;
			 */
		}
		else if (checkprivkey(client->sin_addr, privkey)) {
848
849
			error("%s: privkey mismatch: %s!\n",
			      reqp->nodeid, privkey);
850
851
852
853
			goto skipit;
		}
	}

854
855
856
	/*
	 * Figure out what command was given.
	 */
857
 execute:
858
	for (i = 0; i < numcommands; i++)
859
		if (strncmp(bp, command_array[i].cmdname,
860
861
			    strlen(command_array[i].cmdname)) == 0)
			break;
Mike Hibler's avatar
Mike Hibler committed
862

863
	if (i == numcommands) {
864
		info("%s: INVALID REQUEST: %.8s\n", reqp->nodeid, bp);
865
866
		goto skipit;
	}
867

868
869
870
871
872
873
874
875
876
	/*
	 * XXX: We allow remote nodes to use UDP for isalive only!
	 */
	if (!istcp && !reqp->islocal && command_array[i].func != doisalive) {
		error("%s: Invalid request (%s) from remote node using UDP!\n",
		      reqp->nodeid, command_array[i].cmdname);
		goto skipit;
	}

877
878
879
	/*
	 * Execute it.
	 */
880
#ifdef	WITHSSL
881
	cp = (isssl ? "SSL" : (istcp ? "TCP" : "UDP"));
882
#else
883
	cp = (istcp ? "TCP" : "UDP");
884
#endif
885
886
887
888
889
	/*
	 * XXX hack, don't log "log" contents,
	 * both for privacy and to keep our syslog smaller.
	 */
	if (command_array[i].func == dolog)
890
891
		info("%s: vers:%d %s log %d chars\n",
		     reqp->nodeid, version, cp, strlen(rdata));
892
	else if (command_array[i].func != doisalive || verbose)
893
894
		info("%s: vers:%d %s %s\n", reqp->nodeid,
		     version, cp, command_array[i].cmdname);
895

896
	err = command_array[i].func(sock, reqp, rdata, istcp, version);
897
898
899

	if (err)
		info("%s: %s: returned %d\n",
900
		     reqp->nodeid, command_array[i].cmdname, err);
901

902
 skipit:
903
904
905
	if (!istcp) 
		client_writeback_done(sock,
				      redirect ? &redirect_client : client);
906
	if (byteswritten)
907
908
		info("%s: %s wrote %d bytes\n",
		     reqp->nodeid, command_array[i].cmdname,
909
910
		     byteswritten);

911
	return 0;
912
913
914
915
916
}

/*
 * Accept notification of reboot. 
 */
917
COMMAND_PROTOTYPE(doreboot)
918
{
919
	/*
920
921
	 * This is now a no-op. The things this used to do are now
	 * done by stated when we hit RELOAD/RELOADDONE state
922
	 */
923
924
	return 0;
}
925

926
927
928
929
930
931
932
/*
 * Return emulab nodeid (not the experimental name).
 */
COMMAND_PROTOTYPE(donodeid)
{
	char		buf[MYBUFSIZE];

933
	sprintf(buf, "%s\n", reqp->nodeid);
934
	client_writeback(sock, buf, strlen(buf), tcp);
935
936
937
938
939
940
	return 0;
}

/*
 * Return status of node. Is it allocated to an experiment, or free.
 */
941
COMMAND_PROTOTYPE(dostatus)
942
{
Mike Hibler's avatar
Mike Hibler committed
943
	char		buf[MYBUFSIZE];
944
945
946
947

	/*
	 * Now check reserved table
	 */
948
949
	if (! reqp->allocated) {
		info("STATUS: %s: Node is free\n", reqp->nodeid);
950
951
952
		strcpy(buf, "FREE\n");
		client_writeback(sock, buf, strlen(buf), tcp);
		return 0;
953
954
	}

955
956
	sprintf(buf, "ALLOCATED=%s/%s NICKNAME=%s\n",
		reqp->pid, reqp->eid, reqp->nickname);
957
958
	client_writeback(sock, buf, strlen(buf), tcp);

959
960
	if (verbose)
		info("STATUS: %s: %s", reqp->nodeid, buf);
961
962
963
964
965
966
	return 0;
}

/*
 * Return ifconfig information to client.
 */
967
COMMAND_PROTOTYPE(doifconfig)
968
969
970
{
	MYSQL_RES	*res;	
	MYSQL_ROW	row;
Mike Hibler's avatar
Mike Hibler committed
971
	char		buf[MYBUFSIZE];
972
	int		nrows;
973
974
975
976

	/*
	 * Now check reserved table
	 */
977
	if (! reqp->allocated) {
978
979
		if (verbose)
			info("IFCONFIG: %s: Node is free\n", reqp->nodeid);
980
981
982
		return 1;
	}

Leigh B. Stoller's avatar
Leigh B. Stoller committed
983
984
	/*
	 * Virtual nodes, do not return interface table info. No point.
985
986
	 * Subnode are slightly different. This test might need to be
	 * smarter?
Leigh B. Stoller's avatar
Leigh B. Stoller committed
987
	 */
988
	if (reqp->isvnode && !reqp->issubnode)
Leigh B. Stoller's avatar
Leigh B. Stoller committed
989
990
		goto doveths;

991
992
993
	/*
	 * Find all the interfaces.
	 */
994
	res = mydb_query("select card,IP,IPalias,MAC,current_speed,duplex, "
995
			 " IPaliases,iface,role,mask "
996
			 "from interfaces where node_id='%s'",
997
			 10, reqp->nodeid);
998
	if (!res) {
999
1000
		error("IFCONFIG: %s: DB Error getting interfaces!\n",
		      reqp->nodeid);
1001
1002
1003
1004
		return 1;
	}

	if ((nrows = (int)mysql_num_rows(res)) == 0) {
1005
		error("IFCONFIG: %s: No interfaces!\n", reqp->nodeid);
1006
1007
1008
1009
1010
1011
		mysql_free_result(res);
		return 1;
	}
	while (nrows) {
		row = mysql_fetch_row(res);
		if (row[1] && row[1][0]) {
1012
1013
1014
			int  card    = atoi(row[0]);
			char *iface  = row[7];
			char *role   = row[8];
1015
1016
1017
			char *speed  = "100";
			char *unit   = "Mbps";
			char *duplex = "full";
1018
			char *mask;
1019

1020
			/* Never for the control net; sharks are dead */
1021
			if (strcmp(role, TBDB_IFACEROLE_EXPERIMENT))
1022
1023
				goto skipit;

1024
1025
1026
			/* Do this after above test to avoid error in log */
			mask = CHECKMASK(row[9]);

1027
			/*
1028
			 * Speed and duplex if not the default.
1029
			 */
1030
1031
1032
1033
			if (row[4] && row[4][0])
				speed = row[4];
			if (row[5] && row[5][0])
				duplex = row[5];
1034

1035
			/*
1036
1037
			 * We now use the MAC to determine the interface, but
			 * older images still want that tag at the front.
1038
			 */
1039
1040
1041
1042
1043
			if (vers < 10)
			  sprintf(buf, "INTERFACE=%d ", card);
			else 
			  sprintf(buf, "IFACETYPE=eth ");
			
1044
			sprintf(&buf[strlen(buf)],
1045
				"INET=%s MASK=%s MAC=%s SPEED=%s%s DUPLEX=%s",