tmcd.c 51.7 KB
Newer Older
1
2
3
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
Mike Hibler's avatar
Mike Hibler committed
4
5
#include <arpa/inet.h>
#include <ctype.h>
6
#include <stdio.h>
Mike Hibler's avatar
Mike Hibler committed
7
8
9
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
10
11
12
13
14
15
16
#include <syslog.h>
#include <signal.h>
#include <stdarg.h>
#include <assert.h>
#include <mysql/mysql.h>
#include "decls.h"

17
18
19
20
21
22
23
24
25
26
/*
 * XXX This needs to be localized!
 */
#define FSPROJDIR	"fs.emulab.net:/q/proj"
#define FSUSERDIR	"fs.emulab.net:/users"
#define PROJDIR		"/proj"
#define USERDIR		"/users"
#define RELOADPID	"emulab-ops"
#define RELOADEID	"reloading"

27
#define TESTMODE
Mike Hibler's avatar
Mike Hibler committed
28
#define VERSION		2
29
30
#define NETMASK		"255.255.255.0"

31
32
33
34
35
36
37
/* Defined in configure and passed in via the makefile */
#define DBNAME_SIZE	64
#define DEFAULT_DBNAME	TBDBNAME

static int	debug = 0;
static int	portnum = TBSERVER_PORT;
static char     dbname[DBNAME_SIZE];
38
39
static int	nodeidtoexp(char *nodeid, char *pid, char *eid);
static int	iptonodeid(struct in_addr ipaddr, char *bufp);
40
static int	nodeidtonickname(char *nodeid, char *nickname);
41
static int	nodeidtocontrolnet(char *nodeid, int *net);
42
static int	checkdbredirect(struct in_addr ipaddr);
Mike Hibler's avatar
Mike Hibler committed
43
44
int		client_writeback(int sock, void *buf, int len, int tcp);
void		client_writeback_done(int sock, struct sockaddr_in *client);
45
46
47
48
49
50
MYSQL_RES *	mydb_query(char *query, int ncols, ...);
int		mydb_update(char *query, ...);

/*
 * Commands we support.
 */
Mike Hibler's avatar
Mike Hibler committed
51
52
53
54
55
56
57
static int doreboot(int sock, struct in_addr ipaddr, char *rdata, int tcp);
static int dostatus(int sock, struct in_addr ipaddr, char *rdata, int tcp);
static int doifconfig(int sock, struct in_addr ipaddr, char *rdata, int tcp);
static int doaccounts(int sock, struct in_addr ipaddr, char *rdata, int tcp);
static int dodelay(int sock, struct in_addr ipaddr, char *rdata, int tcp);
static int dohosts(int sock, struct in_addr ipaddr, char *rdata, int tcp);
static int dorpms(int sock, struct in_addr ipaddr, char *rdata, int tcp);
58
static int dodeltas(int sock, struct in_addr ipaddr, char *rdata, int tcp);
59
static int dotarballs(int sock, struct in_addr ipaddr, char *rdata, int tcp);
Mike Hibler's avatar
Mike Hibler committed
60
61
62
63
64
static int dostartcmd(int sock, struct in_addr ipaddr, char *rdata, int tcp);
static int dostartstat(int sock, struct in_addr ipaddr, char *rdata,int tcp);
static int doready(int sock, struct in_addr ipaddr, char *rdata,int tcp);
static int doreadycount(int sock, struct in_addr ipaddr,char *rdata,int tcp);
static int dolog(int sock, struct in_addr ipaddr,char *rdata,int tcp);
65
static int domounts(int sock, struct in_addr ipaddr,char *rdata,int tcp);
66
static int doloadinfo(int sock, struct in_addr ipaddr,char *rdata,int tcp);
Robert Ricci's avatar
Robert Ricci committed
67
static int doreset(int sock, struct in_addr ipaddr,char *rdata,int tcp);
68
69
70

struct command {
	char	*cmdname;
Mike Hibler's avatar
Mike Hibler committed
71
	int    (*func)(int sock, struct in_addr ipaddr, char *rdata, int tcp);
72
} command_array[] = {
73
74
75
76
77
78
79
	{ "reboot",	doreboot },
	{ "status",	dostatus },
	{ "ifconfig",	doifconfig },
	{ "accounts",	doaccounts },
	{ "delay",	dodelay },
	{ "hostnames",	dohosts },
	{ "rpms",	dorpms },
80
	{ "deltas",	dodeltas },
81
	{ "tarballs",	dotarballs },
82
	{ "startupcmd",	dostartcmd },
Mike Hibler's avatar
Mike Hibler committed
83
	{ "startstatus",dostartstat }, /* Leave this before "startstat" */
84
	{ "startstat",	dostartstat },
85
86
	{ "readycount", doreadycount },
	{ "ready",	doready },
Mike Hibler's avatar
Mike Hibler committed
87
	{ "log",	dolog },
88
	{ "mounts",	domounts },
89
	{ "loadinfo",	doloadinfo},
Robert Ricci's avatar
Robert Ricci committed
90
	{ "reset",	doreset},
91
92
93
};
static int numcommands = sizeof(command_array)/sizeof(struct command);

94
95
96
97
98
99
100
101
102
103
/* 
 * Simple struct used to make a linked list of ifaces
 */
struct node_interface {
	char *iface;
	struct node_interface *next;
};

int		directly_connected(struct node_interface *interfaces, char *iface);

104
105
106
107
108
109
110
111
112
113
114
115
116
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"
 "\n";

void
usage()
{
	fprintf(stderr, usagestr);
	exit(1);
}

Mike Hibler's avatar
Mike Hibler committed
117
118
int
main(int argc, char **argv)
119
{
120
	int			tcpsock, udpsock, ch;
Mike Hibler's avatar
Mike Hibler committed
121
122
	int			length, i, err = 0;
	struct sockaddr_in	name;
123

124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
	while ((ch = getopt(argc, argv, "dp:")) != -1)
		switch(ch) {
		case 'p':
			portnum = atoi(optarg);
			break;
		case 'd':
			debug++;

		case 'h':
		case '?':
		default:
			usage();
		}
	argc -= optind;
	argv += optind;

	if (argc)
		usage();

143
144
145
146
147
148
	openlog("tmcd", LOG_PID, LOG_USER);
	syslog(LOG_NOTICE, "daemon starting (version %d)", VERSION);

#ifndef LBS
	(void)daemon(0, 0);
#endif
Mike Hibler's avatar
Mike Hibler committed
149
150
151
152
153

	/*
	 * Setup TCP socket
	 */

154
	/* Create socket from which to read. */
Mike Hibler's avatar
Mike Hibler committed
155
156
	tcpsock = socket(AF_INET, SOCK_STREAM, 0);
	if (tcpsock < 0) {
157
158
159
160
		syslog(LOG_ERR, "opening stream socket: %m");
		exit(1);
	}

161
	i = 1;
Mike Hibler's avatar
Mike Hibler committed
162
	if (setsockopt(tcpsock, SOL_SOCKET, SO_REUSEADDR,
163
164
165
166
167
168
		       (char *)&i, sizeof(i)) < 0)
		syslog(LOG_ERR, "control setsockopt: %m");;
	
	/* Create name. */
	name.sin_family = AF_INET;
	name.sin_addr.s_addr = INADDR_ANY;
169
	name.sin_port = htons((u_short) portnum);
Mike Hibler's avatar
Mike Hibler committed
170
	if (bind(tcpsock, (struct sockaddr *) &name, sizeof(name))) {
171
172
173
174
175
		syslog(LOG_ERR, "binding stream socket: %m");
		exit(1);
	}
	/* Find assigned port value and print it out. */
	length = sizeof(name);
Mike Hibler's avatar
Mike Hibler committed
176
	if (getsockname(tcpsock, (struct sockaddr *) &name, &length)) {
177
178
179
		syslog(LOG_ERR, "getting socket name: %m");
		exit(1);
	}
Mike Hibler's avatar
Mike Hibler committed
180
	if (listen(tcpsock, 20) < 0) {
181
182
183
		syslog(LOG_ERR, "listening on socket: %m");
		exit(1);
	}
Mike Hibler's avatar
Mike Hibler committed
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
	syslog(LOG_NOTICE, "listening on TCP port %d", ntohs(name.sin_port));

	/*
	 * Setup UDP socket
	 */

	/* Create socket from which to read. */
	udpsock = socket(AF_INET, SOCK_DGRAM, 0);
	if (udpsock < 0) {
		syslog(LOG_ERR, "opening dgram socket: %m");
		exit(1);
	}

	i = 1;
	if (setsockopt(udpsock, SOL_SOCKET, SO_REUSEADDR,
		       (char *)&i, sizeof(i)) < 0)
		syslog(LOG_ERR, "control setsockopt: %m");;
	
	/* Create name. */
	name.sin_family = AF_INET;
	name.sin_addr.s_addr = INADDR_ANY;
205
	name.sin_port = htons((u_short) portnum);
Mike Hibler's avatar
Mike Hibler committed
206
207
208
209
210
211
212
213
214
215
216
217
	if (bind(udpsock, (struct sockaddr *) &name, sizeof(name))) {
		syslog(LOG_ERR, "binding stream socket: %m");
		exit(1);
	}

	/* Find assigned port value and print it out. */
	length = sizeof(name);
	if (getsockname(udpsock, (struct sockaddr *) &name, &length)) {
		syslog(LOG_ERR, "getting socket name: %m");
		exit(1);
	}
	syslog(LOG_NOTICE, "listening on UDP port %d", ntohs(name.sin_port));
218
219
220

	while (1) {
		struct sockaddr_in client;
221
222
		struct sockaddr_in redirect_client;
		int		   redirect = 0;
223
		int		   clientsock, length = sizeof(client);
Mike Hibler's avatar
Mike Hibler committed
224
		char		   buf[MYBUFSIZE], *bp, *cp;
Mike Hibler's avatar
Mike Hibler committed
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
		int		   cc, nfds, istcp;
		fd_set		   fds;

		nfds = udpsock;
		if (tcpsock > nfds)
			nfds = tcpsock;
		FD_ZERO(&fds);
		FD_SET(tcpsock, &fds);
		FD_SET(udpsock, &fds);

		nfds = select(nfds+1, &fds, 0, 0, 0);
		if (nfds <= 0) {
			syslog(LOG_ERR, nfds == 0 ?
			       "select: returned zero!?" : "select: %m");
			exit(1);
		}
241

Mike Hibler's avatar
Mike Hibler committed
242
243
244
245
246
		if (FD_ISSET(tcpsock, &fds)) {
			istcp = 1;
			clientsock = accept(tcpsock,
					    (struct sockaddr *)&client,
					    &length);
247

Mike Hibler's avatar
Mike Hibler committed
248
249
250
			/*
			 * Read in the command request.
			 */
Mike Hibler's avatar
Mike Hibler committed
251
			if ((cc = read(clientsock, buf, MYBUFSIZE - 1)) <= 0) {
Mike Hibler's avatar
Mike Hibler committed
252
253
254
255
256
257
258
259
260
261
				if (cc < 0)
					syslog(LOG_ERR, "Reading request: %m");
				syslog(LOG_ERR, "Connection aborted");
				close(clientsock);
				continue;
			}
		} else {
			istcp = 0;
			clientsock = udpsock;

Mike Hibler's avatar
Mike Hibler committed
262
			cc = recvfrom(clientsock, buf, MYBUFSIZE - 1,
Mike Hibler's avatar
Mike Hibler committed
263
264
265
266
267
268
269
				      0, (struct sockaddr *)&client, &length);
			if (cc <= 0) {
				if (cc < 0)
					syslog(LOG_ERR, "Reading request: %m");
				syslog(LOG_ERR, "Connection aborted");
				continue;
			}
270
		}
Mike Hibler's avatar
Mike Hibler committed
271

272
273
		buf[cc] = '\0';
		bp = buf;
274

275
		/*
276
277
278
279
		 * 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.
280
		 */
281
		if (strncmp("REDIRECT=", buf, strlen("REDIRECT=")) == 0) {
282
283
			char *tp;
			
284
			bp += strlen("REDIRECT=");
285
286
287
288
			tp = bp;
			while (! isspace(*tp))
				tp++;
			*tp++ = '\0';
289
290
			redirect_client = client;
			redirect        = 1;
291
292
293
294
295
296
297
			inet_aton(bp, &client.sin_addr);
			bp = tp;
		}
		cp = (char *) malloc(cc + 1);
		assert(cp);
		strcpy(cp, bp);
		
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
#ifndef	TESTMODE
		/*
		 * IN TESTMODE, we allow redirect.
		 * Otherwise not since that would be a (minor) privacy
		 * risk, by allowing testbed nodes to get info for other
		 * nodes.
		 */
		if (redirect) {
			char	buf1[32], buf2[32];

			strcpy(buf1, inet_ntoa(redirect_client.sin_addr));
			strcpy(buf2, inet_ntoa(client.sin_addr));
			
			syslog(LOG_INFO,
			       "%s INVALID REDIRECT: %s", buf1, buf2);
			goto skipit;
		}
#endif
		/*
		 * Check for a redirect using the default DB. This allows
		 * for a simple redirect to a secondary DB for testing.
		 * Not very general. Might change to full blown tmcd
		 * redirection at some point, but this is a very quick and
		 * easy hack. Upon return, the dbname has been changed if
		 * redirection is in force. 
		 */
		strcpy(dbname, DEFAULT_DBNAME);
		if (checkdbredirect(client.sin_addr)) {
			/* Something went wrong */
			goto skipit;
		}
		
330
331
332
		/*
		 * Figure out what command was given.
		 */
Mike Hibler's avatar
Mike Hibler committed
333
		for (i = 0; i < numcommands; i++)
334
			if (strncmp(cp, command_array[i].cmdname,
Mike Hibler's avatar
Mike Hibler committed
335
				    strlen(command_array[i].cmdname)) == 0)
336
				break;
Mike Hibler's avatar
Mike Hibler committed
337
338
339
340

		/*
		 * And execute it.
		 */
341
		if (i == numcommands) {
Mike Hibler's avatar
Mike Hibler committed
342
			syslog(LOG_INFO, "%s INVALID REQUEST: %.8s...",
343
			       inet_ntoa(client.sin_addr), cp);
344
345
			goto skipit;
		}
Mike Hibler's avatar
Mike Hibler committed
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
		else {
			bp = cp + strlen(command_array[i].cmdname);

			/*
			 * XXX hack, don't log "log" contents,
			 * both for privacy and to keep our syslog smaller.
			 */
			if (command_array[i].func == dolog)
				syslog(LOG_INFO, "%s REQUEST: log %d chars",
				       inet_ntoa(client.sin_addr), strlen(bp));
			else
				syslog(LOG_INFO, "%s REQUEST: %s",
				       inet_ntoa(client.sin_addr),
				       command_array[i].cmdname);

			err = command_array[i].func(clientsock,
						    client.sin_addr,
						    bp, istcp);

365
			syslog(LOG_INFO, "%s REQUEST: %s: returned %d",
Mike Hibler's avatar
Mike Hibler committed
366
367
368
369
			       inet_ntoa(client.sin_addr),
			       command_array[i].cmdname, err);
		}

370
	skipit:
371
		free(cp);
372
373
374
		if (redirect) 
			client = redirect_client;

Mike Hibler's avatar
Mike Hibler committed
375
376
377
378
		if (istcp)
			close(clientsock);
		else
			client_writeback_done(clientsock, &client);
379
380
	}

Mike Hibler's avatar
Mike Hibler committed
381
382
	close(tcpsock);
	close(udpsock);
383
384
385
386
387
388
389
390
	syslog(LOG_NOTICE, "daemon terminating");
	exit(0);
}

/*
 * Accept notification of reboot. 
 */
static int
Mike Hibler's avatar
Mike Hibler committed
391
doreboot(int sock, struct in_addr ipaddr, char *rdata, int tcp)
392
393
394
{
	MYSQL_RES	*res;	
	char		nodeid[32];
395
396
	char		pid[64];
	char		eid[64];
397
398
399
400
401
402
403
404

	if (iptonodeid(ipaddr, nodeid)) {
		syslog(LOG_ERR, "REBOOT: %s: No such node", inet_ntoa(ipaddr));
		return 1;
	}

	syslog(LOG_INFO, "REBOOT: %s is reporting a reboot", nodeid);

405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
	/*
	 * Need to check the pid/eid to distinguish between a user
	 * initiated reload, and a admin scheduled reload. We don't want
	 * to deschedule an admin reload, which is supposed to happen when
	 * the node is released.
	 */
	if (nodeidtoexp(nodeid, pid, eid)) {
		syslog(LOG_INFO, "REBOOT: %s: Node is free", nodeid);
		return 0;
	}

	syslog(LOG_INFO, "REBOOT: %s: Node is in experiment %s/%s",
	       nodeid, pid, eid);

	/*
	 * XXX This must match the reservation made in sched_reload
	 *     in the tbsetup directory.
	 */
423
424
	if (strcmp(pid, RELOADPID) ||
	    strcmp(eid, RELOADEID)) {
425
426
427
		return 0;
	}

428
429
430
431
	/*
	 * See if the node was in the reload state. If so we need to clear it
	 * and its reserved status.
	 */
432
	res = mydb_query("select node_id from scheduled_reloads where node_id='%s'",
433
434
435
436
437
438
439
440
441
442
443
444
			 1, nodeid);
	if (!res) {
		syslog(LOG_ERR, "REBOOT: %s: DB Error getting reload!",
		       nodeid);
		return 1;
	}
	if ((int)mysql_num_rows(res) == 0) {
		mysql_free_result(res);
		return 0;
	}
	mysql_free_result(res);

445
	if (mydb_update("delete from scheduled_reloads where node_id='%s'", nodeid)) {
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
		syslog(LOG_ERR, "REBOOT: %s: DB Error clearing reload!",
		       nodeid);
		return 1;
	}
	syslog(LOG_INFO, "REBOOT: %s cleared reload state", nodeid);

	if (mydb_update("delete from reserved where node_id='%s'", nodeid)) {
		syslog(LOG_ERR, "REBOOT: %s: DB Error clearing reload!",
		       nodeid);
		return 1;
	}
	syslog(LOG_INFO, "REBOOT: %s cleared reserved state", nodeid);

	return 0;
}

/*
 * Return status of node. Is it allocated to an experiment, or free.
 */
static int
Mike Hibler's avatar
Mike Hibler committed
466
dostatus(int sock, struct in_addr ipaddr, char *rdata, int tcp)
467
468
469
470
{
	char		nodeid[32];
	char		pid[64];
	char		eid[64];
471
	char		nickname[128];
Mike Hibler's avatar
Mike Hibler committed
472
	char		buf[MYBUFSIZE];
473
474
475
476
477
478
479
480
481
482
483

	if (iptonodeid(ipaddr, nodeid)) {
		syslog(LOG_ERR, "STATUS: %s: No such node", inet_ntoa(ipaddr));
		return 1;
	}

	/*
	 * Now check reserved table
	 */
	if (nodeidtoexp(nodeid, pid, eid)) {
		syslog(LOG_INFO, "STATUS: %s: Node is free", nodeid);
484
485
486
		strcpy(buf, "FREE\n");
		client_writeback(sock, buf, strlen(buf), tcp);
		return 0;
487
488
	}

489
490
491
492
493
494
495
496
497
498
	/*
	 * Need the nickname too.
	 */
	if (nodeidtonickname(nodeid, nickname))
		strcpy(nickname, nodeid);

	sprintf(buf, "ALLOCATED=%s/%s NICKNAME=%s\n", pid, eid, nickname);
	client_writeback(sock, buf, strlen(buf), tcp);

	syslog(LOG_INFO, "STATUS: %s: %s", nodeid, buf);
499
500
501
502
503
504
505
	return 0;
}

/*
 * Return ifconfig information to client.
 */
static int
Mike Hibler's avatar
Mike Hibler committed
506
doifconfig(int sock, struct in_addr ipaddr, char *rdata, int tcp)
507
508
509
510
511
512
{
	MYSQL_RES	*res;	
	MYSQL_ROW	row;
	char		nodeid[32];
	char		pid[64];
	char		eid[64];
Mike Hibler's avatar
Mike Hibler committed
513
	char		buf[MYBUFSIZE];
514
515
516
517
518
519
520
521
522
523
524
525
	int		control_net, nrows;

	if (iptonodeid(ipaddr, nodeid)) {
		syslog(LOG_ERR, "IFCONFIG: %s: No such node",
		       inet_ntoa(ipaddr));
		return 1;
	}

	/*
	 * Now check reserved table
	 */
	if (nodeidtoexp(nodeid, pid, eid)) {
526
		syslog(LOG_INFO, "IFCONFIG: %s: Node is free", nodeid);
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
		return 1;
	}

	/*
	 * Need to know the control network for the machine since
	 * we don't want to mess with that.
	 */
	if (nodeidtocontrolnet(nodeid, &control_net)) {
		syslog(LOG_ERR, "IFCONFIG: %s: No Control Network", nodeid);
		return 1;
	}

	/*
	 * Find all the interfaces.
	 */
542
	res = mydb_query("select card,IP,IPalias,MAC from interfaces "
543
			 "where node_id='%s'",
544
			 4, nodeid);
545
546
547
548
549
550
551
552
553
554
555
556
557
558
	if (!res) {
		syslog(LOG_ERR, "IFCONFIG: %s: DB Error getting interfaces!",
		       nodeid);
		return 1;
	}

	if ((nrows = (int)mysql_num_rows(res)) == 0) {
		syslog(LOG_ERR, "IFCONFIG: %s: No interfaces!", nodeid);
		mysql_free_result(res);
		return 1;
	}
	while (nrows) {
		row = mysql_fetch_row(res);
		if (row[1] && row[1][0]) {
559
560
			int card = atoi(row[0]);

561
562
563
564
565
566
			/*
			 * INTERFACE can go away when all machines running
			 * updated (MAC based) setup. Right now MAC is at
			 * the end (see below) cause of the sharks, but they
			 * should be dead soon too.
			 */
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
			sprintf(buf, "INTERFACE=%d INET=%s MASK=%s",
				card, row[1], NETMASK);

			/*
			 * The point of this sillyness is to look for the
			 * special Shark case. The sharks have only one
			 * interface, so we use an IPalias on what we call
			 * the "control" interface. This test prevents us
			 * from returning an ifconfig line for the control
			 * interface, unless it has an IP alias. I could
			 * change the setup scripts to ignore this case on
			 * the PCs. Might end up doing that at some point. 
			 */
			if (row[2] && row[2][0]) {
				strcat(buf, " IPALIAS=");
				strcat(buf, row[2]);
			}
			else if (card == control_net)
				goto skipit;

587
588
589
590
591
592
593
			/*
			 * Tack on MAC, which should go up above after
			 * Sharks are sunk.
			 */
			strcat(buf, " MAC=");
			strcat(buf, row[3]);

594
			strcat(buf, "\n");
Mike Hibler's avatar
Mike Hibler committed
595
			client_writeback(sock, buf, strlen(buf), tcp);
596
597
			syslog(LOG_INFO, "IFCONFIG: %s", buf);
		}
598
	skipit:
599
600
601
602
603
604
605
606
607
608
609
		nrows--;
	}
	mysql_free_result(res);

	return 0;
}

/*
 * Return account stuff.
 */
static int
Mike Hibler's avatar
Mike Hibler committed
610
doaccounts(int sock, struct in_addr ipaddr, char *rdata, int tcp)
611
612
613
614
615
616
{
	MYSQL_RES	*res;	
	MYSQL_ROW	row;
	char		nodeid[32];
	char		pid[64];
	char		eid[64];
Mike Hibler's avatar
Mike Hibler committed
617
	char		buf[MYBUFSIZE];
618
	int		nrows, gid;
619
	int		shared = 0, tbadmin;
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634

	if (iptonodeid(ipaddr, nodeid)) {
		syslog(LOG_ERR, "ACCOUNTS: %s: No such node",
		       inet_ntoa(ipaddr));
		return 1;
	}

	/*
	 * Now check reserved table
	 */
	if (nodeidtoexp(nodeid, pid, eid)) {
		syslog(LOG_ERR, "ACCOUNTS: %s: Node is free", nodeid);
		return 1;
	}

635
#ifdef  NOSHAREDEXPTS
636
637
638
639
	/*
	 * We have the pid name, but we need the GID number from the
	 * projects table to send over. 
	 */
640
641
642
643
644
645
646
647
648
649
650
651
652
653
	res = mydb_query("select pid,unix_gid from projects where pid='%s'",
			 2, pid);
#else
	/*
	 * Get a list of pid/unix_gid for each group that is allowed
	 * access to the experiments nodes. This is the owner of the
	 * node, plus the additional pids granted access. 
	 */
	res = mydb_query("select p.pid,unix_gid from projects as p "
			 "left join exppid_access as a on p.pid=a.pid "
			 "where p.pid='%s' or "
			 "      (a.exp_pid='%s' and a.exp_eid='%s')",
			 2, pid, pid, eid);
#endif
654
	if (!res) {
655
		syslog(LOG_ERR, "ACCOUNTS: %s: DB Error getting gids!", pid);
656
657
658
659
660
661
662
663
664
		return 1;
	}

	if ((nrows = (int)mysql_num_rows(res)) == 0) {
		syslog(LOG_ERR, "ACCOUNTS: %s: No Project!", pid);
		mysql_free_result(res);
		return 1;
	}

665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
	while (nrows) {
		row = mysql_fetch_row(res);
		if (!row[1] || !row[1][1]) {
			syslog(LOG_ERR, "ACCOUNTS: %s: No Project GID!", pid);
			mysql_free_result(res);
			return 1;
		}

		gid = atoi(row[1]);
		sprintf(buf, "ADDGROUP NAME=%s GID=%d\n", row[0], gid);
		client_writeback(sock, buf, strlen(buf), tcp);
		syslog(LOG_INFO, "ACCOUNTS: %s", buf);

		nrows--;
	}
680
681
682
683
684
	mysql_free_result(res);

	/*
	 * Now onto the users in the project.
	 */
685
#ifdef  NOSHAREDEXPTS
686
	res = mydb_query("select u.uid,u.usr_pswd,u.unix_uid,u.usr_name,"
687
			 "p.trust from users as u "
688
			 "left join proj_memb as p on p.uid=u.uid "
689
			 "where p.pid='%s' and u.status='active'",
690
			 5, pid);
691
#else
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
	/*
	 * See if a shared experiment. Used below.
	 */
	res = mydb_query("select shared from experiments "
			 "where pid='%s' and eid='%s'",
			 1, pid, eid);
	
	if (!res) {
		syslog(LOG_ERR, "ACCOUNTS: %s: DB Error getting shared!", pid);
		return 1;
	}

	if ((nrows = (int)mysql_num_rows(res)) == 0) {
		syslog(LOG_ERR, "ACCOUNTS: %s: No Experiment %s!", pid, eid);
		mysql_free_result(res);
		return 0;
	}
	row = mysql_fetch_row(res);
	shared = atoi(row[0]);
	mysql_free_result(res);
	
713
714
715
716
717
718
719
720
721
722
	/*
	 * This crazy join is going to give us multiple lines for each
	 * user that is allowed on the node, where each line (for each user)
	 * differs by the project PID and it unix GID. The intent is to
	 * build up a list of GIDs for each user to return. Well, a primary
	 * group and a list of aux groups for that user. It might be cleaner
	 * to do this as multiple querys, but this makes it atomic.
	 */
	res = mydb_query("select distinct "
			 "u.uid,u.usr_pswd,u.unix_uid,u.usr_name, "
723
			 "p.trust,p.pid,pr.unix_gid,u.admin from users as u "
724
725
726
727
728
729
			 "left join proj_memb as p on p.uid=u.uid "
			 "left join exppid_access as a "
			 " on a.exp_pid='%s' and a.exp_eid='%s' "
			 "left join projects as pr on p.pid=pr.pid "
			 "where (p.pid='%s' or p.pid=a.pid) "
			 "      and u.status='active' order by u.uid",
730
			 8, pid, eid, pid);
731
#endif
732
733
734
735
736
737
738
739
740
741
742
	if (!res) {
		syslog(LOG_ERR, "ACCOUNTS: %s: DB Error getting users!", pid);
		return 1;
	}

	if ((nrows = (int)mysql_num_rows(res)) == 0) {
		syslog(LOG_ERR, "ACCOUNTS: %s: No Users!", pid);
		mysql_free_result(res);
		return 0;
	}

743
	row = mysql_fetch_row(res);
744
	while (nrows) {
745
746
747
748
		MYSQL_ROW	nextrow;
		int		i, root = 0;
		int		auxgids[128], gcount = 0;
		char		glist[BUFSIZ];
749

750
		gid     = -1;
751
752
		
		while (1) {
753
			tbadmin = root = atoi(row[7]);
754
755
			
			/*
756
			 * The whole point of this mess. Figure out the
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
			 * main GID and the aux GIDs. Perhaps trying to make
			 * distinction between main and aux is unecessary, as
			 * long as the entire set is represented.
			 */
			if (strcmp(row[5], pid) == 0) {
				gid = atoi(row[6]);

				/*
				 * Only people in the main pid can get root
				 * at this time, so do this test here.
				 */
				if ((strcmp(row[4], "local_root") == 0) ||
				    (strcmp(row[4], "group_root") == 0))
					root = 1;
			}
			else {
				auxgids[gcount++] = atoi(row[6]);
			}
			nrows--;

			if (!nrows)
				break;

			/*
			 * See if the next row is the same UID. If so,
			 * we go around the inner loop again.
			 */
			nextrow = mysql_fetch_row(res);
			if (strcmp(row[0], nextrow[0]))
				break;
			row = nextrow;
		}
		/*
		 * Okay, process the UID. If there is no primary gid,
		 * then use one from the list. Then convert the rest of
		 * the list for the GLIST argument below.
		 */
		if (gid == -1) {
			gid = auxgids[--gcount];
		}
		glist[0] = '\0';
		for (i = 0; i < gcount; i++) {
			sprintf(&glist[strlen(glist)], "%d", auxgids[i]);
800

801
802
803
			if (i < gcount-1)
				strcat(glist, ",");
		}
804

805
806
807
808
809
810
811
		/*
		 * Override root when a shared experiment, except for
		 * TB admin people.
		 */
		if (shared && !tbadmin)
			root = 0;

812
813
		sprintf(buf,
			"ADDUSER LOGIN=%s "
814
			"PSWD=%s UID=%s GID=%d ROOT=%d NAME=\"%s\" "
815
			"HOMEDIR=%s/%s GLIST=%s\n",
816
			row[0], row[1], row[2], gid, root, row[3],
817
			USERDIR, row[0], glist);
818
			
Mike Hibler's avatar
Mike Hibler committed
819
		client_writeback(sock, buf, strlen(buf), tcp);
820
		syslog(LOG_INFO, "ACCOUNTS: %s", buf);
821
822

		row = nextrow;
823
824
825
826
827
828
829
830
831
832
	}
	mysql_free_result(res);

	return 0;
}

/*
 * Return account stuff.
 */
static int
Mike Hibler's avatar
Mike Hibler committed
833
dodelay(int sock, struct in_addr ipaddr, char *rdata, int tcp)
834
835
836
837
838
839
{
	MYSQL_RES	*res;	
	MYSQL_ROW	row;
	char		nodeid[32];
	char		pid[64];
	char		eid[64];
Mike Hibler's avatar
Mike Hibler committed
840
	char		buf[MYBUFSIZE];
Mike Hibler's avatar
Mike Hibler committed
841
	int		nrows;
842
843
844
845
846
847
848
849
850
851
852
853
854
855

	if (iptonodeid(ipaddr, nodeid)) {
		syslog(LOG_ERR, "DELAY: %s: No such node",
		       inet_ntoa(ipaddr));
		return 1;
	}

	/*
	 * Now check reserved table
	 */
	if (nodeidtoexp(nodeid, pid, eid))
		return 0;

	/*
856
857
858
	 * Get delay parameters for the machine. The point of this silly
	 * join is to get the type out so that we can pass it back. Of
	 * course, this assumes that the type is the BSD name, not linux.
859
	 */
860
	res = mydb_query("select i.MAC,j.MAC,delay,bandwidth,lossrate "
861
862
863
864
865
866
                         " from delays "
			 "left join interfaces as i on "
			 " i.node_id=delays.node_id and i.iface=iface0 "
			 "left join interfaces as j on "
			 " j.node_id=delays.node_id and j.iface=iface1 "
			 " where delays.node_id='%s'",	 
867
			 5, nodeid);
868
869
870
871
872
873
874
875
876
877
878
879
880
	if (!res) {
		syslog(LOG_ERR, "DELAY: %s: DB Error getting delays!",
		       nodeid);
		return 1;
	}

	if ((nrows = (int)mysql_num_rows(res)) == 0) {
		mysql_free_result(res);
		return 0;
	}
	while (nrows) {
		row = mysql_fetch_row(res);

881
882
883
884
885
886
887
888
889
890
891
		/*
		 * Yikes, this is ugly! Sanity check though, since I saw
		 * some bogus values in the DB.
		 */
		if (!row[0] || !row[1] || !row[2] || !row[3]) {
			syslog(LOG_ERR, "DELAY: %s: DB values are bogus!",
			       nodeid);
			mysql_free_result(res);
			return 1;
		}

892
893
		sprintf(buf,
			"DELAY INT0=%s INT1=%s DELAY=%s BW=%s PLR=%s\n",
894
			row[0], row[1], row[2], row[3], row[4]);
895
			
Mike Hibler's avatar
Mike Hibler committed
896
		client_writeback(sock, buf, strlen(buf), tcp);
897
898
899
900
		nrows--;
		syslog(LOG_INFO, "DELAY: %s", buf);
	}
	mysql_free_result(res);
Mike Hibler's avatar
Mike Hibler committed
901

902
903
904
	return 0;
}

905
906
907
908
/*
 * Return host table information.
 */
static int
Mike Hibler's avatar
Mike Hibler committed
909
dohosts(int sock, struct in_addr ipaddr, char *rdata, int tcp)
910
{
911

912
	char *tmp, *buf, *vname_list;
913
	char pid[64], eid[64];
914
915
916
	char nickname[128]; /* XXX: Shouldn't be statically sized, potential buffer
						 * overflow
						 */ 
917
918
919
920
921
922
923
924
925
926
927
928
929

	char nodeid[32]; /* Testbed ID of the node */
	int control_net; /* Control network interface on this host */
	int ninterfaces; /* Number of interfaces on the host */
	int nnodes; /* Number of other nodes in this experiment */


	char *last_id; /* Used to determine link# */
	int link;
	int seen_direct;

	MYSQL_RES	*interface_result;
	MYSQL_RES	*nodes_result;
930
	MYSQL_RES	*vlan_result;
931
	MYSQL_RES	*reserved_result;
932
933
	MYSQL_ROW	row;

934
935
936
	struct node_interface *connected_interfaces, *temp_interface;
	int nvlans;

937
938
939
940
941
942
943
944
945
946
947
948
	if (iptonodeid(ipaddr, nodeid)) {
		syslog(LOG_ERR, "HOSTNAMES: %s: No such node",
		       inet_ntoa(ipaddr));
		return 1;
	}

	/*
	 * Now check reserved table
	 */
	if (nodeidtoexp(nodeid, pid, eid))
		return 0;
	/*
949
	 * Figure out which of our interfaces is on the control network
950
	 */
951

952
953
954
955
956
957
958
	interface_result = mydb_query("SELECT control_net "
			"FROM nodes LEFT JOIN node_types ON nodes.type = node_types.type "
			"WHERE nodes.node_id = '%s'",
			1, nodeid);

	if (!interface_result) {
		syslog(LOG_ERR, "dohosts: %s: DB Error getting interface!", nodeid);
959
960
		return 1;
	}
961
962
963
964
965
966
967

	/*
	 * There should be exactly 1 control network interface!
	 */
	if ((int)mysql_num_rows(interface_result) != 1) {
		mysql_free_result(interface_result);
		syslog(LOG_ERR, "HOSTNAMES: Unable to get control interface for %s",nodeid);
968
969
970
		return 0;
	}

971
972
973
974
	row = mysql_fetch_row(interface_result);
	control_net = atoi(row[0]);
	mysql_free_result(interface_result);

975
	/*
976
	 * For the virt_lans table, we need the virtual name, not the node_id
977
	 */
978
979
980
981
982
983
984
985
986
987
988
989
	if (nodeidtonickname(nodeid,nickname)) {
		strcpy(nickname,nodeid);
	}

	/*
	 * Now, we need to look through the virt_lans table to find a list of all the
	 * VLANs we're in
	 */
	vlan_result = mydb_query("SELECT vname "
			"FROM virt_lans "
			"WHERE member LIKE '%s:%%' AND pid='%s' AND eid='%s'",
			1, nickname,pid,eid);
990

991
	if (!vlan_result) {
992
		syslog(LOG_ERR, "dohosts: %s: DB Error getting virt_lans!", nodeid);
993
994
995
		return 1;
	}

996
	nvlans = (int)mysql_num_rows(vlan_result);
997
	connected_interfaces = NULL;
998

999
1000
	/*
	 * Initializing vname_list to "0" makes the ORs easier to get right,