tmcd.c 186 KB
Newer Older
Leigh B. Stoller's avatar
Leigh B. Stoller committed
1
2
/*
 * EMULAB-COPYRIGHT
3
 * Copyright (c) 2000-2009 University of Utah and the Flux Group.
Leigh B. Stoller's avatar
Leigh B. Stoller committed
4
5
6
 * All rights reserved.
 */

7
8
9
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
Mike Hibler's avatar
Mike Hibler committed
10
#include <arpa/inet.h>
11
#include <netdb.h>
Mike Hibler's avatar
Mike Hibler committed
12
#include <ctype.h>
13
#include <stdio.h>
Mike Hibler's avatar
Mike Hibler committed
14
#include <stdlib.h>
15
#include <errno.h>
Mike Hibler's avatar
Mike Hibler committed
16
17
#include <string.h>
#include <unistd.h>
18
19
20
21
#include <syslog.h>
#include <signal.h>
#include <stdarg.h>
#include <assert.h>
22
#include <sys/wait.h>
Leigh B. Stoller's avatar
Leigh B. Stoller committed
23
#include <sys/fcntl.h>
24
25
#include <sys/syscall.h>
#include <sys/stat.h>
Kirk Webb's avatar
   
Kirk Webb committed
26
#include <sys/param.h>
27
#include <paths.h>
Austin Clements's avatar
Austin Clements committed
28
#include <setjmp.h>
29
30
#include <pwd.h>
#include <grp.h>
31
32
#include <mysql/mysql.h>
#include "decls.h"
33
#include "config.h"
34
35
#include "ssl.h"
#include "log.h"
36
#include "tbdefs.h"
Leigh B. Stoller's avatar
Leigh B. Stoller committed
37
38
#include "bootwhat.h"
#include "bootinfo.h"
39

40
41
42
43
#ifdef EVENTSYS
#include "event.h"
#endif

44
45
46
/*
 * XXX This needs to be localized!
 */
47
48
49
#define FSPROJDIR	FSNODE ":" FSDIR_PROJ
#define FSGROUPDIR	FSNODE ":" FSDIR_GROUPS
#define FSUSERDIR	FSNODE ":" FSDIR_USERS
50
51
52
#ifdef  FSDIR_SHARE
#define FSSHAREDIR	FSNODE ":" FSDIR_SHARE
#endif
53
54
55
56
57
58
59
60
#ifdef  FSDIR_SCRATCH
#define FSSCRATCHDIR	FSNODE ":" FSDIR_SCRATCH
#endif
#define PROJDIR		PROJROOT_DIR
#define GROUPDIR	GROUPSROOT_DIR
#define USERDIR		USERSROOT_DIR
#define SCRATCHDIR	SCRATCHROOT_DIR
#define SHAREDIR	SHAREROOT_DIR
61
#define NETBEDDIR	"/netbed"
Kirk Webb's avatar
   
Kirk Webb committed
62
#define PLISALIVELOGDIR "/usr/testbed/log/plabisalive"
63
64
#define RELOADPID	"emulab-ops"
#define RELOADEID	"reloading"
Austin Clements's avatar
Austin Clements committed
65
#define FSHOSTID	"/usr/testbed/etc/fshostid"
66
#define DOTSFS		".sfs"
67
68
#define RUNASUSER	"nobody"
#define RUNASGROUP	"nobody"
69
#define NTPSERVER       "ntp1"
70

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

75
#define TESTMODE
76
77
78
79
80
81
82
83
84
85
86
87
#define DEFAULTNETMASK	"255.255.255.0"
/* This can be tossed once all the changes are in place */
static char *
CHECKMASK(char *arg)
{
	if (arg && arg[0])
		return arg;

	error("No netmask defined!\n");
	return DEFAULTNETMASK;
}
/* #define CHECKMASK(arg)  ((arg) && (arg[0]) ? (arg) : DEFAULTNETMASK) */
88

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

92
93
94
95
96
97
98
/* Compiled in slothd parameters
 *
 * 1 - reg_interval  2 - agg_interval  3 - load_thresh  
 * 4 - expt_thresh   5 - ctl_thresh
 */
#define SDPARAMS        "reg=300 agg=5 load=1 expt=5 ctl=1000"

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

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

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

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

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

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

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

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

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

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

/*
 * Flags encode a few other random properties of commands
 */
292
293
294
295
296
#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 */
297
#define F_REMREQSSL	0x20	/* remote nodes must connect with SSL */
298

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

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

void
usage()
{
388
	fprintf(stderr, usagestr, MINCHILDREN, MAXCHILDREN);
389
390
391
	exit(1);
}

392
393
394
395
396
static void
cleanup()
{
	signal(SIGHUP, SIG_IGN);
	killme = 1;
397
	killpg(0, SIGHUP);
398
399
}

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

411
412
413
414
415
416
	/* Just the parent sends this */
	if (numchildren)
		killpg(0, sig);
	signal(sig, setverbose);
}

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

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

	if (argc)
		usage();
468
469
	if (maxchildren < MINCHILDREN || maxchildren > MAXCHILDREN)
		usage();
470

471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
#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
486

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

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

	signal(SIGTERM, cleanup);
	signal(SIGINT, cleanup);
	signal(SIGHUP, cleanup);
552
553
	signal(SIGUSR1, setverbose);
	signal(SIGUSR2, setverbose);
554
555
556
557

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

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

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

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

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

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

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

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

	i = 1;
	if (setsockopt(udpsock, SOL_SOCKET, SO_REUSEADDR,
		       (char *)&i, sizeof(i)) < 0)
762
		pwarning("setsockopt(SO_REUSEADDR)");;
763
764
765
766

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

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

783
784
785
	*tcpsockp = tcpsock;
	*udpsockp = udpsock;
	return 0;
786
}
787

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

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

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

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

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

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

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

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

933
934
935
936
	/*
	 * Init the req structure.
	 */
	bzero(reqp, sizeof(*reqp));
Mike Hibler's avatar
Mike Hibler committed
937

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

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


957
958
		/*
		 * Look for VERSION. 
959
960
		 * Check for clients that are newer than the server
		 * and complain.
961
962
963
		 */
		if (sscanf(bp, "VERSION=%d", &i) == 1) {
			version = i;
964
			if (version > CURRENT_VERSION) {
Mike Hibler's avatar
nits:    
Mike Hibler committed
965
966
				error("version skew on request from %s: "
				      "server=%d, request=%d, "
967
				      "old TMCD installed?\n",
Mike Hibler's avatar
nits:    
Mike Hibler committed
968
				      inet_ntoa(client->sin_addr),
969
970
				      CURRENT_VERSION, version);
			}
971
972
			continue;
		}
Mike Hibler's avatar
Mike Hibler committed
973

974
975
976
977
978
979
980
981
982
983
		/*
		 * 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);
984

985
986
			info("REDIRECTED from %s to %s\n",
			     inet_ntoa(redirect_client.sin_addr), buf);
987

988
989
990
991
992
993
994
995
996
997
998
			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)) {
999
1000
			reqp->isvnode = 1;
			strncpy(reqp->vnodeid, buf, sizeof(reqp->vnodeid));
1001

1002
1003
1004
1005
1006
			if (debug) {
				info("VNODEID %s\n", buf);
			}
			continue;
		}
1007

1008
1009
1010
1011
1012
		/*
		 * 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
1013
1014
1015
		 *
		 * Note that rdata will point to any text after the command.
		 *
1016
1017
1018
1019
1020
		 */
		if (*bp) {
			break;
		}
	}
1021

1022
1023
1024
	/* Start with default DB */
	strcpy(dbname, DEFAULT_DBNAME);

1025
	/*
1026
	 * Map the ip to a nodeid.
1027
	 */
1028
	if ((err = iptonodeid(client->sin_addr, reqp))) {
1029
1030
1031
		if (reqp->isvnode) {
			error("No such vnode %s associated with %s\n",
			      reqp->vnodeid, inet_ntoa(client->sin_addr));
1032
1033
1034
1035
1036
		}
		else {
			error("No such node: %s\n",
			      inet_ntoa(client->sin_addr));
		}
1037
1038
		goto skipit;
	}
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
	
	/*
	 * Redirect geni sliver nodes to the tmcd of their origin.
	 */
	if (reqp->tmcd_redirect[0]) {
		char	buf[BUFSIZ];

		sprintf(buf, "REDIRECT=%s\n", reqp->tmcd_redirect);
		client_writeback(sock, buf, strlen(buf), istcp);
		goto skipit;
	}
1050
1051
1052
1053
1054
1055

	/*
	 * Redirect is allowed from the local host only!
	 * I use this for testing. See below where I test redirect
	 * if the verification fails. 
	 */
1056
	if (