tmcd.c 184 KB
Newer Older
Leigh B. Stoller's avatar
Leigh B. Stoller committed
1 2
/*
 * EMULAB-COPYRIGHT
3
 * Copyright (c) 2000-2008 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>
Leigh B. Stoller's avatar
Leigh B. Stoller committed
23
#include <sys/fcntl.h>
24 25
#include <sys/syscall.h>
#include <sys/stat.h>
Kirk Webb's avatar
 
Kirk Webb committed
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"
Kirk Webb's avatar
 
Kirk Webb committed
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

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

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

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

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

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

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

123 124 125 126
/* socket timeouts */
static int	readtimo = READTIMO;
static int	writetimo = WRITETIMO;

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

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

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

194 195 196 197 198
#ifdef EVENTSYS
int			myevent_send(address_tuple_t address);
static event_handle_t	event_handle = NULL;
#endif

199 200 201
/*
 * Commands we support.
 */
202 203
#define COMMAND_PROTOTYPE(x) \
	static int \
204
	x(int sock, tmcdreq_t *reqp, char *rdata, int tcp, int vers)
205 206

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

276 277
/*
 * The fullconfig slot determines what routines get called when pushing
278
 * out a full configuration. Physnodes get slightly different
279
 * than vnodes, and at some point we might want to distinguish different
280 281 282 283 284
 * types of vnodes (jailed, plab).
 */
#define FULLCONFIG_NONE		0x0
#define FULLCONFIG_PHYS		0x1
#define FULLCONFIG_VIRT		0x2
285 286 287 288 289
#define FULLCONFIG_ALL		(FULLCONFIG_PHYS|FULLCONFIG_VIRT)

/*
 * Flags encode a few other random properties of commands
 */
290 291 292 293 294
#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 */
295

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

373 374 375 376
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"
377
 " -c num	   Specify number of servers (must be %d <= x <= %d)\n"
378
 " -v              More verbose logging\n"
379
 " -i ipaddr       Sets the boss IP addr to return (for multi-homed servers)\n"
380 381 382 383 384
 "\n";

void
usage()
{
385
	fprintf(stderr, usagestr, MINCHILDREN, MAXCHILDREN);
386 387 388
	exit(1);
}

389 390 391 392 393
static void
cleanup()
{
	signal(SIGHUP, SIG_IGN);
	killme = 1;
394
	killpg(0, SIGHUP);
395 396
}

397 398 399 400 401 402 403 404 405
static void
setverbose(int sig)
{
	signal(sig, SIG_IGN);
	
	if (sig == SIGUSR1)
		verbose = 1;
	else
		verbose = 0;
406 407
	info("verbose logging turned %s\n", verbose ? "on" : "off");

408 409 410 411 412 413
	/* Just the parent sends this */
	if (numchildren)
		killpg(0, sig);
	signal(sig, setverbose);
}

Mike Hibler's avatar
Mike Hibler committed
414 415
int
main(int argc, char **argv)
416
{
417
	int			tcpsock, udpsock, i, ch;
418 419 420
	int			alttcpsock, altudpsock;
	int			status, pid;
	int			portnum = TBSERVER_PORT;
421 422
	FILE			*fp;
	char			buf[BUFSIZ];
423
	struct hostent		*he;
424
	extern char		build_info[];
425 426 427 428 429
	int			server_counts[4]; /* udp,tcp,altudp,alttcp */
	struct {
		int	pid;
		int	which;
	} servers[MAXCHILDREN];
430

431
	while ((ch = getopt(argc, argv, "dp:c:Xvi:")) != -1)
432 433 434 435 436 437
		switch(ch) {
		case 'p':
			portnum = atoi(optarg);
			break;
		case 'd':
			debug++;
438
			break;
439 440 441
		case 'c':
			maxchildren = atoi(optarg);
			break;
442 443 444
		case 'X':
			insecure = 1;
			break;
445 446 447
		case 'v':
			verbose++;
			break;
448 449 450 451 452 453 454
		case 'i':
			if (inet_aton(optarg, &myipaddr) == 0) {
				fprintf(stderr, "invalid IP address %s\n",
					optarg);
				usage();
			}
			break;
455 456 457 458 459 460 461 462 463 464
		case 'h':
		case '?':
		default:
			usage();
		}
	argc -= optind;
	argv += optind;

	if (argc)
		usage();
465 466
	if (maxchildren < MINCHILDREN || maxchildren > MAXCHILDREN)
		usage();
467

468 469 470 471 472 473 474 475 476 477 478 479 480 481 482
#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
483

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

528 529 530 531 532 533
	/*
	 * 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) {
534 535
		error("Could not make sockets!");
		exit(1);
536
	    }
537
	    num_alttcpservers = num_altudpservers = 0;
538 539 540 541 542 543
	} else {
	    if (makesockets(portnum, &udpsock, &tcpsock) < 0 ||
		makesockets(TBSERVER_PORT2, &altudpsock, &alttcpsock) < 0) {
		    error("Could not make sockets!");
		    exit(1);
	    }
544 545 546 547 548
	}

	signal(SIGTERM, cleanup);
	signal(SIGINT, cleanup);
	signal(SIGHUP, cleanup);
549 550
	signal(SIGUSR1, setverbose);
	signal(SIGUSR2, setverbose);
551 552 553 554

	/*
	 * Stash the pid away.
	 */
555
	mypid = getpid();
556 557 558
	sprintf(buf, "%s/tmcd.pid", _PATH_VARRUN);
	fp = fopen(buf, "w");
	if (fp != NULL) {
559
		fprintf(fp, "%d\n", mypid);
560 561 562
		(void) fclose(fp);
	}

563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595
	/*
	 * 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);
	}

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

		/*
		 * Parent waits.
		 */
		pid = waitpid(-1, &status, 0);
		if (pid < 0) {
			errorc("waitpid failed");
			continue;
674 675 676 677
		} 
		if (WIFSIGNALED(status)) {
			error("server %d exited with signal %d!\n",
			      pid, WTERMSIG(status));
678
		}
679 680 681
		else if (WIFEXITED(status)) {
			error("server %d exited with status %d!\n",
			      pid, WEXITSTATUS(status));	  
682
		}
683
		numchildren--;
684 685 686 687 688 689 690 691 692 693

		/*
		 * 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;
			}
694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713
		}
		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
714
	/*
715
	 * Setup TCP socket for incoming connections.
Mike Hibler's avatar
Mike Hibler committed
716 717
	 */

718
	/* Create socket from which to read. */
Mike Hibler's avatar
Mike Hibler committed
719 720
	tcpsock = socket(AF_INET, SOCK_STREAM, 0);
	if (tcpsock < 0) {
721
		pfatal("opening stream socket");
722 723
	}

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

	/* Create socket from which to read. */
	udpsock = socket(AF_INET, SOCK_DGRAM, 0);
	if (udpsock < 0) {
753
		pfatal("opening dgram socket");
Mike Hibler's avatar
Mike Hibler committed
754 755 756 757 758
	}

	i = 1;
	if (setsockopt(udpsock, SOL_SOCKET, SO_REUSEADDR,
		       (char *)&i, sizeof(i)) < 0)
759
		pwarning("setsockopt(SO_REUSEADDR)");;
760 761 762 763

	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
764 765 766 767
	
	/* Create name. */
	name.sin_family = AF_INET;
	name.sin_addr.s_addr = INADDR_ANY;
768
	name.sin_port = htons((u_short) portnum);
Mike Hibler's avatar
Mike Hibler committed
769
	if (bind(udpsock, (struct sockaddr *) &name, sizeof(name))) {
770
		pfatal("binding dgram socket");
Mike Hibler's avatar
Mike Hibler committed
771 772 773 774 775
	}

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

780 781 782
	*tcpsockp = tcpsock;
	*udpsockp = udpsock;
	return 0;
783
}
784

785
/*
786
 * Listen for UDP requests. This is not a secure channel, and so this should
787 788 789
 * eventually be killed off.
 */
static void
790
udpserver(int sock, int portnum)
791 792 793 794
{
	char			buf[MYBUFSIZE];
	struct sockaddr_in	client;
	int			length, cc;
795
	unsigned int		nreq = 0;
796
	
797 798
	info("udpserver starting: pid=%d sock=%d portnum=%d\n",
	     mypid, sock, portnum);
799 800 801 802 803

	/*
	 * Wait for udp connections.
	 */
	while (1) {
804
		setproctitle("UDP %d: %u done", portnum, nreq);
805
		length = sizeof(client);		
806
		cc = recvfrom(sock, buf, sizeof(buf) - 1,
807 808 809
			      0, (struct sockaddr *)&client, &length);
		if (cc <= 0) {
			if (cc < 0)
810 811
				errorc("Reading UDP request");
			error("UDP Connection aborted\n");
812
			continue;
813
		}
814 815
		buf[cc] = '\0';
		handle_request(sock, &client, buf, 0);
816
		nreq++;
817 818 819 820
	}
	exit(1);
}

821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846
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;
}

847
/*
848
 * Listen for TCP requests.
849 850
 */
static void
851
tcpserver(int sock, int portnum)
852
{
853
	char			buf[MAXTMCDPACKET];
854 855
	struct sockaddr_in	client;
	int			length, cc, newsock;
856
	unsigned int		nreq = 0;
857
	struct timeval		tv;
858
	
859 860
	info("tcpserver starting: pid=%d sock=%d portnum=%d\n",
	     mypid, sock, portnum);
861 862 863 864 865

	/*
	 * Wait for TCP connections.
	 */
	while (1) {
866
		setproctitle("TCP %d: %u done", portnum, nreq);
867
		length  = sizeof(client);
868 869
		newsock = ACCEPT(sock, (struct sockaddr *)&client, &length,
				 readtimo);
870
		if (newsock < 0) {
871
			errorc("accepting TCP connection");
872
			continue;
873
		}
Mike Hibler's avatar
Mike Hibler committed
874

875 876 877 878 879 880 881 882 883 884 885 886 887 888
		/*
		 * 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
889
		/*
890
		 * Read in the command request.
Mike Hibler's avatar
Mike Hibler committed
891
		 */
892
		if ((cc = READ(newsock, buf, sizeof(buf) - 1)) <= 0) {
893 894 895 896 897 898
			if (cc < 0) {
				if (errno == EWOULDBLOCK)
					errorc("Timeout reading TCP request");
				else
					errorc("Error reading TCP request");
			}
899 900
			error("TCP connection aborted\n");
			CLOSE(newsock);
901
			continue;
902
		}
903 904
		buf[cc] = '\0';
		handle_request(newsock, &client, buf, 1);
905
		CLOSE(newsock);
906
		nreq++;
907 908 909
	}
	exit(1);
}
Mike Hibler's avatar
Mike Hibler committed
910

911 912 913 914
static int
handle_request(int sock, struct sockaddr_in *client, char *rdata, int istcp)
{
	struct sockaddr_in redirect_client;
915
	int		   redirect = 0, havekey = 0;
916
	char		   buf[BUFSIZ], *bp, *cp;
917
	char		   privkey[TBDB_FLEN_PRIVKEY];
918
	int		   i, overbose = 0, err = 0;
919
	int		   version = DEFAULT_VERSION;
920 921
	tmcdreq_t	   tmcdreq, *reqp = &tmcdreq;

922
	byteswritten = 0;
923 924 925 926 927
#ifdef	WITHSSL
	cp = (istcp ? (isssl ? "SSL" : "TCP") : "UDP");
#else
	cp = (istcp ? "TCP" : "UDP");
#endif
928
	setproctitle("%s: %s", inet_ntoa(client->sin_addr), cp);
929

930 931 932 933
	/*
	 * Init the req structure.
	 */
	bzero(reqp, sizeof(*reqp));
Mike Hibler's avatar
Mike Hibler committed
934

935 936 937
	/*
	 * Look for special tags. 
	 */
938
	bp = rdata;
939
	while ((bp = strsep(&rdata, " ")) != NULL) {
940 941 942 943 944 945 946 947 948 949 950 951 952 953
		/*
		 * Look for PRIVKEY. 
		 */
		if (sscanf(bp, "PRIVKEY=%64s", buf)) {
			havekey = 1;
			strncpy(privkey, buf, sizeof(privkey));

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