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

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

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

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

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

void
usage()
{
428
	fprintf(stderr, usagestr, MINCHILDREN, MAXCHILDREN);
429
430
431
	exit(1);
}

432
433
434
435
436
static void
cleanup()
{
	signal(SIGHUP, SIG_IGN);
	killme = 1;
437
	killpg(0, SIGHUP);
Leigh B Stoller's avatar
Leigh B Stoller committed
438
	unlink(pidfile);
439
440
}

441
442
443
444
static void
setverbose(int sig)
{
	signal(sig, SIG_IGN);
445

446
447
448
449
	if (sig == SIGUSR1)
		verbose = 1;
	else
		verbose = 0;
450
451
	info("verbose logging turned %s\n", verbose ? "on" : "off");

452
453
454
455
456
457
	/* Just the parent sends this */
	if (numchildren)
		killpg(0, sig);
	signal(sig, setverbose);
}

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

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

	if (argc)
		usage();
509
510
	if (maxchildren < MINCHILDREN || maxchildren > MAXCHILDREN)
		usage();
511

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

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

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

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

	signal(SIGTERM, cleanup);
	signal(SIGINT, cleanup);
	signal(SIGHUP, cleanup);
597
598
	signal(SIGUSR1, setverbose);
	signal(SIGUSR2, setverbose);
599
600
601
602

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

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
640
641
642
643
	/*
	 * 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);
	}

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

661
662
	while (1) {
		while (!killme && numchildren < maxchildren) {
663
			int which = 3;
664

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

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

697
698
699
700
			/* Child does useful work! Never Returns! */
			signal(SIGTERM, SIG_DFL);
			signal(SIGINT, SIG_DFL);
			signal(SIGHUP, SIG_DFL);
701

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

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

		/*
		 * 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;
			}
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
		}
		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
760
761
	socklen_t		length;
	int			i, udpsock, tcpsock;
762

Mike Hibler's avatar
Mike Hibler committed
763
	/*
764
	 * Setup TCP socket for incoming connections.
Mike Hibler's avatar
Mike Hibler committed
765
766
	 */

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

773
	i = 1;
Mike Hibler's avatar
Mike Hibler committed
774
	if (setsockopt(tcpsock, SOL_SOCKET, SO_REUSEADDR,
775
		       (char *)&i, sizeof(i)) < 0)
776
		pwarning("setsockopt(SO_REUSEADDR)");;
777

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

Mike Hibler's avatar
Mike Hibler committed
795
796
797
798
799
800
801
	/*
	 * Setup UDP socket
	 */

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

	i = 1;
	if (setsockopt(udpsock, SOL_SOCKET, SO_REUSEADDR,
		       (char *)&i, sizeof(i)) < 0)
808
		pwarning("setsockopt(SO_REUSEADDR)");;
809
810
811
812

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

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

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

829
830
831
	*tcpsockp = tcpsock;
	*udpsockp = udpsock;
	return 0;
832
}
833

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

847
848
	info("udpserver starting: pid=%d sock=%d portnum=%d\n",
	     mypid, sock, portnum);
849
850
851
852
853

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

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

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

910
911
	info("tcpserver starting: pid=%d sock=%d portnum=%d\n",
	     mypid, sock, portnum);
912
913
914
915
916

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

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