tmcd.c 142 KB
Newer Older
Leigh B. Stoller's avatar
Leigh B. Stoller committed
1
2
/*
 * EMULAB-COPYRIGHT
3
 * Copyright (c) 2000-2005 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
#define NTPSERVER       "ntp1"
61

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

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

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

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

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

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

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

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

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

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

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

242
243
/*
 * The fullconfig slot determines what routines get called when pushing
244
 * out a full configuration. Physnodes get slightly different
245
246
247
248
249
250
 * 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
251
252
253
254
255
256
#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 */
257
258
#define F_MINLOG	0x2	/* record minimal logging info normally */
#define F_MAXLOG	0x4	/* record maximal logging info normally */
259
#define F_ALLOCATED	0x8	/* node must be allocated to make call */
260

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

327
328
329
330
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"
331
 " -c num	   Specify number of servers (must be %d <= x <= %d)\n"
332
 " -v              More verbose logging\n"
333
 " -i ipaddr       Sets the boss IP addr to return (for multi-homed servers)\n"
334
335
336
337
338
 "\n";

void
usage()
{
339
	fprintf(stderr, usagestr, MINCHILDREN, MAXCHILDREN);
340
341
342
	exit(1);
}

343
344
345
346
347
static void
cleanup()
{
	signal(SIGHUP, SIG_IGN);
	killme = 1;
348
	killpg(0, SIGHUP);
349
350
}

351
352
353
354
355
356
357
358
359
static void
setverbose(int sig)
{
	signal(sig, SIG_IGN);
	
	if (sig == SIGUSR1)
		verbose = 1;
	else
		verbose = 0;
360
361
	info("verbose logging turned %s\n", verbose ? "on" : "off");

362
363
364
365
366
367
	/* Just the parent sends this */
	if (numchildren)
		killpg(0, sig);
	signal(sig, setverbose);
}

Mike Hibler's avatar
Mike Hibler committed
368
369
int
main(int argc, char **argv)
370
{
371
	int			tcpsock, udpsock, i, ch;
372
373
374
	int			alttcpsock, altudpsock;
	int			status, pid;
	int			portnum = TBSERVER_PORT;
375
376
	FILE			*fp;
	char			buf[BUFSIZ];
377
	struct hostent		*he;
378
	extern char		build_info[];
379
380
381
382
383
	int			server_counts[4]; /* udp,tcp,altudp,alttcp */
	struct {
		int	pid;
		int	which;
	} servers[MAXCHILDREN];
384

385
	while ((ch = getopt(argc, argv, "dp:c:Xvi:")) != -1)
386
387
388
389
390
391
		switch(ch) {
		case 'p':
			portnum = atoi(optarg);
			break;
		case 'd':
			debug++;
392
			break;
393
394
395
		case 'c':
			maxchildren = atoi(optarg);
			break;
396
397
398
		case 'X':
			insecure = 1;
			break;
399
400
401
		case 'v':
			verbose++;
			break;
402
403
404
405
406
407
408
		case 'i':
			if (inet_aton(optarg, &myipaddr) == 0) {
				fprintf(stderr, "invalid IP address %s\n",
					optarg);
				usage();
			}
			break;
409
410
411
412
413
414
415
416
417
418
		case 'h':
		case '?':
		default:
			usage();
		}
	argc -= optind;
	argv += optind;

	if (argc)
		usage();
419
420
	if (maxchildren < MINCHILDREN || maxchildren > MAXCHILDREN)
		usage();
421

422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
#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
437

Austin Clements's avatar
Austin Clements committed
438
439
440
441
442
443
444
445
446
447
448
449
450
451
	/*
	 * 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;
452
453
454
				if (debug) {
				    info("fshostid: %s\n", fshostid);
				}
Austin Clements's avatar
Austin Clements committed
455
456
457
458
459
460
461
462
463
			}
			else {
				error("fshostid from %s may be corrupt: %s",
				      FSHOSTID, fshostid);
			}
			fclose(fp);
		}
	}
	
464
465
466
	/*
	 * Grab our IP for security check below.
	 */
467
	if (myipaddr.s_addr == 0) {
468
#ifdef	LBS
469
		strcpy(buf, BOSSNODE);
470
#else
471
472
		if (gethostname(buf, sizeof(buf)) < 0)
			pfatal("getting hostname");
473
#endif
474
475
476
477
478
479
		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);
480
481
	}

482
483
484
485
486
487
	/*
	 * 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) {
488
489
		error("Could not make sockets!");
		exit(1);
490
	    }
491
	    num_alttcpservers = num_altudpservers = 0;
492
493
494
495
496
497
	} else {
	    if (makesockets(portnum, &udpsock, &tcpsock) < 0 ||
		makesockets(TBSERVER_PORT2, &altudpsock, &alttcpsock) < 0) {
		    error("Could not make sockets!");
		    exit(1);
	    }
498
499
500
501
502
	}

	signal(SIGTERM, cleanup);
	signal(SIGINT, cleanup);
	signal(SIGHUP, cleanup);
503
504
	signal(SIGUSR1, setverbose);
	signal(SIGUSR2, setverbose);
505
506
507
508

	/*
	 * Stash the pid away.
	 */
509
	mypid = getpid();
510
511
512
	sprintf(buf, "%s/tmcd.pid", _PATH_VARRUN);
	fp = fopen(buf, "w");
	if (fp != NULL) {
513
		fprintf(fp, "%d\n", mypid);
514
515
516
		(void) fclose(fp);
	}

517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
	/*
	 * 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);
	}

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

		/*
		 * Parent waits.
		 */
		pid = waitpid(-1, &status, 0);
		if (pid < 0) {
			errorc("waitpid failed");
			continue;
628
629
630
631
		} 
		if (WIFSIGNALED(status)) {
			error("server %d exited with signal %d!\n",
			      pid, WTERMSIG(status));
632
		}
633
634
635
		else if (WIFEXITED(status)) {
			error("server %d exited with status %d!\n",
			      pid, WEXITSTATUS(status));	  
636
		}
637
		numchildren--;
638
639
640
641
642
643
644
645
646
647

		/*
		 * 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;
			}
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
		}
		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
668
	/*
669
	 * Setup TCP socket for incoming connections.
Mike Hibler's avatar
Mike Hibler committed
670
671
	 */

672
	/* Create socket from which to read. */
Mike Hibler's avatar
Mike Hibler committed
673
674
	tcpsock = socket(AF_INET, SOCK_STREAM, 0);
	if (tcpsock < 0) {
675
		pfatal("opening stream socket");
676
677
	}

678
	i = 1;
Mike Hibler's avatar
Mike Hibler committed
679
	if (setsockopt(tcpsock, SOL_SOCKET, SO_REUSEADDR,
680
		       (char *)&i, sizeof(i)) < 0)
681
		pwarning("setsockopt(SO_REUSEADDR)");;
682
683
684
685
	
	/* Create name. */
	name.sin_family = AF_INET;
	name.sin_addr.s_addr = INADDR_ANY;
686
	name.sin_port = htons((u_short) portnum);
Mike Hibler's avatar
Mike Hibler committed
687
	if (bind(tcpsock, (struct sockaddr *) &name, sizeof(name))) {
688
		pfatal("binding stream socket");
689
690
691
	}
	/* Find assigned port value and print it out. */
	length = sizeof(name);
Mike Hibler's avatar
Mike Hibler committed
692
	if (getsockname(tcpsock, (struct sockaddr *) &name, &length)) {
693
		pfatal("getsockname");
694
	}
695
	if (listen(tcpsock, 128) < 0) {
696
		pfatal("listen");
697
	}
698
699
	info("listening on TCP port %d\n", ntohs(name.sin_port));
	
Mike Hibler's avatar
Mike Hibler committed
700
701
702
703
704
705
706
	/*
	 * Setup UDP socket
	 */

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

	i = 1;
	if (setsockopt(udpsock, SOL_SOCKET, SO_REUSEADDR,
		       (char *)&i, sizeof(i)) < 0)
713
		pwarning("setsockopt(SO_REUSEADDR)");;
714
715
716
717

	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
718
719
720
721
	
	/* Create name. */
	name.sin_family = AF_INET;
	name.sin_addr.s_addr = INADDR_ANY;
722
	name.sin_port = htons((u_short) portnum);
Mike Hibler's avatar
Mike Hibler committed
723
	if (bind(udpsock, (struct sockaddr *) &name, sizeof(name))) {
724
		pfatal("binding dgram socket");
Mike Hibler's avatar
Mike Hibler committed
725
726
727
728
729
	}

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

734
735
736
	*tcpsockp = tcpsock;
	*udpsockp = udpsock;
	return 0;
737
}
738

739
/*
740
 * Listen for UDP requests. This is not a secure channel, and so this should
741
742
743
 * eventually be killed off.
 */
static void
744
udpserver(int sock, int portnum)
745
746
747
748
{
	char			buf[MYBUFSIZE];
	struct sockaddr_in	client;
	int			length, cc;
749
	unsigned int		nreq = 0;
750
	
751
752
	info("udpserver starting: pid=%d sock=%d portnum=%d\n",
	     mypid, sock, portnum);
753
754
755
756
757

	/*
	 * Wait for udp connections.
	 */
	while (1) {
758
		setproctitle("UDP %d: %u done", portnum, nreq);
759
		length = sizeof(client);		
760
		cc = recvfrom(sock, buf, sizeof(buf) - 1,
761
762
763
			      0, (struct sockaddr *)&client, &length);
		if (cc <= 0) {
			if (cc < 0)
764
765
				errorc("Reading UDP request");
			error("UDP Connection aborted\n");
766
			continue;
767
		}
768
769
		buf[cc] = '\0';
		handle_request(sock, &client, buf, 0);
770
		nreq++;
771
772
773
774
775
	}
	exit(1);
}

/*
776
 * Listen for TCP requests.
777
778
 */
static void
779
tcpserver(int sock, int portnum)
780
{
781
	char			buf[MAXTMCDPACKET];
782
783
	struct sockaddr_in	client;
	int			length, cc, newsock;
784
	unsigned int		nreq = 0;
785
	
786
787
	info("tcpserver starting: pid=%d sock=%d portnum=%d\n",
	     mypid, sock, portnum);
788
789
790
791
792

	/*
	 * Wait for TCP connections.
	 */
	while (1) {
793
		setproctitle("TCP %d: %u done", portnum, nreq);
794
		length  = sizeof(client);
795
		newsock = ACCEPT(sock, (struct sockaddr *)&client, &length);
796
		if (newsock < 0) {
797
			errorc("accepting TCP connection");
798
			continue;
799
		}
Mike Hibler's avatar
Mike Hibler committed
800
801

		/*
802
		 * Read in the command request.
Mike Hibler's avatar
Mike Hibler committed
803
		 */
804
		if ((cc = READ(newsock, buf, sizeof(buf) - 1)) <= 0) {
805
			if (cc < 0)
806
807
808
				errorc("Reading TCP request");
			error("TCP connection aborted\n");
			CLOSE(newsock);
809
			continue;
810
		}
811
812
		buf[cc] = '\0';
		handle_request(newsock, &client, buf, 1);
813
		CLOSE(newsock);
814
		nreq++;
815
816
817
	}
	exit(1);
}
Mike Hibler's avatar
Mike Hibler committed
818

819
820
821
822
static int
handle_request(int sock, struct sockaddr_in *client, char *rdata, int istcp)
{
	struct sockaddr_in redirect_client;
823
	int		   redirect = 0, havekey = 0;
824
	char		   buf[BUFSIZ], *bp, *cp;
825
	char		   privkey[TBDB_FLEN_PRIVKEY];
826
	int		   i, overbose = 0, err = 0;
827
	int		   version = DEFAULT_VERSION;
828
829
	tmcdreq_t	   tmcdreq, *reqp = &tmcdreq;

830
	byteswritten = 0;
831
832
833
834
835
#ifdef	WITHSSL
	cp = (istcp ? (isssl ? "SSL" : "TCP") : "UDP");
#else
	cp = (istcp ? "TCP" : "UDP");
#endif
836
	setproctitle("%s: %s", inet_ntoa(client->sin_addr), cp);
837

838
839
840
841
	/*
	 * Init the req structure.
	 */
	bzero(reqp, sizeof(*reqp));
Mike Hibler's avatar
Mike Hibler committed
842

843
844
845
	/*
	 * Look for special tags. 
	 */
846
	bp = rdata;
847
	while ((bp = strsep(&rdata, " ")) != NULL) {
848
849
850
851
852
853
854
855
856
857
858
859
860
861
		/*
		 * Look for PRIVKEY. 
		 */
		if (sscanf(bp, "PRIVKEY=%64s", buf)) {
			havekey = 1;
			strncpy(privkey, buf, sizeof(privkey));

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


862
863
		/*
		 * Look for VERSION. 
864
865
		 * Check for clients that are newer than the server
		 * and complain.
866
867
868
		 */
		if (sscanf(bp, "VERSION=%d", &i) == 1) {
			version = i;
869
870
871
872
873
			if (version > CURRENT_VERSION) {
				error("%s: version skew: server=%d, request=%d, "
				      "old TMCD installed?\n",
				      CURRENT_VERSION, version);
			}
874
875
			continue;
		}
Mike Hibler's avatar
Mike Hibler committed
876

877
878
879
880
881
882
883
884
885
886
		/*
		 * 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);
887

888
889
			info("REDIRECTED from %s to %s\n",
			     inet_ntoa(redirect_client.sin_addr), buf);
890

891
892
893
894
895
896
897
898
899
900
901
			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)) {
902
903
			reqp->isvnode = 1;
			strncpy(reqp->vnodeid, buf, sizeof(reqp->vnodeid));
904

905
906
907
908
909
			if (debug) {
				info("VNODEID %s\n", buf);
			}
			continue;
		}
910

911
912
913
914
915
		/*
		 * 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
916
917
918
		 *
		 * Note that rdata will point to any text after the command.
		 *
919
920
921
922
923
		 */
		if (*bp) {
			break;
		}
	}
924

925
926
927
	/* Start with default DB */
	strcpy(dbname, DEFAULT_DBNAME);

928
	/*
929
	 * Map the ip to a nodeid.
930
	 */
931
	if ((err = iptonodeid(client->sin_addr, reqp))) {
932
933
934
		if (reqp->isvnode) {
			error("No such vnode %s associated with %s\n",
			      reqp->vnodeid, inet_ntoa(client->sin_addr));
935
936
937
938
939
		}
		else {
			error("No such node: %s\n",
			      inet_ntoa(client->sin_addr));
		}
940
941
942
943
944
945
946
947
		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. 
	 */
948
	if (!insecure && redirect &&
949
	    redirect_client.sin_addr.s_addr != myipaddr.s_addr) {
950
951
952
953
		char	buf1[32], buf2[32];
		
		strcpy(buf1, inet_ntoa(redirect_client.sin_addr));
		strcpy(buf2, inet_ntoa(client->sin_addr));
954
955
956

		if (verbose)
			info("%s INVALID REDIRECT: %s\n", buf1, buf2);
957
958
		goto skipit;
	}
959
960
961
962
963
964

#ifdef  WITHSSL
	/*
	 * If the connection is not SSL, then it must be a local node.
	 */
	if (isssl) {
965
966
		if (tmcd_sslverify_client(reqp->nodeid, reqp->pclass,
					  reqp->ptype,  reqp->islocal)) {
967
			error("%s: SSL verification failure\n", reqp->nodeid);
968
969
970
971
			if (! redirect)
				goto skipit;
		}
	}
972
	else if (!reqp->islocal) {
973
974
975
		if (!istcp)
			goto execute;
		
976
		error("%s: Remote node connected without SSL!\n",reqp->nodeid);
977
978
		if (!insecure)
			goto skipit;
979
	}
980
981
982
983
984
985
986
987
988
	else if (reqp->iscontrol) {
		if (!istcp)
			goto execute;

		error("%s: Control node connection without SSL!\n",
		      reqp->nodeid);
		if (!insecure)
			goto skipit;
	}
989
990
991
992
#else
	/*
	 * When not compiled for ssl, do not allow remote connections.
	 */
993
	if (!reqp->islocal) {
994
995
		error("%s: Remote node connection not allowed (Define SSL)!\n",
		      reqp->nodeid);
996
997
		if (!insecure)
			goto skipit;
998
	}
999
1000
	if (reqp->iscontrol) {
		error("%s: Control node connection not allowed "