tmcd.c 166 KB
Newer Older
Leigh Stoller's avatar
Leigh Stoller committed
1 2
/*
 * EMULAB-COPYRIGHT
3
 * Copyright (c) 2000-2006 University of Utah and the Flux Group.
Leigh Stoller's avatar
Leigh 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>
22
#include <sys/fcntl.h>
23 24
#include <sys/syscall.h>
#include <sys/stat.h>
25
#include <sys/param.h>
26
#include <paths.h>
Austin Clements's avatar
Austin Clements committed
27
#include <setjmp.h>
28 29
#include <pwd.h>
#include <grp.h>
30 31
#include <mysql/mysql.h>
#include "decls.h"
32
#include "config.h"
33 34
#include "ssl.h"
#include "log.h"
35
#include "tbdefs.h"
36

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

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

64 65 66 67
/* socket read/write timeouts in ms */
#define READTIMO	3000
#define WRITETIMO	3000

68
#define TESTMODE
69 70 71 72 73 74 75 76 77 78 79 80
#define DEFAULTNETMASK	"255.255.255.0"
/* This can be tossed once all the changes are in place */
static char *
CHECKMASK(char *arg)
{
	if (arg && arg[0])
		return arg;

	error("No netmask defined!\n");
	return DEFAULTNETMASK;
}
/* #define CHECKMASK(arg)  ((arg) && (arg[0]) ? (arg) : DEFAULTNETMASK) */
81

82 83 84
#define DISKTYPE	"ad"
#define DISKNUM		0

85 86 87 88 89 90 91
/* 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"

92 93
/* Defined in configure and passed in via the makefile */
#define DBNAME_SIZE	64
Austin Clements's avatar
Austin Clements committed
94
#define HOSTID_SIZE	(32+64)
95 96
#define DEFAULT_DBNAME	TBDBNAME

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

116 117 118 119
/* socket timeouts */
static int	readtimo = READTIMO;
static int	writetimo = WRITETIMO;

120
/* thread support */
121
#define MAXCHILDREN	20
122
#define MINCHILDREN	8
123
static int	numchildren;
124 125 126 127
static int	maxchildren       = 13;
static int      num_udpservers    = 3;
static int      num_altudpservers = 1;
static int      num_alttcpservers = 1;
128
static int	mypid;
129
static volatile int killme;
130

131 132 133 134 135
/* Output macro to check for string overflow */
#define OUTPUT(buf, size, format...) \
({ \
	int __count__ = snprintf((buf), (size), ##format); \
        \
136
        if (__count__ >= (size)) { \
137 138 139 140 141 142
		error("Not enough room in output buffer! line %d.\n", __LINE__);\
		return 1; \
	} \
	__count__; \
})

143 144 145 146 147 148 149 150
/*
 * 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;
151
	int		issubnode;
152
	int		islocal;
153
	int		iscontrol;
154
	int		isplabdslice;
155
	int		isplabsvc;
156
	int		elab_in_elab;
157
	int		update_accounts;
158 159
	char		nodeid[TBDB_FLEN_NODEID];
	char		vnodeid[TBDB_FLEN_NODEID];
Leigh Stoller's avatar
Leigh Stoller committed
160
	char		pnodeid[TBDB_FLEN_NODEID]; /* XXX */
161 162 163 164 165
	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];
166
	char		class[TBDB_FLEN_NODECLASS];
167 168
        char		ptype[TBDB_FLEN_NODETYPE];	/* Of physnode */
	char		pclass[TBDB_FLEN_NODECLASS];	/* Of physnode */
169 170
	char		creator[TBDB_FLEN_UID];
	char		swapper[TBDB_FLEN_UID];
171
	char		syncserver[TBDB_FLEN_VNAME];	/* The vname */
172
	char		keyhash[TBDB_FLEN_PRIVKEY];
173
	char		eventkey[TBDB_FLEN_PRIVKEY];
174
	char		sfshostid[TBDB_FLEN_SFSHOSTID];
175 176 177 178 179
	char		testdb[256];
} tmcdreq_t;
static int	iptonodeid(struct in_addr, tmcdreq_t *);
static int	checkdbredirect(tmcdreq_t *);

180 181 182 183 184
#ifdef EVENTSYS
int			myevent_send(address_tuple_t address);
static event_handle_t	event_handle = NULL;
#endif

185 186 187
/*
 * Commands we support.
 */
188 189
#define COMMAND_PROTOTYPE(x) \
	static int \
190
	x(int sock, tmcdreq_t *reqp, char *rdata, int tcp, int vers)
191 192

COMMAND_PROTOTYPE(doreboot);
193
COMMAND_PROTOTYPE(donodeid);
194 195 196 197
COMMAND_PROTOTYPE(dostatus);
COMMAND_PROTOTYPE(doifconfig);
COMMAND_PROTOTYPE(doaccounts);
COMMAND_PROTOTYPE(dodelay);
198
COMMAND_PROTOTYPE(dolinkdelay);
199 200 201 202 203 204 205 206 207
COMMAND_PROTOTYPE(dohosts);
COMMAND_PROTOTYPE(dorpms);
COMMAND_PROTOTYPE(dodeltas);
COMMAND_PROTOTYPE(dotarballs);
COMMAND_PROTOTYPE(dostartcmd);
COMMAND_PROTOTYPE(dostartstat);
COMMAND_PROTOTYPE(doready);
COMMAND_PROTOTYPE(doreadycount);
COMMAND_PROTOTYPE(domounts);
Austin Clements's avatar
Austin Clements committed
208
COMMAND_PROTOTYPE(dosfshostid);
209 210 211 212 213 214 215
COMMAND_PROTOTYPE(doloadinfo);
COMMAND_PROTOTYPE(doreset);
COMMAND_PROTOTYPE(dorouting);
COMMAND_PROTOTYPE(dotrafgens);
COMMAND_PROTOTYPE(donseconfigs);
COMMAND_PROTOTYPE(dostate);
COMMAND_PROTOTYPE(docreator);
216
COMMAND_PROTOTYPE(dotunnels);
217
COMMAND_PROTOTYPE(dovnodelist);
218
COMMAND_PROTOTYPE(dosubnodelist);
219
COMMAND_PROTOTYPE(doisalive);
220
COMMAND_PROTOTYPE(doipodinfo);
221 222
COMMAND_PROTOTYPE(dontpinfo);
COMMAND_PROTOTYPE(dontpdrift);
223
COMMAND_PROTOTYPE(dojailconfig);
224
COMMAND_PROTOTYPE(doplabconfig);
225 226
COMMAND_PROTOTYPE(dosubconfig);
COMMAND_PROTOTYPE(doixpconfig);
227
COMMAND_PROTOTYPE(doslothdparams);
228
COMMAND_PROTOTYPE(doprogagents);
229
COMMAND_PROTOTYPE(dosyncserver);
230
COMMAND_PROTOTYPE(dokeyhash);
231
COMMAND_PROTOTYPE(doeventkey);
232
COMMAND_PROTOTYPE(dofullconfig);
233 234
COMMAND_PROTOTYPE(doroutelist);
COMMAND_PROTOTYPE(dorole);
235
COMMAND_PROTOTYPE(dorusage);
236
COMMAND_PROTOTYPE(dodoginfo);
237
COMMAND_PROTOTYPE(dohostkeys);
Mike Hibler's avatar
Mike Hibler committed
238
COMMAND_PROTOTYPE(dotmcctest);
239
COMMAND_PROTOTYPE(dofwinfo);
240
COMMAND_PROTOTYPE(dohostinfo);
241
COMMAND_PROTOTYPE(doemulabconfig);
242
COMMAND_PROTOTYPE(doeplabconfig);
243
COMMAND_PROTOTYPE(dolocalize);
244 245
COMMAND_PROTOTYPE(dobooterrno);
COMMAND_PROTOTYPE(dobootlog);
246
COMMAND_PROTOTYPE(dobattery);
247
COMMAND_PROTOTYPE(dotopomap);
248
COMMAND_PROTOTYPE(douserenv);
249 250
COMMAND_PROTOTYPE(dotiptunnels);
COMMAND_PROTOTYPE(dorelayconfig);
251
COMMAND_PROTOTYPE(dotraceconfig);
252
COMMAND_PROTOTYPE(doltmap);
253
COMMAND_PROTOTYPE(doltpmap);
254
COMMAND_PROTOTYPE(doelvindport);
255
COMMAND_PROTOTYPE(doplabeventkeys);
256
COMMAND_PROTOTYPE(dointfcmap);
257
COMMAND_PROTOTYPE(domotelog);
258
COMMAND_PROTOTYPE(doportregister);
259

260 261
/*
 * The fullconfig slot determines what routines get called when pushing
262
 * out a full configuration. Physnodes get slightly different
263
 * than vnodes, and at some point we might want to distinguish different
264 265 266 267 268
 * types of vnodes (jailed, plab).
 */
#define FULLCONFIG_NONE		0x0
#define FULLCONFIG_PHYS		0x1
#define FULLCONFIG_VIRT		0x2
269 270 271 272 273
#define FULLCONFIG_ALL		(FULLCONFIG_PHYS|FULLCONFIG_VIRT)

/*
 * Flags encode a few other random properties of commands
 */
274 275 276 277 278
#define F_REMUDP	0x01	/* remote nodes can request using UDP */
#define F_MINLOG	0x02	/* record minimal logging info normally */
#define F_MAXLOG	0x04	/* record maximal logging info normally */
#define F_ALLOCATED	0x08	/* node must be allocated to make call */
#define F_REMNOSSL	0x10	/* remote nodes can request without SSL */
279

280 281
struct command {
	char	*cmdname;
282
	int	fullconfig;	
283
	int	flags;
284
	int    (*func)(int, tmcdreq_t *, char *, int, int);
285
} command_array[] = {
286 287 288
	{ "reboot",	  FULLCONFIG_NONE, 0, doreboot },
	{ "nodeid",	  FULLCONFIG_ALL,  0, donodeid },
	{ "status",	  FULLCONFIG_NONE, 0, dostatus },
289
	{ "ifconfig",	  FULLCONFIG_ALL,  F_ALLOCATED, doifconfig },
290
	{ "accounts",	  FULLCONFIG_ALL,  0, doaccounts },
291 292 293 294 295 296 297 298
	{ "delay",	  FULLCONFIG_ALL,  F_ALLOCATED, dodelay },
	{ "linkdelay",	  FULLCONFIG_ALL,  F_ALLOCATED, dolinkdelay },
	{ "hostnames",	  FULLCONFIG_NONE, F_ALLOCATED, dohosts },
	{ "rpms",	  FULLCONFIG_ALL,  F_ALLOCATED, dorpms },
	{ "deltas",	  FULLCONFIG_NONE, F_ALLOCATED, dodeltas },
	{ "tarballs",	  FULLCONFIG_ALL,  F_ALLOCATED, dotarballs },
	{ "startupcmd",	  FULLCONFIG_ALL,  F_ALLOCATED, dostartcmd },
	{ "startstatus",  FULLCONFIG_NONE, F_ALLOCATED, dostartstat }, /* Before startstat*/
299
	{ "startstat",	  FULLCONFIG_NONE, 0, dostartstat },
300 301 302 303
	{ "readycount",   FULLCONFIG_NONE, F_ALLOCATED, doreadycount },
	{ "ready",	  FULLCONFIG_NONE, F_ALLOCATED, doready },
	{ "mounts",	  FULLCONFIG_ALL,  F_ALLOCATED, domounts },
	{ "sfshostid",	  FULLCONFIG_NONE, F_ALLOCATED, dosfshostid },
304 305
	{ "loadinfo",	  FULLCONFIG_NONE, 0, doloadinfo},
	{ "reset",	  FULLCONFIG_NONE, 0, doreset},
306 307 308 309
	{ "routing",	  FULLCONFIG_ALL,  F_ALLOCATED, dorouting},
	{ "trafgens",	  FULLCONFIG_ALL,  F_ALLOCATED, dotrafgens},
	{ "nseconfigs",	  FULLCONFIG_ALL,  F_ALLOCATED, donseconfigs},
	{ "creator",	  FULLCONFIG_ALL,  F_ALLOCATED, docreator},
310
	{ "state",	  FULLCONFIG_NONE, 0, dostate},
311
	{ "tunnels",	  FULLCONFIG_ALL,  F_ALLOCATED, dotunnels},
312
	{ "vnodelist",	  FULLCONFIG_PHYS, 0, dovnodelist},
313
	{ "subnodelist",  FULLCONFIG_PHYS, 0, dosubnodelist},
314
	{ "isalive",	  FULLCONFIG_NONE, F_REMUDP|F_MINLOG, doisalive},
315
	{ "ipodinfo",	  FULLCONFIG_PHYS, 0, doipodinfo},
316 317
	{ "ntpinfo",	  FULLCONFIG_PHYS, 0, dontpinfo},
	{ "ntpdrift",	  FULLCONFIG_NONE, 0, dontpdrift},
318 319
	{ "jailconfig",	  FULLCONFIG_VIRT, F_ALLOCATED, dojailconfig},
	{ "plabconfig",	  FULLCONFIG_VIRT, F_ALLOCATED, doplabconfig},
320
	{ "subconfig",	  FULLCONFIG_NONE, 0, dosubconfig},
321
        { "sdparams",     FULLCONFIG_PHYS, 0, doslothdparams},
322 323 324 325 326 327 328
        { "programs",     FULLCONFIG_ALL,  F_ALLOCATED, doprogagents},
        { "syncserver",   FULLCONFIG_ALL,  F_ALLOCATED, dosyncserver},
        { "keyhash",      FULLCONFIG_ALL,  F_ALLOCATED, dokeyhash},
        { "eventkey",     FULLCONFIG_ALL,  F_ALLOCATED, doeventkey},
        { "fullconfig",   FULLCONFIG_NONE, F_ALLOCATED, dofullconfig},
        { "routelist",	  FULLCONFIG_PHYS, F_ALLOCATED, doroutelist},
        { "role",	  FULLCONFIG_PHYS, F_ALLOCATED, dorole},
329
        { "rusage",	  FULLCONFIG_NONE, F_REMUDP|F_MINLOG, dorusage},
330
        { "watchdoginfo", FULLCONFIG_ALL,  F_REMUDP|F_MINLOG, dodoginfo},
331
        { "hostkeys",     FULLCONFIG_NONE, 0, dohostkeys},
Mike Hibler's avatar
Mike Hibler committed
332
        { "tmcctest",     FULLCONFIG_NONE, F_MINLOG, dotmcctest},
333
        { "firewallinfo", FULLCONFIG_ALL,  0, dofwinfo},
334
        { "hostinfo",     FULLCONFIG_NONE, 0, dohostinfo},
335
	{ "emulabconfig", FULLCONFIG_NONE, F_ALLOCATED, doemulabconfig},
336
	{ "eplabconfig",  FULLCONFIG_NONE, F_ALLOCATED, doeplabconfig},
337
	{ "localization", FULLCONFIG_PHYS, 0, dolocalize},
338 339
	{ "booterrno",    FULLCONFIG_NONE, 0, dobooterrno},
	{ "bootlog",      FULLCONFIG_NONE, 0, dobootlog},
340
	{ "battery",      FULLCONFIG_NONE, F_REMUDP|F_MINLOG, dobattery},
341
	{ "topomap",      FULLCONFIG_NONE, F_MINLOG|F_ALLOCATED, dotopomap},
342
	{ "userenv",      FULLCONFIG_ALL,  F_ALLOCATED, douserenv},
343
	{ "tiptunnels",	  FULLCONFIG_ALL,  F_ALLOCATED, dotiptunnels},
344
	{ "traceinfo",	  FULLCONFIG_ALL,  F_ALLOCATED, dotraceconfig },
345
	{ "ltmap",        FULLCONFIG_NONE, F_MINLOG|F_ALLOCATED, doltmap},
346
	{ "ltpmap",       FULLCONFIG_NONE, F_MINLOG|F_ALLOCATED, doltpmap},
347
	{ "elvindport",   FULLCONFIG_NONE, 0, doelvindport},
348
	{ "plabeventkeys",FULLCONFIG_NONE, 0, doplabeventkeys},
349
	{ "intfcmap",     FULLCONFIG_NONE, 0, dointfcmap},
350 351
	{ "motelog",      FULLCONFIG_ALL,  F_ALLOCATED, domotelog},
	{ "portregister", FULLCONFIG_NONE, F_REMNOSSL, doportregister},
352 353 354
};
static int numcommands = sizeof(command_array)/sizeof(struct command);

355 356 357 358
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"
359
 " -c num	   Specify number of servers (must be %d <= x <= %d)\n"
360
 " -v              More verbose logging\n"
361
 " -i ipaddr       Sets the boss IP addr to return (for multi-homed servers)\n"
362 363 364 365 366
 "\n";

void
usage()
{
367
	fprintf(stderr, usagestr, MINCHILDREN, MAXCHILDREN);
368 369 370
	exit(1);
}

371 372 373 374 375
static void
cleanup()
{
	signal(SIGHUP, SIG_IGN);
	killme = 1;
376
	killpg(0, SIGHUP);
377 378
}

379 380 381 382 383 384 385 386 387
static void
setverbose(int sig)
{
	signal(sig, SIG_IGN);
	
	if (sig == SIGUSR1)
		verbose = 1;
	else
		verbose = 0;
388 389
	info("verbose logging turned %s\n", verbose ? "on" : "off");

390 391 392 393 394 395
	/* Just the parent sends this */
	if (numchildren)
		killpg(0, sig);
	signal(sig, setverbose);
}

Mike Hibler's avatar
Mike Hibler committed
396 397
int
main(int argc, char **argv)
398
{
399
	int			tcpsock, udpsock, i, ch;
400 401 402
	int			alttcpsock, altudpsock;
	int			status, pid;
	int			portnum = TBSERVER_PORT;
403 404
	FILE			*fp;
	char			buf[BUFSIZ];
405
	struct hostent		*he;
406
	extern char		build_info[];
407 408 409 410 411
	int			server_counts[4]; /* udp,tcp,altudp,alttcp */
	struct {
		int	pid;
		int	which;
	} servers[MAXCHILDREN];
412

413
	while ((ch = getopt(argc, argv, "dp:c:Xvi:")) != -1)
414 415 416 417 418 419
		switch(ch) {
		case 'p':
			portnum = atoi(optarg);
			break;
		case 'd':
			debug++;
420
			break;
421 422 423
		case 'c':
			maxchildren = atoi(optarg);
			break;
424 425 426
		case 'X':
			insecure = 1;
			break;
427 428 429
		case 'v':
			verbose++;
			break;
430 431 432 433 434 435 436
		case 'i':
			if (inet_aton(optarg, &myipaddr) == 0) {
				fprintf(stderr, "invalid IP address %s\n",
					optarg);
				usage();
			}
			break;
437 438 439 440 441 442 443 444 445 446
		case 'h':
		case '?':
		default:
			usage();
		}
	argc -= optind;
	argv += optind;

	if (argc)
		usage();
447 448
	if (maxchildren < MINCHILDREN || maxchildren > MAXCHILDREN)
		usage();
449

450 451 452 453 454 455 456 457 458 459 460 461 462 463 464
#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
465

Austin Clements's avatar
Austin Clements committed
466 467 468 469 470 471 472 473 474 475 476 477 478 479
	/*
	 * 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;
480 481 482
				if (debug) {
				    info("fshostid: %s\n", fshostid);
				}
Austin Clements's avatar
Austin Clements committed
483 484 485 486 487 488 489 490 491
			}
			else {
				error("fshostid from %s may be corrupt: %s",
				      FSHOSTID, fshostid);
			}
			fclose(fp);
		}
	}
	
492 493 494
	/*
	 * Grab our IP for security check below.
	 */
495
	if (myipaddr.s_addr == 0) {
496
#ifdef	LBS
497
		strcpy(buf, BOSSNODE);
498
#else
499 500
		if (gethostname(buf, sizeof(buf)) < 0)
			pfatal("getting hostname");
501
#endif
502 503 504 505 506 507
		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);
508 509
	}

510 511 512 513 514 515
	/*
	 * 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) {
516 517
		error("Could not make sockets!");
		exit(1);
518
	    }
519
	    num_alttcpservers = num_altudpservers = 0;
520 521 522 523 524 525
	} else {
	    if (makesockets(portnum, &udpsock, &tcpsock) < 0 ||
		makesockets(TBSERVER_PORT2, &altudpsock, &alttcpsock) < 0) {
		    error("Could not make sockets!");
		    exit(1);
	    }
526 527 528 529 530
	}

	signal(SIGTERM, cleanup);
	signal(SIGINT, cleanup);
	signal(SIGHUP, cleanup);
531 532
	signal(SIGUSR1, setverbose);
	signal(SIGUSR2, setverbose);
533 534 535 536

	/*
	 * Stash the pid away.
	 */
537
	mypid = getpid();
538 539 540
	sprintf(buf, "%s/tmcd.pid", _PATH_VARRUN);
	fp = fopen(buf, "w");
	if (fp != NULL) {
541
		fprintf(fp, "%d\n", mypid);
542 543 544
		(void) fclose(fp);
	}

545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577
	/*
	 * Change to non-root user!
	 */
	if (geteuid() == 0) {
		struct passwd	*pw;
		uid_t		uid;
		gid_t		gid;

		/*
		 * Must be a valid user of course.
		 */
		if ((pw = getpwnam(RUNASUSER)) == NULL) {
			error("invalid user: %s", RUNASUSER);
			exit(1);
		}
		uid = pw->pw_uid;
		gid = pw->pw_gid;

		if (setgroups(1, &gid)) {
			errorc("setgroups");
			exit(1);
		}
		if (setgid(gid)) {
			errorc("setgid");
			exit(1);
		}
		if (setuid(uid)) {
			errorc("setuid");
			exit(1);
		}
		info("Flipped to user/group %d/%d\n", uid, gid);
	}

578 579
	/*
	 * Now fork a set of children to handle requests. We keep the
580 581 582 583 584 585 586
	 * pool at a set level. There are 4 types of servers, each getting
	 * a different number of servers. We do it this cause otherwise
	 * we have to deal with the select storm problem; a bunch of processes
	 * select on the same set of file descriptors, and all get woken up
	 * when something comes in, then all read from the socket but only
	 * one gets it and the others go back to sleep. There are various ways
	 * to deal with this problem, but all of them are a lot more code!
587
	 */
588 589 590 591 592 593 594
	server_counts[0] = num_udpservers;
	server_counts[1] = num_altudpservers;
	server_counts[2] = num_alttcpservers;
	server_counts[3] = maxchildren -
		(num_udpservers + num_altudpservers + num_altudpservers);
	bzero(servers, sizeof(servers));
	
595 596
	while (1) {
		while (!killme && numchildren < maxchildren) {
597 598 599 600 601 602 603 604 605 606
			int which = 3;
			
			/*
			 * Find which kind of server is short one.
			 */
			for (i = 0; i < 4; i++) {
				if (server_counts[i]) {
					which = i;
					break;
				}
607
			}
608
			
609 610 611 612 613
			if ((pid = fork()) < 0) {
				errorc("forking server");
				goto done;
			}
			if (pid) {
614 615 616 617 618 619 620 621 622 623
				server_counts[which]--;
				/*
				 * Find free slot
				 */
				for (i = 0; i < maxchildren; i++) {
					if (!servers[i].pid)
						break;
				}
				servers[i].pid   = pid;
				servers[i].which = which;
624 625 626
				numchildren++;
				continue;
			}
627 628 629 630
			/* Poor way of knowing parent/child */
			numchildren = 0;
			mypid = getpid();
			
631 632 633 634 635 636
			/* Child does useful work! Never Returns! */
			signal(SIGTERM, SIG_DFL);
			signal(SIGINT, SIG_DFL);
			signal(SIGHUP, SIG_DFL);
			
			switch (which) {
637
			case 0: udpserver(udpsock, portnum);
638
				break;
639
			case 1: udpserver(altudpsock, TBSERVER_PORT2);
640
				break;
641
			case 2: tcpserver(alttcpsock, TBSERVER_PORT2);
642
				break;
643
			case 3: tcpserver(tcpsock, portnum);
644 645 646 647 648 649 650 651 652 653 654 655
				break;
			}
			exit(-1);
		}

		/*
		 * Parent waits.
		 */
		pid = waitpid(-1, &status, 0);
		if (pid < 0) {
			errorc("waitpid failed");
			continue;
656 657 658 659
		} 
		if (WIFSIGNALED(status)) {
			error("server %d exited with signal %d!\n",
			      pid, WTERMSIG(status));
660
		}
661 662 663
		else if (WIFEXITED(status)) {
			error("server %d exited with status %d!\n",
			      pid, WEXITSTATUS(status));	  
664
		}
665
		numchildren--;
666 667 668 669 670 671 672 673 674 675

		/*
		 * Figure out which and what kind of server it was that died.
		 */
		for (i = 0; i < maxchildren; i++) {
			if (servers[i].pid == pid) {
				servers[i].pid = 0;
				server_counts[servers[i].which]++;
				break;
			}
676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695
		}
		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
696
	/*
697
	 * Setup TCP socket for incoming connections.
Mike Hibler's avatar
Mike Hibler committed
698 699
	 */

700
	/* Create socket from which to read. */
Mike Hibler's avatar
Mike Hibler committed
701 702
	tcpsock = socket(AF_INET, SOCK_STREAM, 0);
	if (tcpsock < 0) {
703
		pfatal("opening stream socket");
704 705
	}

706
	i = 1;
Mike Hibler's avatar
Mike Hibler committed
707
	if (setsockopt(tcpsock, SOL_SOCKET, SO_REUSEADDR,
708
		       (char *)&i, sizeof(i)) < 0)
709
		pwarning("setsockopt(SO_REUSEADDR)");;
710 711 712 713
	
	/* Create name. */
	name.sin_family = AF_INET;
	name.sin_addr.s_addr = INADDR_ANY;
714
	name.sin_port = htons((u_short) portnum);
Mike Hibler's avatar
Mike Hibler committed
715
	if (bind(tcpsock, (struct sockaddr *) &name, sizeof(name))) {
716
		pfatal("binding stream socket");
717 718 719
	}
	/* Find assigned port value and print it out. */
	length = sizeof(name);
Mike Hibler's avatar
Mike Hibler committed
720
	if (getsockname(tcpsock, (struct sockaddr *) &name, &length)) {
721
		pfatal("getsockname");
722
	}
723
	if (listen(tcpsock, 128) < 0) {
724
		pfatal("listen");
725
	}
726 727
	info("listening on TCP port %d\n", ntohs(name.sin_port));
	
Mike Hibler's avatar
Mike Hibler committed
728 729 730 731 732 733 734
	/*
	 * Setup UDP socket
	 */

	/* Create socket from which to read. */
	udpsock = socket(AF_INET, SOCK_DGRAM, 0);
	if (udpsock < 0) {
735
		pfatal("opening dgram socket");
Mike Hibler's avatar
Mike Hibler committed
736 737 738 739 740
	}

	i = 1;
	if (setsockopt(udpsock, SOL_SOCKET, SO_REUSEADDR,
		       (char *)&i, sizeof(i)) < 0)
741
		pwarning("setsockopt(SO_REUSEADDR)");;
742 743 744 745

	i = 128 * 1024;
	if (setsockopt(udpsock, SOL_SOCKET, SO_RCVBUF, &i, sizeof(i)) < 0)
		pwarning("setsockopt(SO_RCVBUF)");
Mike Hibler's avatar
Mike Hibler committed
746 747 748 749
	
	/* Create name. */
	name.sin_family = AF_INET;
	name.sin_addr.s_addr = INADDR_ANY;
750
	name.sin_port = htons((u_short) portnum);
Mike Hibler's avatar
Mike Hibler committed
751
	if (bind(udpsock, (struct sockaddr *) &name, sizeof(name))) {
752
		pfatal("binding dgram socket");
Mike Hibler's avatar
Mike Hibler committed
753 754 755 756 757
	}

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

762 763 764
	*tcpsockp = tcpsock;
	*udpsockp = udpsock;
	return 0;
765
}
766

767
/*
768
 * Listen for UDP requests. This is not a secure channel, and so this should
769 770 771
 * eventually be killed off.
 */
static void
772
udpserver(int sock, int portnum)
773 774 775 776
{
	char			buf[MYBUFSIZE];
	struct sockaddr_in	client;
	int			length, cc;
777
	unsigned int		nreq = 0;
778
	
779 780
	info("udpserver starting: pid=%d sock=%d portnum=%d\n",
	     mypid, sock, portnum);
781 782 783 784 785

	/*
	 * Wait for udp connections.
	 */
	while (1) {
786
		setproctitle("UDP %d: %u done", portnum, nreq);
787
		length = sizeof(client);		
788
		cc = recvfrom(sock, buf, sizeof(buf) - 1,
789 790 791
			      0, (struct sockaddr *)&client, &length);
		if (cc <= 0) {
			if (cc < 0)
792 793
				errorc("Reading UDP request");
			error("UDP Connection aborted\n");
794
			continue;
795
		}
796 797
		buf[cc] = '\0';
		handle_request(sock, &client, buf, 0);
798
		nreq++;
799 800 801 802
	}
	exit(1);
}

803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828
int
tmcd_accept(int sock, struct sockaddr *addr, socklen_t *addrlen, int ms)
{
	int	newsock;

	if ((newsock = accept(sock, addr, addrlen)) < 0)
		return -1;

	/*
	 * Set timeout value to keep us from hanging due to a
	 * malfunctioning or malicious client.
	 */
	if (ms > 0) {
		struct timeval tv;

		tv.tv_sec = ms / 1000;
		tv.tv_usec = (ms % 1000) * 1000;
		if (setsockopt(newsock, SOL_SOCKET, SO_RCVTIMEO,
			       &tv, sizeof(tv)) < 0) {
			errorc("setting SO_RCVTIMEO");
		}
	}

	return newsock;
}

829
/*
830
 * Listen for TCP requests.
831 832
 */
static void
833
tcpserver(int sock, int portnum)
834
{
835
	char			buf[MAXTMCDPACKET];
836 837
	struct sockaddr_in	client;
	int			length, cc, newsock;
838
	unsigned int		nreq = 0;
839
	struct timeval		tv;
840
	
841 842
	info("tcpserver starting: pid=%d sock=%d portnum=%d\n",
	     mypid, sock, portnum);
843 844 845 846 847

	/*
	 * Wait for TCP connections.
	 */
	while (1) {
848
		setproctitle("TCP %d: %u done", portnum, nreq);
849
		length  = sizeof(client);
850 851
		newsock = ACCEPT(sock, (struct sockaddr *)&client, &length,
				 readtimo);
852
		if (newsock < 0) {
853
			errorc("accepting TCP connection");
854
			continue;
855
		}
Mike Hibler's avatar
Mike Hibler committed
856

857 858 859 860 861 862 863 864 865 866 867 868 869 870
		/*
		 * Set write timeout value to keep us from hanging due to a
		 * malfunctioning or malicious client.
		 * NOTE: ACCEPT function sets read timeout.
		 */
		tv.tv_sec = writetimo / 1000;
		tv.tv_usec = (writetimo % 1000) * 1000;
		if (setsockopt(newsock, SOL_SOCKET, SO_SNDTIMEO,
			       &tv, sizeof(tv)) < 0) {
			errorc("setting SO_SNDTIMEO");
			CLOSE(newsock);
			continue;
		}

Mike Hibler's avatar
Mike Hibler committed
871
		/*
872
		 * Read in the command request.
Mike Hibler's avatar
Mike Hibler committed
873
		 */
874
		if ((cc = READ(newsock, buf, sizeof(buf) - 1)) <= 0) {
875 876 877 878 879 880
			if (cc < 0) {
				if (errno == EWOULDBLOCK)
					errorc("Timeout reading TCP request");
				else
					errorc("Error reading TCP request");
			}
881 882
			error("TCP connection aborted\n");
			CLOSE(newsock);
883
			continue;
884
		}
885 886
		buf[cc] = '\0';
		handle_request(newsock, &client, buf, 1);
887
		CLOSE(newsock);
888
		nreq++;
889 890 891
	}
	exit(1);
}
Mike Hibler's avatar
Mike Hibler committed
892

893 894 895 896
static int
handle_request(int sock, struct sockaddr_in *client, char *rdata, int istcp)
{
	struct sockaddr_in redirect_client;
897
	int		   redirect = 0, havekey = 0;
898
	char		   buf[BUFSIZ], *bp, *cp;
899
	char		   privkey[TBDB_FLEN_PRIVKEY];
900
	int		   i, overbose = 0, err = 0;
901
	int		   version = DEFAULT_VERSION;
902 903
	tmcdreq_t	   tmcdreq, *reqp = &tmcdreq;

904
	byteswritten = 0;
905 906 907 908 909
#ifdef	WITHSSL
	cp = (istcp ? (isssl ? "SSL" : "TCP") : "UDP");
#else
	cp = (istcp ? "TCP" : "UDP");
#endif
910
	setproctitle("%s: %s", inet_ntoa(client->sin_addr), cp);
911

912 913 914 915
	/*
	 * Init the req structure.
	 */
	bzero(reqp, sizeof(*reqp));
Mike Hibler's avatar
Mike Hibler committed
916

917 918 919
	/*
	 * Look for special tags. 
	 */
920
	bp = rdata;
921
	while ((bp = strsep(&rdata, " ")) != NULL) {
922 923 924 925 926 927 928 929 930 931 932 933 934 935
		/*
		 * Look for PRIVKEY. 
		 */
		if (sscanf(bp, "PRIVKEY=%64s", buf)) {
			havekey = 1;
			strncpy(privkey, buf, sizeof(privkey));

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


936 937
		/*
		 * Look for VERSION. 
938 939
		 * Check for clients that are newer than the server
		 * and complain.
940 941 942
		 */
		if (sscanf(bp, "VERSION=%d", &i) == 1) {
			version = i;
943
			if (version > CURRENT_VERSION) {
944
				error("version skew: server=%d, request=%d, "
945 946 947
				      "old TMCD installed?\n",
				      CURRENT_VERSION, version);
			}
948 949
			continue;
		}
Mike Hibler's avatar
Mike Hibler committed
950

951 952 953 954 955 956 957 958 959 960
		/*
		 * 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);
961

962 963
			info("REDIRECTED from %s to %s\n",
			     inet_ntoa(redirect_client.sin_addr), buf);
964

965 966 967 968 969 970 971 972 973 974 975
			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)) {
976 977
			reqp->isvnode = 1;
			strncpy(reqp->vnodeid, buf, sizeof(reqp->vnodeid));
978

979 980 981 982 983
			if (debug) {
				info("VNODEID %s\n", buf);
			}
			continue;
		}
984

985 986 987 988 989
		/*
		 * 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.
990 991 992
		 *
		 * Note that rdata will point to any text after the command.
		 *
993 994 995 996 997
		 */
		if (*bp) {
			break;
		}
	}
998

999 1000 1001
	/* Start with default DB */
	strcpy(dbname, DEFAULT_DBNAME);

1002
	/*
1003
	 * Map the ip to a nodeid.
1004
	 */
1005
	if ((err = iptonodeid(client->sin_addr, reqp))) {
1006 1007 1008
		if (reqp->isvnode) {
			error("No such vnode %s associated with %s\n",
			      reqp->vnodeid, inet_ntoa(client->sin_addr));
1009 1010 1011 1012 1013
		}
		else {
			error("No such node: %s\n",
			      inet_ntoa(client->sin_addr));
		}
1014 1015 1016 1017 1018 1019 1020 1021
		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. 
	 */
1022
	if (!insecure && redirect &&
1023
	    redirect_client.sin_addr.s_addr != myipaddr.s_addr) {
1024 1025 1026 1027
		char	buf1[32], buf2[32];
		
		strcpy(buf1, inet_ntoa(redirect_client.sin_addr));
		strcpy(buf2, inet_ntoa(client->sin_addr));
1028 1029 1030

		if (verbose)
			info("%s INVALID REDIRECT: %s\n", buf1, buf2);
1031 1032
		goto skipit;
	}
1033 1034 1035 1036 1037 1038

#ifdef  WITHSSL
	/*
	 * If the connection is not SSL, then it must be a local node.
	 */
	if (isssl) {
1039 1040
		if (tmcd_sslverify_client(reqp->nodeid, reqp->pclass,
					  reqp->ptype,  reqp->islocal)) {
1041
			error("%s: SSL verification failure\n", reqp->nodeid);
1042 1043 1044 1045
			if (! redirect)
				goto skipit;
		}
	}
1046 1047 1048 1049 1050 1051 1052 1053 1054
	else if (reqp->iscontrol) {
		if (!istcp)
			goto execute;

		error("%s: Control node connection without SSL!\n",
		      reqp->nodeid);
		if (!insecure)
			goto skipit;
	}
1055 1056 1057 1058
#else
	/*
	 * When not compiled for ssl, do not allow remote connections.
	 */
1059
	if (!reqp->islocal) {
1060 1061
		error("%s: Remote node connection not allowed (Define SSL)!\n",
		      reqp->nodeid);
1062 1063
		if (!insecure)
			goto skipit;
1064
	}
1065 1066 1067 1068 1069 1070
	if (reqp->iscontrol) {
		error("%s: Control node connection not allowed "
		      "(Define SSL)!\n", reqp->nodeid);
		if (!insecure)
			goto skipit;
	}
1071 1072 1073 1074
#endif
	/*
	 * Check for a redirect using the default DB. This allows
	 * for a simple redirect to a secondary DB for testing.
1075
	 * Upon return, the dbname has been changed if redirected.
1076
	 */
1077
	if (checkdbredirect(reqp)) {
1078 1079 1080
		/* Something went wrong */
		goto skipit;
	}
1081

1082 1083 1084 1085 1086
	/*
	 * 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. 
	 */
1087
	if (!reqp->islocal) {
1088
		if (!havekey) {
1089
			error("%s: No privkey sent!\n", reqp->nodeid);
1090 1091 1092 1093 1094 1095 1096
			/*
			 * Skip. Okay, the problem is that the nodes out
			 * there are not reporting the key!
			goto skipit;
			 */
		}
		else if (checkprivkey(client->sin_addr, privkey)) {
1097 1098
			error("%s: privkey mismatch: %s!\n",
			      reqp->nodeid, privkey);
1099 1100 1101 1102
			goto skipit;
		}
	}

1103 1104 1105
	/*
	 * Figure out what command was given.
	 */
1106
 execute:
1107
	for (i = 0; i < numcommands; i++)
1108
		if (strncmp(bp, command_array[i].cmdname,
1109 1110
			    strlen(command_array[i].cmdname)) == 0)
			break;
Mike Hibler's avatar
Mike Hibler committed
1111

1112
	if (i == numcommands) {
1113
		info("%s: INVALID REQUEST: %.8s\n", reqp->nodeid, bp);
1114 1115
		goto skipit;
	}
1116

1117
	/*
1118 1119
	 * If this is a UDP request from a remote node,
	 * make sure it is allowed.
1120
	 */
1121
	if (!istcp && !reqp->islocal &&
1122
	    (command_array[i].flags & F_REMUDP) == 0) {
1123
		error("%s: %s: Invalid UDP request from remote node\n",
1124 1125 1126 1127
		      reqp->nodeid, command_array[i].cmdname);
		goto skipit;
	}

1128 1129 1130 1131 1132 1133 1134 1135 1136 1137
	/*
	 * Ditto for remote node connection without SSL.
	 */
	if (!reqp->islocal && 
	    (command_array[i].flags & F_REMNOSSL) == 0) {
		error("%s: %s: Invalid NO-SSL request from remote node\n",
		      reqp->nodeid, command_array[i].cmdname);
		goto skipit;
	}

1138 1139 1140 1141 1142 1143 1144
	if (!reqp->allocated && (command_array[i].flags & F_ALLOCATED) != 0) {
		if (verbose || (command_array[i].flags & F_MINLOG) == 0)
			error("%s: %s: Invalid request from free node\n",
			      reqp->nodeid, command_array[i].cmdname);
		goto skipit;
	}

1145 1146 1147
	/*
	 * Execute it.
	 */
1148 1149 1150 1151
	if ((command_array[i].flags & F_MAXLOG) != 0) {
		overbose = verbose;
		verbose = 1;
	}
1152
	if (verbose || (command_array[i].flags & F_MINLOG) == 0)
1153 1154
		info("%s: vers:%d %s %s\n", reqp->nodeid,
		     version, cp, command_array[i].cmdname);
1155
	setproctitle("%s: %s %s", reqp->nodeid, cp, command_array[i].cmdname);
1156

1157
	err = command_array[i].func(sock, reqp, rdata, istcp, version);
1158 1159 1160

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

1165
 skipit:
1166 1167 1168
	if (!istcp) 
		client_writeback_done(sock,
				      redirect ? &redirect_client : client);
1169

1170
	if (byteswritten && (command_array[i].flags & F_MINLOG) == 0)
1171 1172
		info("%s: %s wrote %d bytes\n",
		     reqp->nodeid, command_array[i].cmdname,
1173 1174
		     byteswritten);

1175
	return 0;
1176 1177 1178 1179 1180
}

/*
 * Accept notification of reboot. 
 */
1181
COMMAND_PROTOTYPE(doreboot)
1182
{
1183
	/*
1184 1185
	 * This is now a no-op. The things this used to do are now
	 * done by stated when we hit RELOAD/RELOADDONE state
1186
	 */
1187 1188
	return 0;
}
1189

1190 1191 1192 1193 1194 1195 1196
/*
 * Return emulab nodeid (not the experimental name).
 */
COMMAND_PROTOTYPE(donodeid)
{
	char		buf[MYBUFSIZE];

1197
	OUTPUT(buf, sizeof(buf), "%s\n", reqp->nodeid);
1198
	client_writeback(sock, buf, strlen(buf), tcp);
1199 1200 1201 1202 1203 1204
	return 0;
}

/*
 * Return status of node. Is it allocated to an experiment, or free.
 */
1205
COMMAND_PROTOTYPE(dostatus)
1206
{
Mike Hibler's avatar
Mike Hibler committed
1207
	char		buf[MYBUFSIZE];
1208 1209 1210 1211

	/*
	 * Now check reserved table
	 */
1212
	if (! reqp->allocated) {
1213
		info("STATUS: %s: FREE\n", reqp->nodeid);
1214 1215 1216
		strcpy(buf, "FREE\n");
		client_writeback(sock, buf, strlen(buf), tcp);
		return 0;
1217 1218
	}

1219 1220
	OUTPUT(buf, sizeof(buf), "ALLOCATED=%s/%s NICKNAME=%s\n",
	       reqp->pid, reqp->eid, reqp->nickname);
1221 1222
	client_writeback(sock, buf, strlen(buf), tcp);

1223 1224
	if (verbose)
		info("STATUS: %s: %s", reqp->nodeid, buf);
1225 1226 1227 1228 1229 1230
	return 0;
}

/*
 * Return ifconfig information to client.
 */
1231
COMMAND_PROTOTYPE(doifconfig)
1232 1233 1234
{
	MYSQL_RES	*res;	
	MYSQL_ROW	row;
1235
	char		clause[BUFSIZ];
1236
	char		buf[MYBUFSIZE], *ebufp = &buf[MYBUFSIZE];
1237
	int		nrows;
1238
	int		num_interfaces=0;
1239

1240 1241
	/* 
	 * For Virtual Nodes, we return interfaces that belong to it.
Leigh Stoller's avatar
Leigh Stoller committed
1242
	 */
1243
	if (reqp->isvnode && !reqp->issubnode)
1244
		sprintf(clause, "i.vnode_id='%s'", reqp->vnodeid);
1245
	else
1246
		strcpy(clause, "i.vnode_id is NULL");
Leigh Stoller's avatar
Leigh Stoller committed
1247

1248 1249 1250
	/*
	 * Find all the interfaces.
	 */
1251
	res = mydb_query("select i.card,i.IP,i.MAC,i.current_speed,"
1252
			 "       i.duplex,i.IPaliases,i.iface,i.role,i.mask,"
1253
			 "       i.rtabid,i.interface_type,vl.vname "
1254
			 "  from interfaces as i "
1255 1256 1257
			 "left join virt_lans as vl on "
			 "  vl.pid='%s' and vl.eid='%s' and "
			 "  vl.vnode='%s' and vl.ip=i.IP "
1258
			 "where i.node_id='%s' and %s",
1259
			 12, reqp->pid, reqp->eid, reqp->