All new accounts created on Gitlab now require administrator approval. If you invite any collaborators, please let Flux staff know so they can approve the accounts.

tmcd.c 172 KB
Newer Older
Leigh B. Stoller's avatar
Leigh B. Stoller committed
1 2
/*
 * EMULAB-COPYRIGHT
3
 * Copyright (c) 2000-2007 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"
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"
Kirk Webb's avatar
 
Kirk Webb committed
60
#define PLISALIVELOGDIR "/usr/testbed/log/plabisalive"
61 62
#define RELOADPID	"emulab-ops"
#define RELOADEID	"reloading"
Austin Clements's avatar
Austin Clements committed
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 B. Stoller's avatar
Leigh B. 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 165
	char		nodeid[TBDB_FLEN_NODEID];
	char		vnodeid[TBDB_FLEN_NODEID];
Leigh B. Stoller's avatar
Leigh B. Stoller committed
166
	char		pnodeid[TBDB_FLEN_NODEID]; /* XXX */
167 168 169 170 171
	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];
172
	char		class[TBDB_FLEN_NODECLASS];
173 174
        char		ptype[TBDB_FLEN_NODETYPE];	/* Of physnode */
	char		pclass[TBDB_FLEN_NODECLASS];	/* Of physnode */
175 176
	char		creator[TBDB_FLEN_UID];
	char		swapper[TBDB_FLEN_UID];
177
	char		syncserver[TBDB_FLEN_VNAME];	/* The vname */
178
	char		keyhash[TBDB_FLEN_PRIVKEY];
179
	char		eventkey[TBDB_FLEN_PRIVKEY];
180
	char		sfshostid[TBDB_FLEN_SFSHOSTID];
181 182 183 184 185
	char		testdb[256];
} tmcdreq_t;
static int	iptonodeid(struct in_addr, tmcdreq_t *);
static int	checkdbredirect(tmcdreq_t *);

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583
	/*
	 * 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);
	}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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


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

957 958 959 960 961 962 963 964 965 966
		/*
		 * 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 (<