tmcd.c 173 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
	int		exptidx;
165
166
	char		nodeid[TBDB_FLEN_NODEID];
	char		vnodeid[TBDB_FLEN_NODEID];
Leigh B. Stoller's avatar
Leigh B. Stoller committed
167
	char		pnodeid[TBDB_FLEN_NODEID]; /* XXX */
168
169
170
171
172
	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];
173
	char		class[TBDB_FLEN_NODECLASS];
174
175
        char		ptype[TBDB_FLEN_NODETYPE];	/* Of physnode */
	char		pclass[TBDB_FLEN_NODECLASS];	/* Of physnode */
176
177
	char		creator[TBDB_FLEN_UID];
	char		swapper[TBDB_FLEN_UID];
178
	char		syncserver[TBDB_FLEN_VNAME];	/* The vname */
179
	char		keyhash[TBDB_FLEN_PRIVKEY];
180
	char		eventkey[TBDB_FLEN_PRIVKEY];
181
	char		sfshostid[TBDB_FLEN_SFSHOSTID];
182
183
184
185
186
	char		testdb[256];
} tmcdreq_t;
static int	iptonodeid(struct in_addr, tmcdreq_t *);
static int	checkdbredirect(tmcdreq_t *);

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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
584
	/*
	 * 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);
	}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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


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

958
959
960
961
962
963
964
965
966
967
		/*
		 * 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);
968

969
970
			info("REDIRECTED from %s to %s\n",
			     inet_ntoa(redirect_client.sin_addr), buf);
971

972
973
974
975
976
977
978
979
980
981
982
			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)) {
983
984
			reqp->isvnode = 1;
			strncpy(reqp->vnodeid, buf, sizeof(reqp->vnodeid));
985

986
987
988
989
990
			if (debug) {
				info("VNODEID %s\n", buf);
			}
			continue;
		}
991

992
993
994
995
996
		/*
		 * 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
997
998
999
		 *
		 * Note that rdata will point to any text after the command.
		 *
1000
1001
1002
1003
1004
		 */
		if (*bp) {
			break;
		}
	}
1005

1006
1007
1008
	/* Start with default DB */
	strcpy(dbname, DEFAULT_DBNAME);

1009
	/*
1010
	 * Map the ip to a nodeid.
1011
	 */
1012
	if ((err = iptonodeid(client->sin_addr, reqp))) {
1013
1014
1015
		if (reqp->isvnode) {
			error("No such vnode %s associated with %s\n",
			      reqp->vnodeid, inet_ntoa(client->sin_addr));
1016
1017
1018
1019
1020
		}
		else {
			error("No such node: %s\n",
			      inet_ntoa(client->sin_addr));
		}
1021
1022
1023
1024
1025
1026
1027
1028
		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. 
	 */
1029
	if (!insecure && redirect &&
1030
	    redirect_client.sin_addr.s_addr != myipaddr.s_addr) {
1031
1032
1033
1034
		char	buf1[32], buf2[32];
		
		strcpy(buf1, inet_ntoa(redirect_client.sin_addr));
		strcpy(buf2, inet_ntoa(client->sin_addr));
1035
1036
1037

		if (verbose)
			info("%s INVALID REDIRECT: %s\n", buf1, buf2);
1038
1039
		goto skipit;
	}
1040
1041

#ifdef  WITHSSL
1042
1043
1044
1045
1046
1047
	/*
	 * We verify UDP requests below based on the particular request
	 */
	if (!istcp)
		goto execute;

1048
1049
1050
1051
	/*
	 * If the connection is not SSL, then it must be a local node.
	 */
	if (isssl) {
1052
1053
		if (tmcd_sslverify_client(reqp->nodeid, reqp->pclass,
					  reqp->ptype,  reqp->islocal)) {
1054
			error("%s: SSL verification failure\n", reqp->nodeid);
1055
1056
1057
1058
			if (! redirect)
				goto skipit;
		}
	}
1059
1060
1061
1062
1063
1064
	else if (reqp->iscontrol) {
		error("%s: Control node connection without SSL!\n",
		      reqp->nodeid);
		if (!insecure)
			goto skipit;
	}
1065
1066
1067
1068
#else
	/*
	 * When not compiled for ssl, do not allow remote connections.
	 */
1069
	if (!reqp->islocal) {
1070