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

190
191
192
193
194
#ifdef EVENTSYS
int			myevent_send(address_tuple_t address);
static event_handle_t	event_handle = NULL;
#endif

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

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

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

/*
 * Flags encode a few other random properties of commands
 */
285
286
287
288
289
#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 */
290

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

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

void
usage()
{
379
	fprintf(stderr, usagestr, MINCHILDREN, MAXCHILDREN);
380
381
382
	exit(1);
}

383
384
385
386
387
static void
cleanup()
{
	signal(SIGHUP, SIG_IGN);
	killme = 1;
388
	killpg(0, SIGHUP);
389
390
}

391
392
393
394
395
396
397
398
399
static void
setverbose(int sig)
{
	signal(sig, SIG_IGN);
	
	if (sig == SIGUSR1)
		verbose = 1;
	else
		verbose = 0;
400
401
	info("verbose logging turned %s\n", verbose ? "on" : "off");

402
403
404
405
406
407
	/* Just the parent sends this */
	if (numchildren)
		killpg(0, sig);
	signal(sig, setverbose);
}

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

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

	if (argc)
		usage();
459
460
	if (maxchildren < MINCHILDREN || maxchildren > MAXCHILDREN)
		usage();
461

462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
#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
477

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

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

	signal(SIGTERM, cleanup);
	signal(SIGINT, cleanup);
	signal(SIGHUP, cleanup);
543
544
	signal(SIGUSR1, setverbose);
	signal(SIGUSR2, setverbose);
545
546
547
548

	/*
	 * Stash the pid away.
	 */
549
	mypid = getpid();
550
551
552
	sprintf(buf, "%s/tmcd.pid", _PATH_VARRUN);
	fp = fopen(buf, "w");
	if (fp != NULL) {
553
		fprintf(fp, "%d\n", mypid);
554
555
556
		(void) fclose(fp);
	}

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
585
586
587
588
589
	/*
	 * 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);
	}

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

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

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

712
	/* Create socket from which to read. */
Mike Hibler's avatar
Mike Hibler committed
713
714
	tcpsock = socket(AF_INET, SOCK_STREAM, 0);
	if (tcpsock < 0) {
715
		pfatal("opening stream socket");
716
717
	}

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

	/* Create socket from which to read. */
	udpsock = socket(AF_INET, SOCK_DGRAM, 0);
	if (udpsock < 0) {
747
		pfatal("opening dgram socket");
Mike Hibler's avatar
Mike Hibler committed
748
749
750
751
752
	}

	i = 1;
	if (setsockopt(udpsock, SOL_SOCKET, SO_REUSEADDR,
		       (char *)&i, sizeof(i)) < 0)
753
		pwarning("setsockopt(SO_REUSEADDR)");;
754
755
756
757

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

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

774
775
776
	*tcpsockp = tcpsock;
	*udpsockp = udpsock;
	return 0;
777
}
778

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

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

815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
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;
}

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

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

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

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

916
	byteswritten = 0;
917
918
919
920
921
#ifdef	WITHSSL
	cp = (istcp ? (isssl ? "SSL" : "TCP") : "UDP");
#else
	cp = (istcp ? "TCP" : "UDP");
#endif
922
	setproctitle("%s: %s", inet_ntoa(client->sin_addr), cp);
923

924
925
926
927
	/*
	 * Init the req structure.
	 */
	bzero(reqp, sizeof(*reqp));
Mike Hibler's avatar
Mike Hibler committed
928

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

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


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

963
964
965
966
967
968
969
970
971
972
		/*
		 * 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);
973

974
975
			info("REDIRECTED from %s to %s\n",
			     inet_ntoa(redirect_client.sin_addr), buf);
976

977
978
979
980
981
982
983
984
985
986
987
			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)) {
988
989
			reqp->isvnode = 1;
			strncpy(reqp->vnodeid, buf, sizeof(reqp->vnodeid));
990

991
992
993
994
995
			if (debug) {
				info("VNODEID %s\n", buf);
			}
			continue;
		}
996

997
998
999
1000
1001
		/*
		 * 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
1002
1003
1004
		 *
		 * Note that rdata will point to any text after the command.
		 *
1005
1006
1007
1008
1009
		 */
		if (*bp) {
			break;
		}
	}
1010

1011
1012
1013
	/* Start with default DB */
	strcpy(dbname, DEFAULT_DBNAME);

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