tmcd.c 107 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
60
61
#define TESTMODE
#define NETMASK		"255.255.255.0"

62
63
64
#define DISKTYPE	"ad"
#define DISKNUM		0

65
66
67
68
69
70
71
/* 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"

72
73
/* Defined in configure and passed in via the makefile */
#define DBNAME_SIZE	64
Austin Clements's avatar
Austin Clements committed
74
#define HOSTID_SIZE	(32+64)
75
76
#define DEFAULT_DBNAME	TBDBNAME

77
int		debug = 0;
78
static int	verbose = 0;
79
static int	insecure = 0;
80
static int	byteswritten = 0;
81
static char     dbname[DBNAME_SIZE];
82
static struct in_addr myipaddr;
Austin Clements's avatar
Austin Clements committed
83
static char	fshostid[HOSTID_SIZE];
Leigh B. Stoller's avatar
Leigh B. Stoller committed
84
static int	nodeidtoexp(char *nodeid, char *pid, char *eid, char *gid);
85
static int	checkprivkey(struct in_addr, char *);
86
87
88
static void	tcpserver(int sock);
static void	udpserver(int sock);
static int      handle_request(int, struct sockaddr_in *, char *, int);
89
static int	makesockets(int portnum, int *udpsockp, int *tcpsockp);
Mike Hibler's avatar
Mike Hibler committed
90
91
int		client_writeback(int sock, void *buf, int len, int tcp);
void		client_writeback_done(int sock, struct sockaddr_in *client);
92
93
MYSQL_RES *	mydb_query(char *query, int ncols, ...);
int		mydb_update(char *query, ...);
94
static int	safesymlink(char *name1, char *name2);
95

96
/* thread support */
97
#define MAXCHILDREN	20
98
99
#define MINCHILDREN	5
static int	numchildren;
100
static int	maxchildren = 10;
101
static int	mypid;
102
static volatile int killme;
103

104
105
106
107
108
109
110
111
/*
 * 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;
112
	int		issubnode;
113
	int		islocal;
114
	int		iscontrol;
115
	int		update_accounts;
116
117
	char		nodeid[TBDB_FLEN_NODEID];
	char		vnodeid[TBDB_FLEN_NODEID];
Leigh B. Stoller's avatar
Leigh B. Stoller committed
118
	char		pnodeid[TBDB_FLEN_NODEID]; /* XXX */
119
120
121
122
123
	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];
124
	char		class[TBDB_FLEN_NODECLASS];
125
126
        char		ptype[TBDB_FLEN_NODETYPE];	/* Of physnode */
	char		pclass[TBDB_FLEN_NODECLASS];	/* Of physnode */
127
128
	char		creator[TBDB_FLEN_UID];
	char		swapper[TBDB_FLEN_UID];
129
	char		syncserver[TBDB_FLEN_VNAME];	/* The vname */
130
131
132
133
134
	char		testdb[256];
} tmcdreq_t;
static int	iptonodeid(struct in_addr, tmcdreq_t *);
static int	checkdbredirect(tmcdreq_t *);

135
136
137
138
139
#ifdef EVENTSYS
int			myevent_send(address_tuple_t address);
static event_handle_t	event_handle = NULL;
#endif

140
141
142
/*
 * Commands we support.
 */
143
144
#define COMMAND_PROTOTYPE(x) \
	static int \
145
	x(int sock, tmcdreq_t *reqp, char *rdata, int tcp, int vers)
146
147

COMMAND_PROTOTYPE(doreboot);
148
COMMAND_PROTOTYPE(donodeid);
149
150
151
152
COMMAND_PROTOTYPE(dostatus);
COMMAND_PROTOTYPE(doifconfig);
COMMAND_PROTOTYPE(doaccounts);
COMMAND_PROTOTYPE(dodelay);
153
COMMAND_PROTOTYPE(dolinkdelay);
154
155
156
157
158
159
160
161
162
163
164
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
165
COMMAND_PROTOTYPE(dosfshostid);
166
167
168
169
170
171
172
COMMAND_PROTOTYPE(doloadinfo);
COMMAND_PROTOTYPE(doreset);
COMMAND_PROTOTYPE(dorouting);
COMMAND_PROTOTYPE(dotrafgens);
COMMAND_PROTOTYPE(donseconfigs);
COMMAND_PROTOTYPE(dostate);
COMMAND_PROTOTYPE(docreator);
173
COMMAND_PROTOTYPE(dotunnels);
174
COMMAND_PROTOTYPE(dovnodelist);
175
COMMAND_PROTOTYPE(dosubnodelist);
176
COMMAND_PROTOTYPE(doisalive);
177
COMMAND_PROTOTYPE(doipodinfo);
178
179
180
COMMAND_PROTOTYPE(doatarball);
COMMAND_PROTOTYPE(dontpinfo);
COMMAND_PROTOTYPE(dontpdrift);
181
COMMAND_PROTOTYPE(dojailconfig);
182
COMMAND_PROTOTYPE(doplabconfig);
183
184
COMMAND_PROTOTYPE(dosubconfig);
COMMAND_PROTOTYPE(doixpconfig);
185
COMMAND_PROTOTYPE(doslothdparams);
186
COMMAND_PROTOTYPE(doprogagents);
187
COMMAND_PROTOTYPE(dosyncserver);
188
189
190

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

237
238
239
240
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"
241
 " -c num	   Specify number of servers (must be %d <= x <= %d)\n"
242
243
244
245
246
 "\n";

void
usage()
{
247
	fprintf(stderr, usagestr, MINCHILDREN, MAXCHILDREN);
248
249
250
	exit(1);
}

251
252
253
254
255
static void
cleanup()
{
	signal(SIGHUP, SIG_IGN);
	killme = 1;
256
	killpg(0, SIGHUP);
257
258
}

259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
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
274
275
int
main(int argc, char **argv)
276
{
277
278
279
280
	int			tcpsock, udpsock, i, ch, foo[4];
	int			alttcpsock, altudpsock;
	int			status, pid;
	int			portnum = TBSERVER_PORT;
281
282
	FILE			*fp;
	char			buf[BUFSIZ];
283
	struct hostent		*he;
284
	extern char		build_info[];
285

286
	while ((ch = getopt(argc, argv, "dp:c:Xv")) != -1)
287
288
289
290
291
292
		switch(ch) {
		case 'p':
			portnum = atoi(optarg);
			break;
		case 'd':
			debug++;
293
			break;
294
295
296
		case 'c':
			maxchildren = atoi(optarg);
			break;
297
298
299
		case 'X':
			insecure = 1;
			break;
300
301
302
		case 'v':
			verbose++;
			break;
303
304
305
306
307
308
309
310
311
312
		case 'h':
		case '?':
		default:
			usage();
		}
	argc -= optind;
	argv += optind;

	if (argc)
		usage();
313
314
	if (maxchildren < MINCHILDREN || maxchildren > MAXCHILDREN)
		usage();
315

316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
#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
331

Austin Clements's avatar
Austin Clements committed
332
333
334
335
336
337
338
339
340
341
342
343
344
345
	/*
	 * 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;
346
347
348
				if (debug) {
				    info("fshostid: %s\n", fshostid);
				}
Austin Clements's avatar
Austin Clements committed
349
350
351
352
353
354
355
356
357
			}
			else {
				error("fshostid from %s may be corrupt: %s",
				      FSHOSTID, fshostid);
			}
			fclose(fp);
		}
	}
	
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
	/*
	 * 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);

373
374
375
376
377
378
	/*
	 * 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) {
379
380
		error("Could not make sockets!");
		exit(1);
381
382
383
384
385
386
387
	    }
	} else {
	    if (makesockets(portnum, &udpsock, &tcpsock) < 0 ||
		makesockets(TBSERVER_PORT2, &altudpsock, &alttcpsock) < 0) {
		    error("Could not make sockets!");
		    exit(1);
	    }
388
389
390
391
392
	}

	signal(SIGTERM, cleanup);
	signal(SIGINT, cleanup);
	signal(SIGHUP, cleanup);
393
394
	signal(SIGUSR1, setverbose);
	signal(SIGUSR2, setverbose);
395
396
397
398

	/*
	 * Stash the pid away.
	 */
399
	mypid = getpid();
400
401
402
	sprintf(buf, "%s/tmcd.pid", _PATH_VARRUN);
	fp = fopen(buf, "w");
	if (fp != NULL) {
403
		fprintf(fp, "%d\n", mypid);
404
405
406
407
408
409
410
411
412
413
414
415
416
417
		(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;
418
			else if (!debug && !foo[2])
419
				which = 2;
420
			else if (!debug && !foo[3])
421
				which = 3;
422

423
424
425
426
427
428
429
430
431
			if ((pid = fork()) < 0) {
				errorc("forking server");
				goto done;
			}
			if (pid) {
				foo[which] = pid;
				numchildren++;
				continue;
			}
432
433
434
435
			/* Poor way of knowing parent/child */
			numchildren = 0;
			mypid = getpid();
			
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
			/* 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;
461
462
463
464
		} 
		if (WIFSIGNALED(status)) {
			error("server %d exited with signal %d!\n",
			      pid, WTERMSIG(status));
465
		}
466
467
468
		else if (WIFEXITED(status)) {
			error("server %d exited with status %d!\n",
			      pid, WEXITSTATUS(status));	  
469
		}
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
		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
494
	/*
495
	 * Setup TCP socket for incoming connections.
Mike Hibler's avatar
Mike Hibler committed
496
497
	 */

498
	/* Create socket from which to read. */
Mike Hibler's avatar
Mike Hibler committed
499
500
	tcpsock = socket(AF_INET, SOCK_STREAM, 0);
	if (tcpsock < 0) {
501
		pfatal("opening stream socket");
502
503
	}

504
	i = 1;
Mike Hibler's avatar
Mike Hibler committed
505
	if (setsockopt(tcpsock, SOL_SOCKET, SO_REUSEADDR,
506
		       (char *)&i, sizeof(i)) < 0)
507
		pwarning("setsockopt(SO_REUSEADDR)");;
508
509
510
511
	
	/* Create name. */
	name.sin_family = AF_INET;
	name.sin_addr.s_addr = INADDR_ANY;
512
	name.sin_port = htons((u_short) portnum);
Mike Hibler's avatar
Mike Hibler committed
513
	if (bind(tcpsock, (struct sockaddr *) &name, sizeof(name))) {
514
		pfatal("binding stream socket");
515
516
517
	}
	/* Find assigned port value and print it out. */
	length = sizeof(name);
Mike Hibler's avatar
Mike Hibler committed
518
	if (getsockname(tcpsock, (struct sockaddr *) &name, &length)) {
519
		pfatal("getsockname");
520
	}
521
	if (listen(tcpsock, 128) < 0) {
522
		pfatal("listen");
523
	}
524
525
	info("listening on TCP port %d\n", ntohs(name.sin_port));
	
Mike Hibler's avatar
Mike Hibler committed
526
527
528
529
530
531
532
	/*
	 * Setup UDP socket
	 */

	/* Create socket from which to read. */
	udpsock = socket(AF_INET, SOCK_DGRAM, 0);
	if (udpsock < 0) {
533
		pfatal("opening dgram socket");
Mike Hibler's avatar
Mike Hibler committed
534
535
536
537
538
	}

	i = 1;
	if (setsockopt(udpsock, SOL_SOCKET, SO_REUSEADDR,
		       (char *)&i, sizeof(i)) < 0)
539
		pwarning("setsockopt(SO_REUSEADDR)");;
Mike Hibler's avatar
Mike Hibler committed
540
541
542
543
	
	/* Create name. */
	name.sin_family = AF_INET;
	name.sin_addr.s_addr = INADDR_ANY;
544
	name.sin_port = htons((u_short) portnum);
Mike Hibler's avatar
Mike Hibler committed
545
	if (bind(udpsock, (struct sockaddr *) &name, sizeof(name))) {
546
		pfatal("binding dgram socket");
Mike Hibler's avatar
Mike Hibler committed
547
548
549
550
551
	}

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

556
557
558
	*tcpsockp = tcpsock;
	*udpsockp = udpsock;
	return 0;
559
}
560

561
/*
562
 * Listen for UDP requests. This is not a secure channel, and so this should
563
564
565
566
567
568
569
570
571
 * eventually be killed off.
 */
static void
udpserver(int sock)
{
	char			buf[MYBUFSIZE];
	struct sockaddr_in	client;
	int			length, cc;
	
572
	info("udpserver starting: pid=%d sock=%d\n", mypid, sock);
573
574
575
576
577
578

	/*
	 * Wait for udp connections.
	 */
	while (1) {
		length = sizeof(client);		
579
		cc = recvfrom(sock, buf, sizeof(buf) - 1,
580
581
582
			      0, (struct sockaddr *)&client, &length);
		if (cc <= 0) {
			if (cc < 0)
583
584
				errorc("Reading UDP request");
			error("UDP Connection aborted\n");
585
			continue;
586
		}
587
588
589
590
591
592
593
		buf[cc] = '\0';
		handle_request(sock, &client, buf, 0);
	}
	exit(1);
}

/*
594
 * Listen for TCP requests.
595
596
597
598
599
600
601
602
 */
static void
tcpserver(int sock)
{
	char			buf[MYBUFSIZE];
	struct sockaddr_in	client;
	int			length, cc, newsock;
	
603
	info("tcpserver starting: pid=%d sock=%d\n", mypid, sock);
604
605
606
607
608
609

	/*
	 * Wait for TCP connections.
	 */
	while (1) {
		length  = sizeof(client);
610
		newsock = ACCEPT(sock, (struct sockaddr *)&client, &length);
611
		if (newsock < 0) {
612
			errorc("accepting TCP connection");
613
			continue;
614
		}
Mike Hibler's avatar
Mike Hibler committed
615
616

		/*
617
		 * Read in the command request.
Mike Hibler's avatar
Mike Hibler committed
618
		 */
619
		if ((cc = READ(newsock, buf, sizeof(buf) - 1)) <= 0) {
620
			if (cc < 0)
621
622
623
				errorc("Reading TCP request");
			error("TCP connection aborted\n");
			CLOSE(newsock);
624
			continue;
625
		}
626
627
		buf[cc] = '\0';
		handle_request(newsock, &client, buf, 1);
628
		CLOSE(newsock);
629
630
631
	}
	exit(1);
}
Mike Hibler's avatar
Mike Hibler committed
632

633
634
635
636
static int
handle_request(int sock, struct sockaddr_in *client, char *rdata, int istcp)
{
	struct sockaddr_in redirect_client;
637
	int		   redirect = 0, havekey = 0;
638
	char		   buf[BUFSIZ], *bp, *cp;
639
	char		   privkey[TBDB_FLEN_PRIVKEY];
640
	int		   i, err = 0;
641
	int		   version = DEFAULT_VERSION;
642
643
644
645
646
647
	tmcdreq_t	   tmcdreq, *reqp = &tmcdreq;

	/*
	 * Init the req structure.
	 */
	bzero(reqp, sizeof(*reqp));
Mike Hibler's avatar
Mike Hibler committed
648

649
650
651
	/*
	 * Look for special tags. 
	 */
652
	bp = rdata;
653
	while ((bp = strsep(&rdata, " ")) != NULL) {
654
655
656
657
658
659
660
661
662
663
664
665
666
667
		/*
		 * Look for PRIVKEY. 
		 */
		if (sscanf(bp, "PRIVKEY=%64s", buf)) {
			havekey = 1;
			strncpy(privkey, buf, sizeof(privkey));

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


668
669
670
671
672
673
674
		/*
		 * Look for VERSION. 
		 */
		if (sscanf(bp, "VERSION=%d", &i) == 1) {
			version = i;
			continue;
		}
Mike Hibler's avatar
Mike Hibler committed
675

676
677
678
679
680
681
682
683
684
685
		/*
		 * 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);
686

687
688
			info("REDIRECTED from %s to %s\n",
			     inet_ntoa(redirect_client.sin_addr), buf);
689

690
691
692
693
694
695
696
697
698
699
700
			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)) {
701
702
			reqp->isvnode = 1;
			strncpy(reqp->vnodeid, buf, sizeof(reqp->vnodeid));
703

704
705
706
707
708
			if (debug) {
				info("VNODEID %s\n", buf);
			}
			continue;
		}
709

710
711
712
713
714
		/*
		 * 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
715
716
717
		 *
		 * Note that rdata will point to any text after the command.
		 *
718
719
720
721
722
		 */
		if (*bp) {
			break;
		}
	}
723

724
725
726
	/* Start with default DB */
	strcpy(dbname, DEFAULT_DBNAME);

727
	/*
728
	 * Map the ip to a nodeid.
729
	 */
730
	if ((err = iptonodeid(client->sin_addr, reqp))) {
731
732
		if (err == 2) {
			error("No such node vnode mapping %s on %s\n",
733
			      reqp->vnodeid, reqp->nodeid);
734
735
736
737
738
		}
		else {
			error("No such node: %s\n",
			      inet_ntoa(client->sin_addr));
		}
739
740
741
742
743
744
745
746
		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. 
	 */
747
	if (!insecure && redirect &&
748
	    redirect_client.sin_addr.s_addr != myipaddr.s_addr) {
749
750
751
752
		char	buf1[32], buf2[32];
		
		strcpy(buf1, inet_ntoa(redirect_client.sin_addr));
		strcpy(buf2, inet_ntoa(client->sin_addr));
753
754
755

		if (verbose)
			info("%s INVALID REDIRECT: %s\n", buf1, buf2);
756
757
		goto skipit;
	}
758
759
760
761
762
763

#ifdef  WITHSSL
	/*
	 * If the connection is not SSL, then it must be a local node.
	 */
	if (isssl) {
764
765
		if (tmcd_sslverify_client(reqp->nodeid, reqp->pclass,
					  reqp->ptype,  reqp->islocal)) {
766
			error("%s: SSL verification failure\n", reqp->nodeid);
767
768
769
770
			if (! redirect)
				goto skipit;
		}
	}
771
	else if (!reqp->islocal) {
772
773
774
		if (!istcp)
			goto execute;
		
775
		error("%s: Remote node connected without SSL!\n",reqp->nodeid);
776
777
		if (!insecure)
			goto skipit;
778
	}
779
780
781
782
783
784
785
786
787
	else if (reqp->iscontrol) {
		if (!istcp)
			goto execute;

		error("%s: Control node connection without SSL!\n",
		      reqp->nodeid);
		if (!insecure)
			goto skipit;
	}
788
789
790
791
#else
	/*
	 * When not compiled for ssl, do not allow remote connections.
	 */
792
	if (!reqp->islocal) {
793
794
		error("%s: Remote node connection not allowed (Define SSL)!\n",
		      reqp->nodeid);
795
796
		if (!insecure)
			goto skipit;
797
	}
798
799
800
801
802
803
	if (reqp->iscontrol) {
		error("%s: Control node connection not allowed "
		      "(Define SSL)!\n", reqp->nodeid);
		if (!insecure)
			goto skipit;
	}
804
805
806
807
#endif
	/*
	 * Check for a redirect using the default DB. This allows
	 * for a simple redirect to a secondary DB for testing.
808
	 * Upon return, the dbname has been changed if redirected.
809
	 */
810
	if (checkdbredirect(reqp)) {
811
812
813
		/* Something went wrong */
		goto skipit;
	}
814

815
816
817
818
819
	/*
	 * 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. 
	 */
820
	if (!reqp->islocal) {
821
		if (!havekey) {
822
			error("%s: No privkey sent!\n", reqp->nodeid);
823
824
825
826
827
828
829
			/*
			 * Skip. Okay, the problem is that the nodes out
			 * there are not reporting the key!
			goto skipit;
			 */
		}
		else if (checkprivkey(client->sin_addr, privkey)) {
830
831
			error("%s: privkey mismatch: %s!\n",
			      reqp->nodeid, privkey);
832
833
834
835
			goto skipit;
		}
	}

836
837
838
	/*
	 * Figure out what command was given.
	 */
839
 execute:
840
	for (i = 0; i < numcommands; i++)
841
		if (strncmp(bp, command_array[i].cmdname,
842
843
			    strlen(command_array[i].cmdname)) == 0)
			break;
Mike Hibler's avatar
Mike Hibler committed
844

845
	if (i == numcommands) {
846
		info("%s: INVALID REQUEST: %.8s\n", reqp->nodeid, bp);
847
848
		goto skipit;
	}
849

850
851
852
853
854
855
856
857
858
	/*
	 * 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;
	}

859
860
861
	/*
	 * Execute it.
	 */
862
	byteswritten = 0;
863
#ifdef	WITHSSL
864
	cp = (isssl ? "SSL" : (istcp ? "TCP" : "UDP"));
865
#else
866
	cp = (istcp ? "TCP" : "UDP");
867
#endif
868
869
870
871
872
	/*
	 * XXX hack, don't log "log" contents,
	 * both for privacy and to keep our syslog smaller.
	 */
	if (command_array[i].func == dolog)
873
874
		info("%s: vers:%d %s log %d chars\n",
		     reqp->nodeid, version, cp, strlen(rdata));
875
	else if (command_array[i].func != doisalive || verbose)
876
877
		info("%s: vers:%d %s %s\n", reqp->nodeid,
		     version, cp, command_array[i].cmdname);
878

879
	err = command_array[i].func(sock, reqp, rdata, istcp, version);
880
881
882

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

885
 skipit:
886
887
888
	if (!istcp) 
		client_writeback_done(sock,
				      redirect ? &redirect_client : client);
889
	if (byteswritten)
890
891
		info("%s: %s wrote %d bytes\n",
		     reqp->nodeid, command_array[i].cmdname,
892
893
		     byteswritten);

894
	return 0;
895
896
897
898
899
}

/*
 * Accept notification of reboot. 
 */
900
COMMAND_PROTOTYPE(doreboot)
901
{
902
	/*
903
904
	 * This is now a no-op. The things this used to do are now
	 * done by stated when we hit RELOAD/RELOADDONE state
905
	 */
906
907
	return 0;
}
908

909
910
911
912
913
914
915
/*
 * Return emulab nodeid (not the experimental name).
 */
COMMAND_PROTOTYPE(donodeid)
{
	char		buf[MYBUFSIZE];

916
	sprintf(buf, "%s\n", reqp->nodeid);
917
	client_writeback(sock, buf, strlen(buf), tcp);
918
919
920
921
922
923
	return 0;
}

/*
 * Return status of node. Is it allocated to an experiment, or free.
 */
924
COMMAND_PROTOTYPE(dostatus)
925
{
Mike Hibler's avatar
Mike Hibler committed
926
	char		buf[MYBUFSIZE];
927
928
929
930

	/*
	 * Now check reserved table
	 */
931
932
	if (! reqp->allocated) {
		info("STATUS: %s: Node is free\n", reqp->nodeid);
933
934
935
		strcpy(buf, "FREE\n");
		client_writeback(sock, buf, strlen(buf), tcp);
		return 0;
936
937
	}

938
939
	sprintf(buf, "ALLOCATED=%s/%s NICKNAME=%s\n",
		reqp->pid, reqp->eid, reqp->nickname);
940
941
	client_writeback(sock, buf, strlen(buf), tcp);

942
943
	if (verbose)
		info("STATUS: %s: %s", reqp->nodeid, buf);
944
945
946
947
948
949
	return 0;
}

/*
 * Return ifconfig information to client.
 */
950
COMMAND_PROTOTYPE(doifconfig)
951
952
953
{
	MYSQL_RES	*res;	
	MYSQL_ROW	row;
Mike Hibler's avatar
Mike Hibler committed
954
	char		buf[MYBUFSIZE];
955
	int		nrows;
956
957
958
959

	/*
	 * Now check reserved table
	 */
960
	if (! reqp->allocated) {
961
962
		if (verbose)
			info("IFCONFIG: %s: Node is free\n", reqp->nodeid);
963
964
965
		return 1;
	}

Leigh B. Stoller's avatar
Leigh B. Stoller committed
966
967
	/*
	 * Virtual nodes, do not return interface table info. No point.
968
969
	 * Subnode are slightly different. This test might need to be
	 * smarter?
Leigh B. Stoller's avatar
Leigh B. Stoller committed
970
	 */
971
	if (reqp->isvnode && !reqp->issubnode)
Leigh B. Stoller's avatar
Leigh B. Stoller committed
972
973
		goto doveths;

974
975
976
	/*
	 * Find all the interfaces.
	 */
977
	res = mydb_query("select card,IP,IPalias,MAC,current_speed,duplex, "
978
			 " IPaliases,iface,role "
979
			 "from interfaces where node_id='%s'",
980
			 9, reqp->nodeid);
981
	if (!res) {
982
983
		error("IFCONFIG: %s: DB Error getting interfaces!\n",
		      reqp->nodeid);
984
985
986
987
		return 1;
	}

	if ((nrows = (int)mysql_num_rows(res)) == 0) {
988
		error("IFCONFIG: %s: No interfaces!\n", reqp->nodeid);
989
990
991
992
993
994
		mysql_free_result(res);
		return 1;
	}
	while (nrows) {
		row = mysql_fetch_row(res);
		if (row[1] && row[1][0]) {
995
996
997
			int  card    = atoi(row[0]);
			char *iface  = row[7];
			char *role   = row[8];
998
999
1000
			char *speed  = "100";
			char *unit   = "Mbps";
			char *duplex = "full";
1001

1002
			/* Never for the control net; sharks are dead */
1003
			if (strcmp(role, TBDB_IFACEROLE_EXPERIMENT))
1004
1005
				goto skipit;

1006
			/*
1007
			 * Speed and duplex if not the default.
1008
			 */
1009
1010
1011
1012
			if (row[4] && row[4][0])
				speed = row[4];
			if (row[5] && row[5][0])
				duplex = row[5];
1013

1014
			/*
1015
1016
			 * We now use the MAC to determine the interface, but
			 * older images still want that tag at the front.
1017
			 */
1018
1019
1020
1021
1022
			if (vers < 10)
			  sprintf(buf, "INTERFACE=%d ", card);
			else 
			  sprintf(buf, "IFACETYPE=eth ");
			
1023
			sprintf(&buf[strlen(buf)],
1024
1025
				"INET=%s MASK=%s MAC=%s SPEED=%s%s DUPLEX=%s",
				row[1], NETMASK, row[3], speed, unit, duplex);
1026

1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
			/* Tack on IPaliases */
			if (vers >= 8) {
				char *aliases = "";
				
				if (row[6] && row[6][0])
					aliases = row[6];
					
				sprintf(&buf[strlen(buf)],
					" IPALIASES=\"%s\"", aliases);
			}

1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
			/*
			 * Tack on iface for IXPs. This should be a flag on
			 * the interface instead of a match against type.
			 */
			if (vers >= 11) {
				sprintf(&buf[strlen(buf)],
					" IFACE=%s",
					(strcmp(reqp->class, "ixp") ?
					 "" : iface));
			}

1049
			strcat(buf, "\n");
Mike Hibler's avatar
Mike Hibler committed
1050
			client_writeback(sock, buf, strlen(buf), tcp);
1051
1052
			if (verbose)
				info("IFCONFIG: %s", buf);
1053
		}
1054
	skipit:
1055