tmcd.c 169 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
15
16
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
17
18
19
20
#include <syslog.h>
#include <signal.h>
#include <stdarg.h>
#include <assert.h>
21
#include <sys/wait.h>
Leigh B. Stoller's avatar
Leigh B. Stoller committed
22
#include <sys/fcntl.h>
23
24
#include <sys/syscall.h>
#include <sys/stat.h>
Kirk Webb's avatar
   
Kirk Webb committed
25
#include <sys/param.h>
26
#include <paths.h>
Austin Clements's avatar
Austin Clements committed
27
#include <setjmp.h>
28
29
#include <pwd.h>
#include <grp.h>
30
31
#include <mysql/mysql.h>
#include "decls.h"
32
#include "config.h"
33
34
#include "ssl.h"
#include "log.h"
35
#include "tbdefs.h"
36

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

41
42
43
/*
 * XXX This needs to be localized!
 */
44
45
46
#define FSPROJDIR	FSNODE ":" FSDIR_PROJ
#define FSGROUPDIR	FSNODE ":" FSDIR_GROUPS
#define FSUSERDIR	FSNODE ":" FSDIR_USERS
47
48
49
#ifdef  FSDIR_SHARE
#define FSSHAREDIR	FSNODE ":" FSDIR_SHARE
#endif
50
51
52
53
54
55
56
57
#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
58
#define NETBEDDIR	"/netbed"
Kirk Webb's avatar
   
Kirk Webb committed
59
#define PLISALIVELOGDIR "/usr/testbed/log/plabisalive"
60
61
#define RELOADPID	"emulab-ops"
#define RELOADEID	"reloading"
Austin Clements's avatar
Austin Clements committed
62
#define FSHOSTID	"/usr/testbed/etc/fshostid"
63
#define DOTSFS		".sfs"
64
65
#define RUNASUSER	"nobody"
#define RUNASGROUP	"nobody"
66
#define NTPSERVER       "ntp1"
67

68
69
70
71
/* socket read/write timeouts in ms */
#define READTIMO	3000
#define WRITETIMO	3000

72
#define TESTMODE
73
74
75
76
77
78
79
80
81
82
83
84
#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) */
85

86
87
88
#define DISKTYPE	"ad"
#define DISKNUM		0

89
90
91
92
93
94
95
/* 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"

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

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

120
121
122
123
/* socket timeouts */
static int	readtimo = READTIMO;
static int	writetimo = WRITETIMO;

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

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

147
148
149
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 {
	int		allocated;
	int		jailflag;
	int		isvnode;
155
	int		issubnode;
156
	int		islocal;
157
	int		iscontrol;
158
	int		isplabdslice;
159
	int		isplabsvc;
160
	int		elab_in_elab;
161
        int		singlenet;	  /* Modifier for elab_in_elab */
162
	int		update_accounts;
163
164
	char		nodeid[TBDB_FLEN_NODEID];
	char		vnodeid[TBDB_FLEN_NODEID];
Leigh B. Stoller's avatar
Leigh B. Stoller committed
165
	char		pnodeid[TBDB_FLEN_NODEID]; /* XXX */
166
167
168
169
170
	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];
171
	char		class[TBDB_FLEN_NODECLASS];
172
173
        char		ptype[TBDB_FLEN_NODETYPE];	/* Of physnode */
	char		pclass[TBDB_FLEN_NODECLASS];	/* Of physnode */
174
175
	char		creator[TBDB_FLEN_UID];
	char		swapper[TBDB_FLEN_UID];
176
	char		syncserver[TBDB_FLEN_VNAME];	/* The vname */
177
	char		keyhash[TBDB_FLEN_PRIVKEY];
178
	char		eventkey[TBDB_FLEN_PRIVKEY];
179
	char		sfshostid[TBDB_FLEN_SFSHOSTID];
180
181
182
183
184
	char		testdb[256];
} tmcdreq_t;
static int	iptonodeid(struct in_addr, tmcdreq_t *);
static int	checkdbredirect(tmcdreq_t *);

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

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

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

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

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

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

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

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

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

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

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

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

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

	if (argc)
		usage();
452
453
	if (maxchildren < MINCHILDREN || maxchildren > MAXCHILDREN)
		usage();
454

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

767
768
769
	*tcpsockp = tcpsock;
	*udpsockp = udpsock;
	return 0;
770
}
771

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

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

808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
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;
}

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

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

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

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

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

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

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

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


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

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

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

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

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

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