tmcd.c 173 KB
Newer Older
Leigh Stoller's avatar
Leigh Stoller committed
1 2
/*
 * EMULAB-COPYRIGHT
3
 * Copyright (c) 2000-2007 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
#include <stdlib.h>
15
#include <errno.h>
Mike Hibler's avatar
Mike Hibler committed
16 17
#include <string.h>
#include <unistd.h>
18 19 20 21
#include <syslog.h>
#include <signal.h>
#include <stdarg.h>
#include <assert.h>
22
#include <sys/wait.h>
23
#include <sys/fcntl.h>
24 25
#include <sys/syscall.h>
#include <sys/stat.h>
26
#include <sys/param.h>
27
#include <paths.h>
Austin Clements's avatar
Austin Clements committed
28
#include <setjmp.h>
29 30
#include <pwd.h>
#include <grp.h>
31 32
#include <mysql/mysql.h>
#include "decls.h"
33
#include "config.h"
34 35
#include "ssl.h"
#include "log.h"
36
#include "tbdefs.h"
37

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

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

69 70 71 72
/* socket read/write timeouts in ms */
#define READTIMO	3000
#define WRITETIMO	3000

73
#define TESTMODE
74 75 76 77 78 79 80 81 82 83 84 85
#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) */
86

87 88 89
#define DISKTYPE	"ad"
#define DISKNUM		0

90 91 92 93 94 95 96
/* 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"

97 98
/* Defined in configure and passed in via the makefile */
#define DBNAME_SIZE	64
Austin Clements's avatar
Austin Clements committed
99
#define HOSTID_SIZE	(32+64)
100 101
#define DEFAULT_DBNAME	TBDBNAME

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

121 122 123 124
/* socket timeouts */
static int	readtimo = READTIMO;
static int	writetimo = WRITETIMO;

125
/* thread support */
126
#define MAXCHILDREN	20
127
#define MINCHILDREN	8
128
static int	numchildren;
129 130 131 132
static int	maxchildren       = 13;
static int      num_udpservers    = 3;
static int      num_altudpservers = 1;
static int      num_alttcpservers = 1;
133
static int	mypid;
134
static volatile int killme;
135

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

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

187 188 189 190 191
#ifdef EVENTSYS
int			myevent_send(address_tuple_t address);
static event_handle_t	event_handle = NULL;
#endif

192 193 194
/*
 * Commands we support.
 */
195 196
#define COMMAND_PROTOTYPE(x) \
	static int \
197
	x(int sock, tmcdreq_t *reqp, char *rdata, int tcp, int vers)
198 199

COMMAND_PROTOTYPE(doreboot);
200
COMMAND_PROTOTYPE(donodeid);
201 202 203 204
COMMAND_PROTOTYPE(dostatus);
COMMAND_PROTOTYPE(doifconfig);
COMMAND_PROTOTYPE(doaccounts);
COMMAND_PROTOTYPE(dodelay);
205
COMMAND_PROTOTYPE(dolinkdelay);
206 207 208 209 210 211 212 213 214
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
215
COMMAND_PROTOTYPE(dosfshostid);
216 217 218 219 220 221 222
COMMAND_PROTOTYPE(doloadinfo);
COMMAND_PROTOTYPE(doreset);
COMMAND_PROTOTYPE(dorouting);
COMMAND_PROTOTYPE(dotrafgens);
COMMAND_PROTOTYPE(donseconfigs);
COMMAND_PROTOTYPE(dostate);
COMMAND_PROTOTYPE(docreator);
223
COMMAND_PROTOTYPE(dotunnels);
224
COMMAND_PROTOTYPE(dovnodelist);
225
COMMAND_PROTOTYPE(dosubnodelist);
226
COMMAND_PROTOTYPE(doisalive);
227
COMMAND_PROTOTYPE(doipodinfo);
228 229
COMMAND_PROTOTYPE(dontpinfo);
COMMAND_PROTOTYPE(dontpdrift);
230
COMMAND_PROTOTYPE(dojailconfig);
231
COMMAND_PROTOTYPE(doplabconfig);
232 233
COMMAND_PROTOTYPE(dosubconfig);
COMMAND_PROTOTYPE(doixpconfig);
234
COMMAND_PROTOTYPE(doslothdparams);
235
COMMAND_PROTOTYPE(doprogagents);
236
COMMAND_PROTOTYPE(dosyncserver);
237
COMMAND_PROTOTYPE(dokeyhash);
238
COMMAND_PROTOTYPE(doeventkey);
239
COMMAND_PROTOTYPE(dofullconfig);
240 241
COMMAND_PROTOTYPE(doroutelist);
COMMAND_PROTOTYPE(dorole);
242
COMMAND_PROTOTYPE(dorusage);
243
COMMAND_PROTOTYPE(dodoginfo);
244
COMMAND_PROTOTYPE(dohostkeys);
Mike Hibler's avatar
Mike Hibler committed
245
COMMAND_PROTOTYPE(dotmcctest);
246
COMMAND_PROTOTYPE(dofwinfo);
247
COMMAND_PROTOTYPE(dohostinfo);
248
COMMAND_PROTOTYPE(doemulabconfig);
249
COMMAND_PROTOTYPE(doeplabconfig);
250
COMMAND_PROTOTYPE(dolocalize);
251 252
COMMAND_PROTOTYPE(dobooterrno);
COMMAND_PROTOTYPE(dobootlog);
253
COMMAND_PROTOTYPE(dobattery);
254
COMMAND_PROTOTYPE(dotopomap);
255
COMMAND_PROTOTYPE(douserenv);
256 257
COMMAND_PROTOTYPE(dotiptunnels);
COMMAND_PROTOTYPE(dorelayconfig);
258
COMMAND_PROTOTYPE(dotraceconfig);
259
COMMAND_PROTOTYPE(doltmap);
260
COMMAND_PROTOTYPE(doltpmap);
261
COMMAND_PROTOTYPE(doelvindport);
262
COMMAND_PROTOTYPE(doplabeventkeys);
263
COMMAND_PROTOTYPE(dointfcmap);
264
COMMAND_PROTOTYPE(domotelog);
265
COMMAND_PROTOTYPE(doportregister);
266

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

/*
 * Flags encode a few other random properties of commands
 */
281 282 283 284 285
#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 */
286

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

362 363 364 365
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"
366
 " -c num	   Specify number of servers (must be %d <= x <= %d)\n"
367
 " -v              More verbose logging\n"
368
 " -i ipaddr       Sets the boss IP addr to return (for multi-homed servers)\n"
369 370 371 372 373
 "\n";

void
usage()
{
374
	fprintf(stderr, usagestr, MINCHILDREN, MAXCHILDREN);
375 376 377
	exit(1);
}

378 379 380 381 382
static void
cleanup()
{
	signal(SIGHUP, SIG_IGN);
	killme = 1;
383
	killpg(0, SIGHUP);
384 385
}

386 387 388 389 390 391 392 393 394
static void
setverbose(int sig)
{
	signal(sig, SIG_IGN);
	
	if (sig == SIGUSR1)
		verbose = 1;
	else
		verbose = 0;
395 396
	info("verbose logging turned %s\n", verbose ? "on" : "off");

397 398 399 400 401 402
	/* Just the parent sends this */
	if (numchildren)
		killpg(0, sig);
	signal(sig, setverbose);
}

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

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

	if (argc)
		usage();
454 455
	if (maxchildren < MINCHILDREN || maxchildren > MAXCHILDREN)
		usage();
456

457 458 459 460 461 462 463 464 465 466 467 468 469 470 471
#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
472

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

517 518 519 520 521 522
	/*
	 * 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) {
523 524
		error("Could not make sockets!");
		exit(1);
525
	    }
526
	    num_alttcpservers = num_altudpservers = 0;
527 528 529 530 531 532
	} else {
	    if (makesockets(portnum, &udpsock, &tcpsock) < 0 ||
		makesockets(TBSERVER_PORT2, &altudpsock, &alttcpsock) < 0) {
		    error("Could not make sockets!");
		    exit(1);
	    }
533 534 535 536 537
	}

	signal(SIGTERM, cleanup);
	signal(SIGINT, cleanup);
	signal(SIGHUP, cleanup);
538 539
	signal(SIGUSR1, setverbose);
	signal(SIGUSR2, setverbose);
540 541 542 543

	/*
	 * Stash the pid away.
	 */
544
	mypid = getpid();
545 546 547
	sprintf(buf, "%s/tmcd.pid", _PATH_VARRUN);
	fp = fopen(buf, "w");
	if (fp != NULL) {
548
		fprintf(fp, "%d\n", mypid);
549 550 551
		(void) fclose(fp);
	}

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 578 579 580 581 582 583 584
	/*
	 * 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);
	}

585 586
	/*
	 * Now fork a set of children to handle requests. We keep the
587 588 589 590 591 592 593
	 * 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!
594
	 */
595 596 597 598 599 600 601
	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));
	
602 603
	while (1) {
		while (!killme && numchildren < maxchildren) {
604 605 606 607 608 609 610 611 612 613
			int which = 3;
			
			/*
			 * Find which kind of server is short one.
			 */
			for (i = 0; i < 4; i++) {
				if (server_counts[i]) {
					which = i;
					break;
				}
614
			}
615
			
616 617 618 619 620
			if ((pid = fork()) < 0) {
				errorc("forking server");
				goto done;
			}
			if (pid) {
621 622 623 624 625 626 627 628 629 630
				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;
631 632 633
				numchildren++;
				continue;
			}
634 635 636 637
			/* Poor way of knowing parent/child */
			numchildren = 0;
			mypid = getpid();
			
638 639 640 641 642 643
			/* Child does useful work! Never Returns! */
			signal(SIGTERM, SIG_DFL);
			signal(SIGINT, SIG_DFL);
			signal(SIGHUP, SIG_DFL);
			
			switch (which) {
644
			case 0: udpserver(udpsock, portnum);
645
				break;
646
			case 1: udpserver(altudpsock, TBSERVER_PORT2);
647
				break;
648
			case 2: tcpserver(alttcpsock, TBSERVER_PORT2);
649
				break;
650
			case 3: tcpserver(tcpsock, portnum);
651 652 653 654 655 656 657 658 659 660 661 662
				break;
			}
			exit(-1);
		}

		/*
		 * Parent waits.
		 */
		pid = waitpid(-1, &status, 0);
		if (pid < 0) {
			errorc("waitpid failed");
			continue;
663 664 665 666
		} 
		if (WIFSIGNALED(status)) {
			error("server %d exited with signal %d!\n",
			      pid, WTERMSIG(status));
667
		}
668 669 670
		else if (WIFEXITED(status)) {
			error("server %d exited with status %d!\n",
			      pid, WEXITSTATUS(status));	  
671
		}
672
		numchildren--;
673 674 675 676 677 678 679 680 681 682

		/*
		 * 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;
			}
683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702
		}
		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
703
	/*
704
	 * Setup TCP socket for incoming connections.
Mike Hibler's avatar
Mike Hibler committed
705 706
	 */

707
	/* Create socket from which to read. */
Mike Hibler's avatar
Mike Hibler committed
708 709
	tcpsock = socket(AF_INET, SOCK_STREAM, 0);
	if (tcpsock < 0) {
710
		pfatal("opening stream socket");
711 712
	}

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

	/* Create socket from which to read. */
	udpsock = socket(AF_INET, SOCK_DGRAM, 0);
	if (udpsock < 0) {
742
		pfatal("opening dgram socket");
Mike Hibler's avatar
Mike Hibler committed
743 744 745 746 747
	}

	i = 1;
	if (setsockopt(udpsock, SOL_SOCKET, SO_REUSEADDR,
		       (char *)&i, sizeof(i)) < 0)
748
		pwarning("setsockopt(SO_REUSEADDR)");;
749 750 751 752

	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
753 754 755 756
	
	/* Create name. */
	name.sin_family = AF_INET;
	name.sin_addr.s_addr = INADDR_ANY;
757
	name.sin_port = htons((u_short) portnum);
Mike Hibler's avatar
Mike Hibler committed
758
	if (bind(udpsock, (struct sockaddr *) &name, sizeof(name))) {
759
		pfatal("binding dgram socket");
Mike Hibler's avatar
Mike Hibler committed
760 761 762 763 764
	}

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

769 770 771
	*tcpsockp = tcpsock;
	*udpsockp = udpsock;
	return 0;
772
}
773

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

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

810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835
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;
}

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

	/*
	 * Wait for TCP connections.
	 */
	while (1) {
855
		setproctitle("TCP %d: %u done", portnum, nreq);
856
		length  = sizeof(client);
857 858
		newsock = ACCEPT(sock, (struct sockaddr *)&client, &length,
				 readtimo);
859
		if (newsock < 0) {
860
			errorc("accepting TCP connection");
861
			continue;
862
		}
Mike Hibler's avatar
Mike Hibler committed
863

864 865 866 867 868 869 870 871 872 873 874 875 876 877
		/*
		 * 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
878
		/*
879
		 * Read in the command request.
Mike Hibler's avatar
Mike Hibler committed
880
		 */
881
		if ((cc = READ(newsock, buf, sizeof(buf) - 1)) <= 0) {
882 883 884 885 886 887
			if (cc < 0) {
				if (errno == EWOULDBLOCK)
					errorc("Timeout reading TCP request");
				else
					errorc("Error reading TCP request");
			}
888 889
			error("TCP connection aborted\n");
			CLOSE(newsock);
890
			continue;
891
		}
892 893
		buf[cc] = '\0';
		handle_request(newsock, &client, buf, 1);
894
		CLOSE(newsock);
895
		nreq++;
896 897 898
	}
	exit(1);
}
Mike Hibler's avatar
Mike Hibler committed
899

900 901 902 903
static int
handle_request(int sock, struct sockaddr_in *client, char *rdata, int istcp)
{
	struct sockaddr_in redirect_client;
904
	int		   redirect = 0, havekey = 0;
905
	char		   buf[BUFSIZ], *bp, *cp;
906
	char		   privkey[TBDB_FLEN_PRIVKEY];
907
	int		   i, overbose = 0, err = 0;
908
	int		   version = DEFAULT_VERSION;
909 910
	tmcdreq_t	   tmcdreq, *reqp = &tmcdreq;

911
	byteswritten = 0;
912 913 914 915 916
#ifdef	WITHSSL
	cp = (istcp ? (isssl ? "SSL" : "TCP") : "UDP");
#else
	cp = (istcp ? "TCP" : "UDP");
#endif
917
	setproctitle("%s: %s", inet_ntoa(client->sin_addr), cp);
918

919 920 921 922
	/*
	 * Init the req structure.
	 */
	bzero(reqp, sizeof(*reqp));
Mike Hibler's avatar
Mike Hibler committed
923

924 925 926
	/*
	 * Look for special tags. 
	 */
927
	bp = rdata;
928
	while ((bp = strsep(&rdata, " ")) != NULL) {
929 930 931 932 933 934 935 936 937 938 939 940 941 942
		/*
		 * Look for PRIVKEY. 
		 */
		if (sscanf(bp, "PRIVKEY=%64s", buf)) {
			havekey = 1;
			strncpy(privkey, buf, sizeof(privkey));

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


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

958 959 960 961 962 963 964 965 966 967
		/*
		 * 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);
968

969 970
			info("REDIRECTED from %s to %s\n",
			     inet_ntoa(redirect_client.sin_addr), buf);
971

972 973 974 975 976 977 978 979 980 981 982
			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)) {
983 984
			reqp->isvnode = 1;
			strncpy(reqp->vnodeid, buf, sizeof(reqp->vnodeid));
985

986 987 988 989 990
			if (debug) {
				info("VNODEID %s\n", buf);
			}
			continue;
		}
991

992 993 994 995 996
		/*
		 * 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.
997 998 999
		 *
		 * Note that rdata will point to any text after the command.
		 *
1000 1001 1002 1003 1004
		 */
		if (*bp) {
			break;
		}
	}
1005

1006 1007 1008
	/* Start with default DB */
	strcpy(dbname, DEFAULT_DBNAME);

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

		if (verbose)
			info("%s INVALID REDIRECT: %s\n", buf1, buf2);
1038 1039
		goto skipit;
	}
1040 1041

#ifdef  WITHSSL
1042 1043 1044 1045 1046 1047
	/*
	 * We verify UDP requests below based on the particular request
	 */
	if (!istcp)
		goto execute;

1048 1049 1050 1051
	/*
	 * If the connection is not SSL, then it must be a local node.
	 */
	if (isssl) {
1052 1053
		if (tmcd_sslverify_client(reqp->nodeid, reqp->pclass,
					  reqp->ptype,  reqp->islocal)) {
1054
			error("%s: SSL verification failure\n", reqp->nodeid);
1055 1056 1057 1058
			if (! redirect)
				goto skipit;
		}
	}
1059 1060 1061 1062 1063 1064
	else if (reqp->iscontrol) {
		error("%s: Control node connection without SSL!\n",
		      reqp->nodeid);
		if (!insecure)
			goto skipit;
	}
1065 1066 1067 1068
#else
	/*
	 * When not compiled for ssl, do not allow remote connections.
	 */
1069
	if (!reqp->islocal) {
1070 1071
		error("%s: Remote node connection not allowed (Define SSL)!\n",
		      reqp->nodeid);
1072 1073
		if (!insecure)
			goto skipit;
1074
	}
1075 1076 1077 1078 1079 1080
	if (reqp->iscontrol) {
		error("%s: Control node connection not allowed "
		      "(Define SSL)!\n", reqp->nodeid);
		if (!insecure)
			goto skipit;
	}
1081 1082 1083 1084
#endif
	/*
	 * Check for a redirect using the default DB. This allows
	 * for a simple redirect to a secondary DB for testing.
1085
	 * Upon return, the dbname has been changed if redirected.
1086
	 */
1087
	if (checkdbredirect(reqp)) {
1088 1089 1090
		/* Something went wrong */
		goto skipit;
	}
1091

1092
	/*
1093 1094 1095
	 * Do private key check. (Non-plab) widearea nodes must report a
	 * private key. It comes over ssl of course. At present we skip
	 * this check for ron nodes. 
1096
	 */
1097
	if (!reqp->islocal && !(reqp->isplabdslice || reqp->isplabsvc)) {
1098
		if (!havekey) {
1099
			error("%s: No privkey sent!\n", reqp->nodeid);
1100 1101 1102 1103 1104 1105 1106
			/*
			 * Skip. Okay, the problem is that the nodes out
			 * there are not reporting the key!
			goto skipit;
			 */
		}
		else if (checkprivkey(client->sin_addr, privkey)) {
1107 1108
			error("%s: privkey mismatch: %s!\n",
			      reqp->nodeid, privkey);
1109 1110 1111 1112
			goto skipit;
		}
	}

1113 1114 1115
	/*
	 * Figure out what command was given.
	 */
1116
 execute:
1117
	for (i = 0; i < numcommands; i++)
1118
		if (strncmp(bp, command_array[i].cmdname,
1119 1120
			    strlen(command_array[i].cmdname)) == 0)
			break;
Mike Hibler's avatar
Mike Hibler committed
1121

1122
	if (i == numcommands) {
1123
		info("%s: INVALID REQUEST: %.8s\n", reqp->nodeid, bp);
1124 1125
		goto skipit;
	}
1126

1127
	/*
1128 1129
	 * If this is a UDP request from a remote node,
	 * make sure it is allowed.
1130
	 */
1131
	if (!istcp && !reqp->islocal &&
1132
	    (command_array[i].flags & F_REMUDP) == 0) {
1133
		error("%s: %s: Invalid UDP request from remote node\n",
1134 1135 1136 1137
		      reqp->nodeid, command_array[i].cmdname);
		goto skipit;
	}

1138 1139 1140
	/*
	 * Ditto for remote node connection without SSL.
	 */
1141
	if (istcp && !isssl && !reqp->islocal && 
1142 1143 1144 1145 1146 1147
	    (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;
	}

1148 1149 1150 1151 1152 1153 1154
	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;
	}

1155 1156 1157
	/*
	 * Execute it.
	 */
1158 1159 1160 1161
	if ((command_array[i].flags & F_MAXLOG) != 0) {
		overbose = verbose;
		verbose = 1;
	}
1162
	if (verbose || (command_array[i].flags & F_MINLOG) == 0)
1163 1164
		info("%s: vers:%d %s %s\n", reqp->nodeid,
		     version, cp, command_array[i].cmdname);
1165
	setproctitle("%s: %s %s", reqp->nodeid, cp, command_array[i].cmdname);
1166

1167
	err = command_array[i].func(sock, reqp, rdata, istcp, version);
1168 1169 1170

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

1175
 skipit:
1176 1177 1178
	if (!istcp) 
		client_writeback_done(sock,
				      redirect ? &redirect_client : client);
1179

1180
	if (byteswritten && (command_array[i].flags & F_MINLOG) == 0)
1181 1182
		info("%s: %s wrote %d bytes\n",
		     reqp->nodeid, command_array[i].cmdname,
1183 1184
		     byteswritten);

1185
	return 0;
1186 1187 1188 1189 1190
}

/*
 * Accept notification of reboot. 
 */
1191
COMMAND_PROTOTYPE(doreboot)
1192
{
1193
	/*
1194 1195
	 * This is now a no-op. The things this used to do are now
	 * done by stated when we hit RELOAD/RELOADDONE state
1196
	 */
1197 1198
	return 0;
}
1199

1200 1201 1202 1203 1204 1205 1206
/*
 * Return emulab nodeid (not the experimental name).
 */
COMMAND_PROTOTYPE(donodeid)
{
	char		buf[MYBUFSIZE];

1207
	OUTPUT(buf, sizeof(buf), "%s\n", reqp->nodeid);
1208
	client_writeback(sock, buf, strlen(buf), tcp);
1209 1210 1211 1212 1213 1214
	return 0;
}

/*
 * Return status of node. Is it allocated to an experiment, or free.
 */
1215
COMMAND_PROTOTYPE(dostatus)
1216
{
Mike Hibler's avatar
Mike Hibler committed
1217
	char		buf[MYBUFSIZE];
1218 1219 1220 1221

	/*
	 * Now check reserved table
	 */
1222
	if (! reqp->allocated) {
1223
		info("STATUS: %s: FREE\n", reqp->nodeid);
1224 1225 1226
		strcpy(buf, "FREE\n");
		client_writeback(sock, buf, strlen(buf), tcp);
		return 0;
1227 1228
	}

1229 1230
	OUTPUT(buf, sizeof(buf), "ALLOCATED=%s/%s NICKNAME=%s\n",
	       reqp->pid, reqp->eid, reqp->nickname);
1231 1232
	client_writeback(sock, buf, strlen(buf), tcp);

1233 1234
	if (verbose)
		info("STATUS: %s: %s", reqp->nodeid, buf);
1235 1236 1237 1238 1239 1240
	return 0;
}

/*
 * Return ifconfig information to client.
 */
1241
COMMAND_PROTOTYPE(doifconfig)
1242 1243 1244
{
	MYSQL_RES	*res;	
	MYSQL_ROW	row;
1245
	char		clause[BUFSIZ];
1246
	char		buf[MYBUFSIZE], *ebufp = &buf[MYBUFSIZE];
1247
	int		nrows;
1248
	int		num_interfaces=0;
1249

1250 1251
	/* 
	 * For Virtual Nodes, we return interfaces that belong to it.
Leigh Stoller's avatar
Leigh Stoller committed
1252
	 */
1253
	if (reqp->isvnode && !reqp->issubnode)
1254
		sprintf(clause, "i.vnode_id='%s'", reqp->vnodeid);
1255
	else
1256
		strcpy(clause, "i.vnode_id is NULL");
Leigh Stoller's avatar
Leigh Stoller committed
1257

1258 1259 1260
	/*