tmcd.c 224 KB
Newer Older
Leigh B. Stoller's avatar
Leigh B. Stoller committed
1
2
/*
 * EMULAB-COPYRIGHT
3
 * Copyright (c) 2000-2010 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: Not sure this is okay! */
#include "tpm.h"

47
48
49
/*
 * XXX This needs to be localized!
 */
50
51
52
#define FSPROJDIR	FSNODE ":" FSDIR_PROJ
#define FSGROUPDIR	FSNODE ":" FSDIR_GROUPS
#define FSUSERDIR	FSNODE ":" FSDIR_USERS
53
54
55
#ifdef  FSDIR_SHARE
#define FSSHAREDIR	FSNODE ":" FSDIR_SHARE
#endif
56
57
58
59
60
61
62
63
#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
64
#define NETBEDDIR	"/netbed"
Kirk Webb's avatar
   
Kirk Webb committed
65
#define PLISALIVELOGDIR "/usr/testbed/log/plabisalive"
66
67
#define RELOADPID	"emulab-ops"
#define RELOADEID	"reloading"
Austin Clements's avatar
Austin Clements committed
68
#define FSHOSTID	"/usr/testbed/etc/fshostid"
69
#define DOTSFS		".sfs"
70
71
#define RUNASUSER	"nobody"
#define RUNASGROUP	"nobody"
72
#define NTPSERVER       "ntp1"
73
#define PROTOUSER	"elabman"
74
75
76
77
#define PRIVKEY_LEN	128
#define URN_LEN		128
#define XSTRINGIFY(s)   STRINGIFY(s)
#define STRINGIFY(s)	#s
78

79
80
81
82
83
/* XXX backward compat */
#ifndef TBCOREDIR
#define	TBCOREDIR	TBROOT "/tmp"
#endif

84
85
86
87
/* socket read/write timeouts in ms */
#define READTIMO	3000
#define WRITETIMO	3000

88
#define TESTMODE
89
90
91
92
93
94
95
96
97
98
99
100
#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) */
101

102
103
104
#define DISKTYPE	"ad"
#define DISKNUM		0

105
106
/* Compiled in slothd parameters
 *
107
 * 1 - reg_interval  2 - agg_interval  3 - load_thresh
108
109
110
111
 * 4 - expt_thresh   5 - ctl_thresh
 */
#define SDPARAMS        "reg=300 agg=5 load=1 expt=5 ctl=1000"

112
113
/* Defined in configure and passed in via the makefile */
#define DBNAME_SIZE	64
Austin Clements's avatar
Austin Clements committed
114
#define HOSTID_SIZE	(32+64)
115
116
#define DEFAULT_DBNAME	TBDBNAME

Robert P Ricci's avatar
Robert P Ricci committed
117
118
119
120
/* For secure disk loading */
#define SECURELOAD_OPMODE "SECURELOAD"
#define SECURELOAD_STATE  "RELOADSETUP"

121
int		debug = 0;
122
static int	verbose = 0;
123
static int	insecure = 0;
124
static int	byteswritten = 0;
Leigh B Stoller's avatar
Leigh B Stoller committed
125
static char	pidfile[MAXPATHLEN];
126
static char     dbname[DBNAME_SIZE];
127
static struct in_addr myipaddr;
Austin Clements's avatar
Austin Clements committed
128
static char	fshostid[HOSTID_SIZE];
Leigh B. Stoller's avatar
Leigh B. Stoller committed
129
static int	nodeidtoexp(char *nodeid, char *pid, char *eid, char *gid);
130
131
static void	tcpserver(int sock, int portnum);
static void	udpserver(int sock, int portnum);
132
static int      handle_request(int, struct sockaddr_in *, char *, int);
133
static int      checkcerts(char*);
134
static int	makesockets(int portnum, int *udpsockp, int *tcpsockp);
Mike Hibler's avatar
Mike Hibler committed
135
136
int		client_writeback(int sock, void *buf, int len, int tcp);
void		client_writeback_done(int sock, struct sockaddr_in *client);
137
138
MYSQL_RES *	mydb_query(char *query, int ncols, ...);
int		mydb_update(char *query, ...);
139
static int	safesymlink(char *name1, char *name2);
140

141
142
143
144
/* socket timeouts */
static int	readtimo = READTIMO;
static int	writetimo = WRITETIMO;

145
/* thread support */
146
#define MAXCHILDREN	20
147
#define MINCHILDREN	8
148
static int	numchildren;
149
150
151
152
static int	maxchildren       = 13;
static int      num_udpservers    = 3;
static int      num_altudpservers = 1;
static int      num_alttcpservers = 1;
153
static int	mypid;
154
static volatile int killme;
155

156
157
158
159
160
/* Output macro to check for string overflow */
#define OUTPUT(buf, size, format...) \
({ \
	int __count__ = snprintf((buf), (size), ##format); \
        \
161
        if (__count__ >= (size)) { \
162
163
164
165
166
167
		error("Not enough room in output buffer! line %d.\n", __LINE__);\
		return 1; \
	} \
	__count__; \
})

168
169
170
171
172
/*
 * 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
173
	struct in_addr  client;
174
175
176
	int		allocated;
	int		jailflag;
	int		isvnode;
177
	int		issubnode;
178
	int		islocal;
179
	int		isdedicatedwa;
180
	int		iscontrol;
181
	int		isplabdslice;
182
	int		isplabsvc;
183
	int		elab_in_elab;
184
        int		singlenet;	  /* Modifier for elab_in_elab */
185
	int		update_accounts;
186
	int		exptidx;
187
188
189
	int		creator_idx;
	int		swapper_idx;
	int		swapper_isadmin;
190
        int		genisliver_idx;
191
        int		geniflags;
192
193
	char		nodeid[TBDB_FLEN_NODEID];
	char		vnodeid[TBDB_FLEN_NODEID];
Leigh B. Stoller's avatar
Leigh B. Stoller committed
194
	char		pnodeid[TBDB_FLEN_NODEID]; /* XXX */
195
196
197
198
199
	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];
200
	char		class[TBDB_FLEN_NODECLASS];
201
202
        char		ptype[TBDB_FLEN_NODETYPE];	/* Of physnode */
	char		pclass[TBDB_FLEN_NODECLASS];	/* Of physnode */
203
204
	char		creator[TBDB_FLEN_UID];
	char		swapper[TBDB_FLEN_UID];
205
	char		syncserver[TBDB_FLEN_VNAME];	/* The vname */
206
	char		keyhash[TBDB_FLEN_PRIVKEY];
207
	char		eventkey[TBDB_FLEN_PRIVKEY];
208
	char		sfshostid[TBDB_FLEN_SFSHOSTID];
209
	char		testdb[TBDB_FLEN_TINYTEXT];
210
	char		sharing_mode[TBDB_FLEN_TINYTEXT];
211
212
	char            privkey[PRIVKEY_LEN+1];
	char            urn[URN_LEN+1];
213
} tmcdreq_t;
214
static int	iptonodeid(struct in_addr, tmcdreq_t *, char*);
215
216
static int	checkdbredirect(tmcdreq_t *);

217
218
219
220
221
#ifdef EVENTSYS
int			myevent_send(address_tuple_t address);
static event_handle_t	event_handle = NULL;
#endif

222
223
224
/*
 * Commands we support.
 */
225
226
#define COMMAND_PROTOTYPE(x) \
	static int \
227
	x(int sock, tmcdreq_t *reqp, char *rdata, int tcp, int vers)
228
229

COMMAND_PROTOTYPE(doreboot);
230
COMMAND_PROTOTYPE(donodeid);
231
232
233
234
COMMAND_PROTOTYPE(dostatus);
COMMAND_PROTOTYPE(doifconfig);
COMMAND_PROTOTYPE(doaccounts);
COMMAND_PROTOTYPE(dodelay);
235
COMMAND_PROTOTYPE(dolinkdelay);
236
237
238
239
240
241
242
243
244
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
245
COMMAND_PROTOTYPE(dosfshostid);
246
247
248
249
250
251
252
COMMAND_PROTOTYPE(doloadinfo);
COMMAND_PROTOTYPE(doreset);
COMMAND_PROTOTYPE(dorouting);
COMMAND_PROTOTYPE(dotrafgens);
COMMAND_PROTOTYPE(donseconfigs);
COMMAND_PROTOTYPE(dostate);
COMMAND_PROTOTYPE(docreator);
253
COMMAND_PROTOTYPE(dotunnels);
254
COMMAND_PROTOTYPE(dovnodelist);
255
COMMAND_PROTOTYPE(dosubnodelist);
256
COMMAND_PROTOTYPE(doisalive);
257
COMMAND_PROTOTYPE(doipodinfo);
258
259
COMMAND_PROTOTYPE(dontpinfo);
COMMAND_PROTOTYPE(dontpdrift);
260
COMMAND_PROTOTYPE(dojailconfig);
261
COMMAND_PROTOTYPE(doplabconfig);
262
263
COMMAND_PROTOTYPE(dosubconfig);
COMMAND_PROTOTYPE(doixpconfig);
264
COMMAND_PROTOTYPE(doslothdparams);
265
COMMAND_PROTOTYPE(doprogagents);
266
COMMAND_PROTOTYPE(dosyncserver);
267
COMMAND_PROTOTYPE(dokeyhash);
268
COMMAND_PROTOTYPE(doeventkey);
269
COMMAND_PROTOTYPE(dofullconfig);
270
271
COMMAND_PROTOTYPE(doroutelist);
COMMAND_PROTOTYPE(dorole);
272
COMMAND_PROTOTYPE(dorusage);
273
COMMAND_PROTOTYPE(dodoginfo);
Mike Hibler's avatar
Mike Hibler committed
274
COMMAND_PROTOTYPE(dohostkeys);
Mike Hibler's avatar
Mike Hibler committed
275
COMMAND_PROTOTYPE(dotmcctest);
276
COMMAND_PROTOTYPE(dofwinfo);
277
COMMAND_PROTOTYPE(dohostinfo);
278
COMMAND_PROTOTYPE(doemulabconfig);
279
COMMAND_PROTOTYPE(doeplabconfig);
280
COMMAND_PROTOTYPE(dolocalize);
281
COMMAND_PROTOTYPE(dorootpswd);
282
283
COMMAND_PROTOTYPE(dobooterrno);
COMMAND_PROTOTYPE(dobootlog);
Timothy Stack's avatar
   
Timothy Stack committed
284
COMMAND_PROTOTYPE(dobattery);
285
COMMAND_PROTOTYPE(dotopomap);
Timothy Stack's avatar
   
Timothy Stack committed
286
COMMAND_PROTOTYPE(douserenv);
Timothy Stack's avatar
   
Timothy Stack committed
287
288
COMMAND_PROTOTYPE(dotiptunnels);
COMMAND_PROTOTYPE(dorelayconfig);
289
COMMAND_PROTOTYPE(dotraceconfig);
290
COMMAND_PROTOTYPE(doltmap);
291
COMMAND_PROTOTYPE(doltpmap);
Kirk Webb's avatar
   
Kirk Webb committed
292
COMMAND_PROTOTYPE(doelvindport);
Kirk Webb's avatar
   
Kirk Webb committed
293
COMMAND_PROTOTYPE(doplabeventkeys);
294
COMMAND_PROTOTYPE(dointfcmap);
295
COMMAND_PROTOTYPE(domotelog);
296
COMMAND_PROTOTYPE(doportregister);
Leigh B. Stoller's avatar
Leigh B. Stoller committed
297
COMMAND_PROTOTYPE(dobootwhat);
298
299
COMMAND_PROTOTYPE(dotpmblob);
COMMAND_PROTOTYPE(dotpmpubkey);
300
COMMAND_PROTOTYPE(dotpmdummy);
301
COMMAND_PROTOTYPE(dodhcpdconf);
302
COMMAND_PROTOTYPE(dosecurestate);
303
COMMAND_PROTOTYPE(doquoteprep);
304
COMMAND_PROTOTYPE(doimagekey);
305

306
307
/*
 * The fullconfig slot determines what routines get called when pushing
308
 * out a full configuration. Physnodes get slightly different
309
 * than vnodes, and at some point we might want to distinguish different
310
311
312
313
314
 * types of vnodes (jailed, plab).
 */
#define FULLCONFIG_NONE		0x0
#define FULLCONFIG_PHYS		0x1
#define FULLCONFIG_VIRT		0x2
315
316
317
318
319
#define FULLCONFIG_ALL		(FULLCONFIG_PHYS|FULLCONFIG_VIRT)

/*
 * Flags encode a few other random properties of commands
 */
320
321
322
323
324
#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 */
325
#define F_REMREQSSL	0x20	/* remote nodes must connect with SSL */
326
#define F_REQTPM	0x40	/* require TPM on client */
327

328
329
struct command {
	char	*cmdname;
330
	int	fullconfig;
331
	int	flags;
332
	int    (*func)(int, tmcdreq_t *, char *, int, int);
333
} command_array[] = {
334
335
336
	{ "reboot",	  FULLCONFIG_NONE, 0, doreboot },
	{ "nodeid",	  FULLCONFIG_ALL,  0, donodeid },
	{ "status",	  FULLCONFIG_NONE, 0, dostatus },
337
	{ "ifconfig",	  FULLCONFIG_ALL,  F_ALLOCATED, doifconfig },
338
	{ "accounts",	  FULLCONFIG_ALL,  F_REMREQSSL, doaccounts },
339
340
341
342
343
344
345
346
	{ "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*/
347
	{ "startstat",	  FULLCONFIG_NONE, 0, dostartstat },
348
349
350
351
	{ "readycount",   FULLCONFIG_NONE, F_ALLOCATED, doreadycount },
	{ "ready",	  FULLCONFIG_NONE, F_ALLOCATED, doready },
	{ "mounts",	  FULLCONFIG_ALL,  F_ALLOCATED, domounts },
	{ "sfshostid",	  FULLCONFIG_NONE, F_ALLOCATED, dosfshostid },
352
353
	{ "loadinfo",	  FULLCONFIG_NONE, 0, doloadinfo},
	{ "reset",	  FULLCONFIG_NONE, 0, doreset},
354
355
356
357
	{ "routing",	  FULLCONFIG_ALL,  F_ALLOCATED, dorouting},
	{ "trafgens",	  FULLCONFIG_ALL,  F_ALLOCATED, dotrafgens},
	{ "nseconfigs",	  FULLCONFIG_ALL,  F_ALLOCATED, donseconfigs},
	{ "creator",	  FULLCONFIG_ALL,  F_ALLOCATED, docreator},
358
	{ "state",	  FULLCONFIG_NONE, 0, dostate},
359
	{ "tunnels",	  FULLCONFIG_ALL,  F_ALLOCATED, dotunnels},
360
	{ "vnodelist",	  FULLCONFIG_PHYS, 0, dovnodelist},
Timothy Stack's avatar
   
Timothy Stack committed
361
	{ "subnodelist",  FULLCONFIG_PHYS, 0, dosubnodelist},
362
	{ "isalive",	  FULLCONFIG_NONE, F_REMUDP|F_MINLOG, doisalive},
363
	{ "ipodinfo",	  FULLCONFIG_PHYS, 0, doipodinfo},
364
365
	{ "ntpinfo",	  FULLCONFIG_PHYS, 0, dontpinfo},
	{ "ntpdrift",	  FULLCONFIG_NONE, 0, dontpdrift},
366
367
	{ "jailconfig",	  FULLCONFIG_VIRT, F_ALLOCATED, dojailconfig},
	{ "plabconfig",	  FULLCONFIG_VIRT, F_ALLOCATED, doplabconfig},
Timothy Stack's avatar
   
Timothy Stack committed
368
	{ "subconfig",	  FULLCONFIG_NONE, 0, dosubconfig},
369
        { "sdparams",     FULLCONFIG_PHYS, 0, doslothdparams},
370
371
        { "programs",     FULLCONFIG_ALL,  F_ALLOCATED, doprogagents},
        { "syncserver",   FULLCONFIG_ALL,  F_ALLOCATED, dosyncserver},
372
        { "keyhash",      FULLCONFIG_ALL,  F_ALLOCATED|F_REMREQSSL, dokeyhash},
373
        { "eventkey",     FULLCONFIG_ALL,  F_ALLOCATED|F_REMREQSSL, doeventkey},
374
375
376
        { "fullconfig",   FULLCONFIG_NONE, F_ALLOCATED, dofullconfig},
        { "routelist",	  FULLCONFIG_PHYS, F_ALLOCATED, doroutelist},
        { "role",	  FULLCONFIG_PHYS, F_ALLOCATED, dorole},
377
        { "rusage",	  FULLCONFIG_NONE, F_REMUDP|F_MINLOG, dorusage},
378
        { "watchdoginfo", FULLCONFIG_ALL,  F_REMUDP|F_MINLOG, dodoginfo},
Mike Hibler's avatar
Mike Hibler committed
379
        { "hostkeys",     FULLCONFIG_NONE, 0, dohostkeys},
Mike Hibler's avatar
Mike Hibler committed
380
        { "tmcctest",     FULLCONFIG_NONE, F_MINLOG, dotmcctest},
381
        { "firewallinfo", FULLCONFIG_ALL,  0, dofwinfo},
382
        { "hostinfo",     FULLCONFIG_NONE, 0, dohostinfo},
383
	{ "emulabconfig", FULLCONFIG_NONE, F_ALLOCATED, doemulabconfig},
384
	{ "eplabconfig",  FULLCONFIG_NONE, F_ALLOCATED, doeplabconfig},
385
	{ "localization", FULLCONFIG_PHYS, 0, dolocalize},
386
	{ "rootpswd",     FULLCONFIG_NONE, F_REMREQSSL, dorootpswd},
387
388
	{ "booterrno",    FULLCONFIG_NONE, 0, dobooterrno},
	{ "bootlog",      FULLCONFIG_NONE, 0, dobootlog},
Timothy Stack's avatar
   
Timothy Stack committed
389
	{ "battery",      FULLCONFIG_NONE, F_REMUDP|F_MINLOG, dobattery},
390
	{ "topomap",      FULLCONFIG_NONE, F_MINLOG|F_ALLOCATED, dotopomap},
391
	{ "userenv",      FULLCONFIG_ALL,  F_ALLOCATED, douserenv},
Timothy Stack's avatar
   
Timothy Stack committed
392
	{ "tiptunnels",	  FULLCONFIG_ALL,  F_ALLOCATED, dotiptunnels},
393
	{ "traceinfo",	  FULLCONFIG_ALL,  F_ALLOCATED, dotraceconfig },
Kirk Webb's avatar
   
Kirk Webb committed
394
	{ "ltmap",        FULLCONFIG_NONE, F_MINLOG|F_ALLOCATED, doltmap},
395
	{ "ltpmap",       FULLCONFIG_NONE, F_MINLOG|F_ALLOCATED, doltpmap},
Kirk Webb's avatar
   
Kirk Webb committed
396
	{ "elvindport",   FULLCONFIG_NONE, 0, doelvindport},
397
	{ "plabeventkeys",FULLCONFIG_NONE, F_REMREQSSL, doplabeventkeys},
398
	{ "intfcmap",     FULLCONFIG_NONE, 0, dointfcmap},
399
400
	{ "motelog",      FULLCONFIG_ALL,  F_ALLOCATED, domotelog},
	{ "portregister", FULLCONFIG_NONE, F_REMNOSSL, doportregister},
Leigh B. Stoller's avatar
Leigh B. Stoller committed
401
	{ "bootwhat",	  FULLCONFIG_NONE, 0, dobootwhat },
402
403
	{ "tpmblob",	  FULLCONFIG_ALL, 0, dotpmblob },
	{ "tpmpubkey",	  FULLCONFIG_ALL, 0, dotpmpubkey },
404
	{ "tpmdummy",	  FULLCONFIG_ALL, F_REQTPM, dotpmdummy },
405
	{ "dhcpdconf",	  FULLCONFIG_ALL, 0, dodhcpdconf },
406
407
	{ "securestate",  FULLCONFIG_NONE, F_REMREQSSL, dosecurestate},
	{ "quoteprep",    FULLCONFIG_NONE, F_REMREQSSL, doquoteprep},
408
	{ "imagekey",     FULLCONFIG_NONE, F_REQTPM, doimagekey},
409
410
411
};
static int numcommands = sizeof(command_array)/sizeof(struct command);

412
char *usagestr =
413
414
415
 "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"
416
 " -c num	   Specify number of servers (must be %d <= x <= %d)\n"
417
 " -v              More verbose logging\n"
418
 " -i ipaddr       Sets the boss IP addr to return (for multi-homed servers)\n"
419
420
421
422
423
 "\n";

void
usage()
{
424
	fprintf(stderr, usagestr, MINCHILDREN, MAXCHILDREN);
425
426
427
	exit(1);
}

428
429
430
431
432
static void
cleanup()
{
	signal(SIGHUP, SIG_IGN);
	killme = 1;
433
	killpg(0, SIGHUP);
Leigh B Stoller's avatar
Leigh B Stoller committed
434
	unlink(pidfile);
435
436
}

437
438
439
440
static void
setverbose(int sig)
{
	signal(sig, SIG_IGN);
441

442
443
444
445
	if (sig == SIGUSR1)
		verbose = 1;
	else
		verbose = 0;
446
447
	info("verbose logging turned %s\n", verbose ? "on" : "off");

448
449
450
451
452
453
	/* Just the parent sends this */
	if (numchildren)
		killpg(0, sig);
	signal(sig, setverbose);
}

Mike Hibler's avatar
Mike Hibler committed
454
455
int
main(int argc, char **argv)
456
{
457
	int			tcpsock, udpsock, i, ch;
458
459
460
	int			alttcpsock, altudpsock;
	int			status, pid;
	int			portnum = TBSERVER_PORT;
461
462
	FILE			*fp;
	char			buf[BUFSIZ];
463
	struct hostent		*he;
464
	extern char		build_info[];
465
466
467
468
469
	int			server_counts[4]; /* udp,tcp,altudp,alttcp */
	struct {
		int	pid;
		int	which;
	} servers[MAXCHILDREN];
470

471
	while ((ch = getopt(argc, argv, "dp:c:Xvi:")) != -1)
472
473
474
475
476
477
		switch(ch) {
		case 'p':
			portnum = atoi(optarg);
			break;
		case 'd':
			debug++;
478
			break;
479
480
481
		case 'c':
			maxchildren = atoi(optarg);
			break;
482
483
484
		case 'X':
			insecure = 1;
			break;
485
486
487
		case 'v':
			verbose++;
			break;
488
489
490
491
492
493
494
		case 'i':
			if (inet_aton(optarg, &myipaddr) == 0) {
				fprintf(stderr, "invalid IP address %s\n",
					optarg);
				usage();
			}
			break;
495
496
497
498
499
500
501
502
503
504
		case 'h':
		case '?':
		default:
			usage();
		}
	argc -= optind;
	argv += optind;

	if (argc)
		usage();
505
506
	if (maxchildren < MINCHILDREN || maxchildren > MAXCHILDREN)
		usage();
507

508
509
510
511
512
513
#ifdef  WITHSSL
	if (tmcd_server_sslinit()) {
		error("SSL init failed!\n");
		exit(1);
	}
#endif
514
	if (debug)
515
516
517
		loginit(0, 0);
	else {
		/* Become a daemon */
518
519
520
521
522
		if (chdir(TBCOREDIR)) {
			daemon(0, 0);
		} else {
			daemon(1, 0);
		}
523
524
525
526
		loginit(1, "tmcd");
	}
	info("daemon starting (version %d)\n", CURRENT_VERSION);
	info("%s\n", build_info);
Mike Hibler's avatar
Mike Hibler committed
527

Austin Clements's avatar
Austin Clements committed
528
529
530
531
532
533
534
535
536
537
538
539
540
541
	/*
	 * 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;
542
543
544
				if (debug) {
				    info("fshostid: %s\n", fshostid);
				}
Austin Clements's avatar
Austin Clements committed
545
546
547
548
549
550
551
552
			}
			else {
				error("fshostid from %s may be corrupt: %s",
				      FSHOSTID, fshostid);
			}
			fclose(fp);
		}
	}
553

554
555
556
	/*
	 * Grab our IP for security check below.
	 */
557
	if (myipaddr.s_addr == 0) {
558
#ifdef	LBS
559
		strcpy(buf, BOSSNODE);
560
#else
561
562
		if (gethostname(buf, sizeof(buf)) < 0)
			pfatal("getting hostname");
563
#endif
564
565
566
567
568
569
		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);
570
571
	}

572
	/*
573
	 * If we were given a port on the command line, don't open the
574
575
576
577
	 * alternate ports
	 */
	if (portnum != TBSERVER_PORT) {
	    if (makesockets(portnum, &udpsock, &tcpsock) < 0) {
578
579
		error("Could not make sockets!");
		exit(1);
580
	    }
581
	    num_alttcpservers = num_altudpservers = 0;
582
583
584
585
586
587
	} else {
	    if (makesockets(portnum, &udpsock, &tcpsock) < 0 ||
		makesockets(TBSERVER_PORT2, &altudpsock, &alttcpsock) < 0) {
		    error("Could not make sockets!");
		    exit(1);
	    }
588
589
590
591
592
	}

	signal(SIGTERM, cleanup);
	signal(SIGINT, cleanup);
	signal(SIGHUP, cleanup);
593
594
	signal(SIGUSR1, setverbose);
	signal(SIGUSR2, setverbose);
595
596
597
598

	/*
	 * Stash the pid away.
	 */
599
	mypid = getpid();
Leigh B Stoller's avatar
Leigh B Stoller committed
600
601
	sprintf(pidfile, "%s/tmcd.pid", _PATH_VARRUN);
	fp = fopen(pidfile, "w");
602
	if (fp != NULL) {
603
		fprintf(fp, "%d\n", mypid);
604
605
606
		(void) fclose(fp);
	}

607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
	/*
	 * 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);
	}

640
641
	/*
	 * Now fork a set of children to handle requests. We keep the
642
643
644
645
646
647
648
	 * 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!
649
	 */
650
651
652
653
654
655
	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));
656

657
658
	while (1) {
		while (!killme && numchildren < maxchildren) {
659
			int which = 3;
660

661
662
663
664
665
666
667
668
			/*
			 * Find which kind of server is short one.
			 */
			for (i = 0; i < 4; i++) {
				if (server_counts[i]) {
					which = i;
					break;
				}
669
			}
670

671
672
673
674
675
			if ((pid = fork()) < 0) {
				errorc("forking server");
				goto done;
			}
			if (pid) {
676
677
678
679
680
681
682
683
684
685
				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;
686
687
688
				numchildren++;
				continue;
			}
689
690
691
			/* Poor way of knowing parent/child */
			numchildren = 0;
			mypid = getpid();
692

693
694
695
696
			/* Child does useful work! Never Returns! */
			signal(SIGTERM, SIG_DFL);
			signal(SIGINT, SIG_DFL);
			signal(SIGHUP, SIG_DFL);
697

698
			switch (which) {
699
			case 0: udpserver(udpsock, portnum);
700
				break;
701
			case 1: udpserver(altudpsock, TBSERVER_PORT2);
702
				break;
703
			case 2: tcpserver(alttcpsock, TBSERVER_PORT2);
704
				break;
705
			case 3: tcpserver(tcpsock, portnum);
706
707
708
709
710
711
712
713
714
715
716
717
				break;
			}
			exit(-1);
		}

		/*
		 * Parent waits.
		 */
		pid = waitpid(-1, &status, 0);
		if (pid < 0) {
			errorc("waitpid failed");
			continue;
718
		}
719
720
721
		if (WIFSIGNALED(status)) {
			error("server %d exited with signal %d!\n",
			      pid, WTERMSIG(status));
722
		}
723
724
		else if (WIFEXITED(status)) {
			error("server %d exited with status %d!\n",
725
			      pid, WEXITSTATUS(status));
726
		}
727
		numchildren--;
728
729
730
731
732
733
734
735
736
737

		/*
		 * 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;
			}
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
		}
		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;
Mike Hibler's avatar
lint    
Mike Hibler committed
756
757
	socklen_t		length;
	int			i, udpsock, tcpsock;
758

Mike Hibler's avatar
Mike Hibler committed
759
	/*
760
	 * Setup TCP socket for incoming connections.
Mike Hibler's avatar
Mike Hibler committed
761
762
	 */

763
	/* Create socket from which to read. */
Mike Hibler's avatar
Mike Hibler committed
764
765
	tcpsock = socket(AF_INET, SOCK_STREAM, 0);
	if (tcpsock < 0) {
766
		pfatal("opening stream socket");
767
768
	}

769
	i = 1;
Mike Hibler's avatar
Mike Hibler committed
770
	if (setsockopt(tcpsock, SOL_SOCKET, SO_REUSEADDR,
771
		       (char *)&i, sizeof(i)) < 0)
772
		pwarning("setsockopt(SO_REUSEADDR)");;
773

774
775
776
	/* Create name. */
	name.sin_family = AF_INET;
	name.sin_addr.s_addr = INADDR_ANY;
777
	name.sin_port = htons((u_short) portnum);
Mike Hibler's avatar
Mike Hibler committed
778
	if (bind(tcpsock, (struct sockaddr *) &name, sizeof(name))) {
779
		pfatal("binding stream socket");
780
781
782
	}
	/* Find assigned port value and print it out. */
	length = sizeof(name);
Mike Hibler's avatar
Mike Hibler committed
783
	if (getsockname(tcpsock, (struct sockaddr *) &name, &length)) {
784
		pfatal("getsockname");
785
	}
786
	if (listen(tcpsock, 128) < 0) {
787
		pfatal("listen");
788
	}
789
	info("listening on TCP port %d\n", ntohs(name.sin_port));
790

Mike Hibler's avatar
Mike Hibler committed
791
792
793
794
795
796
797
	/*
	 * Setup UDP socket
	 */

	/* Create socket from which to read. */
	udpsock = socket(AF_INET, SOCK_DGRAM, 0);
	if (udpsock < 0) {
798
		pfatal("opening dgram socket");
Mike Hibler's avatar
Mike Hibler committed
799
800
801
802
803
	}

	i = 1;
	if (setsockopt(udpsock, SOL_SOCKET, SO_REUSEADDR,
		       (char *)&i, sizeof(i)) < 0)
804
		pwarning("setsockopt(SO_REUSEADDR)");;
805
806
807
808

	i = 128 * 1024;
	if (setsockopt(udpsock, SOL_SOCKET, SO_RCVBUF, &i, sizeof(i)) < 0)
		pwarning("setsockopt(SO_RCVBUF)");
809

Mike Hibler's avatar
Mike Hibler committed
810
811
812
	/* Create name. */
	name.sin_family = AF_INET;
	name.sin_addr.s_addr = INADDR_ANY;
813
	name.sin_port = htons((u_short) portnum);
Mike Hibler's avatar
Mike Hibler committed
814
	if (bind(udpsock, (struct sockaddr *) &name, sizeof(name))) {
815
		pfatal("binding dgram socket");
Mike Hibler's avatar
Mike Hibler committed
816
817
818
819
820
	}

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

825
826
827
	*tcpsockp = tcpsock;
	*udpsockp = udpsock;
	return 0;
828
}
829

830
/*
831
 * Listen for UDP requests. This is not a secure channel, and so this should
832
833
834
 * eventually be killed off.
 */
static void
835
udpserver(int sock, int portnum)
836
837
838
{
	char			buf[MYBUFSIZE];
	struct sockaddr_in	client;
Mike Hibler's avatar
lint    
Mike Hibler committed
839
840
	socklen_t		length;
	int			cc;
841
	unsigned int		nreq = 0;
842

843
844
	info("udpserver starting: pid=%d sock=%d portnum=%d\n",
	     mypid, sock, portnum);
845
846
847
848
849

	/*
	 * Wait for udp connections.
	 */
	while (1) {
850
		setproctitle("UDP %d: %u done", portnum, nreq);
851
		length = sizeof(client);
852
		cc = recvfrom(sock, buf, sizeof(buf) - 1,
853
854
855
			      0, (struct sockaddr *)&client, &length);
		if (cc <= 0) {
			if (cc < 0)
856
857
				errorc("Reading UDP request");
			error("UDP Connection aborted\n");
858
			continue;
859
		}
860
861
		buf[cc] = '\0';
		handle_request(sock, &client, buf, 0);
862
		nreq++;
863
864
865
866
	}
	exit(1);
}

867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
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;
}

893
/*
894
 * Listen for TCP requests.
895
896
 */
static void
897
tcpserver(int sock, int portnum)
898
{
899
	char			buf[MAXTMCDPACKET];
900
	struct sockaddr_in	client;
Mike Hibler's avatar
lint    
Mike Hibler committed
901
902
	socklen_t		length;
	int			cc, newsock;
903
	unsigned int		nreq = 0;
904
	struct timeval		tv;
905

906
907
	info("tcpserver starting: pid=%d sock=%d portnum=%d\n",
	     mypid, sock, portnum);
908
909
910
911
912

	/*
	 * Wait for TCP connections.
	 */
	while (1) {
913
		setproctitle("TCP %d: %u done", portnum, nreq);
914
		length  = sizeof(client);
915
916
		newsock = ACCEPT(sock, (struct sockaddr *)&client, &length,
				 readtimo);
917
		if (newsock < 0) {
918
			errorc("accepting TCP connection");
919
			continue;
920
		}
Mike Hibler's avatar
Mike Hibler committed
921

922
923
924
925
926
927
928
929
930
931
932
933
934
935
		/*
		 * 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
936
		/*
937
		 * Read in the command request.
Mike Hibler's avatar
Mike Hibler committed
938
		 */
939
		if ((cc = READ(newsock, buf, sizeof(buf) - 1)) <= 0) {
940
941
942
943
944
945
			if (cc < 0) {
				if (errno == EWOULDBLOCK)
					errorc("Timeout reading TCP request");
				else
					errorc("Error reading TCP request");
			}
946
947
			error("TCP connection aborted\n");
			CLOSE(newsock);
948
			continue;
949
		}
950
951
		buf[cc] = '\0';
		handle_request(newsock, &client, buf, 1);
952
		CLOSE(newsock);
953
		nreq++;
954
955
956
	}
	exit(1);
}
Mike Hibler's avatar
Mike Hibler committed
957

958
959
960
//#define error(x...)	fprintf(stderr, ##x)
//#define info(x...)	fprintf(stderr, ##x)

961
962
963
964
static int
handle_request(int sock, struct sockaddr_in *client, char *rdata, int istcp)
{
	struct sockaddr_in redirect_client;
965
	int		   redirect = 0;
966
	char		   buf[BUFSIZ], *bp, *cp;
967
968
	char		   privkeybuf[PRIVKEY_LEN];
	char		   *privkey = (char *) NULL;
969
	int		   i, overbose = 0, err = 0;
970
	int		   version = DEFAULT_VERSION;
971
972
	tmcdreq_t	   tmcdreq, *reqp = &tmcdreq;

973
	byteswritten = 0;
974
975
976
977
978
#ifdef	WITHSSL
	cp = (istcp ? (isssl ? "SSL" : "TCP") : "UDP");
#else
	cp = (istcp ? "TCP" : "UDP");
#endif
979
	setproctitle("%s: %s", inet_ntoa(client->sin_addr), cp);
980

981
982
983
984
	/*
	 * Init the req structure.
	 */
	bzero(reqp, sizeof(*reqp));
Mike Hibler's avatar
Mike Hibler committed
985

986
	/*
987
	 * Look for special tags.
988
	 */
989
	bp = rdata;
990
	while ((bp = strsep(&rdata, " ")) != NULL) {
991
		/*
992
		 * Look for PRIVKEY.
993
		 */
994
995
996
997
998
		if (sscanf(bp, "PRIVKEY=%" XSTRINGIFY(PRIVKEY_LEN) "s", buf)) {
			if (strlen(buf) < 16) {
				info("tmcd client provided short privkey");
				goto skipit;
			}
999
1000
1001
1002
1003
1004
1005
			for (i = 0; i < strlen(buf); i++){
				if (! isxdigit(buf[i])) {
					info("tmcd client provided invalid "
					     "characters in privkey");
					goto skipit;
				}
			}
1006
1007
			strncpy(privkeybuf, buf, sizeof(privkeybuf));
			privkey = privkeybuf;
1008
1009

			if (debug) {
1010
				info("%s: PRIVKEY %s\n", reqp->nodeid, buf);
1011
1012
1013
1014
1015
			}
			continue;
		}


1016
		/*
1017
		 * Look for VERSION.
1018
1019
		 * Check for clients that are newer than the server
		 * and complain.
1020
1021
1022
		 */
		if (sscanf(bp, "VERSION=%d", &i) == 1) {
			version = i;
1023
			if (version > CURRENT_VERSION) {
Mike Hibler's avatar
nits:    
Mike Hibler committed
1024
1025
				error("version skew on request from %s: "
				      "server=%d, request=%d, "
1026
				      "old TMCD installed?\n",
Mike Hibler's avatar
nits:    
Mike Hibler committed
1027
				      inet_ntoa(client->sin_addr),
1028
1029
				      CURRENT_VERSION, version);
			}
1030
1031
			continue;
		}
Mike Hibler's avatar
Mike Hibler committed
1032

1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
		/*
		 * 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);
1043

1044
1045
			info("REDIRECTED from %s to %s\n",
			     inet_ntoa(redirect_client.sin_addr), buf);
1046

1047
1048
			continue;
		}
1049

1050
1051
1052
1053
1054
1055
		/*
		 * 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.
		 */
		if (sscanf(bp, "VNODEID=%30s", buf)) {
1056
1057
1058
1059
1060
1061
1062
1063
			for (i = 0; i < strlen(buf); i++){
				if (! (isalnum(buf[i]) ||
				       buf[i] == '_' || buf[i] == '-')) {
					info("tmcd client provided invalid "
					     "characters in vnodeid");
					goto skipit;
				}
			}