tmcd.c 196 KB
Newer Older
Leigh B. Stoller's avatar
Leigh B. Stoller committed
1 2
/*
 * EMULAB-COPYRIGHT
3
 * Copyright (c) 2000-2009 University of Utah and the Flux Group.
Leigh B. Stoller's avatar
Leigh B. Stoller committed
4 5 6
 * All rights reserved.
 */

7 8 9
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
Mike Hibler's avatar
Mike Hibler committed
10
#include <arpa/inet.h>
11
#include <netdb.h>
Mike Hibler's avatar
Mike Hibler committed
12
#include <ctype.h>
13
#include <stdio.h>
Mike Hibler's avatar
Mike Hibler committed
14
#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"
Leigh B. Stoller's avatar
Leigh B. Stoller committed
37 38
#include "bootwhat.h"
#include "bootinfo.h"
39

40 41 42 43
#ifdef EVENTSYS
#include "event.h"
#endif

44 45 46
/*
 * XXX This needs to be localized!
 */
47 48 49
#define FSPROJDIR	FSNODE ":" FSDIR_PROJ
#define FSGROUPDIR	FSNODE ":" FSDIR_GROUPS
#define FSUSERDIR	FSNODE ":" FSDIR_USERS
50 51 52
#ifdef  FSDIR_SHARE
#define FSSHAREDIR	FSNODE ":" FSDIR_SHARE
#endif
53 54 55 56 57 58 59 60
#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
61
#define NETBEDDIR	"/netbed"
62
#define PLISALIVELOGDIR "/usr/testbed/log/plabisalive"
63 64
#define RELOADPID	"emulab-ops"
#define RELOADEID	"reloading"
Austin Clements's avatar
Austin Clements committed
65
#define FSHOSTID	"/usr/testbed/etc/fshostid"
66
#define DOTSFS		".sfs"
67 68
#define RUNASUSER	"nobody"
#define RUNASGROUP	"nobody"
69
#define NTPSERVER       "ntp1"
70
#define PROTOUSER	"elabman"
71

72 73 74 75
/* socket read/write timeouts in ms */
#define READTIMO	3000
#define WRITETIMO	3000

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

90 91 92
#define DISKTYPE	"ad"
#define DISKNUM		0

93 94 95 96 97 98 99
/* 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"

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

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

125 126 127 128
/* socket timeouts */
static int	readtimo = READTIMO;
static int	writetimo = WRITETIMO;

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

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

152 153 154 155 156
/*
 * 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 {
Leigh B. Stoller's avatar
Leigh B. Stoller committed
157
	struct in_addr  client;
158 159 160
	int		allocated;
	int		jailflag;
	int		isvnode;
161
	int		issubnode;
162
	int		islocal;
163
	int		isdedicatedwa;
164
	int		iscontrol;
165
	int		isplabdslice;
166
	int		isplabsvc;
167
	int		elab_in_elab;
168
        int		singlenet;	  /* Modifier for elab_in_elab */
169
	int		update_accounts;
170
	int		exptidx;
171 172 173
	int		creator_idx;
	int		swapper_idx;
	int		swapper_isadmin;
174
        int		genisliver_idx;
175
        int		geniflags;
176 177
	char		nodeid[TBDB_FLEN_NODEID];
	char		vnodeid[TBDB_FLEN_NODEID];
Leigh B. Stoller's avatar
Leigh B. Stoller committed
178
	char		pnodeid[TBDB_FLEN_NODEID]; /* XXX */
179 180 181 182 183
	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];
184
	char		class[TBDB_FLEN_NODECLASS];
185 186
        char		ptype[TBDB_FLEN_NODETYPE];	/* Of physnode */
	char		pclass[TBDB_FLEN_NODECLASS];	/* Of physnode */
187 188
	char		creator[TBDB_FLEN_UID];
	char		swapper[TBDB_FLEN_UID];
189
	char		syncserver[TBDB_FLEN_VNAME];	/* The vname */
190
	char		keyhash[TBDB_FLEN_PRIVKEY];
191
	char		eventkey[TBDB_FLEN_PRIVKEY];
192
	char		sfshostid[TBDB_FLEN_SFSHOSTID];
193
	char		testdb[TBDB_FLEN_TINYTEXT];
194
	char		sharing_mode[TBDB_FLEN_TINYTEXT];
195
	char            privkey[TBDB_FLEN_PRIVKEY+1];
196
} tmcdreq_t;
197
static int	iptonodeid(struct in_addr, tmcdreq_t *, char*);
198 199
static int	checkdbredirect(tmcdreq_t *);

200 201 202 203 204
#ifdef EVENTSYS
int			myevent_send(address_tuple_t address);
static event_handle_t	event_handle = NULL;
#endif

205 206 207
/*
 * Commands we support.
 */
208 209
#define COMMAND_PROTOTYPE(x) \
	static int \
210
	x(int sock, tmcdreq_t *reqp, char *rdata, int tcp, int vers)
211 212

COMMAND_PROTOTYPE(doreboot);
213
COMMAND_PROTOTYPE(donodeid);
214 215 216 217
COMMAND_PROTOTYPE(dostatus);
COMMAND_PROTOTYPE(doifconfig);
COMMAND_PROTOTYPE(doaccounts);
COMMAND_PROTOTYPE(dodelay);
218
COMMAND_PROTOTYPE(dolinkdelay);
219 220 221 222 223 224 225 226 227
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
228
COMMAND_PROTOTYPE(dosfshostid);
229 230 231 232 233 234 235
COMMAND_PROTOTYPE(doloadinfo);
COMMAND_PROTOTYPE(doreset);
COMMAND_PROTOTYPE(dorouting);
COMMAND_PROTOTYPE(dotrafgens);
COMMAND_PROTOTYPE(donseconfigs);
COMMAND_PROTOTYPE(dostate);
COMMAND_PROTOTYPE(docreator);
236
COMMAND_PROTOTYPE(dotunnels);
237
COMMAND_PROTOTYPE(dovnodelist);
238
COMMAND_PROTOTYPE(dosubnodelist);
239
COMMAND_PROTOTYPE(doisalive);
240
COMMAND_PROTOTYPE(doipodinfo);
241 242
COMMAND_PROTOTYPE(dontpinfo);
COMMAND_PROTOTYPE(dontpdrift);
243
COMMAND_PROTOTYPE(dojailconfig);
244
COMMAND_PROTOTYPE(doplabconfig);
245 246
COMMAND_PROTOTYPE(dosubconfig);
COMMAND_PROTOTYPE(doixpconfig);
247
COMMAND_PROTOTYPE(doslothdparams);
248
COMMAND_PROTOTYPE(doprogagents);
249
COMMAND_PROTOTYPE(dosyncserver);
250
COMMAND_PROTOTYPE(dokeyhash);
251
COMMAND_PROTOTYPE(doeventkey);
252
COMMAND_PROTOTYPE(dofullconfig);
253 254
COMMAND_PROTOTYPE(doroutelist);
COMMAND_PROTOTYPE(dorole);
255
COMMAND_PROTOTYPE(dorusage);
256
COMMAND_PROTOTYPE(dodoginfo);
257
COMMAND_PROTOTYPE(dohostkeys);
Mike Hibler's avatar
Mike Hibler committed
258
COMMAND_PROTOTYPE(dotmcctest);
259
COMMAND_PROTOTYPE(dofwinfo);
260
COMMAND_PROTOTYPE(dohostinfo);
261
COMMAND_PROTOTYPE(doemulabconfig);
262
COMMAND_PROTOTYPE(doeplabconfig);
263
COMMAND_PROTOTYPE(dolocalize);
264
COMMAND_PROTOTYPE(dorootpswd);
265 266
COMMAND_PROTOTYPE(dobooterrno);
COMMAND_PROTOTYPE(dobootlog);
267
COMMAND_PROTOTYPE(dobattery);
268
COMMAND_PROTOTYPE(dotopomap);
269
COMMAND_PROTOTYPE(douserenv);
270 271
COMMAND_PROTOTYPE(dotiptunnels);
COMMAND_PROTOTYPE(dorelayconfig);
272
COMMAND_PROTOTYPE(dotraceconfig);
273
COMMAND_PROTOTYPE(doltmap);
274
COMMAND_PROTOTYPE(doltpmap);
275
COMMAND_PROTOTYPE(doelvindport);
276
COMMAND_PROTOTYPE(doplabeventkeys);
277
COMMAND_PROTOTYPE(dointfcmap);
278
COMMAND_PROTOTYPE(domotelog);
279
COMMAND_PROTOTYPE(doportregister);
Leigh B. Stoller's avatar
Leigh B. Stoller committed
280
COMMAND_PROTOTYPE(dobootwhat);
281 282
COMMAND_PROTOTYPE(dotpmblob);
COMMAND_PROTOTYPE(dotpmpubkey);
283
COMMAND_PROTOTYPE(dotpmdummy);
284

285 286
/*
 * The fullconfig slot determines what routines get called when pushing
287
 * out a full configuration. Physnodes get slightly different
288
 * than vnodes, and at some point we might want to distinguish different
289 290 291 292 293
 * types of vnodes (jailed, plab).
 */
#define FULLCONFIG_NONE		0x0
#define FULLCONFIG_PHYS		0x1
#define FULLCONFIG_VIRT		0x2
294 295 296 297 298
#define FULLCONFIG_ALL		(FULLCONFIG_PHYS|FULLCONFIG_VIRT)

/*
 * Flags encode a few other random properties of commands
 */
299 300 301 302 303
#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 */
304
#define F_REMREQSSL	0x20	/* remote nodes must connect with SSL */
305
#define F_REQTPM	0x40	/* require TPM on client */
306

307 308
struct command {
	char	*cmdname;
309
	int	fullconfig;	
310
	int	flags;
311
	int    (*func)(int, tmcdreq_t *, char *, int, int);
312
} command_array[] = {
313 314 315
	{ "reboot",	  FULLCONFIG_NONE, 0, doreboot },
	{ "nodeid",	  FULLCONFIG_ALL,  0, donodeid },
	{ "status",	  FULLCONFIG_NONE, 0, dostatus },
316
	{ "ifconfig",	  FULLCONFIG_ALL,  F_ALLOCATED, doifconfig },
317
	{ "accounts",	  FULLCONFIG_ALL,  F_REMREQSSL, doaccounts },
318 319 320 321 322 323 324 325
	{ "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*/
326
	{ "startstat",	  FULLCONFIG_NONE, 0, dostartstat },
327 328 329 330
	{ "readycount",   FULLCONFIG_NONE, F_ALLOCATED, doreadycount },
	{ "ready",	  FULLCONFIG_NONE, F_ALLOCATED, doready },
	{ "mounts",	  FULLCONFIG_ALL,  F_ALLOCATED, domounts },
	{ "sfshostid",	  FULLCONFIG_NONE, F_ALLOCATED, dosfshostid },
331 332
	{ "loadinfo",	  FULLCONFIG_NONE, 0, doloadinfo},
	{ "reset",	  FULLCONFIG_NONE, 0, doreset},
333 334 335 336
	{ "routing",	  FULLCONFIG_ALL,  F_ALLOCATED, dorouting},
	{ "trafgens",	  FULLCONFIG_ALL,  F_ALLOCATED, dotrafgens},
	{ "nseconfigs",	  FULLCONFIG_ALL,  F_ALLOCATED, donseconfigs},
	{ "creator",	  FULLCONFIG_ALL,  F_ALLOCATED, docreator},
337
	{ "state",	  FULLCONFIG_NONE, 0, dostate},
338
	{ "tunnels",	  FULLCONFIG_ALL,  F_ALLOCATED, dotunnels},
339
	{ "vnodelist",	  FULLCONFIG_PHYS, 0, dovnodelist},
340
	{ "subnodelist",  FULLCONFIG_PHYS, 0, dosubnodelist},
341
	{ "isalive",	  FULLCONFIG_NONE, F_REMUDP|F_MINLOG, doisalive},
342
	{ "ipodinfo",	  FULLCONFIG_PHYS, 0, doipodinfo},
343 344
	{ "ntpinfo",	  FULLCONFIG_PHYS, 0, dontpinfo},
	{ "ntpdrift",	  FULLCONFIG_NONE, 0, dontpdrift},
345 346
	{ "jailconfig",	  FULLCONFIG_VIRT, F_ALLOCATED, dojailconfig},
	{ "plabconfig",	  FULLCONFIG_VIRT, F_ALLOCATED, doplabconfig},
347
	{ "subconfig",	  FULLCONFIG_NONE, 0, dosubconfig},
348
        { "sdparams",     FULLCONFIG_PHYS, 0, doslothdparams},
349 350
        { "programs",     FULLCONFIG_ALL,  F_ALLOCATED, doprogagents},
        { "syncserver",   FULLCONFIG_ALL,  F_ALLOCATED, dosyncserver},
351
        { "keyhash",      FULLCONFIG_ALL,  F_ALLOCATED|F_REMREQSSL, dokeyhash},
352
        { "eventkey",     FULLCONFIG_ALL,  F_ALLOCATED, doeventkey},
353 354 355
        { "fullconfig",   FULLCONFIG_NONE, F_ALLOCATED, dofullconfig},
        { "routelist",	  FULLCONFIG_PHYS, F_ALLOCATED, doroutelist},
        { "role",	  FULLCONFIG_PHYS, F_ALLOCATED, dorole},
356
        { "rusage",	  FULLCONFIG_NONE, F_REMUDP|F_MINLOG, dorusage},
357
        { "watchdoginfo", FULLCONFIG_ALL,  F_REMUDP|F_MINLOG, dodoginfo},
358
        { "hostkeys",     FULLCONFIG_NONE, 0, dohostkeys},
Mike Hibler's avatar
Mike Hibler committed
359
        { "tmcctest",     FULLCONFIG_NONE, F_MINLOG, dotmcctest},
360
        { "firewallinfo", FULLCONFIG_ALL,  0, dofwinfo},
361
        { "hostinfo",     FULLCONFIG_NONE, 0, dohostinfo},
362
	{ "emulabconfig", FULLCONFIG_NONE, F_ALLOCATED, doemulabconfig},
363
	{ "eplabconfig",  FULLCONFIG_NONE, F_ALLOCATED, doeplabconfig},
364
	{ "localization", FULLCONFIG_PHYS, 0, dolocalize},
365
	{ "rootpswd",     FULLCONFIG_NONE, F_REMREQSSL, dorootpswd},
366 367
	{ "booterrno",    FULLCONFIG_NONE, 0, dobooterrno},
	{ "bootlog",      FULLCONFIG_NONE, 0, dobootlog},
368
	{ "battery",      FULLCONFIG_NONE, F_REMUDP|F_MINLOG, dobattery},
369
	{ "topomap",      FULLCONFIG_NONE, F_MINLOG|F_ALLOCATED, dotopomap},
370
	{ "userenv",      FULLCONFIG_ALL,  F_ALLOCATED, douserenv},
371
	{ "tiptunnels",	  FULLCONFIG_ALL,  F_ALLOCATED, dotiptunnels},
372
	{ "traceinfo",	  FULLCONFIG_ALL,  F_ALLOCATED, dotraceconfig },
373
	{ "ltmap",        FULLCONFIG_NONE, F_MINLOG|F_ALLOCATED, doltmap},
374
	{ "ltpmap",       FULLCONFIG_NONE, F_MINLOG|F_ALLOCATED, doltpmap},
375
	{ "elvindport",   FULLCONFIG_NONE, 0, doelvindport},
376
	{ "plabeventkeys",FULLCONFIG_NONE, F_REMREQSSL, doplabeventkeys},
377
	{ "intfcmap",     FULLCONFIG_NONE, 0, dointfcmap},
378 379
	{ "motelog",      FULLCONFIG_ALL,  F_ALLOCATED, domotelog},
	{ "portregister", FULLCONFIG_NONE, F_REMNOSSL, doportregister},
Leigh B. Stoller's avatar
Leigh B. Stoller committed
380
	{ "bootwhat",	  FULLCONFIG_NONE, 0, dobootwhat },
381 382
	{ "tpmblob",	  FULLCONFIG_ALL, 0, dotpmblob },
	{ "tpmpubkey",	  FULLCONFIG_ALL, 0, dotpmpubkey },
383
	{ "tpmdummy",	  FULLCONFIG_ALL, F_REQTPM, dotpmdummy },
384 385 386
};
static int numcommands = sizeof(command_array)/sizeof(struct command);

387 388 389 390
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"
391
 " -c num	   Specify number of servers (must be %d <= x <= %d)\n"
392
 " -v              More verbose logging\n"
393
 " -i ipaddr       Sets the boss IP addr to return (for multi-homed servers)\n"
394 395 396 397 398
 "\n";

void
usage()
{
399
	fprintf(stderr, usagestr, MINCHILDREN, MAXCHILDREN);
400 401 402
	exit(1);
}

403 404 405 406 407
static void
cleanup()
{
	signal(SIGHUP, SIG_IGN);
	killme = 1;
408
	killpg(0, SIGHUP);
409 410
}

411 412 413 414 415 416 417 418 419
static void
setverbose(int sig)
{
	signal(sig, SIG_IGN);
	
	if (sig == SIGUSR1)
		verbose = 1;
	else
		verbose = 0;
420 421
	info("verbose logging turned %s\n", verbose ? "on" : "off");

422 423 424 425 426 427
	/* Just the parent sends this */
	if (numchildren)
		killpg(0, sig);
	signal(sig, setverbose);
}

Mike Hibler's avatar
Mike Hibler committed
428 429
int
main(int argc, char **argv)
430
{
431
	int			tcpsock, udpsock, i, ch;
432 433 434
	int			alttcpsock, altudpsock;
	int			status, pid;
	int			portnum = TBSERVER_PORT;
435 436
	FILE			*fp;
	char			buf[BUFSIZ];
437
	struct hostent		*he;
438
	extern char		build_info[];
439 440 441 442 443
	int			server_counts[4]; /* udp,tcp,altudp,alttcp */
	struct {
		int	pid;
		int	which;
	} servers[MAXCHILDREN];
444

445
	while ((ch = getopt(argc, argv, "dp:c:Xvi:")) != -1)
446 447 448 449 450 451
		switch(ch) {
		case 'p':
			portnum = atoi(optarg);
			break;
		case 'd':
			debug++;
452
			break;
453 454 455
		case 'c':
			maxchildren = atoi(optarg);
			break;
456 457 458
		case 'X':
			insecure = 1;
			break;
459 460 461
		case 'v':
			verbose++;
			break;
462 463 464 465 466 467 468
		case 'i':
			if (inet_aton(optarg, &myipaddr) == 0) {
				fprintf(stderr, "invalid IP address %s\n",
					optarg);
				usage();
			}
			break;
469 470 471 472 473 474 475 476 477 478
		case 'h':
		case '?':
		default:
			usage();
		}
	argc -= optind;
	argv += optind;

	if (argc)
		usage();
479 480
	if (maxchildren < MINCHILDREN || maxchildren > MAXCHILDREN)
		usage();
481

482 483 484 485 486 487 488 489 490 491 492 493 494 495 496
#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
497

Austin Clements's avatar
Austin Clements committed
498 499 500 501 502 503 504 505 506 507 508 509 510 511
	/*
	 * 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;
512 513 514
				if (debug) {
				    info("fshostid: %s\n", fshostid);
				}
Austin Clements's avatar
Austin Clements committed
515 516 517 518 519 520 521 522 523
			}
			else {
				error("fshostid from %s may be corrupt: %s",
				      FSHOSTID, fshostid);
			}
			fclose(fp);
		}
	}
	
524 525 526
	/*
	 * Grab our IP for security check below.
	 */
527
	if (myipaddr.s_addr == 0) {
528
#ifdef	LBS
529
		strcpy(buf, BOSSNODE);
530
#else
531 532
		if (gethostname(buf, sizeof(buf)) < 0)
			pfatal("getting hostname");
533
#endif
534 535 536 537 538 539
		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);
540 541
	}

542 543 544 545 546 547
	/*
	 * 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) {
548 549
		error("Could not make sockets!");
		exit(1);
550
	    }
551
	    num_alttcpservers = num_altudpservers = 0;
552 553 554 555 556 557
	} else {
	    if (makesockets(portnum, &udpsock, &tcpsock) < 0 ||
		makesockets(TBSERVER_PORT2, &altudpsock, &alttcpsock) < 0) {
		    error("Could not make sockets!");
		    exit(1);
	    }
558 559 560 561 562
	}

	signal(SIGTERM, cleanup);
	signal(SIGINT, cleanup);
	signal(SIGHUP, cleanup);
563 564
	signal(SIGUSR1, setverbose);
	signal(SIGUSR2, setverbose);
565 566 567 568

	/*
	 * Stash the pid away.
	 */
569
	mypid = getpid();
570 571 572
	sprintf(buf, "%s/tmcd.pid", _PATH_VARRUN);
	fp = fopen(buf, "w");
	if (fp != NULL) {
573
		fprintf(fp, "%d\n", mypid);
574 575 576
		(void) fclose(fp);
	}

577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609
	/*
	 * 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);
	}

610 611
	/*
	 * Now fork a set of children to handle requests. We keep the
612 613 614 615 616 617 618
	 * 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!
619
	 */
620 621 622 623 624 625 626
	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));
	
627 628
	while (1) {
		while (!killme && numchildren < maxchildren) {
629 630 631 632 633 634 635 636 637 638
			int which = 3;
			
			/*
			 * Find which kind of server is short one.
			 */
			for (i = 0; i < 4; i++) {
				if (server_counts[i]) {
					which = i;
					break;
				}
639
			}
640
			
641 642 643 644 645
			if ((pid = fork()) < 0) {
				errorc("forking server");
				goto done;
			}
			if (pid) {
646 647 648 649 650 651 652 653 654 655
				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;
656 657 658
				numchildren++;
				continue;
			}
659 660 661 662
			/* Poor way of knowing parent/child */
			numchildren = 0;
			mypid = getpid();
			
663 664 665 666 667 668
			/* Child does useful work! Never Returns! */
			signal(SIGTERM, SIG_DFL);
			signal(SIGINT, SIG_DFL);
			signal(SIGHUP, SIG_DFL);
			
			switch (which) {
669
			case 0: udpserver(udpsock, portnum);
670
				break;
671
			case 1: udpserver(altudpsock, TBSERVER_PORT2);
672
				break;
673
			case 2: tcpserver(alttcpsock, TBSERVER_PORT2);
674
				break;
675
			case 3: tcpserver(tcpsock, portnum);
676 677 678 679 680 681 682 683 684 685 686 687
				break;
			}
			exit(-1);
		}

		/*
		 * Parent waits.
		 */
		pid = waitpid(-1, &status, 0);
		if (pid < 0) {
			errorc("waitpid failed");
			continue;
688 689 690 691
		} 
		if (WIFSIGNALED(status)) {
			error("server %d exited with signal %d!\n",
			      pid, WTERMSIG(status));
692
		}
693 694 695
		else if (WIFEXITED(status)) {
			error("server %d exited with status %d!\n",
			      pid, WEXITSTATUS(status));	  
696
		}
697
		numchildren--;
698 699 700 701 702 703 704 705 706 707

		/*
		 * 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;
			}
708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725
		}
		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;
Mike Hibler's avatar
lint  
Mike Hibler committed
726 727
	socklen_t		length;
	int			i, udpsock, tcpsock;
728

Mike Hibler's avatar
Mike Hibler committed
729
	/*
730
	 * Setup TCP socket for incoming connections.
Mike Hibler's avatar
Mike Hibler committed
731 732
	 */

733
	/* Create socket from which to read. */
Mike Hibler's avatar
Mike Hibler committed
734 735
	tcpsock = socket(AF_INET, SOCK_STREAM, 0);
	if (tcpsock < 0) {
736
		pfatal("opening stream socket");
737 738
	}

739
	i = 1;
Mike Hibler's avatar
Mike Hibler committed
740
	if (setsockopt(tcpsock, SOL_SOCKET, SO_REUSEADDR,
741
		       (char *)&i, sizeof(i)) < 0)
742
		pwarning("setsockopt(SO_REUSEADDR)");;
743 744 745 746
	
	/* Create name. */
	name.sin_family = AF_INET;
	name.sin_addr.s_addr = INADDR_ANY;
747
	name.sin_port = htons((u_short) portnum);
Mike Hibler's avatar
Mike Hibler committed
748
	if (bind(tcpsock, (struct sockaddr *) &name, sizeof(name))) {
749
		pfatal("binding stream socket");
750 751 752
	}
	/* Find assigned port value and print it out. */
	length = sizeof(name);
Mike Hibler's avatar
Mike Hibler committed
753
	if (getsockname(tcpsock, (struct sockaddr *) &name, &length)) {
754
		pfatal("getsockname");
755
	}
756
	if (listen(tcpsock, 128) < 0) {
757
		pfatal("listen");
758
	}
759 760
	info("listening on TCP port %d\n", ntohs(name.sin_port));
	
Mike Hibler's avatar
Mike Hibler committed
761 762 763 764 765 766 767
	/*
	 * Setup UDP socket
	 */

	/* Create socket from which to read. */
	udpsock = socket(AF_INET, SOCK_DGRAM, 0);
	if (udpsock < 0) {
768
		pfatal("opening dgram socket");
Mike Hibler's avatar
Mike Hibler committed
769 770 771 772 773
	}

	i = 1;
	if (setsockopt(udpsock, SOL_SOCKET, SO_REUSEADDR,
		       (char *)&i, sizeof(i)) < 0)
774
		pwarning("setsockopt(SO_REUSEADDR)");;
775 776 777 778

	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
779 780 781 782
	
	/* Create name. */
	name.sin_family = AF_INET;
	name.sin_addr.s_addr = INADDR_ANY;
783
	name.sin_port = htons((u_short) portnum);
Mike Hibler's avatar
Mike Hibler committed
784
	if (bind(udpsock, (struct sockaddr *) &name, sizeof(name))) {
785
		pfatal("binding dgram socket");
Mike Hibler's avatar
Mike Hibler committed
786 787 788 789 790
	}

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

795 796 797
	*tcpsockp = tcpsock;
	*udpsockp = udpsock;
	return 0;
798
}
799

800
/*
801
 * Listen for UDP requests. This is not a secure channel, and so this should
802 803 804
 * eventually be killed off.
 */
static void
805
udpserver(int sock, int portnum)
806 807 808
{
	char			buf[MYBUFSIZE];
	struct sockaddr_in	client;
Mike Hibler's avatar
lint  
Mike Hibler committed
809 810
	socklen_t		length;
	int			cc;
811
	unsigned int		nreq = 0;
812
	
813 814
	info("udpserver starting: pid=%d sock=%d portnum=%d\n",
	     mypid, sock, portnum);
815 816 817 818 819

	/*
	 * Wait for udp connections.
	 */
	while (1) {
820
		setproctitle("UDP %d: %u done", portnum, nreq);
821
		length = sizeof(client);		
822
		cc = recvfrom(sock, buf, sizeof(buf) - 1,
823 824 825
			      0, (struct sockaddr *)&client, &length);
		if (cc <= 0) {
			if (cc < 0)
826 827
				errorc("Reading UDP request");
			error("UDP Connection aborted\n");
828
			continue;
829
		}
830 831
		buf[cc] = '\0';
		handle_request(sock, &client, buf, 0);
832
		nreq++;
833 834 835 836
	}
	exit(1);
}

837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862
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;
}

863
/*
864
 * Listen for TCP requests.
865 866
 */
static void
867
tcpserver(int sock, int portnum)
868
{
869
	char			buf[MAXTMCDPACKET];
870
	struct sockaddr_in	client;
Mike Hibler's avatar
lint  
Mike Hibler committed
871 872
	socklen_t		length;
	int			cc, newsock;
873
	unsigned int		nreq = 0;
874
	struct timeval		tv;
875
	
876 877
	info("tcpserver starting: pid=%d sock=%d portnum=%d\n",
	     mypid, sock, portnum);
878 879 880 881 882

	/*
	 * Wait for TCP connections.
	 */
	while (1) {
883
		setproctitle("TCP %d: %u done", portnum, nreq);
884
		length  = sizeof(client);
885 886
		newsock = ACCEPT(sock, (struct sockaddr *)&client, &length,
				 readtimo);
887
		if (newsock < 0) {
888
			errorc("accepting TCP connection");
889
			continue;
890
		}
Mike Hibler's avatar
Mike Hibler committed
891

892 893 894 895 896 897 898 899 900 901 902 903 904 905
		/*
		 * 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
906
		/*
907
		 * Read in the command request.
Mike Hibler's avatar
Mike Hibler committed
908
		 */
909
		if ((cc = READ(newsock, buf, sizeof(buf) - 1)) <= 0) {
910 911 912 913 914 915
			if (cc < 0) {
				if (errno == EWOULDBLOCK)
					errorc("Timeout reading TCP request");
				else
					errorc("Error reading TCP request");
			}
916 917
			error("TCP connection aborted\n");
			CLOSE(newsock);
918
			continue;
919
		}
920 921
		buf[cc] = '\0';
		handle_request(newsock, &client, buf, 1);
922
		CLOSE(newsock);
923
		nreq++;
924 925 926
	}
	exit(1);
}
Mike Hibler's avatar
Mike Hibler committed
927

928 929 930
//#define error(x...)	fprintf(stderr, ##x)
//#define info(x...)	fprintf(stderr, ##x)

931 932 933 934
static int
handle_request(int sock, struct sockaddr_in *client, char *rdata, int istcp)
{
	struct sockaddr_in redirect_client;
935
	int		   redirect = 0, havekey = 0;
936
	char		   buf[BUFSIZ], *bp, *cp;
937
	char		   privkey[TBDB_FLEN_PRIVKEY];
938
	int		   i, overbose = 0, err = 0;
939
	int		   version = DEFAULT_VERSION;
940 941
	tmcdreq_t	   tmcdreq, *reqp = &tmcdreq;

942
	byteswritten = 0;
943 944 945 946 947
#ifdef	WITHSSL
	cp = (istcp ? (isssl ? "SSL" : "TCP") : "UDP");
#else
	cp = (istcp ? "TCP" : "UDP");
#endif
948
	setproctitle("%s: %s", inet_ntoa(client->sin_addr), cp);
949

950 951 952 953
	/*
	 * Init the req structure.
	 */
	bzero(reqp, sizeof(*reqp));
Mike Hibler's avatar
Mike Hibler committed
954

955 956 957
	/*
	 * Look for special tags. 
	 */
958
	bp = rdata;
959
	while ((bp = strsep(&rdata, " ")) != NULL) {
960 961 962 963
		/*
		 * Look for PRIVKEY. 
		 */
		if (sscanf(bp, "PRIVKEY=%64s", buf)) {
964 965 966 967 968 969 970
			for (i = 0; i < strlen(buf); i++){
				if (! isxdigit(buf[i])) {
					info("tmcd client provided invalid "
					     "characters in privkey");
					goto skipit;
				}
			}
971 972 973 974
			havekey = 1;
			strncpy(privkey, buf, sizeof(privkey));

			if (debug) {
975
				info("%s: PRIVKEY %s\n", reqp->nodeid, buf);
976 977 978 979 980
			}
			continue;
		}


981 982
		/*
		 * Look for VERSION. 
983 984
		 * Check for clients that are newer than the server
		 * and complain.
985 986 987
		 */
		if (sscanf(bp, "VERSION=%d", &i) == 1) {
			version = i;
988
			if (version > CURRENT_VERSION) {
Mike Hibler's avatar
nits:  
Mike Hibler committed
989 990
				error("version skew on request from %s: "
				      "server=%d, request=%d, "
991
				      "old TMCD installed?\n",
Mike Hibler's avatar
nits:  
Mike Hibler committed
992
				      inet_ntoa(client->sin_addr),
993 994
				      CURRENT_VERSION, version);
			}
995 996
			continue;
		}
Mike Hibler's avatar
Mike Hibler committed
997

998 999 1000 1001 1002 1003 1004 1005 1006 1007
		/*
		 * 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);
1008

1009 1010
			info("REDIRECTED from %s to %s\n",
			     inet_ntoa(redirect_client.sin_addr), buf);
1011

1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022
			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)) {
1023 1024 1025 1026 1027 1028 1029 1030
			for (i = 0; i < strlen(buf); i++){
				if (! (isalnum(buf[i]) ||
				       buf[i] == '_' || buf[i] == '-')) {
					info("tmcd client provided invalid "
					     "characters in vnodeid");
					goto skipit;
				}
			}
1031 1032
			reqp->isvnode = 1;
			strncpy(reqp->vnodeid, buf, sizeof(reqp->vnodeid));
1033

1034 1035 1036 1037 1038
			if (debug) {
				info("VNODEID %s\n", buf);
			}
			continue;
		}
1039

1040 1041 1042 1043 1044
		/*
		 * 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.
1045 1046 1047
		 *
		 * Note that rdata will point to any text after the command.
		 *
1048 1049 1050 1051 1052
		 */
		if (*bp) {
			break;
		}
	}
1053

1054 1055 1056
	/* Start with default DB */
	strcpy(dbname, DEFAULT_DBNAME);

1057
	/*
1058
	 * Map the ip to a nodeid.