tmcd.c 51.2 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 doloadaddr(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
	{ "loadaddr",	doloadaddr},
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
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
	int		nrows, gid;

	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;
	}

634
#ifdef  NOSHAREDEXPTS
635
636
637
638
	/*
	 * We have the pid name, but we need the GID number from the
	 * projects table to send over. 
	 */
639
640
641
642
643
644
645
646
647
648
649
650
651
652
	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
653
	if (!res) {
654
		syslog(LOG_ERR, "ACCOUNTS: %s: DB Error getting gids!", pid);
655
656
657
658
659
660
661
662
663
		return 1;
	}

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

664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
	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--;
	}
679
680
681
682
683
	mysql_free_result(res);

	/*
	 * Now onto the users in the project.
	 */
684
#ifdef  NOSHAREDEXPTS
685
	res = mydb_query("select u.uid,u.usr_pswd,u.unix_uid,u.usr_name,"
686
			 "p.trust from users as u "
687
			 "left join proj_memb as p on p.uid=u.uid "
688
			 "where p.pid='%s' and u.status='active'",
689
			 5, pid);
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
#else
	/*
	 * 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, "
			 "p.trust,p.pid,pr.unix_gid from users as u "
			 "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",
			 7, pid, eid, pid);
#endif
710
711
712
713
714
715
716
717
718
719
720
	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;
	}

721
	row = mysql_fetch_row(res);
722
	while (nrows) {
723
724
725
726
		MYSQL_ROW	nextrow;
		int		i, root = 0;
		int		auxgids[128], gcount = 0;
		char		glist[BUFSIZ];
727

728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
		gid = -1;
		
		while (1) {
			
			/*
			 * This is whole point of this mess. Figure out the
			 * 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]);
777

778
779
780
			if (i < gcount-1)
				strcat(glist, ",");
		}
781
782
783

		sprintf(buf,
			"ADDUSER LOGIN=%s "
784
			"PSWD=%s UID=%s GID=%d ROOT=%d NAME=\"%s\" "
785
			"HOMEDIR=%s/%s GLIST=%s\n",
786
			row[0], row[1], row[2], gid, root, row[3],
787
			USERDIR, row[0], glist);
788
			
Mike Hibler's avatar
Mike Hibler committed
789
		client_writeback(sock, buf, strlen(buf), tcp);
790
		syslog(LOG_INFO, "ACCOUNTS: %s", buf);
791
792

		row = nextrow;
793
794
795
796
797
798
799
800
801
802
	}
	mysql_free_result(res);

	return 0;
}

/*
 * Return account stuff.
 */
static int
Mike Hibler's avatar
Mike Hibler committed
803
dodelay(int sock, struct in_addr ipaddr, char *rdata, int tcp)
804
805
806
807
808
809
{
	MYSQL_RES	*res;	
	MYSQL_ROW	row;
	char		nodeid[32];
	char		pid[64];
	char		eid[64];
Mike Hibler's avatar
Mike Hibler committed
810
	char		buf[MYBUFSIZE];
Mike Hibler's avatar
Mike Hibler committed
811
	int		nrows;
812
813
814
815
816
817
818
819
820
821
822
823
824
825

	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;

	/*
826
827
828
	 * 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.
829
	 */
830
831
832
833
834
835
836
837
838
	res = mydb_query("select iface0,i.interface_type,"
			 " iface1,j.interface_type,delay,bandwidth,lossrate "
                         " 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'",	 
			 7, nodeid);
839
840
841
842
843
844
845
846
847
848
849
	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) {
850
851
		char	card0[64], card1[64];
		
852
853
		row = mysql_fetch_row(res);

854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
		/*
		 * 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;
		}
		strcpy(card0, row[1]);
		strcat(card0, strpbrk(row[0], "0123456789"));
		strcpy(card1, row[3]);
		strcat(card1, strpbrk(row[2], "0123456789"));

869
870
		sprintf(buf,
			"DELAY INT0=%s INT1=%s DELAY=%s BW=%s PLR=%s\n",
871
			card0, card1, row[4], row[5], row[6]);
872
			
Mike Hibler's avatar
Mike Hibler committed
873
		client_writeback(sock, buf, strlen(buf), tcp);
874
875
876
877
		nrows--;
		syslog(LOG_INFO, "DELAY: %s", buf);
	}
	mysql_free_result(res);
Mike Hibler's avatar
Mike Hibler committed
878

879
880
881
	return 0;
}

882
883
884
885
/*
 * Return host table information.
 */
static int
Mike Hibler's avatar
Mike Hibler committed
886
dohosts(int sock, struct in_addr ipaddr, char *rdata, int tcp)
887
{
888

889
	char *tmp, *buf, *vname_list;
890
	char pid[64], eid[64];
891
892
893
	char nickname[128]; /* XXX: Shouldn't be statically sized, potential buffer
						 * overflow
						 */ 
894
895
896
897
898
899
900
901
902
903
904
905
906

	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;
907
	MYSQL_RES	*vlan_result;
908
	MYSQL_RES	*reserved_result;
909
910
	MYSQL_ROW	row;

911
912
913
	struct node_interface *connected_interfaces, *temp_interface;
	int nvlans;

914
915
916
917
918
919
920
921
922
923
924
925
	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;
	/*
926
	 * Figure out which of our interfaces is on the control network
927
	 */
928

929
930
931
932
933
934
935
	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);
936
937
		return 1;
	}
938
939
940
941
942
943
944

	/*
	 * 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);
945
946
947
		return 0;
	}

948
949
950
951
	row = mysql_fetch_row(interface_result);
	control_net = atoi(row[0]);
	mysql_free_result(interface_result);

952
	/*
953
	 * For the virt_lans table, we need the virtual name, not the node_id
954
	 */
955
956
957
958
959
960
961
962
963
964
965
966
	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);
967

968
	if (!vlan_result) {
969
		syslog(LOG_ERR, "dohosts: %s: DB Error getting virt_lans!", nodeid);
970
971
972
		return 1;
	}

973
	nvlans = (int)mysql_num_rows(vlan_result);
974
	connected_interfaces = NULL;
975

976
977
978
979
980
981
982
	/*
	 * Initializing vname_list to "0" makes the ORs easier to get right,
	 * and I want to make sure this string gets malloc()ed, to prevent
	 * problems with the free() inside the loop
	 */
	vname_list = (char*)malloc(2);
	strcpy(vname_list,"0");
983

984
	while (nvlans--) {
985
		row = mysql_fetch_row(vlan_result);
986

987
		/*
988
		 * Add this vlan to an OR of vlan names that could contain direct neighbors
989
		 */
990
991
992
993
994
995
996
		tmp = (char*)malloc(strlen(vname_list) + strlen(row[0]) +14);
		strcpy(tmp,vname_list);
		strcat(tmp," OR vname ='");
		strcat(tmp,row[0]);
		strcat(tmp,"'");
		free(vname_list);
		vname_list = tmp;
997

998
999
	}
	mysql_free_result(vlan_result);
1000

1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
	/*
	 * Now, get the members of all these VLANs
	 */
	vlan_result = mydb_query("SELECT member "
			"FROM virt_lans "
			"WHERE pid='%s' AND eid='%s' AND ( %s )",
			1, pid,eid,vname_list);
	free(vname_list);

	if (!vlan_result) {
		syslog(LOG_ERR, "dohosts: %s: DB Error getting virt_lan members!", nodeid);
		return 1;
	}

	nvlans = (int)mysql_num_rows(vlan_result);
	connected_interfaces = NULL;

	while (nvlans--) {
		struct node_interface *interface;

		row = mysql_fetch_row(vlan_result);

		/*
1024
1025
		 * Add this member to the linked list of directly connected
		 * interfaces
1026
1027
1028
1029
1030
1031
		 */
		interface = (struct node_interface *)malloc(sizeof(struct node_interface *));
		interface->iface = (char*)malloc(strlen(row[0]) +1);
		strcpy(interface->iface,row[0]);
		interface->next = connected_interfaces;
		connected_interfaces = interface;
1032

1033
	}
1034
1035

	mysql_free_result(vlan_result);
1036
1037

	/*
1038
1039
	 * Grab a list of all other hosts in this experiment - we'll sort out the
	 * directly connected ones while looping through them.
1040
	 */
1041
	nodes_result = 
1042
		mydb_query("SELECT DISTINCT i.node_id, i.IP, i.IPalias, r.vname, CONCAT(r.vname,':',p.vport), i.card = t.control_net "
1043
1044
1045
				"FROM interfaces AS i LEFT JOIN reserved AS r ON i.node_id = r.node_id "
				"LEFT JOIN nodes AS n ON i.node_id = n.node_id "
				"LEFT JOIN node_types AS t ON n.type = t.type "
1046
1047
				"LEFT JOIN portmap AS p ON i.iface = p.pport AND p.vnode=r.vname "
				"WHERE IP IS NOT NULL AND IP != '' AND r.pid='%s' AND r.eid='%s'"
1048
				"ORDER BY node_id DESC, IP",
1049
				6,pid,eid);
1050
1051
1052
1053

	if (!nodes_result) {
		syslog(LOG_ERR, "dohosts: %s: DB Error getting other nodes "
				"in the experiment!", nodeid);
1054
1055
1056
		return 1;
	}
	
1057
1058
1059
	if ((nnodes = (int)mysql_num_rows(nodes_result)) == 0) {
		syslog(LOG_ERR, "dohosts: %s: No nodes in this experiment!", nodeid);
		mysql_free_result(nodes_result);
1060
1061
1062
		return 0;
	}

1063
1064
1065
1066
1067
1068
	last_id = ""; 
	link = 0; /* Acts as 'lindex' from the virt_names table */
	seen_direct = 0; /* Set to 1 if we've already seen at least
						one direct connection to this node */
	while (nnodes--) {
		MYSQL_ROW node_row = mysql_fetch_row(nodes_result);
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
		char	  *vname, vbuf[256];

		/*
		 * Watch for null vnames. Just construct something to make
		 * everyone happy.
		 */
		if (node_row[3])
			vname = node_row[3];
		else {
			strcpy(vbuf, "V");
			strcat(vbuf, node_row[0]);
			vname = vbuf;
		}
1082

1083
1084
1085
1086
		/*
		 * Either the IP or IPalias column (or both!) could have
		 * matched. Code duplication galore!
		 */
1087

1088
1089
1090
		/*
		 * Let's take care of the IP first!
		 */
1091

1092
		/* Skip the control network interface */
1093
		if (!(node_row[5] && atoi(node_row[5]))) {
1094

1095
1096
1097
			/*
			 * Keep track of node_ids, so we can get the LINK number rigth
			 */
1098
1099
1100
1101
1102
1103
			if (!strcmp(node_row[0],last_id)) {
				link++;
			} else {
				link = seen_direct = 0;
				last_id = node_row[0];
			}
1104

1105

1106
1107
1108
1109
			if (directly_connected(connected_interfaces,node_row[4])) {
				sprintf(buf, "NAME=%s LINK=%i IP=%s ALIAS=%s\n",
						vname, link, node_row[1],
						(!seen_direct) ? vname : " ");
1110
1111
1112
				seen_direct = 1;
			} else {
				sprintf(buf, "NAME=%s LINK=%i IP=%s ALIAS= \n",
1113
					vname, link, node_row[1]);
1114
1115
1116
1117
1118
			}

			client_writeback(sock, buf, strlen(buf), tcp);
			syslog(LOG_INFO, "HOSTNAMES: %s", buf);
		}
1119

1120
1121
1122
		/*
		 * Make sure it really has an IPalias!
		 */
1123

1124
1125
1126
1127
1128
1129
1130
		if (node_row[2] && strcmp(node_row[2],"")) {
			if (!strcmp(node_row[0],last_id)) {
				link++;
			} else {
				link = seen_direct = 0;
				last_id = node_row[0];
			}
1131

1132
1133
1134
1135
			if (directly_connected(connected_interfaces,node_row[4])) {
				sprintf(buf, "NAME=%s LINK=%i IP=%s ALIAS=%s\n",
						vname, link, node_row[2],
						(!seen_direct) ? vname : " ");
1136
1137
1138
				seen_direct = 1;
			} else {
				sprintf(buf, "NAME=%s LINK=%i IP=%s ALIAS= \n",
1139
					vname, link, node_row[2]);
1140
			}
1141

1142
1143
			client_writeback(sock, buf, strlen(buf), tcp);
			syslog(LOG_INFO, "HOSTNAMES: %s", buf);
1144

1145
		}
1146
	}
1147
	mysql_free_result(nodes_result);
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158

	/*
	 * Clean up our linked list of interfaces
	 */
	while (connected_interfaces != NULL) {
		temp_interface = connected_interfaces;
		connected_interfaces = connected_interfaces->next;
		free(temp_interface->iface);
		free(temp_interface);
	}
	
1159
	return 0;
1160

1161
1162
}

Leigh B. Stoller's avatar
Leigh B. Stoller committed
1163
1164
1165
1166
/*
 * Return RPM stuff.
 */
static int
Mike Hibler's avatar
Mike Hibler committed
1167
dorpms(int sock, struct in_addr ipaddr, char *rdata, int tcp)
Leigh B. Stoller's avatar
Leigh B. Stoller committed
1168
1169
1170
1171
1172
1173
{
	MYSQL_RES	*res;	
	MYSQL_ROW	row;
	char		nodeid[32];
	char		pid[64];
	char		eid[64];
Mike Hibler's avatar
Mike Hibler committed
1174
	char		buf[MYBUFSIZE], *bp, *sp;
Leigh B. Stoller's avatar
Leigh B. Stoller committed
1175
1176

	if (iptonodeid(ipaddr, nodeid)) {
1177
		syslog(LOG_ERR, "TARBALLS: %s: No such node",
Leigh B. Stoller's avatar
Leigh B. Stoller committed
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
		       inet_ntoa(ipaddr));
		return 1;
	}

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

	/*
1189
	 * Get RPM list for the node.
Leigh B. Stoller's avatar
Leigh B. Stoller committed
1190
	 */
1191
1192
	res = mydb_query("select rpms from nodes where node_id='%s' ",
			 1, nodeid);
Leigh B. Stoller's avatar
Leigh B. Stoller committed
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208

	if (!res) {
		syslog(LOG_ERR, "RPMS: %s: DB Error getting RPMS!",
		       nodeid);
		return 1;
	}

	if ((int)mysql_num_rows(res) == 0) {
		mysql_free_result(res);
		return 0;
	}

	/*
	 * Text string is a colon separated list.
	 */
	row = mysql_fetch_row(res);
1209
	if (! row[0] || !row[0][0]) {
1210
1211
1212
1213
		mysql_free_result(res);
		return 0;
	}
	
Leigh B. Stoller's avatar
Leigh B. Stoller committed
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
	bp  = row[0];
	sp  = bp;
	do {
		bp = strsep(&sp, ":");

		sprintf(buf, "RPM=%s\n", bp);
		client_writeback(sock, buf, strlen(buf), tcp);
		syslog(LOG_INFO, "RPM: %s", buf);
		
	} while (bp = sp);
	
	mysql_free_result(res);
	return 0;
}

1229
/*
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
 * Return Tarball stuff.
 */
static int
dotarballs(int sock, struct in_addr ipaddr, char *rdata, int tcp)
{
	MYSQL_RES	*res;	
	MYSQL_ROW	row;
	char		nodeid[32];
	char		pid[64];
	char		eid[64];
	char		buf[MYBUFSIZE], *bp, *sp, *tp;

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

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

	/*
	 * Get Tarball list for the node.
	 */
	res = mydb_query("select tarballs from nodes where node_id='%s' ",
			 1, nodeid);

	if (!res) {
		syslog(LOG_ERR, "TARBALLS: %s: DB Error getting tarballs!",
		       nodeid);
		return 1;
	}

	if ((int)mysql_num_rows(res) == 0) {
		mysql_free_result(res);
		return 0;
	}

	/*
	 * Text string is a colon separated list of "dir filename". 
	 */
	row = mysql_fetch_row(res);
	if (! row[0] || !row[0][0]) {
		mysql_free_result(res);
		return 0;
	}
	
	bp  = row[0];
	sp  = bp;
	do {
		bp = strsep(&sp, ":");
		if ((tp = strchr(bp, ' ')) == NULL)
			continue;
		*tp++ = '\0';

		sprintf(buf, "DIR=%s TARBALL=%s\n", bp, tp);
		client_writeback(sock, buf, strlen(buf), tcp);
		syslog(LOG_INFO, "TARBALLS: %s", buf);
		
	} while (bp = sp);
	
	mysql_free_result(res);
	return 0;
}

/*
 * Return Deltas stuff.
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
 */
static int
dodeltas(int sock, struct in_addr ipaddr, char *rdata, int tcp)
{
	MYSQL_RES	*res;	
	MYSQL_ROW	row;
	char		nodeid[32];
	char		pid[64];
	char		eid[64];
	char		buf[MYBUFSIZE], *bp, *sp;

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

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

	/*
	 * Get Delta list for the node.
	 */
	res = mydb_query("select deltas from nodes where node_id='%s' ",
			 1, nodeid);

	if (!res) {
		syslog(LOG_ERR, "DELTAS: %s: DB Error getting Deltas!",
		       nodeid);
		return 1;
	}

	if ((int)mysql_num_rows(res) == 0) {
		mysql_free_result(res);
		return 0;
	}

	/*
	 * Text string is a colon separated list.
	 */
	row = mysql_fetch_row(res);
	if (! row[0] || !row[0][0]) {
		mysql_free_result(res);
		return 0;
	}
	
	bp  = row[0];
	sp  = bp;
	do {
		bp = strsep(&sp, ":");

		sprintf(buf, "DELTA=%s\n", bp);
		client_writeback(sock, buf, strlen(buf), tcp);
		syslog(LOG_INFO, "DELTAS: %s", buf);
		
	} while (bp = sp);
	
	mysql_free_result(res);
	return 0;
}

Leigh B. Stoller's avatar
Leigh B. Stoller committed
1364
/*
1365
1366
 * Return node run command. We return the command to run, plus the UID
 * of the experiment creator to run it as!
Leigh B. Stoller's avatar
Leigh B. Stoller committed
1367
1368
 */
static int
Mike Hibler's avatar
Mike Hibler committed
1369
dostartcmd(int sock, struct in_addr ipaddr, char *rdata, int tcp)
Leigh B. Stoller's avatar
Leigh B. Stoller committed
1370
1371
1372
1373
1374
1375
{
	MYSQL_RES	*res;	
	MYSQL_ROW	row;
	char		nodeid[32];
	char		pid[64];
	char		eid[64];
Mike Hibler's avatar
Mike Hibler committed
1376
	char		buf[MYBUFSIZE], *bp, *sp;
Leigh B. Stoller's avatar
Leigh B. Stoller committed
1377
1378

	if (iptonodeid(ipaddr, nodeid)) {
1379
		syslog(LOG_ERR, "STARTUPCMD: %s: No such node",
Leigh B. Stoller's avatar
Leigh B. Stoller committed
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
		       inet_ntoa(ipaddr));
		return 1;
	}

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

	/*
1391
	 * Get run command for the node.
Leigh B. Stoller's avatar
Leigh B. Stoller committed
1392
	 */
1393
1394
	res = mydb_query("select startupcmd from nodes where node_id='%s'",
			 1, nodeid);
Leigh B. Stoller's avatar
Leigh B. Stoller committed
1395
1396

	if (!res) {
1397
1398
		syslog(LOG_ERR, "STARTUPCMD: %s: DB Error getting "
		       "startup command!",
Leigh B. Stoller's avatar
Leigh B. Stoller committed
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
		       nodeid);
		return 1;
	}

	if ((int)mysql_num_rows(res) == 0) {
		mysql_free_result(res);
		return 0;
	}

	/*
	 * Simple text string.
	 */
	row = mysql_fetch_row(res);
1412
	if (! row[0] || !row[0][0]) {
Leigh B. Stoller's avatar
Leigh B. Stoller committed
1413
1414
1415
		mysql_free_result(res);
		return 0;
	}
1416
	sprintf(buf, "CMD='%s' UID=", row[0]);
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
	mysql_free_result(res);

	/*
	 * Now get the UID from the experiments table and tack that onto
	 * the string just created.
	 */
	res = mydb_query("select expt_head_uid from experiments "
			 "where eid='%s' and pid='%s'",
			 1, eid, pid);

	if (!res) {
		syslog(LOG_ERR, "STARTUPCMD: %s: DB Error getting UID!",
		       nodeid);
		return 1;
	}

	if ((int)mysql_num_rows(res) == 0) {
		mysql_free_result(res);
		return 0;
	}
	row = mysql_fetch_row(res);
	strcat(buf, row[0]);
	strcat(buf, "\n");
	mysql_free_result(res);
Leigh B. Stoller's avatar
Leigh B. Stoller committed
1441
1442
	
	client_writeback(sock, buf, strlen(buf), tcp);
1443
	syslog(LOG_INFO, "STARTUPCMD: %s", buf);
Leigh B. Stoller's avatar
Leigh B. Stoller committed
1444
	
1445
1446
1447
1448
1449
1450
1451
	return 0;
}

/*
 * Accept notification of start command exit status. 
 */
static int
Mike Hibler's avatar
Mike Hibler committed
1452
dostartstat(int sock, struct in_addr ipaddr, char *rdata, int tcp)
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
{
	MYSQL_RES	*res;	
	char		nodeid[32];
	char		pid[64];
	char		eid[64];
	char		*exitstatus;

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

	/*
Mike Hibler's avatar
Mike Hibler committed
1467
	 * Dig out the exit status
1468
	 */
Mike Hibler's avatar
Mike Hibler committed
1469
1470
1471
	while (isspace(*rdata))
		rdata++;
	exitstatus = rdata;
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485