tmcd.c 228 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
	char            privkey[PRIVKEY_LEN+1];
212 213
        /* This key is a replacement for privkey, on protogeni resources */
	char            external_key[PRIVKEY_LEN+1];
214
} tmcdreq_t;
215
static int	iptonodeid(struct in_addr, tmcdreq_t *, char*);
216 217
static int	checkdbredirect(tmcdreq_t *);

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

835
/*