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

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

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

61
#define TESTMODE
62
63
64
65
66
67
68
69
70
71
72
73
#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) */
74

75
76
77
#define DISKTYPE	"ad"
#define DISKNUM		0

78
79
80
81
82
83
84
/* 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"

85
86
/* Defined in configure and passed in via the makefile */
#define DBNAME_SIZE	64
Austin Clements's avatar
Austin Clements committed
87
#define HOSTID_SIZE	(32+64)
88
89
#define DEFAULT_DBNAME	TBDBNAME

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

109
/* thread support */
110
#define MAXCHILDREN	20
111
#define MINCHILDREN	8
112
static int	numchildren;
113
114
115
116
static int	maxchildren       = 13;
static int      num_udpservers    = 3;
static int      num_altudpservers = 1;
static int      num_alttcpservers = 1;
117
static int	mypid;
118
static volatile int killme;
119

120
121
122
123
124
/* Output macro to check for string overflow */
#define OUTPUT(buf, size, format...) \
({ \
	int __count__ = snprintf((buf), (size), ##format); \
        \
125
        if (__count__ >= (size)) { \
126
127
128
129
130
131
		error("Not enough room in output buffer! line %d.\n", __LINE__);\
		return 1; \
	} \
	__count__; \
})

132
133
134
135
136
137
138
139
/*
 * 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;
140
	int		issubnode;
141
	int		islocal;
142
	int		iscontrol;
143
	int		isplabdslice;
144
	int		isplabsvc;
145
	int		elab_in_elab;
146
	int		update_accounts;
147
148
	char		nodeid[TBDB_FLEN_NODEID];
	char		vnodeid[TBDB_FLEN_NODEID];
Leigh B. Stoller's avatar
Leigh B. Stoller committed
149
	char		pnodeid[TBDB_FLEN_NODEID]; /* XXX */
150
151
152
153
154
	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];
155
	char		class[TBDB_FLEN_NODECLASS];
156
157
        char		ptype[TBDB_FLEN_NODETYPE];	/* Of physnode */
	char		pclass[TBDB_FLEN_NODECLASS];	/* Of physnode */
158
159
	char		creator[TBDB_FLEN_UID];
	char		swapper[TBDB_FLEN_UID];
160
	char		syncserver[TBDB_FLEN_VNAME];	/* The vname */
161
	char		keyhash[TBDB_FLEN_PRIVKEY];
162
	char		eventkey[TBDB_FLEN_PRIVKEY];
163
	char		sfshostid[TBDB_FLEN_SFSHOSTID];
164
	char		testdb[256];
165
	int		veth_encapsulate;
166
167
168
169
} tmcdreq_t;
static int	iptonodeid(struct in_addr, tmcdreq_t *);
static int	checkdbredirect(tmcdreq_t *);

170
171
172
173
174
#ifdef EVENTSYS
int			myevent_send(address_tuple_t address);
static event_handle_t	event_handle = NULL;
#endif

175
176
177
/*
 * Commands we support.
 */
178
179
#define COMMAND_PROTOTYPE(x) \
	static int \
180
	x(int sock, tmcdreq_t *reqp, char *rdata, int tcp, int vers)
181
182

COMMAND_PROTOTYPE(doreboot);
183
COMMAND_PROTOTYPE(donodeid);
184
185
186
187
COMMAND_PROTOTYPE(dostatus);
COMMAND_PROTOTYPE(doifconfig);
COMMAND_PROTOTYPE(doaccounts);
COMMAND_PROTOTYPE(dodelay);
188
COMMAND_PROTOTYPE(dolinkdelay);
189
190
191
192
193
194
195
196
197
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
198
COMMAND_PROTOTYPE(dosfshostid);
199
200
201
202
203
204
205
COMMAND_PROTOTYPE(doloadinfo);
COMMAND_PROTOTYPE(doreset);
COMMAND_PROTOTYPE(dorouting);
COMMAND_PROTOTYPE(dotrafgens);
COMMAND_PROTOTYPE(donseconfigs);
COMMAND_PROTOTYPE(dostate);
COMMAND_PROTOTYPE(docreator);
206
COMMAND_PROTOTYPE(dotunnels);
207
COMMAND_PROTOTYPE(dovnodelist);
208
COMMAND_PROTOTYPE(dosubnodelist);
209
COMMAND_PROTOTYPE(doisalive);
210
COMMAND_PROTOTYPE(doipodinfo);
211
212
COMMAND_PROTOTYPE(dontpinfo);
COMMAND_PROTOTYPE(dontpdrift);
213
COMMAND_PROTOTYPE(dojailconfig);
214
COMMAND_PROTOTYPE(doplabconfig);
215
216
COMMAND_PROTOTYPE(dosubconfig);
COMMAND_PROTOTYPE(doixpconfig);
217
COMMAND_PROTOTYPE(doslothdparams);
218
COMMAND_PROTOTYPE(doprogagents);
219
COMMAND_PROTOTYPE(dosyncserver);
220
COMMAND_PROTOTYPE(dokeyhash);
221
COMMAND_PROTOTYPE(doeventkey);
222
COMMAND_PROTOTYPE(dofullconfig);
223
224
COMMAND_PROTOTYPE(doroutelist);
COMMAND_PROTOTYPE(dorole);
225
COMMAND_PROTOTYPE(dorusage);
226
COMMAND_PROTOTYPE(dodoginfo);
Mike Hibler's avatar
Mike Hibler committed
227
COMMAND_PROTOTYPE(dohostkeys);
Mike Hibler's avatar
Mike Hibler committed
228
COMMAND_PROTOTYPE(dotmcctest);
229
COMMAND_PROTOTYPE(dofwinfo);
230
COMMAND_PROTOTYPE(dohostinfo);
231
COMMAND_PROTOTYPE(doemulabconfig);
232
COMMAND_PROTOTYPE(dolocalize);
233

234
235
/*
 * The fullconfig slot determines what routines get called when pushing
236
 * out a full configuration. Physnodes get slightly different
237
238
239
240
241
242
 * then vnodes, and at some point we might want to distinguish different
 * types of vnodes (jailed, plab).
 */
#define FULLCONFIG_NONE		0x0
#define FULLCONFIG_PHYS		0x1
#define FULLCONFIG_VIRT		0x2
243
244
245
246
247
248
#define FULLCONFIG_ALL		(FULLCONFIG_PHYS|FULLCONFIG_VIRT)

/*
 * Flags encode a few other random properties of commands
 */
#define F_REMUDP	0x1	/* remote nodes can request using UDP */
249
250
#define F_MINLOG	0x2	/* record minimal logging info normally */
#define F_MAXLOG	0x4	/* record maximal logging info normally */
251
#define F_ALLOCATED	0x8	/* node must be allocated to make call */
252

253
254
struct command {
	char	*cmdname;
255
	int	fullconfig;	
256
	int	flags;
257
	int    (*func)(int, tmcdreq_t *, char *, int, int);
258
} command_array[] = {
259
260
261
	{ "reboot",	  FULLCONFIG_NONE, 0, doreboot },
	{ "nodeid",	  FULLCONFIG_ALL,  0, donodeid },
	{ "status",	  FULLCONFIG_NONE, 0, dostatus },
262
	{ "ifconfig",	  FULLCONFIG_ALL,  F_ALLOCATED, doifconfig },
263
	{ "accounts",	  FULLCONFIG_ALL,  0, doaccounts },
264
265
266
267
268
269
270
271
	{ "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*/
272
	{ "startstat",	  FULLCONFIG_NONE, 0, dostartstat },
273
274
275
276
	{ "readycount",   FULLCONFIG_NONE, F_ALLOCATED, doreadycount },
	{ "ready",	  FULLCONFIG_NONE, F_ALLOCATED, doready },
	{ "mounts",	  FULLCONFIG_ALL,  F_ALLOCATED, domounts },
	{ "sfshostid",	  FULLCONFIG_NONE, F_ALLOCATED, dosfshostid },
277
278
	{ "loadinfo",	  FULLCONFIG_NONE, 0, doloadinfo},
	{ "reset",	  FULLCONFIG_NONE, 0, doreset},
279
280
281
282
	{ "routing",	  FULLCONFIG_ALL,  F_ALLOCATED, dorouting},
	{ "trafgens",	  FULLCONFIG_ALL,  F_ALLOCATED, dotrafgens},
	{ "nseconfigs",	  FULLCONFIG_ALL,  F_ALLOCATED, donseconfigs},
	{ "creator",	  FULLCONFIG_ALL,  F_ALLOCATED, docreator},
283
	{ "state",	  FULLCONFIG_NONE, 0, dostate},
284
	{ "tunnels",	  FULLCONFIG_ALL,  F_ALLOCATED, dotunnels},
285
	{ "vnodelist",	  FULLCONFIG_PHYS, 0, dovnodelist},
286
	{ "subnodelist",  FULLCONFIG_PHYS, F_ALLOCATED, dosubnodelist},
287
288
289
290
	{ "isalive",	  FULLCONFIG_NONE, F_REMUDP|F_MINLOG, doisalive},
	{ "ipodinfo",	  FULLCONFIG_NONE, 0, doipodinfo},
	{ "ntpinfo",	  FULLCONFIG_PHYS, 0, dontpinfo},
	{ "ntpdrift",	  FULLCONFIG_NONE, 0, dontpdrift},
291
292
293
	{ "jailconfig",	  FULLCONFIG_VIRT, F_ALLOCATED, dojailconfig},
	{ "plabconfig",	  FULLCONFIG_VIRT, F_ALLOCATED, doplabconfig},
	{ "subconfig",	  FULLCONFIG_NONE, F_ALLOCATED, dosubconfig},
294
        { "sdparams",     FULLCONFIG_PHYS, 0, doslothdparams},
295
296
297
298
299
300
301
        { "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},
302
        { "rusage",	  FULLCONFIG_NONE, F_REMUDP|F_MINLOG, dorusage},
303
        { "watchdoginfo", FULLCONFIG_NONE, F_REMUDP|F_MINLOG, dodoginfo},
Mike Hibler's avatar
Mike Hibler committed
304
        { "hostkeys",     FULLCONFIG_NONE, 0, dohostkeys},
Mike Hibler's avatar
Mike Hibler committed
305
        { "tmcctest",     FULLCONFIG_NONE, F_MINLOG, dotmcctest},
306
        { "firewallinfo", FULLCONFIG_ALL,  0, dofwinfo},
307
        { "hostinfo",     FULLCONFIG_NONE, 0, dohostinfo},
308
	{ "emulabconfig", FULLCONFIG_NONE, F_ALLOCATED, doemulabconfig},
309
	{ "localization", FULLCONFIG_PHYS, 0, dolocalize},
310
311
312
};
static int numcommands = sizeof(command_array)/sizeof(struct command);

313
314
315
316
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"
317
 " -c num	   Specify number of servers (must be %d <= x <= %d)\n"
318
 " -v              More verbose logging\n"
319
 " -i ipaddr       Sets the boss IP addr to return (for multi-homed servers)\n"
320
321
322
323
324
 "\n";

void
usage()
{
325
	fprintf(stderr, usagestr, MINCHILDREN, MAXCHILDREN);
326
327
328
	exit(1);
}

329
330
331
332
333
static void
cleanup()
{
	signal(SIGHUP, SIG_IGN);
	killme = 1;
334
	killpg(0, SIGHUP);
335
336
}

337
338
339
340
341
342
343
344
345
static void
setverbose(int sig)
{
	signal(sig, SIG_IGN);
	
	if (sig == SIGUSR1)
		verbose = 1;
	else
		verbose = 0;
346
347
	info("verbose logging turned %s\n", verbose ? "on" : "off");

348
349
350
351
352
353
	/* Just the parent sends this */
	if (numchildren)
		killpg(0, sig);
	signal(sig, setverbose);
}

Mike Hibler's avatar
Mike Hibler committed
354
355
int
main(int argc, char **argv)
356
{
357
	int			tcpsock, udpsock, i, ch;
358
359
360
	int			alttcpsock, altudpsock;
	int			status, pid;
	int			portnum = TBSERVER_PORT;
361
362
	FILE			*fp;
	char			buf[BUFSIZ];
363
	struct hostent		*he;
364
	extern char		build_info[];
365
366
367
368
369
	int			server_counts[4]; /* udp,tcp,altudp,alttcp */
	struct {
		int	pid;
		int	which;
	} servers[MAXCHILDREN];
370

371
	while ((ch = getopt(argc, argv, "dp:c:Xvi:")) != -1)
372
373
374
375
376
377
		switch(ch) {
		case 'p':
			portnum = atoi(optarg);
			break;
		case 'd':
			debug++;
378
			break;
379
380
381
		case 'c':
			maxchildren = atoi(optarg);
			break;
382
383
384
		case 'X':
			insecure = 1;
			break;
385
386
387
		case 'v':
			verbose++;
			break;
388
389
390
391
392
393
394
		case 'i':
			if (inet_aton(optarg, &myipaddr) == 0) {
				fprintf(stderr, "invalid IP address %s\n",
					optarg);
				usage();
			}
			break;
395
396
397
398
399
400
401
402
403
404
		case 'h':
		case '?':
		default:
			usage();
		}
	argc -= optind;
	argv += optind;

	if (argc)
		usage();
405
406
	if (maxchildren < MINCHILDREN || maxchildren > MAXCHILDREN)
		usage();
407

408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
#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
423

Austin Clements's avatar
Austin Clements committed
424
425
426
427
428
429
430
431
432
433
434
435
436
437
	/*
	 * 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;
438
439
440
				if (debug) {
				    info("fshostid: %s\n", fshostid);
				}
Austin Clements's avatar
Austin Clements committed
441
442
443
444
445
446
447
448
449
			}
			else {
				error("fshostid from %s may be corrupt: %s",
				      FSHOSTID, fshostid);
			}
			fclose(fp);
		}
	}
	
450
451
452
	/*
	 * Grab our IP for security check below.
	 */
453
	if (myipaddr.s_addr == 0) {
454
#ifdef	LBS
455
		strcpy(buf, BOSSNODE);
456
#else
457
458
		if (gethostname(buf, sizeof(buf)) < 0)
			pfatal("getting hostname");
459
#endif
460
461
462
463
464
465
		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);
466
467
	}

468
469
470
471
472
473
	/*
	 * 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) {
474
475
		error("Could not make sockets!");
		exit(1);
476
	    }
477
	    num_alttcpservers = num_altudpservers = 0;
478
479
480
481
482
483
	} else {
	    if (makesockets(portnum, &udpsock, &tcpsock) < 0 ||
		makesockets(TBSERVER_PORT2, &altudpsock, &alttcpsock) < 0) {
		    error("Could not make sockets!");
		    exit(1);
	    }
484
485
486
487
488
	}

	signal(SIGTERM, cleanup);
	signal(SIGINT, cleanup);
	signal(SIGHUP, cleanup);
489
490
	signal(SIGUSR1, setverbose);
	signal(SIGUSR2, setverbose);
491
492
493
494

	/*
	 * Stash the pid away.
	 */
495
	mypid = getpid();
496
497
498
	sprintf(buf, "%s/tmcd.pid", _PATH_VARRUN);
	fp = fopen(buf, "w");
	if (fp != NULL) {
499
		fprintf(fp, "%d\n", mypid);
500
501
502
		(void) fclose(fp);
	}

503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
	/*
	 * 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);
	}

536
537
	/*
	 * Now fork a set of children to handle requests. We keep the
538
539
540
541
542
543
544
	 * 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!
545
	 */
546
547
548
549
550
551
552
	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));
	
553
554
	while (1) {
		while (!killme && numchildren < maxchildren) {
555
556
557
558
559
560
561
562
563
564
			int which = 3;
			
			/*
			 * Find which kind of server is short one.
			 */
			for (i = 0; i < 4; i++) {
				if (server_counts[i]) {
					which = i;
					break;
				}
565
			}
566
			
567
568
569
570
571
			if ((pid = fork()) < 0) {
				errorc("forking server");
				goto done;
			}
			if (pid) {
572
573
574
575
576
577
578
579
580
581
				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;
582
583
584
				numchildren++;
				continue;
			}
585
586
587
588
			/* Poor way of knowing parent/child */
			numchildren = 0;
			mypid = getpid();
			
589
590
591
592
593
594
			/* Child does useful work! Never Returns! */
			signal(SIGTERM, SIG_DFL);
			signal(SIGINT, SIG_DFL);
			signal(SIGHUP, SIG_DFL);
			
			switch (which) {
595
			case 0: udpserver(udpsock, portnum);
596
				break;
597
			case 1: udpserver(altudpsock, TBSERVER_PORT2);
598
				break;
599
			case 2: tcpserver(alttcpsock, TBSERVER_PORT2);
600
				break;
601
			case 3: tcpserver(tcpsock, portnum);
602
603
604
605
606
607
608
609
610
611
612
613
				break;
			}
			exit(-1);
		}

		/*
		 * Parent waits.
		 */
		pid = waitpid(-1, &status, 0);
		if (pid < 0) {
			errorc("waitpid failed");
			continue;
614
615
616
617
		} 
		if (WIFSIGNALED(status)) {
			error("server %d exited with signal %d!\n",
			      pid, WTERMSIG(status));
618
		}
619
620
621
		else if (WIFEXITED(status)) {
			error("server %d exited with status %d!\n",
			      pid, WEXITSTATUS(status));	  
622
		}
623
		numchildren--;
624
625
626
627
628
629
630
631
632
633

		/*
		 * 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;
			}
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
		}
		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
654
	/*
655
	 * Setup TCP socket for incoming connections.
Mike Hibler's avatar
Mike Hibler committed
656
657
	 */

658
	/* Create socket from which to read. */
Mike Hibler's avatar
Mike Hibler committed
659
660
	tcpsock = socket(AF_INET, SOCK_STREAM, 0);
	if (tcpsock < 0) {
661
		pfatal("opening stream socket");
662
663
	}

664
	i = 1;
Mike Hibler's avatar
Mike Hibler committed
665
	if (setsockopt(tcpsock, SOL_SOCKET, SO_REUSEADDR,
666
		       (char *)&i, sizeof(i)) < 0)
667
		pwarning("setsockopt(SO_REUSEADDR)");;
668
669
670
671
	
	/* Create name. */
	name.sin_family = AF_INET;
	name.sin_addr.s_addr = INADDR_ANY;
672
	name.sin_port = htons((u_short) portnum);
Mike Hibler's avatar
Mike Hibler committed
673
	if (bind(tcpsock, (struct sockaddr *) &name, sizeof(name))) {
674
		pfatal("binding stream socket");
675
676
677
	}
	/* Find assigned port value and print it out. */
	length = sizeof(name);
Mike Hibler's avatar
Mike Hibler committed
678
	if (getsockname(tcpsock, (struct sockaddr *) &name, &length)) {
679
		pfatal("getsockname");
680
	}
681
	if (listen(tcpsock, 128) < 0) {
682
		pfatal("listen");
683
	}
684
685
	info("listening on TCP port %d\n", ntohs(name.sin_port));
	
Mike Hibler's avatar
Mike Hibler committed
686
687
688
689
690
691
692
	/*
	 * Setup UDP socket
	 */

	/* Create socket from which to read. */
	udpsock = socket(AF_INET, SOCK_DGRAM, 0);
	if (udpsock < 0) {
693
		pfatal("opening dgram socket");
Mike Hibler's avatar
Mike Hibler committed
694
695
696
697
698
	}

	i = 1;
	if (setsockopt(udpsock, SOL_SOCKET, SO_REUSEADDR,
		       (char *)&i, sizeof(i)) < 0)
699
		pwarning("setsockopt(SO_REUSEADDR)");;
700
701
702
703

	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
704
705
706
707
	
	/* Create name. */
	name.sin_family = AF_INET;
	name.sin_addr.s_addr = INADDR_ANY;
708
	name.sin_port = htons((u_short) portnum);
Mike Hibler's avatar
Mike Hibler committed
709
	if (bind(udpsock, (struct sockaddr *) &name, sizeof(name))) {
710
		pfatal("binding dgram socket");
Mike Hibler's avatar
Mike Hibler committed
711
712
713
714
715
	}

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

720
721
722
	*tcpsockp = tcpsock;
	*udpsockp = udpsock;
	return 0;
723
}
724

725
/*
726
 * Listen for UDP requests. This is not a secure channel, and so this should
727
728
729
 * eventually be killed off.
 */
static void
730
udpserver(int sock, int portnum)
731
732
733
734
{
	char			buf[MYBUFSIZE];
	struct sockaddr_in	client;
	int			length, cc;
735
	unsigned int		nreq = 0;
736
	
737
738
	info("udpserver starting: pid=%d sock=%d portnum=%d\n",
	     mypid, sock, portnum);
739
740
741
742
743

	/*
	 * Wait for udp connections.
	 */
	while (1) {
744
		setproctitle("UDP %d: %u done", portnum, nreq);
745
		length = sizeof(client);		
746
		cc = recvfrom(sock, buf, sizeof(buf) - 1,
747
748
749
			      0, (struct sockaddr *)&client, &length);
		if (cc <= 0) {
			if (cc < 0)
750
751
				errorc("Reading UDP request");
			error("UDP Connection aborted\n");
752
			continue;
753
		}
754
755
		buf[cc] = '\0';
		handle_request(sock, &client, buf, 0);
756
		nreq++;
757
758
759
760
761
	}
	exit(1);
}

/*
762
 * Listen for TCP requests.
763
764
 */
static void
765
tcpserver(int sock, int portnum)
766
767
768
769
{
	char			buf[MYBUFSIZE];
	struct sockaddr_in	client;
	int			length, cc, newsock;
770
	unsigned int		nreq = 0;
771
	
772
773
	info("tcpserver starting: pid=%d sock=%d portnum=%d\n",
	     mypid, sock, portnum);
774
775
776
777
778

	/*
	 * Wait for TCP connections.
	 */
	while (1) {
779
		setproctitle("TCP %d: %u done", portnum, nreq);
780
		length  = sizeof(client);
781
		newsock = ACCEPT(sock, (struct sockaddr *)&client, &length);
782
		if (newsock < 0) {
783
			errorc("accepting TCP connection");
784
			continue;
785
		}
Mike Hibler's avatar
Mike Hibler committed
786
787

		/*
788
		 * Read in the command request.
Mike Hibler's avatar
Mike Hibler committed
789
		 */
790
		if ((cc = READ(newsock, buf, sizeof(buf) - 1)) <= 0) {
791
			if (cc < 0)
792
793
794
				errorc("Reading TCP request");
			error("TCP connection aborted\n");
			CLOSE(newsock);
795
			continue;
796
		}
797
798
		buf[cc] = '\0';
		handle_request(newsock, &client, buf, 1);
799
		CLOSE(newsock);
800
		nreq++;
801
802
803
	}
	exit(1);
}
Mike Hibler's avatar
Mike Hibler committed
804

805
806
807
808
static int
handle_request(int sock, struct sockaddr_in *client, char *rdata, int istcp)
{
	struct sockaddr_in redirect_client;
809
	int		   redirect = 0, havekey = 0;
810
	char		   buf[BUFSIZ], *bp, *cp;
811
	char		   privkey[TBDB_FLEN_PRIVKEY];
812
	int		   i, overbose = 0, err = 0;
813
	int		   version = DEFAULT_VERSION;
814
815
	tmcdreq_t	   tmcdreq, *reqp = &tmcdreq;

816
	byteswritten = 0;
817
818
819
820
821
822
#ifdef	WITHSSL
	cp = (istcp ? (isssl ? "SSL" : "TCP") : "UDP");
#else
	cp = (istcp ? "TCP" : "UDP");
#endif
	setproctitle("%s: %s", reqp->nodeid, cp);
823

824
825
826
827
	/*
	 * Init the req structure.
	 */
	bzero(reqp, sizeof(*reqp));
Mike Hibler's avatar
Mike Hibler committed
828

829
830
831
	/*
	 * Look for special tags. 
	 */
832
	bp = rdata;
833
	while ((bp = strsep(&rdata, " ")) != NULL) {
834
835
836
837
838
839
840
841
842
843
844
845
846
847
		/*
		 * Look for PRIVKEY. 
		 */
		if (sscanf(bp, "PRIVKEY=%64s", buf)) {
			havekey = 1;
			strncpy(privkey, buf, sizeof(privkey));

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


848
849
850
851
852
853
854
		/*
		 * Look for VERSION. 
		 */
		if (sscanf(bp, "VERSION=%d", &i) == 1) {
			version = i;
			continue;
		}
Mike Hibler's avatar
Mike Hibler committed
855

856
857
858
859
860
861
862
863
864
865
		/*
		 * 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);
866

867
868
			info("REDIRECTED from %s to %s\n",
			     inet_ntoa(redirect_client.sin_addr), buf);
869

870
871
872
873
874
875
876
877
878
879
880
			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)) {
881
882
			reqp->isvnode = 1;
			strncpy(reqp->vnodeid, buf, sizeof(reqp->vnodeid));
883

884
885
886
887
888
			if (debug) {
				info("VNODEID %s\n", buf);
			}
			continue;
		}
889

890
891
892
893
894
		/*
		 * 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.
Leigh B. Stoller's avatar
Leigh B. Stoller committed
895
896
897
		 *
		 * Note that rdata will point to any text after the command.
		 *
898
899
900
901
902
		 */
		if (*bp) {
			break;
		}
	}
903

904
905
906
	/* Start with default DB */
	strcpy(dbname, DEFAULT_DBNAME);

907
	/*
908
	 * Map the ip to a nodeid.
909
	 */
910
	if ((err = iptonodeid(client->sin_addr, reqp))) {
911
912
913
		if (reqp->isvnode) {
			error("No such vnode %s associated with %s\n",
			      reqp->vnodeid, inet_ntoa(client->sin_addr));
914
915
916
917
918
		}
		else {
			error("No such node: %s\n",
			      inet_ntoa(client->sin_addr));
		}
919
920
921
922
923
924
925
926
		goto skipit;
	}

	/*
	 * Redirect is allowed from the local host only!
	 * I use this for testing. See below where I test redirect
	 * if the verification fails. 
	 */
927
	if (!insecure && redirect &&
928
	    redirect_client.sin_addr.s_addr != myipaddr.s_addr) {
929
930
931
932
		char	buf1[32], buf2[32];
		
		strcpy(buf1, inet_ntoa(redirect_client.sin_addr));
		strcpy(buf2, inet_ntoa(client->sin_addr));
933
934
935

		if (verbose)
			info("%s INVALID REDIRECT: %s\n", buf1, buf2);
936
937
		goto skipit;
	}
938
939
940
941
942
943

#ifdef  WITHSSL
	/*
	 * If the connection is not SSL, then it must be a local node.
	 */
	if (isssl) {
944
945
		if (tmcd_sslverify_client(reqp->nodeid, reqp->pclass,
					  reqp->ptype,  reqp->islocal)) {
946
			error("%s: SSL verification failure\n", reqp->nodeid);
947
948
949
950
			if (! redirect)
				goto skipit;
		}
	}
951
	else if (!reqp->islocal) {
952
953
954
		if (!istcp)
			goto execute;
		
955
		error("%s: Remote node connected without SSL!\n",reqp->nodeid);
956
957
		if (!insecure)
			goto skipit;
958
	}
959
960
961
962
963
964
965
966
967
	else if (reqp->iscontrol) {
		if (!istcp)
			goto execute;

		error("%s: Control node connection without SSL!\n",
		      reqp->nodeid);
		if (!insecure)
			goto skipit;
	}
968
969
970
971
#else
	/*
	 * When not compiled for ssl, do not allow remote connections.
	 */
972
	if (!reqp->islocal) {
973
974
		error("%s: Remote node connection not allowed (Define SSL)!\n",
		      reqp->nodeid);
975
976
		if (!insecure)
			goto skipit;
977
	}
978
979
980
981
982
983
	if (reqp->iscontrol) {
		error("%s: Control node connection not allowed "
		      "(Define SSL)!\n", reqp->nodeid);
		if (!insecure)
			goto skipit;
	}
984
985
986
987
#endif
	/*
	 * Check for a redirect using the default DB. This allows
	 * for a simple redirect to a secondary DB for testing.
988
	 * Upon return, the dbname has been changed if redirected.
989
	 */
990
	if (checkdbredirect(reqp)) {
991
992
993
		/* Something went wrong */
		goto skipit;
	}
994

995
996
997
998
999
	/*
	 * Do private key check. widearea nodes must report a private key
	 * It comes over ssl of course. At present we skip this check for
	 * ron nodes. 
	 */
1000
	if (!reqp->islocal) {
For faster browsing, not all history is shown. View entire blame