tmcd.c 205 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
80
/* XXX backward compat */
#ifndef TBCOREDIR
#define	TBCOREDIR	TBROOT "/tmp"
#endif

81
82
83
84
/* socket read/write timeouts in ms */
#define READTIMO	3000
#define WRITETIMO	3000

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

99
100
101
#define DISKTYPE	"ad"
#define DISKNUM		0

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

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

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

134
135
136
137
/* socket timeouts */
static int	readtimo = READTIMO;
static int	writetimo = WRITETIMO;

138
/* thread support */
139
#define MAXCHILDREN	20
140
#define MINCHILDREN	8
141
static int	numchildren;
142
143
144
145
static int	maxchildren       = 13;
static int      num_udpservers    = 3;
static int      num_altudpservers = 1;
static int      num_alttcpservers = 1;
146
static int	mypid;
147
static volatile int killme;
148

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

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

210
211
212
213
214
#ifdef EVENTSYS
int			myevent_send(address_tuple_t address);
static event_handle_t	event_handle = NULL;
#endif

215
216
217
/*
 * Commands we support.
 */
218
219
#define COMMAND_PROTOTYPE(x) \
	static int \
220
	x(int sock, tmcdreq_t *reqp, char *rdata, int tcp, int vers)
221
222

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

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

/*
 * Flags encode a few other random properties of commands
 */
310
311
312
313
314
#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 */
315
#define F_REMREQSSL	0x20	/* remote nodes must connect with SSL */
316
#define F_REQTPM	0x40	/* require TPM on client */
317

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

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

void
usage()
{
411
	fprintf(stderr, usagestr, MINCHILDREN, MAXCHILDREN);
412
413
414
	exit(1);
}

415
416
417
418
419
static void
cleanup()
{
	signal(SIGHUP, SIG_IGN);
	killme = 1;
420
	killpg(0, SIGHUP);
Leigh B Stoller's avatar
Leigh B Stoller committed
421
	unlink(pidfile);
422
423
}

424
425
426
427
static void
setverbose(int sig)
{
	signal(sig, SIG_IGN);
428

429
430
431
432
	if (sig == SIGUSR1)
		verbose = 1;
	else
		verbose = 0;
433
434
	info("verbose logging turned %s\n", verbose ? "on" : "off");

435
436
437
438
439
440
	/* Just the parent sends this */
	if (numchildren)
		killpg(0, sig);
	signal(sig, setverbose);
}

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

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

	if (argc)
		usage();
492
493
	if (maxchildren < MINCHILDREN || maxchildren > MAXCHILDREN)
		usage();
494

495
496
497
498
499
500
#ifdef  WITHSSL
	if (tmcd_server_sslinit()) {
		error("SSL init failed!\n");
		exit(1);
	}
#endif
501
	if (debug)
502
503
504
		loginit(0, 0);
	else {
		/* Become a daemon */
505
506
507
508
509
		if (chdir(TBCOREDIR)) {
			daemon(0, 0);
		} else {
			daemon(1, 0);
		}
510
511
512
513
		loginit(1, "tmcd");
	}
	info("daemon starting (version %d)\n", CURRENT_VERSION);
	info("%s\n", build_info);
Mike Hibler's avatar
Mike Hibler committed
514

Austin Clements's avatar
Austin Clements committed
515
516
517
518
519
520
521
522
523
524
525
526
527
528
	/*
	 * 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;
529
530
531
				if (debug) {
				    info("fshostid: %s\n", fshostid);
				}
Austin Clements's avatar
Austin Clements committed
532
533
534
535
536
537
538
539
			}
			else {
				error("fshostid from %s may be corrupt: %s",
				      FSHOSTID, fshostid);
			}
			fclose(fp);
		}
	}
540

541
542
543
	/*
	 * Grab our IP for security check below.
	 */
544
	if (myipaddr.s_addr == 0) {
545
#ifdef	LBS
546
		strcpy(buf, BOSSNODE);
547
#else
548
549
		if (gethostname(buf, sizeof(buf)) < 0)
			pfatal("getting hostname");
550
#endif
551
552
553
554
555
556
		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);
557
558
	}

559
	/*
560
	 * If we were given a port on the command line, don't open the
561
562
563
564
	 * alternate ports
	 */
	if (portnum != TBSERVER_PORT) {
	    if (makesockets(portnum, &udpsock, &tcpsock) < 0) {
565
566
		error("Could not make sockets!");
		exit(1);
567
	    }
568
	    num_alttcpservers = num_altudpservers = 0;
569
570
571
572
573
574
	} else {
	    if (makesockets(portnum, &udpsock, &tcpsock) < 0 ||
		makesockets(TBSERVER_PORT2, &altudpsock, &alttcpsock) < 0) {
		    error("Could not make sockets!");
		    exit(1);
	    }
575
576
577
578
579
	}

	signal(SIGTERM, cleanup);
	signal(SIGINT, cleanup);
	signal(SIGHUP, cleanup);
580
581
	signal(SIGUSR1, setverbose);
	signal(SIGUSR2, setverbose);
582
583
584
585

	/*
	 * Stash the pid away.
	 */
586
	mypid = getpid();
Leigh B Stoller's avatar
Leigh B Stoller committed
587
588
	sprintf(pidfile, "%s/tmcd.pid", _PATH_VARRUN);
	fp = fopen(pidfile, "w");
589
	if (fp != NULL) {
590
		fprintf(fp, "%d\n", mypid);
591
592
593
		(void) fclose(fp);
	}

594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
	/*
	 * 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);
	}

627
628
	/*
	 * Now fork a set of children to handle requests. We keep the
629
630
631
632
633
634
635
	 * 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!
636
	 */
637
638
639
640
641
642
	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));
643

644
645
	while (1) {
		while (!killme && numchildren < maxchildren) {
646
			int which = 3;
647

648
649
650
651
652
653
654
655
			/*
			 * Find which kind of server is short one.
			 */
			for (i = 0; i < 4; i++) {
				if (server_counts[i]) {
					which = i;
					break;
				}
656
			}
657

658
659
660
661
662
			if ((pid = fork()) < 0) {
				errorc("forking server");
				goto done;
			}
			if (pid) {
663
664
665
666
667
668
669
670
671
672
				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;
673
674
675
				numchildren++;
				continue;
			}
676
677
678
			/* Poor way of knowing parent/child */
			numchildren = 0;
			mypid = getpid();
679

680
681
682
683
			/* Child does useful work! Never Returns! */
			signal(SIGTERM, SIG_DFL);
			signal(SIGINT, SIG_DFL);
			signal(SIGHUP, SIG_DFL);
684

685
			switch (which) {
686
			case 0: udpserver(udpsock, portnum);
687
				break;
688
			case 1: udpserver(altudpsock, TBSERVER_PORT2);
689
				break;
690
			case 2: tcpserver(alttcpsock, TBSERVER_PORT2);
691
				break;
692
			case 3: tcpserver(tcpsock, portnum);
693
694
695
696
697
698
699
700
701
702
703
704
				break;
			}
			exit(-1);
		}

		/*
		 * Parent waits.
		 */
		pid = waitpid(-1, &status, 0);
		if (pid < 0) {
			errorc("waitpid failed");
			continue;
705
		}
706
707
708
		if (WIFSIGNALED(status)) {
			error("server %d exited with signal %d!\n",
			      pid, WTERMSIG(status));
709
		}
710
711
		else if (WIFEXITED(status)) {
			error("server %d exited with status %d!\n",
712
			      pid, WEXITSTATUS(status));
713
		}
714
		numchildren--;
715
716
717
718
719
720
721
722
723
724

		/*
		 * 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;
			}
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
		}
		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
743
744
	socklen_t		length;
	int			i, udpsock, tcpsock;
745

Mike Hibler's avatar
Mike Hibler committed
746
	/*
747
	 * Setup TCP socket for incoming connections.
Mike Hibler's avatar
Mike Hibler committed
748
749
	 */

750
	/* Create socket from which to read. */
Mike Hibler's avatar
Mike Hibler committed
751
752
	tcpsock = socket(AF_INET, SOCK_STREAM, 0);
	if (tcpsock < 0) {
753
		pfatal("opening stream socket");
754
755
	}

756
	i = 1;
Mike Hibler's avatar
Mike Hibler committed
757
	if (setsockopt(tcpsock, SOL_SOCKET, SO_REUSEADDR,
758
		       (char *)&i, sizeof(i)) < 0)
759
		pwarning("setsockopt(SO_REUSEADDR)");;
760

761
762
763
	/* Create name. */
	name.sin_family = AF_INET;
	name.sin_addr.s_addr = INADDR_ANY;
764
	name.sin_port = htons((u_short) portnum);
Mike Hibler's avatar
Mike Hibler committed
765
	if (bind(tcpsock, (struct sockaddr *) &name, sizeof(name))) {
766
		pfatal("binding stream socket");
767
768
769
	}
	/* Find assigned port value and print it out. */
	length = sizeof(name);
Mike Hibler's avatar
Mike Hibler committed
770
	if (getsockname(tcpsock, (struct sockaddr *) &name, &length)) {
771
		pfatal("getsockname");
772
	}
773
	if (listen(tcpsock, 128) < 0) {
774
		pfatal("listen");
775
	}
776
	info("listening on TCP port %d\n", ntohs(name.sin_port));
777

Mike Hibler's avatar
Mike Hibler committed
778
779
780
781
782
783
784
	/*
	 * Setup UDP socket
	 */

	/* Create socket from which to read. */
	udpsock = socket(AF_INET, SOCK_DGRAM, 0);
	if (udpsock < 0) {
785
		pfatal("opening dgram socket");
Mike Hibler's avatar
Mike Hibler committed
786
787
788
789
790
	}

	i = 1;
	if (setsockopt(udpsock, SOL_SOCKET, SO_REUSEADDR,
		       (char *)&i, sizeof(i)) < 0)
791
		pwarning("setsockopt(SO_REUSEADDR)");;
792
793
794
795

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

Mike Hibler's avatar
Mike Hibler committed
797
798
799
	/* Create name. */
	name.sin_family = AF_INET;
	name.sin_addr.s_addr = INADDR_ANY;
800
	name.sin_port = htons((u_short) portnum);
Mike Hibler's avatar
Mike Hibler committed
801
	if (bind(udpsock, (struct sockaddr *) &name, sizeof(name))) {
802
		pfatal("binding dgram socket");
Mike Hibler's avatar
Mike Hibler committed
803
804
805
806
807
	}

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

812
813
814
	*tcpsockp = tcpsock;
	*udpsockp = udpsock;
	return 0;
815
}
816

817
/*
818
 * Listen for UDP requests. This is not a secure channel, and so this should
819
820
821
 * eventually be killed off.
 */
static void
822
udpserver(int sock, int portnum)
823
824
825
{
	char			buf[MYBUFSIZE];
	struct sockaddr_in	client;
Mike Hibler's avatar
lint    
Mike Hibler committed
826
827
	socklen_t		length;
	int			cc;
828
	unsigned int		nreq = 0;
829

830
831
	info("udpserver starting: pid=%d sock=%d portnum=%d\n",
	     mypid, sock, portnum);
832
833
834
835
836

	/*
	 * Wait for udp connections.
	 */
	while (1) {
837
		setproctitle("UDP %d: %u done", portnum, nreq);
838
		length = sizeof(client);
839
		cc = recvfrom(sock, buf, sizeof(buf) - 1,
840
841
842
			      0, (struct sockaddr *)&client, &length);
		if (cc <= 0) {
			if (cc < 0)
843
844
				errorc("Reading UDP request");
			error("UDP Connection aborted\n");
845
			continue;
846
		}
847
848
		buf[cc] = '\0';
		handle_request(sock, &client, buf, 0);
849
		nreq++;
850
851
852
853
	}
	exit(1);
}

854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
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;
}

880
/*
881
 * Listen for TCP requests.
882
883
 */
static void
884
tcpserver(int sock, int portnum)
885
{
886
	char			buf[MAXTMCDPACKET];
887
	struct sockaddr_in	client;
Mike Hibler's avatar
lint    
Mike Hibler committed
888
889
	socklen_t		length;
	int			cc, newsock;
890
	unsigned int		nreq = 0;
891
	struct timeval		tv;
892

893
894
	info("tcpserver starting: pid=%d sock=%d portnum=%d\n",
	     mypid, sock, portnum);
895
896
897
898
899

	/*
	 * Wait for TCP connections.
	 */
	while (1) {
900
		setproctitle("TCP %d: %u done", portnum, nreq);
901
		length  = sizeof(client);
902
903
		newsock = ACCEPT(sock, (struct sockaddr *)&client, &length,
				 readtimo);
904
		if (newsock < 0) {
905
			errorc("accepting TCP connection");
906
			continue;
907
		}
Mike Hibler's avatar
Mike Hibler committed
908

909
910
911
912
913
914
915
916
917
918
919
920
921
922
		/*
		 * 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
923
		/*
924
		 * Read in the command request.
Mike Hibler's avatar
Mike Hibler committed
925
		 */
926
		if ((cc = READ(newsock, buf, sizeof(buf) - 1)) <= 0) {
927
928
929
930
931
932
			if (cc < 0) {
				if (errno == EWOULDBLOCK)
					errorc("Timeout reading TCP request");
				else
					errorc("Error reading TCP request");
			}
933
934
			error("TCP connection aborted\n");
			CLOSE(newsock);
935
			continue;
936
		}
937
938
		buf[cc] = '\0';
		handle_request(newsock, &client, buf, 1);
939
		CLOSE(newsock);
940
		nreq++;
941
942
943
	}
	exit(1);
}
Mike Hibler's avatar
Mike Hibler committed
944

945
946
947
//#define error(x...)	fprintf(stderr, ##x)
//#define info(x...)	fprintf(stderr, ##x)

948
949
950
951
static int
handle_request(int sock, struct sockaddr_in *client, char *rdata, int istcp)
{
	struct sockaddr_in redirect_client;
952
	int		   redirect = 0;
953
	char		   buf[BUFSIZ], *bp, *cp;
954
955
	char		   privkeybuf[PRIVKEY_LEN];
	char		   *privkey = (char *) NULL;
956
	int		   i, overbose = 0, err = 0;
957
	int		   version = DEFAULT_VERSION;
958
959
	tmcdreq_t	   tmcdreq, *reqp = &tmcdreq;

Leigh B. Stoller's avatar