tmcd.c 203 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 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
#define PROTOUSER	"elabman"
71
72
73
74
#define PRIVKEY_LEN	128
#define URN_LEN		128
#define XSTRINGIFY(s)   STRINGIFY(s)
#define STRINGIFY(s)	#s
75

76
77
78
79
/* socket read/write timeouts in ms */
#define READTIMO	3000
#define WRITETIMO	3000

80
#define TESTMODE
81
82
83
84
85
86
87
88
89
90
91
92
#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) */
93

94
95
96
#define DISKTYPE	"ad"
#define DISKNUM		0

97
98
/* Compiled in slothd parameters
 *
99
 * 1 - reg_interval  2 - agg_interval  3 - load_thresh
100
101
102
103
 * 4 - expt_thresh   5 - ctl_thresh
 */
#define SDPARAMS        "reg=300 agg=5 load=1 expt=5 ctl=1000"

104
105
/* Defined in configure and passed in via the makefile */
#define DBNAME_SIZE	64
Austin Clements's avatar
Austin Clements committed
106
#define HOSTID_SIZE	(32+64)
107
108
#define DEFAULT_DBNAME	TBDBNAME

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

128
129
130
131
/* socket timeouts */
static int	readtimo = READTIMO;
static int	writetimo = WRITETIMO;

132
/* thread support */
133
#define MAXCHILDREN	20
134
#define MINCHILDREN	8
135
static int	numchildren;
136
137
138
139
static int	maxchildren       = 13;
static int      num_udpservers    = 3;
static int      num_altudpservers = 1;
static int      num_alttcpservers = 1;
140
static int	mypid;
141
static volatile int killme;
142

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

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

204
205
206
207
208
#ifdef EVENTSYS
int			myevent_send(address_tuple_t address);
static event_handle_t	event_handle = NULL;
#endif

209
210
211
/*
 * Commands we support.
 */
212
213
#define COMMAND_PROTOTYPE(x) \
	static int \
214
	x(int sock, tmcdreq_t *reqp, char *rdata, int tcp, int vers)
215
216

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

290
291
/*
 * The fullconfig slot determines what routines get called when pushing
292
 * out a full configuration. Physnodes get slightly different
293
 * than vnodes, and at some point we might want to distinguish different
294
295
296
297
298
 * types of vnodes (jailed, plab).
 */
#define FULLCONFIG_NONE		0x0
#define FULLCONFIG_PHYS		0x1
#define FULLCONFIG_VIRT		0x2
299
300
301
302
303
#define FULLCONFIG_ALL		(FULLCONFIG_PHYS|FULLCONFIG_VIRT)

/*
 * Flags encode a few other random properties of commands
 */
304
305
306
307
308
#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 */
309
#define F_REMREQSSL	0x20	/* remote nodes must connect with SSL */
310
#define F_REQTPM	0x40	/* require TPM on client */
311

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

393
char *usagestr =
394
395
396
 "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"
397
 " -c num	   Specify number of servers (must be %d <= x <= %d)\n"
398
 " -v              More verbose logging\n"
399
 " -i ipaddr       Sets the boss IP addr to return (for multi-homed servers)\n"
400
401
402
403
404
 "\n";

void
usage()
{
405
	fprintf(stderr, usagestr, MINCHILDREN, MAXCHILDREN);
406
407
408
	exit(1);
}

409
410
411
412
413
static void
cleanup()
{
	signal(SIGHUP, SIG_IGN);
	killme = 1;
414
	killpg(0, SIGHUP);
415
416
}

417
418
419
420
static void
setverbose(int sig)
{
	signal(sig, SIG_IGN);
421

422
423
424
425
	if (sig == SIGUSR1)
		verbose = 1;
	else
		verbose = 0;
426
427
	info("verbose logging turned %s\n", verbose ? "on" : "off");

428
429
430
431
432
433
	/* Just the parent sends this */
	if (numchildren)
		killpg(0, sig);
	signal(sig, setverbose);
}

Mike Hibler's avatar
Mike Hibler committed
434
435
int
main(int argc, char **argv)
436
{
437
	int			tcpsock, udpsock, i, ch;
438
439
440
	int			alttcpsock, altudpsock;
	int			status, pid;
	int			portnum = TBSERVER_PORT;
441
442
	FILE			*fp;
	char			buf[BUFSIZ];
443
	struct hostent		*he;
444
	extern char		build_info[];
445
446
447
448
449
	int			server_counts[4]; /* udp,tcp,altudp,alttcp */
	struct {
		int	pid;
		int	which;
	} servers[MAXCHILDREN];
450

451
	while ((ch = getopt(argc, argv, "dp:c:Xvi:")) != -1)
452
453
454
455
456
457
		switch(ch) {
		case 'p':
			portnum = atoi(optarg);
			break;
		case 'd':
			debug++;
458
			break;
459
460
461
		case 'c':
			maxchildren = atoi(optarg);
			break;
462
463
464
		case 'X':
			insecure = 1;
			break;
465
466
467
		case 'v':
			verbose++;
			break;
468
469
470
471
472
473
474
		case 'i':
			if (inet_aton(optarg, &myipaddr) == 0) {
				fprintf(stderr, "invalid IP address %s\n",
					optarg);
				usage();
			}
			break;
475
476
477
478
479
480
481
482
483
484
		case 'h':
		case '?':
		default:
			usage();
		}
	argc -= optind;
	argv += optind;

	if (argc)
		usage();
485
486
	if (maxchildren < MINCHILDREN || maxchildren > MAXCHILDREN)
		usage();
487

488
489
490
491
492
493
#ifdef  WITHSSL
	if (tmcd_server_sslinit()) {
		error("SSL init failed!\n");
		exit(1);
	}
#endif
494
	if (debug)
495
496
497
498
499
500
501
502
		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
503

Austin Clements's avatar
Austin Clements committed
504
505
506
507
508
509
510
511
512
513
514
515
516
517
	/*
	 * 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;
518
519
520
				if (debug) {
				    info("fshostid: %s\n", fshostid);
				}
Austin Clements's avatar
Austin Clements committed
521
522
523
524
525
526
527
528
			}
			else {
				error("fshostid from %s may be corrupt: %s",
				      FSHOSTID, fshostid);
			}
			fclose(fp);
		}
	}
529

530
531
532
	/*
	 * Grab our IP for security check below.
	 */
533
	if (myipaddr.s_addr == 0) {
534
#ifdef	LBS
535
		strcpy(buf, BOSSNODE);
536
#else
537
538
		if (gethostname(buf, sizeof(buf)) < 0)
			pfatal("getting hostname");
539
#endif
540
541
542
543
544
545
		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);
546
547
	}

548
	/*
549
	 * If we were given a port on the command line, don't open the
550
551
552
553
	 * alternate ports
	 */
	if (portnum != TBSERVER_PORT) {
	    if (makesockets(portnum, &udpsock, &tcpsock) < 0) {
554
555
		error("Could not make sockets!");
		exit(1);
556
	    }
557
	    num_alttcpservers = num_altudpservers = 0;
558
559
560
561
562
563
	} else {
	    if (makesockets(portnum, &udpsock, &tcpsock) < 0 ||
		makesockets(TBSERVER_PORT2, &altudpsock, &alttcpsock) < 0) {
		    error("Could not make sockets!");
		    exit(1);
	    }
564
565
566
567
568
	}

	signal(SIGTERM, cleanup);
	signal(SIGINT, cleanup);
	signal(SIGHUP, cleanup);
569
570
	signal(SIGUSR1, setverbose);
	signal(SIGUSR2, setverbose);
571
572
573
574

	/*
	 * Stash the pid away.
	 */
575
	mypid = getpid();
576
577
578
	sprintf(buf, "%s/tmcd.pid", _PATH_VARRUN);
	fp = fopen(buf, "w");
	if (fp != NULL) {
579
		fprintf(fp, "%d\n", mypid);
580
581
582
		(void) fclose(fp);
	}

583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
	/*
	 * 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);
	}

616
617
	/*
	 * Now fork a set of children to handle requests. We keep the
618
619
620
621
622
623
624
	 * 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!
625
	 */
626
627
628
629
630
631
	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));
632

633
634
	while (1) {
		while (!killme && numchildren < maxchildren) {
635
			int which = 3;
636

637
638
639
640
641
642
643
644
			/*
			 * Find which kind of server is short one.
			 */
			for (i = 0; i < 4; i++) {
				if (server_counts[i]) {
					which = i;
					break;
				}
645
			}
646

647
648
649
650
651
			if ((pid = fork()) < 0) {
				errorc("forking server");
				goto done;
			}
			if (pid) {
652
653
654
655
656
657
658
659
660
661
				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;
662
663
664
				numchildren++;
				continue;
			}
665
666
667
			/* Poor way of knowing parent/child */
			numchildren = 0;
			mypid = getpid();
668

669
670
671
672
			/* Child does useful work! Never Returns! */
			signal(SIGTERM, SIG_DFL);
			signal(SIGINT, SIG_DFL);
			signal(SIGHUP, SIG_DFL);
673

674
			switch (which) {
675
			case 0: udpserver(udpsock, portnum);
676
				break;
677
			case 1: udpserver(altudpsock, TBSERVER_PORT2);
678
				break;
679
			case 2: tcpserver(alttcpsock, TBSERVER_PORT2);
680
				break;
681
			case 3: tcpserver(tcpsock, portnum);
682
683
684
685
686
687
688
689
690
691
692
693
				break;
			}
			exit(-1);
		}

		/*
		 * Parent waits.
		 */
		pid = waitpid(-1, &status, 0);
		if (pid < 0) {
			errorc("waitpid failed");
			continue;
694
		}
695
696
697
		if (WIFSIGNALED(status)) {
			error("server %d exited with signal %d!\n",
			      pid, WTERMSIG(status));
698
		}
699
700
		else if (WIFEXITED(status)) {
			error("server %d exited with status %d!\n",
701
			      pid, WEXITSTATUS(status));
702
		}
703
		numchildren--;
704
705
706
707
708
709
710
711
712
713

		/*
		 * 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;
			}
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
		}
		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
732
733
	socklen_t		length;
	int			i, udpsock, tcpsock;
734

Mike Hibler's avatar
Mike Hibler committed
735
	/*
736
	 * Setup TCP socket for incoming connections.
Mike Hibler's avatar
Mike Hibler committed
737
738
	 */

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

745
	i = 1;
Mike Hibler's avatar
Mike Hibler committed
746
	if (setsockopt(tcpsock, SOL_SOCKET, SO_REUSEADDR,
747
		       (char *)&i, sizeof(i)) < 0)
748
		pwarning("setsockopt(SO_REUSEADDR)");;
749

750
751
752
	/* Create name. */
	name.sin_family = AF_INET;
	name.sin_addr.s_addr = INADDR_ANY;
753
	name.sin_port = htons((u_short) portnum);
Mike Hibler's avatar
Mike Hibler committed
754
	if (bind(tcpsock, (struct sockaddr *) &name, sizeof(name))) {
755
		pfatal("binding stream socket");
756
757
758
	}
	/* Find assigned port value and print it out. */
	length = sizeof(name);
Mike Hibler's avatar
Mike Hibler committed
759
	if (getsockname(tcpsock, (struct sockaddr *) &name, &length)) {
760
		pfatal("getsockname");
761
	}
762
	if (listen(tcpsock, 128) < 0) {
763
		pfatal("listen");
764
	}
765
	info("listening on TCP port %d\n", ntohs(name.sin_port));
766

Mike Hibler's avatar
Mike Hibler committed
767
768
769
770
771
772
773
	/*
	 * Setup UDP socket
	 */

	/* Create socket from which to read. */
	udpsock = socket(AF_INET, SOCK_DGRAM, 0);
	if (udpsock < 0) {
774
		pfatal("opening dgram socket");
Mike Hibler's avatar
Mike Hibler committed
775
776
777
778
779
	}

	i = 1;
	if (setsockopt(udpsock, SOL_SOCKET, SO_REUSEADDR,
		       (char *)&i, sizeof(i)) < 0)
780
		pwarning("setsockopt(SO_REUSEADDR)");;
781
782
783
784

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

Mike Hibler's avatar
Mike Hibler committed
786
787
788
	/* Create name. */
	name.sin_family = AF_INET;
	name.sin_addr.s_addr = INADDR_ANY;
789
	name.sin_port = htons((u_short) portnum);
Mike Hibler's avatar
Mike Hibler committed
790
	if (bind(udpsock, (struct sockaddr *) &name, sizeof(name))) {
791
		pfatal("binding dgram socket");
Mike Hibler's avatar
Mike Hibler committed
792
793
794
795
796
	}

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

801
802
803
	*tcpsockp = tcpsock;
	*udpsockp = udpsock;
	return 0;
804
}
805

806
/*
807
 * Listen for UDP requests. This is not a secure channel, and so this should
808
809
810
 * eventually be killed off.
 */
static void
811
udpserver(int sock, int portnum)
812
813
814
{
	char			buf[MYBUFSIZE];
	struct sockaddr_in	client;
Mike Hibler's avatar
lint    
Mike Hibler committed
815
816
	socklen_t		length;
	int			cc;
817
	unsigned int		nreq = 0;
818

819
820
	info("udpserver starting: pid=%d sock=%d portnum=%d\n",
	     mypid, sock, portnum);
821
822
823
824
825

	/*
	 * Wait for udp connections.
	 */
	while (1) {
826
		setproctitle("UDP %d: %u done", portnum, nreq);
827
		length = sizeof(client);
828
		cc = recvfrom(sock, buf, sizeof(buf) - 1,
829
830
831
			      0, (struct sockaddr *)&client, &length);
		if (cc <= 0) {
			if (cc < 0)
832
833
				errorc("Reading UDP request");
			error("UDP Connection aborted\n");
834
			continue;
835
		}
836
837
		buf[cc] = '\0';
		handle_request(sock, &client, buf, 0);
838
		nreq++;
839
840
841
842
	}
	exit(1);
}

843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
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;
}

869
/*
870
 * Listen for TCP requests.
871
872
 */
static void
873
tcpserver(int sock, int portnum)
874
{
875
	char			buf[MAXTMCDPACKET];
876
	struct sockaddr_in	client;
Mike Hibler's avatar
lint    
Mike Hibler committed
877
878
	socklen_t		length;
	int			cc, newsock;
879
	unsigned int		nreq = 0;
880
	struct timeval		tv;
881

882
883
	info("tcpserver starting: pid=%d sock=%d portnum=%d\n",
	     mypid, sock, portnum);
884
885
886
887
888

	/*
	 * Wait for TCP connections.
	 */
	while (1) {
889
		setproctitle("TCP %d: %u done", portnum, nreq);
890
		length  = sizeof(client);
891
892
		newsock = ACCEPT(sock, (struct sockaddr *)&client, &length,
				 readtimo);
893
		if (newsock < 0) {
894
			errorc("accepting TCP connection");
895
			continue;
896
		}
Mike Hibler's avatar
Mike Hibler committed
897

898
899
900
901
902
903
904
905
906
907
908
909
910
911
		/*
		 * 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
912
		/*
913
		 * Read in the command request.
Mike Hibler's avatar
Mike Hibler committed
914
		 */
915
		if ((cc = READ(newsock, buf, sizeof(buf) - 1)) <= 0) {
916
917
918
919
920
921
			if (cc < 0) {
				if (errno == EWOULDBLOCK)
					errorc("Timeout reading TCP request");
				else
					errorc("Error reading TCP request");
			}
922
923
			error("TCP connection aborted\n");
			CLOSE(newsock);
924
			continue;
925
		}
926
927
		buf[cc] = '\0';
		handle_request(newsock, &client, buf, 1);
928
		CLOSE(newsock);
929
		nreq++;
930
931
932
	}
	exit(1);
}
Mike Hibler's avatar
Mike Hibler committed
933

934
935
936
//#define error(x...)	fprintf(stderr, ##x)
//#define info(x...)	fprintf(stderr, ##x)

937
938
939
940
static int
handle_request(int sock, struct sockaddr_in *client, char *rdata, int istcp)
{
	struct sockaddr_in redirect_client;
941
	int		   redirect = 0;
942
	char		   buf[BUFSIZ], *bp, *cp;
943
944
	char		   privkeybuf[PRIVKEY_LEN];
	char		   *privkey = (char *) NULL;
945
	int		   i, overbose = 0, err = 0;
946
	int		   version = DEFAULT_VERSION;
947
948
	tmcdreq_t	   tmcdreq, *reqp = &tmcdreq;

949
	byteswritten = 0;
950
951
952
953
954
#ifdef	WITHSSL
	cp = (istcp ? (isssl ? "SSL" : "TCP") : "UDP");
#else
	cp = (istcp ? "TCP" : "UDP");
#endif
955
	setproctitle("%s: %s", inet_ntoa(client->sin_addr), cp);
956

957
958
959
960
	/*
	 * Init the req structure.
	 */
	bzero(reqp, sizeof(*reqp));
Mike Hibler's avatar
Mike Hibler committed
961

962
	/*
963
	 * Look for special tags.
964
	 */
965
	bp = rdata;
966
	while ((bp = strsep(&rdata, " ")) != NULL) {
967
		/*
968
		 * Look for PRIVKEY.
969
		 */
970
971
972
973
974
		if (sscanf(bp, "PRIVKEY=%" XSTRINGIFY(PRIVKEY_LEN) "s", buf)) {
			if (strlen(buf) < 16) {
				info("tmcd client provided short privkey");
				goto skipit;
			}
975
976
977
978
979
980
981
			for (i = 0; i < strlen(buf); i++){
				if (! isxdigit(buf[i])) {
					info("tmcd client provided invalid "
					     "characters in privkey");
					goto skipit;
				}
			}
982
983
			strncpy(privkeybuf, buf, sizeof(privkeybuf));
			privkey = privkeybuf;
984
985

			if (debug) {
986
				info("%s: PRIVKEY %s\n", reqp->nodeid, buf);