tmcd.c 366 KB
Newer Older
Leigh Stoller's avatar
Leigh Stoller committed
1
/*
2
 * Copyright (c) 2000-2018 University of Utah and the Flux Group.
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
 * 
 * {{{EMULAB-LICENSE
 * 
 * This file is part of the Emulab network testbed software.
 * 
 * This file is free software: you can redistribute it and/or modify it
 * under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or (at
 * your option) any later version.
 * 
 * This file is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Affero General Public
 * License for more details.
 * 
 * You should have received a copy of the GNU Affero General Public License
 * along with this file.  If not, see <http://www.gnu.org/licenses/>.
 * 
 * }}}
Leigh Stoller's avatar
Leigh Stoller committed
22 23
 */

24 25 26
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
Mike Hibler's avatar
Mike Hibler committed
27
#include <arpa/inet.h>
28
#include <netdb.h>
Mike Hibler's avatar
Mike Hibler committed
29
#include <ctype.h>
30
#include <stdio.h>
Mike Hibler's avatar
Mike Hibler committed
31
#include <stdlib.h>
32
#include <errno.h>
Mike Hibler's avatar
Mike Hibler committed
33 34
#include <string.h>
#include <unistd.h>
35 36 37 38
#include <syslog.h>
#include <signal.h>
#include <stdarg.h>
#include <assert.h>
39
#include <sys/wait.h>
40
#include <sys/fcntl.h>
41 42
#include <sys/syscall.h>
#include <sys/stat.h>
43
#include <sys/param.h>
44
#include <paths.h>
Austin Clements's avatar
Austin Clements committed
45
#include <setjmp.h>
46 47
#include <pwd.h>
#include <grp.h>
48
#include <mysql/mysql.h>
49
#include "tmcd.h"
50
#include "config.h"
51 52
#include "ssl.h"
#include "log.h"
53
#include "tbdefs.h"
54
#include "bsdefs.h"
Leigh Stoller's avatar
Leigh Stoller committed
55 56
#include "bootwhat.h"
#include "bootinfo.h"
57

58 59 60 61
#ifdef EVENTSYS
#include "event.h"
#endif

62 63 64
/* XXX: Not sure this is okay! */
#include "tpm.h"

65 66 67
/*
 * XXX This needs to be localized!
 */
68 69 70
#define FSPROJDIR	FSNODE ":" FSDIR_PROJ
#define FSGROUPDIR	FSNODE ":" FSDIR_GROUPS
#define FSUSERDIR	FSNODE ":" FSDIR_USERS
71 72 73
#ifdef  FSDIR_SHARE
#define FSSHAREDIR	FSNODE ":" FSDIR_SHARE
#endif
74 75 76
#ifdef  FSDIR_SCRATCH
#define FSSCRATCHDIR	FSNODE ":" FSDIR_SCRATCH
#endif
77 78
/* XXX InstaGeni Rack Hack. Hack until I decide on a better way */
#define FSJAILIP       "172.17.253.254"
79 80 81 82 83
#define PROJDIR		PROJROOT_DIR
#define GROUPDIR	GROUPSROOT_DIR
#define USERDIR		USERSROOT_DIR
#define SCRATCHDIR	SCRATCHROOT_DIR
#define SHAREDIR	SHAREROOT_DIR
84
#define NETBEDDIR	"/netbed"
85
#define PLISALIVELOGDIR "/usr/testbed/log/plabisalive"
86 87
#define RELOADPID	"emulab-ops"
#define RELOADEID	"reloading"
88
#define FSHOSTID	"/usr/testbed/etc/fshostid"
89
#define DOTSFS		".sfs"
90 91
#define RUNASUSER	"nobody"
#define RUNASGROUP	"nobody"
Mike Hibler's avatar
Mike Hibler committed
92
#ifndef NTPSERVER
93
#define NTPSERVER       "ntp1"
Mike Hibler's avatar
Mike Hibler committed
94
#endif
95
#define PROTOUSER	"elabman"
96 97 98 99
#define PRIVKEY_LEN	128
#define URN_LEN		128
#define XSTRINGIFY(s)   STRINGIFY(s)
#define STRINGIFY(s)	#s
100

101 102 103 104 105
/* XXX backward compat */
#ifndef TBCOREDIR
#define	TBCOREDIR	TBROOT "/tmp"
#endif

106 107 108 109
/* socket read/write timeouts in ms */
#define READTIMO	3000
#define WRITETIMO	3000

110
#define TESTMODE
111 112 113 114 115 116 117 118 119 120 121 122
#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) */
123

124 125 126
#define DISKTYPE	"ad"
#define DISKNUM		0

127 128
/* Compiled in slothd parameters
 *
129
 * 1 - reg_interval  2 - agg_interval  3 - load_thresh
130 131 132 133
 * 4 - expt_thresh   5 - ctl_thresh
 */
#define SDPARAMS        "reg=300 agg=5 load=1 expt=5 ctl=1000"

134 135 136 137 138 139 140 141 142 143
/*
 * Compiled in tarball/rpm param.
 * Should come out of the DB.
 */
#ifdef SPEWFROMOPS
#define TARRPM_SERVER	USERNODE
#else
#define TARRPM_SERVER	BOSSNODE
#endif

144 145 146 147 148 149
#ifdef IMAGEPROVENANCE
#define WITHPROVENANCE	1
#else
#define WITHPROVENANCE	0
#endif

150 151 152 153 154 155 156
#ifdef WITHZFS
#undef WITHZFS
#define WITHZFS	1
#else
#define WITHZFS	0
#endif

157 158
/* Defined in configure and passed in via the makefile */
#define DBNAME_SIZE	64
Austin Clements's avatar
Austin Clements committed
159
#define HOSTID_SIZE	(32+64)
160 161
#define DEFAULT_DBNAME	TBDBNAME

Robert P Ricci's avatar
Robert P Ricci committed
162 163 164 165
/* For secure disk loading */
#define SECURELOAD_OPMODE "SECURELOAD"
#define SECURELOAD_STATE  "RELOADSETUP"

166 167 168
/* Our answer to "geni-get --version" */
#define GENI_VERSION "1"

169 170 171 172 173 174 175 176
/* Taint support */
#define TB_TAINTSTATE_BLACKBOX  1
#define TB_TAINTSTATE_USERONLY  2
#define TB_TAINTSTATE_DANGEROUS 4
#define HAS_ANY_TAINTS(tset, tcheck) (tset & tcheck)
#define HAS_ALL_TAINTS(tset, tcheck) ((tset & tcheck) == tcheck)
#define HAS_TAINT(tset, tcheck) HAS_ALL_TAINTS(tset, tcheck)

177 178 179 180 181 182
/* Per-experiment root keypair support */
#define TB_ROOTKEYS_NONE	0
#define TB_ROOTKEYS_PRIVATE	1
#define TB_ROOTKEYS_PUBLIC	2
#define TB_ROOTKEYS_BOTH	3

183 184 185 186 187
typedef struct {
	char pid[TBDB_FLEN_PID];
	char gid[TBDB_FLEN_GID];
	char name[TBDB_FLEN_IMAGENAME];
	char version[11]; /* Max size of a 32-bit int in string form. */
188
	char *path; /* A dynamically-allocated path string, if relevant. */
189 190
} imstrings_t;

191
int		debug = 0;
192
static int	verbose = 0;
193
static int	insecure = 0;
194
static int	byteswritten = 0;
Leigh Stoller's avatar
Leigh Stoller committed
195
static char	pidfile[MAXPATHLEN];
196
static char     dbname[DBNAME_SIZE];
197
static struct in_addr myipaddr;
198
static struct in_addr cnet, cmask, jnet, jmask;
Austin Clements's avatar
Austin Clements committed
199
static char	fshostid[HOSTID_SIZE];
Leigh Stoller's avatar
Leigh Stoller committed
200
static int	nodeidtoexp(char *nodeid, char *pid, char *eid, char *gid);
201 202
static void	tcpserver(int sock, int portnum);
static void	udpserver(int sock, int portnum);
203
static int      handle_request(int, struct sockaddr_in *, char *, int, int);
204
static int      checkcerts(char*);
205
static int	makesockets(int portnum, int *udpsockp, int *tcpsockp);
Mike Hibler's avatar
Mike Hibler committed
206 207
int		client_writeback(int sock, void *buf, int len, int tcp);
void		client_writeback_done(int sock, struct sockaddr_in *client);
208 209
MYSQL_RES *	mydb_query(char *query, int ncols, ...);
int		mydb_update(char *query, ...);
210
static int	safesymlink(char *name1, char *name2);
211
static int      getImageInfo(char *path, char *nodeid, char *pid, char *imagename,
212
			     unsigned int *mtime, off_t *isize);
213
static int	getrandomchars(char *buf, int len);
214

215 216 217 218
/* socket timeouts */
static int	readtimo = READTIMO;
static int	writetimo = WRITETIMO;

219
/* thread support */
220
#define MAXCHILDREN	20
221
#define MINCHILDREN	8
222
static int	numchildren;
223 224 225 226
static int	maxchildren       = 13;
static int      num_udpservers    = 3;
static int      num_altudpservers = 1;
static int      num_alttcpservers = 1;
227
static int	mypid;
228
static volatile int killme;
229

230 231 232 233 234
/* Output macro to check for string overflow */
#define OUTPUT(buf, size, format...) \
({ \
	int __count__ = snprintf((buf), (size), ##format); \
        \
235
        if (__count__ >= (size)) { \
236 237 238 239 240 241
		error("Not enough room in output buffer! line %d.\n", __LINE__);\
		return 1; \
	} \
	__count__; \
})

242 243 244 245 246
/*
 * 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 Stoller's avatar
Leigh Stoller committed
247
	struct in_addr  client;
248 249 250
	int		allocated;
	int		jailflag;
	int		isvnode;
251
	int		isroutable_vnode; /* only valid if isvnode==1 */
252
	int		asvnode;
253
	int		issubnode;
254
	int		islocal;
255
	int		isdedicatedwa;
256
	int		iscontrol;
257
	int		isplabdslice;
258
	int		isplabsvc;
259
	int		elab_in_elab;
260
        int		singlenet;	  /* Modifier for elab_in_elab */
261
	int		update_accounts;
262
	int		exptidx;
263 264 265
	int		creator_idx;
	int		swapper_idx;
	int		swapper_isadmin;
266
        int		genisliver_idx;
267
        int		geniflags;
268
	int		isnonlocal_pid;
269 270
	unsigned short  taintstates;
	unsigned short  experiment_keys;
271
	char            nfsmounts[TBDB_FLEN_TINYTEXT];
272 273
	char		nodeid[TBDB_FLEN_NODEID];
	char		vnodeid[TBDB_FLEN_NODEID];
Leigh Stoller's avatar
Leigh Stoller committed
274
	char		pnodeid[TBDB_FLEN_NODEID]; /* XXX */
275 276 277 278 279
	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];
280
	char		class[TBDB_FLEN_NODECLASS];
281 282
        char		ptype[TBDB_FLEN_NODETYPE];	/* Of physnode */
	char		pclass[TBDB_FLEN_NODECLASS];	/* Of physnode */
283 284
	char		creator[TBDB_FLEN_UID];
	char		swapper[TBDB_FLEN_UID];
285
	char		syncserver[TBDB_FLEN_VNAME];	/* The vname */
286
	char		keyhash[TBDB_FLEN_PRIVKEY];
287
	char		eventkey[TBDB_FLEN_PRIVKEY];
288
	char		sfshostid[TBDB_FLEN_SFSHOSTID];
289
	char		testdb[TBDB_FLEN_TINYTEXT];
290
	char		sharing_mode[TBDB_FLEN_TINYTEXT];
291
	char		erole[TBDB_FLEN_TINYTEXT];
292
	char            privkey[PRIVKEY_LEN+1];
293
	char		nodeuuid[TBDB_FLEN_UUID];
294 295
        /* This key is a replacement for privkey, on protogeni resources */
	char            external_key[PRIVKEY_LEN+1];
296
} tmcdreq_t;
297
static int	iptonodeid(struct in_addr, tmcdreq_t *, char*);
298
static int	checkdbredirect(tmcdreq_t *);
299
static int      sendstoreconf(int sock, int tcp, tmcdreq_t *reqp, char *bscmd, 
300
			      char *vname, int dopersist, char *localproto);
301
static int      get_imagestrings(tmcdreq_t *reqp, imstrings_t *imstrings);
302
static void     free_imagestrings_content(imstrings_t *imstrings);
303

304 305 306 307 308
#ifdef EVENTSYS
int			myevent_send(address_tuple_t address);
static event_handle_t	event_handle = NULL;
#endif

309 310 311
/*
 * Commands we support.
 */
312 313
#define COMMAND_PROTOTYPE(x) \
	static int \
314
	x(int sock, tmcdreq_t *reqp, char *rdata, int tcp, int vers)
315 316

COMMAND_PROTOTYPE(doreboot);
317
COMMAND_PROTOTYPE(donodeid);
318
COMMAND_PROTOTYPE(donodetype);
319
COMMAND_PROTOTYPE(donodeuuid);
David Johnson's avatar
David Johnson committed
320
COMMAND_PROTOTYPE(domanifest);
321 322 323
COMMAND_PROTOTYPE(dostatus);
COMMAND_PROTOTYPE(doifconfig);
COMMAND_PROTOTYPE(doaccounts);
324
COMMAND_PROTOTYPE(dobridges);
325
COMMAND_PROTOTYPE(dodelay);
326
COMMAND_PROTOTYPE(dolinkdelay);
327 328 329 330
COMMAND_PROTOTYPE(dohosts);
COMMAND_PROTOTYPE(dorpms);
COMMAND_PROTOTYPE(dodeltas);
COMMAND_PROTOTYPE(dotarballs);
331
COMMAND_PROTOTYPE(doblobs);
332 333 334 335
COMMAND_PROTOTYPE(dostartcmd);
COMMAND_PROTOTYPE(dostartstat);
COMMAND_PROTOTYPE(doready);
COMMAND_PROTOTYPE(doreadycount);
336
COMMAND_PROTOTYPE(dostorageconfig);
337
COMMAND_PROTOTYPE(domounts);
Austin Clements's avatar
Austin Clements committed
338
COMMAND_PROTOTYPE(dosfshostid);
339 340 341 342 343 344 345
COMMAND_PROTOTYPE(doloadinfo);
COMMAND_PROTOTYPE(doreset);
COMMAND_PROTOTYPE(dorouting);
COMMAND_PROTOTYPE(dotrafgens);
COMMAND_PROTOTYPE(donseconfigs);
COMMAND_PROTOTYPE(dostate);
COMMAND_PROTOTYPE(docreator);
346
COMMAND_PROTOTYPE(dotunnels);
347
COMMAND_PROTOTYPE(dovnodelist);
348
COMMAND_PROTOTYPE(dosubnodelist);
349
COMMAND_PROTOTYPE(doisalive);
350
COMMAND_PROTOTYPE(doipodinfo);
351 352
COMMAND_PROTOTYPE(dontpinfo);
COMMAND_PROTOTYPE(dontpdrift);
353
COMMAND_PROTOTYPE(dojailconfig);
354
COMMAND_PROTOTYPE(doplabconfig);
355 356
COMMAND_PROTOTYPE(dosubconfig);
COMMAND_PROTOTYPE(doixpconfig);
357
COMMAND_PROTOTYPE(doslothdparams);
358
COMMAND_PROTOTYPE(doprogagents);
359
COMMAND_PROTOTYPE(dosyncserver);
360
COMMAND_PROTOTYPE(dokeyhash);
361
COMMAND_PROTOTYPE(doeventkey);
362
COMMAND_PROTOTYPE(dofullconfig);
363 364
COMMAND_PROTOTYPE(doroutelist);
COMMAND_PROTOTYPE(dorole);
365
COMMAND_PROTOTYPE(dorusage);
366
COMMAND_PROTOTYPE(dodoginfo);
367
COMMAND_PROTOTYPE(dohostkeys);
Mike Hibler's avatar
Mike Hibler committed
368
COMMAND_PROTOTYPE(dotmcctest);
369
COMMAND_PROTOTYPE(dofwinfo);
370
COMMAND_PROTOTYPE(dohostinfo);
371
COMMAND_PROTOTYPE(doemulabconfig);
372
COMMAND_PROTOTYPE(doeplabconfig);
373
COMMAND_PROTOTYPE(dolocalize);
374
COMMAND_PROTOTYPE(dorootpswd);
375 376
COMMAND_PROTOTYPE(dobooterrno);
COMMAND_PROTOTYPE(dobootlog);
377
COMMAND_PROTOTYPE(dobattery);
378
COMMAND_PROTOTYPE(dotopomap);
379
COMMAND_PROTOTYPE(douserenv);
380 381
COMMAND_PROTOTYPE(dotiptunnels);
COMMAND_PROTOTYPE(dorelayconfig);
382
COMMAND_PROTOTYPE(dotraceconfig);
383
COMMAND_PROTOTYPE(doltmap);
384
COMMAND_PROTOTYPE(doltpmap);
385
COMMAND_PROTOTYPE(doelvindport);
386
COMMAND_PROTOTYPE(doplabeventkeys);
387
COMMAND_PROTOTYPE(dointfcmap);
388
COMMAND_PROTOTYPE(domotelog);
389
COMMAND_PROTOTYPE(doportregister);
Leigh Stoller's avatar
Leigh Stoller committed
390
COMMAND_PROTOTYPE(dobootwhat);
391 392
COMMAND_PROTOTYPE(dotpmblob);
COMMAND_PROTOTYPE(dotpmpubkey);
393
COMMAND_PROTOTYPE(dotpmdummy);
394
COMMAND_PROTOTYPE(dodhcpdconf);
395
COMMAND_PROTOTYPE(dosecurestate);
396
COMMAND_PROTOTYPE(doquoteprep);
397
COMMAND_PROTOTYPE(doimagekey);
398
COMMAND_PROTOTYPE(donodeattributes);
399
COMMAND_PROTOTYPE(dodisks);
400
COMMAND_PROTOTYPE(doarpinfo);
401
COMMAND_PROTOTYPE(dohwinfo);
402
COMMAND_PROTOTYPE(dotiplineinfo);
403
COMMAND_PROTOTYPE(doimageid);
404
COMMAND_PROTOTYPE(doimagesize);
405
COMMAND_PROTOTYPE(dopnetnodeattrs);
406
COMMAND_PROTOTYPE(doserviceinfo);
Mike Hibler's avatar
Mike Hibler committed
407
COMMAND_PROTOTYPE(dosubbossinfo);
408
COMMAND_PROTOTYPE(dopublicaddrinfo);
409 410 411 412 413 414 415 416
#if PROTOGENI_SUPPORT
COMMAND_PROTOTYPE(dogeniclientid);
COMMAND_PROTOTYPE(dogenisliceurn);
COMMAND_PROTOTYPE(dogenisliceemail);
COMMAND_PROTOTYPE(dogeniuserurn);
COMMAND_PROTOTYPE(dogeniuseremail);
COMMAND_PROTOTYPE(dogenigeniuser);
COMMAND_PROTOTYPE(dogenimanifest);
417 418
COMMAND_PROTOTYPE(dogenicert);
COMMAND_PROTOTYPE(dogenikey);
419 420 421 422
COMMAND_PROTOTYPE(dogenicontrolmac);
COMMAND_PROTOTYPE(dogeniversion);
COMMAND_PROTOTYPE(dogenigetversion);
COMMAND_PROTOTYPE(dogenisliverstatus);
423 424
COMMAND_PROTOTYPE(dogenistatus);
COMMAND_PROTOTYPE(dogenicommands);
425
COMMAND_PROTOTYPE(dogeniall);
426
COMMAND_PROTOTYPE(dogeniparam);
427
COMMAND_PROTOTYPE(dogeniinvalid);
428
#endif
429

430 431
/*
 * The fullconfig slot determines what routines get called when pushing
432
 * out a full configuration. Physnodes get slightly different
433
 * than vnodes, and at some point we might want to distinguish different
434 435 436 437 438
 * types of vnodes (jailed, plab).
 */
#define FULLCONFIG_NONE		0x0
#define FULLCONFIG_PHYS		0x1
#define FULLCONFIG_VIRT		0x2
439 440 441 442 443
#define FULLCONFIG_ALL		(FULLCONFIG_PHYS|FULLCONFIG_VIRT)

/*
 * Flags encode a few other random properties of commands
 */
444 445 446 447 448
#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 */
449
#define F_REMREQSSL	0x20	/* remote nodes must connect with SSL */
450
#define F_REQTPM	0x40	/* require TPM on client */
451

452 453
struct command {
	char	*cmdname;
454
	int	fullconfig;
455
	int	flags;
456
	int    (*func)(int, tmcdreq_t *, char *, int, int);
457
} command_array[] = {
458 459
	{ "reboot",	  FULLCONFIG_NONE, 0, doreboot },
	{ "nodeid",	  FULLCONFIG_ALL,  0, donodeid },
460
	{ "nodetype",	  FULLCONFIG_ALL,  0, donodetype },
461
	{ "nodeuuid",	  FULLCONFIG_ALL,  0, donodeuuid },
David Johnson's avatar
David Johnson committed
462
	{ "manifest",	  FULLCONFIG_ALL,  0, domanifest },
463
	{ "status",	  FULLCONFIG_NONE, 0, dostatus },
464
	{ "ifconfig",	  FULLCONFIG_ALL,  F_ALLOCATED, doifconfig },
465
	{ "accounts",	  FULLCONFIG_ALL,  F_REMREQSSL, doaccounts },
466
	{ "delay",	  FULLCONFIG_ALL,  F_ALLOCATED, dodelay },
467
	{ "bridges",	  FULLCONFIG_ALL,  F_ALLOCATED, dobridges },
468 469 470 471 472
	{ "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 },
473
	{ "blobs",	  FULLCONFIG_ALL,  F_ALLOCATED, doblobs },
474 475
	{ "startupcmd",	  FULLCONFIG_ALL,  F_ALLOCATED, dostartcmd },
	{ "startstatus",  FULLCONFIG_NONE, F_ALLOCATED, dostartstat }, /* Before startstat*/
476
	{ "startstat",	  FULLCONFIG_NONE, 0, dostartstat },
477 478
	{ "readycount",   FULLCONFIG_NONE, F_ALLOCATED, doreadycount },
	{ "ready",	  FULLCONFIG_NONE, F_ALLOCATED, doready },
479
	{ "storageconfig", FULLCONFIG_ALL, F_ALLOCATED, dostorageconfig},
480 481
	{ "mounts",	  FULLCONFIG_ALL,  F_ALLOCATED, domounts },
	{ "sfshostid",	  FULLCONFIG_NONE, F_ALLOCATED, dosfshostid },
482 483
	{ "loadinfo",	  FULLCONFIG_NONE, 0, doloadinfo},
	{ "reset",	  FULLCONFIG_NONE, 0, doreset},
484 485 486 487
	{ "routing",	  FULLCONFIG_ALL,  F_ALLOCATED, dorouting},
	{ "trafgens",	  FULLCONFIG_ALL,  F_ALLOCATED, dotrafgens},
	{ "nseconfigs",	  FULLCONFIG_ALL,  F_ALLOCATED, donseconfigs},
	{ "creator",	  FULLCONFIG_ALL,  F_ALLOCATED, docreator},
488
	{ "state",	  FULLCONFIG_NONE, 0, dostate},
489
	{ "tunnels",	  FULLCONFIG_ALL,  F_ALLOCATED, dotunnels},
490
	{ "vnodelist",	  FULLCONFIG_PHYS, 0, dovnodelist},
491
	{ "subnodelist",  FULLCONFIG_PHYS, 0, dosubnodelist},
492
	{ "isalive",	  FULLCONFIG_NONE, F_REMUDP|F_MINLOG, doisalive},
493
	{ "ipodinfo",	  FULLCONFIG_PHYS, 0, doipodinfo},
494 495
	{ "ntpinfo",	  FULLCONFIG_PHYS, 0, dontpinfo},
	{ "ntpdrift",	  FULLCONFIG_NONE, 0, dontpdrift},
496 497
	{ "jailconfig",	  FULLCONFIG_VIRT, F_ALLOCATED, dojailconfig},
	{ "plabconfig",	  FULLCONFIG_VIRT, F_ALLOCATED, doplabconfig},
498
	{ "subconfig",	  FULLCONFIG_NONE, 0, dosubconfig},
499
        { "sdparams",     FULLCONFIG_PHYS, 0, doslothdparams},
500 501
        { "programs",     FULLCONFIG_ALL,  F_ALLOCATED, doprogagents},
        { "syncserver",   FULLCONFIG_ALL,  F_ALLOCATED, dosyncserver},
502
        { "keyhash",      FULLCONFIG_ALL,  F_ALLOCATED|F_REMREQSSL, dokeyhash},
503
        { "eventkey",     FULLCONFIG_ALL,  F_ALLOCATED|F_REMREQSSL, doeventkey},
504 505 506
        { "fullconfig",   FULLCONFIG_NONE, F_ALLOCATED, dofullconfig},
        { "routelist",	  FULLCONFIG_PHYS, F_ALLOCATED, doroutelist},
        { "role",	  FULLCONFIG_PHYS, F_ALLOCATED, dorole},
507
        { "rusage",	  FULLCONFIG_NONE, F_REMUDP|F_MINLOG, dorusage},
508
        { "watchdoginfo", FULLCONFIG_ALL,  F_REMUDP|F_MINLOG, dodoginfo},
509
        { "hostkeys",     FULLCONFIG_NONE, 0, dohostkeys},
Mike Hibler's avatar
Mike Hibler committed
510
        { "tmcctest",     FULLCONFIG_NONE, F_MINLOG, dotmcctest},
511
        { "firewallinfo", FULLCONFIG_ALL,  0, dofwinfo},
512
        { "hostinfo",     FULLCONFIG_NONE, 0, dohostinfo},
513
	{ "emulabconfig", FULLCONFIG_NONE, F_ALLOCATED, doemulabconfig},
514
	{ "eplabconfig",  FULLCONFIG_NONE, F_ALLOCATED, doeplabconfig},
515
	{ "localization", FULLCONFIG_PHYS, 0, dolocalize},
516
	{ "rootpswd",     FULLCONFIG_NONE, F_REMREQSSL, dorootpswd},
517 518
	{ "booterrno",    FULLCONFIG_NONE, 0, dobooterrno},
	{ "bootlog",      FULLCONFIG_NONE, 0, dobootlog},
519
	{ "battery",      FULLCONFIG_NONE, F_REMUDP|F_MINLOG, dobattery},
520
	{ "topomap",      FULLCONFIG_NONE, F_MINLOG|F_ALLOCATED, dotopomap},
521
	{ "userenv",      FULLCONFIG_ALL,  F_ALLOCATED, douserenv},
522
	{ "tiptunnels",	  FULLCONFIG_ALL,  F_ALLOCATED, dotiptunnels},
523
	{ "traceinfo",	  FULLCONFIG_ALL,  F_ALLOCATED, dotraceconfig },
524
	{ "ltmap",        FULLCONFIG_NONE, F_MINLOG|F_ALLOCATED, doltmap},
525
	{ "ltpmap",       FULLCONFIG_NONE, F_MINLOG|F_ALLOCATED, doltpmap},
526
	{ "elvindport",   FULLCONFIG_NONE, 0, doelvindport},
527
	{ "plabeventkeys",FULLCONFIG_NONE, F_REMREQSSL, doplabeventkeys},
528
	{ "intfcmap",     FULLCONFIG_NONE, 0, dointfcmap},
529 530
	{ "motelog",      FULLCONFIG_ALL,  F_ALLOCATED, domotelog},
	{ "portregister", FULLCONFIG_NONE, F_REMNOSSL, doportregister},
Leigh Stoller's avatar
Leigh Stoller committed
531
	{ "bootwhat",	  FULLCONFIG_NONE, 0, dobootwhat },
532 533
	{ "tpmblob",	  FULLCONFIG_ALL, 0, dotpmblob },
	{ "tpmpubkey",	  FULLCONFIG_ALL, 0, dotpmpubkey },
534
	{ "tpmdummy",	  FULLCONFIG_ALL, F_REQTPM, dotpmdummy },
535
	{ "dhcpdconf",	  FULLCONFIG_ALL, 0, dodhcpdconf },
536 537
	{ "securestate",  FULLCONFIG_NONE, F_REMREQSSL, dosecurestate},
	{ "quoteprep",    FULLCONFIG_NONE, F_REMREQSSL, doquoteprep},
538
	{ "imagekey",     FULLCONFIG_NONE, F_REQTPM, doimagekey},
539
	{ "nodeattributes", FULLCONFIG_ALL, 0, donodeattributes},
540
	{ "disks",	  FULLCONFIG_ALL, 0, dodisks},
541
	{ "arpinfo",	  FULLCONFIG_NONE, 0, doarpinfo},
542
	{ "hwinfo",	  FULLCONFIG_NONE, 0, dohwinfo},
543
	{ "tiplineinfo",  FULLCONFIG_NONE,  F_ALLOCATED, dotiplineinfo},
544
	{ "imageinfo",      FULLCONFIG_NONE,  F_ALLOCATED, doimageid},
545
	{ "imagesize",   FULLCONFIG_NONE,  F_ALLOCATED, doimagesize},
Kirk Webb's avatar
Kirk Webb committed
546
	{ "pnetnodeattrs", FULLCONFIG_NONE, F_ALLOCATED, dopnetnodeattrs},
Mike Hibler's avatar
Mike Hibler committed
547 548
	{ "serviceinfo",  FULLCONFIG_NONE, 0, doserviceinfo },
	{ "subbossinfo",  FULLCONFIG_NONE, 0, dosubbossinfo },
549
	{ "publicaddrinfo",  FULLCONFIG_NONE, F_ALLOCATED, dopublicaddrinfo },
550 551 552 553 554 555 556 557 558
#if PROTOGENI_SUPPORT
	{ "geni_client_id", FULLCONFIG_NONE, 0, dogeniclientid },
	{ "geni_slice_urn", FULLCONFIG_NONE, 0, dogenisliceurn },
	{ "geni_slice_email", FULLCONFIG_NONE, 0, dogenisliceemail },
	{ "geni_user_urn", FULLCONFIG_NONE, 0, dogeniuserurn },
	{ "geni_user_email", FULLCONFIG_NONE, 0, dogeniuseremail },
	/* Yes, "geni_user" is a stupid name.  Wasn't my idea. */
	{ "geni_geni_user", FULLCONFIG_NONE, 0, dogenigeniuser },
	{ "geni_manifest", FULLCONFIG_NONE, 0, dogenimanifest },
559 560
	{ "geni_certificate", FULLCONFIG_NONE, 0, dogenicert },
	{ "geni_key", FULLCONFIG_NONE, 0, dogenikey },
561 562 563 564
	{ "geni_control_mac", FULLCONFIG_NONE, 0, dogenicontrolmac },
	{ "geni_version", FULLCONFIG_NONE, 0, dogeniversion },
	{ "geni_getversion", FULLCONFIG_NONE, 0, dogenigetversion },
	{ "geni_sliverstatus", FULLCONFIG_NONE, 0, dogenisliverstatus },
565 566
	{ "geni_status", FULLCONFIG_NONE, 0, dogenistatus },
	{ "geni_commands", FULLCONFIG_NONE, 0, dogenicommands },
567
	{ "geni_all",     FULLCONFIG_NONE, 0, dogeniall },
568
	{ "geni_param",   FULLCONFIG_NONE, 0, dogeniparam },
569 570 571
	/* A rather ugly hack to avoid making error handling a special case.
	   THIS MUST BE THE LAST ENTRY IN THE ARRAY! */
	{ "geni_invalid", FULLCONFIG_NONE, 0, dogeniinvalid }
572
#endif
573 574 575
};
static int numcommands = sizeof(command_array)/sizeof(struct command);

576
char *usagestr =
577 578 579
 "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"
580
 " -c num	   Specify number of servers (must be %d <= x <= %d)\n"
581
 " -v              More verbose logging\n"
582
 " -i ipaddr       Sets the boss IP addr to return (for multi-homed servers)\n"
583 584 585 586 587
 "\n";

void
usage()
{
588
	fprintf(stderr, usagestr, MINCHILDREN, MAXCHILDREN);
589 590 591
	exit(1);
}

592 593 594 595 596
static void
cleanup()
{
	signal(SIGHUP, SIG_IGN);
	killme = 1;
597
	killpg(0, SIGHUP);
Leigh Stoller's avatar
Leigh Stoller committed
598
	unlink(pidfile);
599 600
}

601 602 603 604
static void
setverbose(int sig)
{
	signal(sig, SIG_IGN);
605

606 607 608 609
	if (sig == SIGUSR1)
		verbose = 1;
	else
		verbose = 0;
610 611
	info("verbose logging turned %s\n", verbose ? "on" : "off");

612 613 614 615 616 617
	/* Just the parent sends this */
	if (numchildren)
		killpg(0, sig);
	signal(sig, setverbose);
}

Mike Hibler's avatar
Mike Hibler committed
618 619
int
main(int argc, char **argv)
620
{
621
	int			tcpsock, udpsock, i, ch;
622 623 624
	int			alttcpsock, altudpsock;
	int			status, pid;
	int			portnum = TBSERVER_PORT;
625 626
	FILE			*fp;
	char			buf[BUFSIZ];
627
	struct hostent		*he;
628
	extern char		build_info[];
629 630 631 632 633
	int			server_counts[4]; /* udp,tcp,altudp,alttcp */
	struct {
		int	pid;
		int	which;
	} servers[MAXCHILDREN];
634

635
	while ((ch = getopt(argc, argv, "dp:c:Xvi:")) != -1)
636 637 638 639 640 641
		switch(ch) {
		case 'p':
			portnum = atoi(optarg);
			break;
		case 'd':
			debug++;
642
			break;
643 644 645
		case 'c':
			maxchildren = atoi(optarg);
			break;
646 647 648
		case 'X':
			insecure = 1;
			break;
649 650 651
		case 'v':
			verbose++;
			break;
652 653 654 655 656 657 658
		case 'i':
			if (inet_aton(optarg, &myipaddr) == 0) {
				fprintf(stderr, "invalid IP address %s\n",
					optarg);
				usage();
			}
			break;
659 660 661 662 663 664 665 666 667 668
		case 'h':
		case '?':
		default:
			usage();
		}
	argc -= optind;
	argv += optind;

	if (argc)
		usage();
669 670
	if (maxchildren < MINCHILDREN || maxchildren > MAXCHILDREN)
		usage();
671

672 673 674 675 676 677
#ifdef  WITHSSL
	if (tmcd_server_sslinit()) {
		error("SSL init failed!\n");
		exit(1);
	}
#endif
678
	if (debug)
679 680 681
		loginit(0, 0);
	else {
		/* Become a daemon */
682 683 684 685 686
		if (chdir(TBCOREDIR)) {
			daemon(0, 0);
		} else {
			daemon(1, 0);
		}
687 688 689 690
		loginit(1, "tmcd");
	}
	info("daemon starting (version %d)\n", CURRENT_VERSION);
	info("%s\n", build_info);
Mike Hibler's avatar
Mike Hibler committed
691

Austin Clements's avatar
Austin Clements committed
692 693 694 695 696 697 698 699 700 701 702 703 704 705
	/*
	 * 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;
706 707 708
				if (debug) {
				    info("fshostid: %s\n", fshostid);
				}
Austin Clements's avatar
Austin Clements committed
709 710 711 712 713 714 715 716
			}
			else {
				error("fshostid from %s may be corrupt: %s",
				      FSHOSTID, fshostid);
			}
			fclose(fp);
		}
	}
717

718 719 720
	/*
	 * Grab our IP for security check below.
	 */
721
	if (myipaddr.s_addr == 0) {
722
#ifdef	LBS
723
		strcpy(buf, BOSSNODE);
724
#else
725 726
		if (gethostname(buf, sizeof(buf)) < 0)
			pfatal("getting hostname");
727
#endif
728 729 730 731 732 733
		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);
734 735
	}

736
	/*
737
	 * If we were given a port on the command line, don't open the
738 739 740 741
	 * alternate ports
	 */
	if (portnum != TBSERVER_PORT) {
	    if (makesockets(portnum, &udpsock, &tcpsock) < 0) {
742 743
		error("Could not make sockets!");
		exit(1);
744
	    }
745
	    num_alttcpservers = num_altudpservers = 0;
746 747 748 749 750 751
	} else {
	    if (makesockets(portnum, &udpsock, &tcpsock) < 0 ||
		makesockets(TBSERVER_PORT2, &altudpsock, &alttcpsock) < 0) {
		    error("Could not make sockets!");
		    exit(1);
	    }
752 753
	}

754 755 756 757 758 759 760 761 762 763 764 765 766
	/*
	 * Get control net info into a usable form.
	 */
	if (!inet_aton(CONTROL_NETWORK, &cnet) ||
	    !inet_aton(CONTROL_NETMASK, &cmask) ||
	    !inet_aton(JAILIPBASE, &jnet) ||
	    !inet_aton(JAILIPMASK, &jmask)) {
		error("Could not convert control net addrs/masks");
		exit(1);
	}
	cnet.s_addr &= cmask.s_addr;
	jnet.s_addr &= jmask.s_addr;

767 768 769
	signal(SIGTERM, cleanup);
	signal(SIGINT, cleanup);
	signal(SIGHUP, cleanup);
770 771
	signal(SIGUSR1, setverbose);
	signal(SIGUSR2, setverbose);
772 773 774 775

	/*
	 * Stash the pid away.
	 */
776
	mypid = getpid();
Leigh Stoller's avatar
Leigh Stoller committed
777 778
	sprintf(pidfile, "%s/tmcd.pid", _PATH_VARRUN);
	fp = fopen(pidfile, "w");
779
	if (fp != NULL) {
780
		fprintf(fp, "%d\n", mypid);
781 782 783
		(void) fclose(fp);
	}

784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816
	/*
	 * 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);
	}

817 818
	/*
	 * Now fork a set of children to handle requests. We keep the
819 820 821 822 823 824 825
	 * 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!
826
	 */
827 828 829 830 831 832
	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));
833

834 835
	while (1) {
		while (!killme && numchildren < maxchildren) {
836
			int which = 3;
837

838 839 840 841 842 843 844 845
			/*
			 * Find which kind of server is short one.
			 */
			for (i = 0; i < 4; i++) {
				if (server_counts[i]) {
					which = i;
					break;
				}
846
			}
847

848 849 850 851 852
			if ((pid = fork()) < 0) {
				errorc("forking server");
				goto done;
			}
			if (pid) {
853 854 855 856 857 858 859 860 861 862
				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;
863 864 865
				numchildren++;
				continue;
			}
866 867 868
			/* Poor way of knowing parent/child */
			numchildren = 0;
			mypid = getpid();
869

870 871 872 873
			/* Child does useful work! Never Returns! */
			signal(SIGTERM, SIG_DFL);
			signal(SIGINT, SIG_DFL);
			signal(SIGHUP, SIG_DFL);
874

875
			switch (which) {
876
			case 0: udpserver(udpsock, portnum);
877
				break;
878
			case 1: udpserver(altudpsock, TBSERVER_PORT2);
879
				break;
880
			case 2: tcpserver(alttcpsock, TBSERVER_PORT2);
881
				break;
882
			case 3: tcpserver(tcpsock, portnum);
883 884 885 886 887 888 889 890 891 892 893 894
				break;
			}
			exit(-1);
		}

		/*
		 * Parent waits.
		 */
		pid = waitpid(-1, &status, 0);
		if (pid < 0) {
			errorc("waitpid failed");
			continue;
895
		}
896 897 898
		if (WIFSIGNALED(status)) {
			error("server %d exited with signal %d!\n",
			      pid, WTERMSIG(status));
899
		}
900 901
		else if (WIFEXITED(status)) {
			error("server %d exited with status %d!\n",
902
			      pid, WEXITSTATUS(status));
903
		}
904
		numchildren--;
905 906 907 908 909 910 911 912 913 914

		/*
		 * 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;
			}
915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932
		}
		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
Mike Hibler committed
933 934
	socklen_t		length;
	int			i, udpsock, tcpsock;
935

Mike Hibler's avatar
Mike Hibler committed
936
	/*
937
	 * Setup TCP socket for incoming connections.
Mike Hibler's avatar
Mike Hibler committed
938 939
	 */

940
	/* Create socket from which to read. */
Mike Hibler's avatar
Mike Hibler committed
941 942
	tcpsock = socket(AF_INET, SOCK_STREAM, 0);
	if (tcpsock < 0) {
943
		pfatal("opening stream socket");
944 945
	}

946
	i = 1;
Mike Hibler's avatar
Mike Hibler committed
947
	if (setsockopt(tcpsock, SOL_SOCKET, SO_REUSEADDR,
948
		       (char *)&i, sizeof(i)) < 0)
949
		pwarning("setsockopt(SO_REUSEADDR)");;
950

951 952 953
	/* Create name. */
	name.sin_family = AF_INET;
	name.sin_addr.s_addr = INADDR_ANY;
954
	name.sin_port = htons((u_short) portnum);
Mike Hibler's avatar
Mike Hibler committed
955
	if (bind(tcpsock, (struct sockaddr *) &name, sizeof(name))) {
956
		pfatal("binding stream socket");
957 958 959
	}
	/* Find assigned port value and print it out. */
	length = sizeof(name);
Mike Hibler's avatar
Mike Hibler committed
960
	if (getsockname(tcpsock, (struct sockaddr *) &name, &length)) {
961
		pfatal("getsockname");
962
	}
963
	if (listen(tcpsock, 512) < 0) {
964
		pfatal("listen");
965
	}
966
	info("listening on TCP port %d\n", ntohs(name.sin_port));
967

Mike Hibler's avatar
Mike Hibler committed
968 969 970 971 972 973 974
	/*
	 * Setup UDP socket
	 */

	/* Create socket from which to read. */
	udpsock = socket(AF_INET, SOCK_DGRAM, 0);
	if (udpsock < 0) {
975
		pfatal("opening dgram socket");
Mike Hibler's avatar
Mike Hibler committed
976 977 978 979 980
	}

	i = 1;
	if (setsockopt(udpsock, SOL_SOCKET, SO_REUSEADDR,
		       (char *)&i, sizeof(i)) < 0)
981
		pwarning("setsockopt(SO_REUSEADDR)");;
982 983 984 985

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

Mike Hibler's avatar
Mike Hibler committed
987 988 989
	/* Create name. */
	name.sin_family = AF_INET;
	name.sin_addr.s_addr = INADDR_ANY;
990
	name.sin_port = htons((u_short) portnum);
Mike Hibler's avatar
Mike Hibler committed
991
	if (bind(udpsock, (struct sockaddr *) &name, sizeof(name))) {
992
		pfatal("binding dgram socket");
Mike Hibler's avatar
Mike Hibler committed
993 994 995 996 997
	}

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

1002 1003 1004
	*tcpsockp = tcpsock;
	*udpsockp = udpsock;
	return 0;
1005
}
1006

1007
/*
1008
 * Listen for UDP requests. This is not a secure channel, and so this should
1009 1010 1011
 * eventually be killed off.
 */
static void
1012
udpserver(int sock, int portnum)
1013 1014 1015
{
	char			buf[MYBUFSIZE];
	struct sockaddr_in	client;
Mike Hibler's avatar
Mike Hibler committed
1016 1017
	socklen_t		length;
	int			cc;
1018
	unsigned int		nreq = 0;
1019

1020 1021
	info("udpserver starting: pid=%d sock=%d portnum=%d\n",
	     mypid, sock, portnum);
1022 1023 1024 1025 1026

	/*
	 * Wait for udp connections.
	 */
	while (1) {
1027
		setproctitle("UDP %d: %u done", portnum, nreq);
1028
		length = sizeof(client);
1029
		cc = recvfrom(sock, buf, sizeof(buf) - 1,
1030 1031 1032
			      0, (struct sockaddr *)&client, &length);
		if (cc <= 0) {
			if (cc < 0)
1033 1034
				errorc("Reading UDP request");
			error("UDP Connection aborted\n");
1035
			continue;
1036
		}
1037
		buf[cc] = '\0';
1038
		handle_request(sock, &client, buf, cc, 0);
1039
		nreq++;
1040 1041 1042 1043
	}
	exit(1);
}

1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069
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;
}

1070
/*
1071
 * Listen for TCP requests.
1072 1073
 */
static void
1074
tcpserver(int sock, int portnum)
1075
{
1076
	char			buf[MAXTMCDPACKET];
1077
	struct sockaddr_in	client;
Mike Hibler's avatar
Mike Hibler committed
1078 1079
	socklen_t		length;
	int			cc, newsock;
1080
	unsigned int		nreq = 0;
1081
	struct timeval		tv;
1082

1083 1084
	info("tcpserver starting: pid=%d sock=%d portnum=%d\n",
	     mypid, sock, portnum);
1085 1086 1087 1088 1089

	/*
	 * Wait for TCP connections.
	 */
	while (1) {
1090
		setproctitle("TCP %d: %u done", portnum, nreq);
1091
		length  = sizeof(client);
1092 1093
		newsock = ACCEPT(sock, (struct sockaddr *)&client, &length,
				 readtimo);
1094
		if (newsock < 0) {
1095
			errorc("accepting TCP connection");
1096
			continue;
1097
		}
Mike Hibler's avatar
Mike Hibler committed
1098

1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112
		/*
		 * 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
1113
		/*
1114
		 * Read in the command request.
Mike Hibler's avatar
Mike Hibler committed
1115
		 */
1116
		if ((cc = READ(newsock, buf, sizeof(buf) - 1)) <= 0) {
1117 1118 1119 1120 1121 1122
			if (cc < 0) {
				if (errno == EWOULDBLOCK)
					errorc("Timeout reading TCP request");
				else
					errorc("Error reading TCP request");
			}
1123 1124
			error("TCP connection aborted\n");
			CLOSE(newsock);
1125
			continue;
1126
		}
1127
		buf[cc] = '\0';
1128
		handle_request(newsock, &client, buf, cc, 1);
1129
		CLOSE(newsock);
1130
		nreq++;
1131 1132 1133
	}
	exit(1);
}
Mike Hibler's avatar
Mike Hibler committed
1134

1135 1136 1137
//#define error(x...)	fprintf(stderr, ##x)
//#define info(x...)	fprintf(stderr, ##x)

1138
static int
1139
handle_request(int sock, struct sockaddr_in *client, char *rdata, int rdatalen, int istcp)
1140 1141
{
	struct sockaddr_in redirect_client;
1142
	int		   redirect = 0;
1143
	char		   buf[BUFSIZ], *bp, *cp, *ordata;
1144 1145
	char		   privkeybuf[PRIVKEY_LEN];
	char		   *privkey = (char *) NULL;
1146
	int		   i, overbose = 0, err = 0;
1147
	int		   version = DEFAULT_VERSION;
1148 1149
	tmcdreq_t	   tmcdreq, *reqp = &tmcdreq;

1150
	byteswritten = 0;
1151 1152 1153 1154 1155
#ifdef	WITHSSL
	cp = (istcp ? (isssl ? "SSL" : "TCP") : "UDP");
#else
	cp = (istcp ? "TCP" : "UDP");
#endif
1156
	setproctitle("%s: %s", inet_ntoa(client->sin_addr), cp);
1157

1158 1159 1160 1161
	/*
	 * Init the req structure.
	 */
	bzero(reqp, sizeof(*reqp));
Mike Hibler's avatar
Mike Hibler committed
1162

1163
	/*
1164
	 * Look for special tags.
1165
	 */
1166
	bp = ordata = rdata;
1167
	while ((bp = strsep(&rdata, " ")) != NULL) {
1168
		/*