libsetup.pm 79 KB
Newer Older
1
#!/usr/bin/perl -w
2

Leigh B. Stoller's avatar
Leigh B. Stoller committed
3
4
#
# EMULAB-COPYRIGHT
5
# Copyright (c) 2000-2012 University of Utah and the Flux Group.
Leigh B. Stoller's avatar
Leigh B. Stoller committed
6
7
# All rights reserved.
#
8
# TODO: Signal handlers for protecting db files.
Leigh B. Stoller's avatar
Leigh B. Stoller committed
9

10
11
12
13
14
15
16
#
# Common routines and constants for the client bootime setup stuff.
#
package libsetup;
use Exporter;
@ISA = "Exporter";
@EXPORT =
Ryan Jackson's avatar
Ryan Jackson committed
17
    qw ( libsetup_init libsetup_setvnodeid libsetup_settimeout cleanup_node
18
	 getifconfig getrouterconfig gettrafgenconfig gettunnelconfig
Ryan Jackson's avatar
Ryan Jackson committed
19
	 check_nickname	bootsetup startcmdstatus whatsmynickname
20
	 TBForkCmd vnodejailsetup plabsetup vnodeplabsetup
Ryan Jackson's avatar
Ryan Jackson committed
21
	 jailsetup dojailconfig findiface libsetup_getvnodeid
Timothy Stack's avatar
   
Timothy Stack committed
22
	 ixpsetup libsetup_refresh gettopomap getfwconfig gettiptunnelconfig
23
	 gettraceconfig genhostsfile getmotelogconfig calcroutes fakejailsetup
24
	 getlocalevserver genvnodesetup getgenvnodeconfig stashgenvnodeconfig
25
         getlinkdelayconfig getloadinfo getbootwhat getnodeattributes
26
	 forcecopy getnodeuuid
27
         getmanifest fetchmanifestblobs runbootscript runhooks 
28
         build_fake_macs
29

30
31
	 TBDebugTimeStamp TBDebugTimeStampWithDate
	 TBDebugTimeStampsOn TBDebugTimeStampsOff
32

33
	 MFS REMOTE REMOTEDED CONTROL WINDOWS JAILED PLAB LOCALROOTFS IXP
34
	 USESFS SHADOW FSRVTYPE PROJDIR EXPDIR
35

36
	 SIMTRAFGEN SIMHOST ISDELAYNODEPATH JAILHOST DELAYHOST STARGATE
37
	 ISFW FAKEJAILED LINUXJAILED GENVNODE GENVNODETYPE GENVNODEHOST
Ryan Jackson's avatar
Ryan Jackson committed
38
	 SHAREDHOST SUBBOSS
39

40
	 CONFDIR LOGDIR TMDELAY TMBRIDGES TMJAILNAME TMSIMRC TMCC TMCCBIN
41
	 TMNICKNAME TMSTARTUPCMD FINDIF
42
	 TMROUTECONFIG TMLINKDELAY TMDELMAP TMTOPOMAP TMLTMAP TMLTPMAP
43
	 TMGATEDCONFIG TMSYNCSERVER TMKEYHASH TMNODEID TMNODEUUID TMEVENTKEY
Ryan Jackson's avatar
Ryan Jackson committed
44
	 TMCREATOR TMSWAPPER TMFWCONFIG TMGENVNODECONFIG
Mike Hibler's avatar
Mike Hibler committed
45
	 INXENVM
46
47
48
49
50
       );

# Must come after package declaration!
use English;

51
my $debug = 0;
52

53
54
# The tmcc library.
use libtmcc;
55
use librc;
56
57
58
59
60
61
62

#
# This is the VERSION. We send it through to tmcd so it knows what version
# responses this file is expecting.
#
# BE SURE TO BUMP THIS AS INCOMPATIBILE CHANGES TO TMCD ARE MADE!
#
63
64
65
66
# IMPORTANT NOTE: if you change the version here, you must also change it
# in clientside/lib/tmcd/tmcd.h!
#
sub TMCD_VERSION()	{ 34; };
67
68
69
70
71
libtmcc::configtmcc("version", TMCD_VERSION());

# Control tmcc timeout.
sub libsetup_settimeout($) { libtmcc::configtmcc("timeout", $_[0]); };

72
# Refresh tmcc cache.
73
74
sub libsetup_refresh()	   { libtmcc::tmccgetconfig(); };

75
#
76
77
# For virtual (multiplexed nodes). If defined, tack onto tmcc command.
# and use in pathnames. Used in conjunction with jailed virtual nodes.
78
# I am also using this for subnodes; eventually everything will be subnodes.
79
#
80
my $vnodeid;
81
82
sub libsetup_setvnodeid($)
{
83
84
85
86
87
88
89
90
91
92
93
    my ($vid) = @_;

    if ($vid =~ /^([-\w]+)$/) {
	$vid = $1;
    }
    else {
	die("Bad data in vnodeid: $vid");
    }

    $vnodeid = $vid;
    libtmcc::configtmcc("subnode", $vnodeid);
94
95
96
97
98
}
sub libsetup_getvnodeid()
{
    return $vnodeid;
}
99

100
#
Ryan Jackson's avatar
Ryan Jackson committed
101
102
# True if running inside a jail. Set just below.
#
103
104
my $injail;

105
106
107
108
109
110
#
# True if $injail == TRUE and running on Linux.
# Right now this means vserves on RHL.
#
my $inlinuxjail;

111
112
113
114
115
#
# True if running inside a vm.
#
my $ingenvnode;

116
117
#
# True if running as a fake jail (no jail, just processes).
Ryan Jackson's avatar
Ryan Jackson committed
118
#
119
120
my $nojail;

121
122
123
124
125
#
# True if running in a Plab vserver.
#
my $inplab;

126
127
128
129
130
131
#
# Ditto for IXP, although currently there is no "in" IXP setup; it
# is all done from outside.
#
my $inixp;

132
133
134
135
136
137
#
# Shadow mode. Run the client side against a remote tmcd.
#
my $shadow;
my $SHADOWDIR = "$VARDIR/shadow";

138
139
140
141
142
143
144
#
# Fileserver type.
# Default is "racy NFS" (the historical only choice) until proven otherwise
# (via "mounts" tmcc call).
#
my $fsrvtype = "NFS-RACY";

145
146
147
#
# The role of this pnode
#
148
my $role;
149

150
151
152
# Load up the paths. Its conditionalized to be compatabile with older images.
# Note this file has probably already been loaded by the caller.
BEGIN
153
{
154
155
156
157
158
    if (! -e "/etc/emulab/paths.pm") {
	die("Yikes! Could not require /etc/emulab/paths.pm!\n");
    }
    require "/etc/emulab/paths.pm";
    import emulabpaths;
159
    $SHADOWDIR = "$VARDIR/shadow";
160

161
    # Make sure these exist! They will not exist on a PLAB vserver initially.
162
163
    mkdir("$VARDIR", 0775);
    mkdir("$VARDIR/jails", 0775);
164
    mkdir("$VARDIR/vms", 0775);
165
166
167
168
    mkdir("$VARDIR/db", 0755);
    mkdir("$VARDIR/logs", 0775);
    mkdir("$VARDIR/boot", 0775);
    mkdir("$VARDIR/lock", 0775);
169
170
171
172
173
    mkdir("$SHADOWDIR", 0775);
    mkdir("$SHADOWDIR/db", 0755);
    mkdir("$SHADOWDIR/logs", 0775);
    mkdir("$SHADOWDIR/boot", 0775);
    mkdir("$SHADOWDIR/lock", 0775);
174

175
176
177
178
179
    #
    # Shadow mode allows the client side to run against remote tmcd.
    #
    if (exists($ENV{'SHADOW'})) {
	$shadow = $ENV{'SHADOW'};
180
	my ($server,$idkey) = split(',', $shadow);
181
182
183
184
185
186
187
188
189
	#
	# Need to taint check these to avoid breakage later.
	#
	if ($server =~ /^([-\w\.]+)$/) {
	    $server = $1;
	}
	else {
	    die("Bad data in server: $server");
	}
190
191
	if ($idkey =~ /^([-\w\+\:\.]*)$/) {
	    $idkey = $1;
192
193
	}
	else {
194
	    die("Bad data in urn: $idkey");
195
	}
Ryan Jackson's avatar
Ryan Jackson committed
196

197
198
199
	# The cache needs to go in a difference location.
	libtmcc::configtmcc("cachedir", $SHADOWDIR);
	libtmcc::configtmcc("server", $server);
200
	libtmcc::configtmcc("idkey", $idkey);
201
202
	# No proxy.
	libtmcc::configtmcc("noproxy", 1);
203
    }
204
205
206
    #
    # Determine if running inside a jail. This affects the paths below.
    #
207
    if (-e "$BOOTDIR/jailname") {
208
	open(VN, "$BOOTDIR/jailname");
209
	my $vid = <VN>;
210
211
	close(VN);

212
	libsetup_setvnodeid($vid);
213
	$injail = 1;
214
215
216
	if ($^O eq "linux") {
	    $inlinuxjail = 1;
	}
217
    }
218
219
220
221
222
223
224
    elsif (exists($ENV{'FAKEJAIL'})) {
	# Fake jail.
	libsetup_setvnodeid($ENV{'FAKEJAIL'});
	$nojail = 1;
    }
    elsif (-e "$BOOTDIR/plabname") {
	# Running inside a Plab vserver.
225
	open(VN, "$BOOTDIR/plabname");
226
	my $vid = <VN>;
227
228
	close(VN);

229
	libsetup_setvnodeid($vid);
230
231
	$inplab = 1;
    }
232
233
234
235
236
237
238
239
240
    elsif (-e "$BOOTDIR/vmname") {
	open(VN, "$BOOTDIR/vmname");
	my $vid = <VN>;
	close(VN);

	libsetup_setvnodeid($vid);
	$ingenvnode = 1;

    }
241

242
    $role = "";
Ryan Jackson's avatar
Ryan Jackson committed
243
    # Get our role.
244
245
246
247
    if (-e "$BOOTDIR/role") {
	open(VN, "$BOOTDIR/role");
	$role = <VN>;
	close(VN);
248
	chomp($role);
249
    }
250
251
}

252
#
Ryan Jackson's avatar
Ryan Jackson committed
253
# This "local" library provides the OS dependent part.
254
#
255
use liblocsetup;
256

257
258
259
260
#
# These are the paths of various files and scripts that are part of the
# setup library.
#
261
sub TMCC()		{ "$BINDIR/tmcc"; }
262
sub TMCCBIN()		{ "$BINDIR/tmcc.bin"; }
263
sub FINDIF()		{ "$BINDIR/findif"; }
264
sub TMUSESFS()		{ "$BOOTDIR/usesfs"; }
265
266
sub ISSIMTRAFGENPATH()	{ "$BOOTDIR/simtrafgen"; }
sub ISDELAYNODEPATH()	{ "$BOOTDIR/isdelaynode"; }
267
sub TMTOPOMAP()		{ "$BOOTDIR/topomap";}
Timothy Stack's avatar
   
Timothy Stack committed
268
sub TMLTMAP()		{ "$BOOTDIR/ltmap";}
269
sub TMLTPMAP()		{ "$BOOTDIR/ltpmap";}
270

271
#
272
# This path is valid only *outside* the jail when its setup.
Ryan Jackson's avatar
Ryan Jackson committed
273
#
274
275
sub JAILDIR()		{ "$VARDIR/jails/$vnodeid"; }

276
277
278
279
#
# This path is valid only *outside* the vm.  Sucks, but this is the best we
# can do.  Probably the only way to make this vm-specific if necessary is to
# have them create their dir and symlink.
Ryan Jackson's avatar
Ryan Jackson committed
280
#
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
sub GENVNODEDIR()	{ "$VARDIR/vms/$vnodeid"; }

#
# XXX: eventually this needs to come from tmcd, but that's not ready yet.
#
sub GENVNODETYPE() {
    if (-e "$ETCDIR/genvmtype") {
	my $vmtype = `cat $ETCDIR/genvmtype`;
	chomp($vmtype);
	return $vmtype;
    }

    return undef;
}

296
297
298
#
# Also valid outside the jail, this is where we put local project storage.
#
299
sub LOCALROOTFS() {
Ryan Jackson's avatar
Ryan Jackson committed
300
    return "/users/local"
301
	if (REMOTE());
Ryan Jackson's avatar
Ryan Jackson committed
302
    return "$VARDIR/jails/local"
303
	if (JAILED());
Ryan Jackson's avatar
Ryan Jackson committed
304
    return "$VARDIR/vms/local"
305
306
	if (GENVNODE());
}
307

308
309
310
#
# Okay, here is the path mess. There are three environments.
# 1. A local node where everything goes in one place ($VARDIR/boot).
311
# 2. A virtual node inside a jail or a Plab vserver ($VARDIR/boot).
Ryan Jackson's avatar
Ryan Jackson committed
312
# 3. A virtual (or sub) node, from the outside.
313
#
314
# As for #3, whether setting up a old-style (fake) virtual node or a new style
315
316
317
# jailed node, the code that sets it up needs a different per-vnode path.
#
sub CONFDIR() {
318
319
    return "$SHADOWDIR/boot"
	if ($shadow);
320
    return $BOOTDIR
321
	if ($injail || $inplab || $ingenvnode);
Ryan Jackson's avatar
Ryan Jackson committed
322
    return GENVNODEDIR()
323
	if ($vnodeid && GENVNODETYPE());
324
325
    return JAILDIR()
	if ($vnodeid);
326
    return $BOOTDIR;
327
}
328
329
# Cause of fakejails, we want log files in the right place.
sub LOGDIR() {
330
331
    return "$SHADOWDIR/logs"
	if ($shadow);
332
    return $LOGDIR
333
	if ($injail || $inplab || $ingenvnode);
Ryan Jackson's avatar
Ryan Jackson committed
334
    return GENVNODEDIR()
335
	if ($vnodeid && GENVNODETYPE());
Ryan Jackson's avatar
Ryan Jackson committed
336
    return JAILDIR()
337
338
339
	if ($vnodeid);
    return $LOGDIR;
}
340

341
342
#
# The rest of these depend on the environment running in (inside/outside jail).
Ryan Jackson's avatar
Ryan Jackson committed
343
#
344
345
sub TMNICKNAME()	{ CONFDIR() . "/nickname";}
sub TMJAILNAME()	{ CONFDIR() . "/jailname";}
346
sub TMFAKEJAILNAME()	{ CONFDIR() . "/fakejail";}
347
sub TMJAILCONFIG()	{ CONFDIR() . "/jailconfig";}
348
sub TMGENVNODECONFIG()  { CONFDIR() . "/genvnodeconfig";}
349
350
sub TMSTARTUPCMD()	{ CONFDIR() . "/startupcmd";}
sub TMROUTECONFIG()     { CONFDIR() . "/rc.route";}
351
sub TMGATEDCONFIG()     { CONFDIR() . "/gated.conf";}
352
sub TMBRIDGES()		{ CONFDIR() . "/rc.bridges";}
353
354
sub TMDELAY()		{ CONFDIR() . "/rc.delay";}
sub TMLINKDELAY()	{ CONFDIR() . "/rc.linkdelay";}
355
sub TMDELMAP()		{ CONFDIR() . "/delay_mapping";}
356
sub TMSYNCSERVER()	{ CONFDIR() . "/syncserver";}
357
sub TMKEYHASH()		{ CONFDIR() . "/keyhash";}
358
sub TMEVENTKEY()	{ CONFDIR() . "/eventkey";}
359
sub TMNODEID()		{ CONFDIR() . "/nodeid";}
360
sub TMNODEUUID()	{ CONFDIR() . "/nodeuuid";}
361
362
sub TMROLE()		{ CONFDIR() . "/role";}
sub TMSIMRC()		{ CONFDIR() . "/rc.simulator";}
363
sub TMCREATOR()		{ CONFDIR() . "/creator";}
364
sub TMSWAPPER()		{ CONFDIR() . "/swapper";}
365
sub TMFWCONFIG()	{ CONFDIR() . "/rc.fw";}
366
367
368

#
# This is a debugging thing for my home network.
369
370
371
372
373
374
375
376
377
378
#
my $NODE = "";
if (defined($ENV{'TMCCARGS'})) {
    if ($ENV{'TMCCARGS'} =~ /^([-\w\s]*)$/) {
	$NODE .= " $1";
    }
    else {
	die("Tainted TMCCARGS from environment: $ENV{'TMCCARGS'}!\n");
    }
}
379
380

# Locals
381
382
383
my $pid		= "";
my $eid		= "";
my $vname	= "";
384
385
386
387
388
389
my $TIMESTAMPS  = 0;

# Allow override from the environment;
if (defined($ENV{'TIMESTAMPS'})) {
    $TIMESTAMPS = $ENV{'TIMESTAMPS'};
}
390

391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
#
# Any reason NOT to hardwire these?
#
sub PROJDIR() {
    my $p = $pid;
    if (!$p) {
	($p, undef, undef) = check_nickname();
	return ""
	    if (!$p);
    }
    return "/proj/$p";
}

sub EXPDIR() {
    my $p = $pid;
    my $e = $eid;
    if (!$p || !$e) {
	($p, $e, undef) = check_nickname();
	return ""
	    if (!$p || !$e);
    }
    return "/proj/$p/exp/$e";
}

415
416
417
# When on the MFS, we do a much smaller set of stuff.
# Cause of the way the packages are loaded (which I do not understand),
# this is computed on the fly instead of once.
418
sub MFS()	{ if (-e "$ETCDIR/ismfs") { return 1; } else { return 0; } }
419

420
421
422
#
# Same for a remote node.
#
423
424
sub REMOTE()	{ if (-e "$ETCDIR/isrem") { return 1; } else { return 0; } }

425
426
427
428
429
#
# Same for a dedicated remote node.
#
sub REMOTEDED()	{ if (-e "$ETCDIR/isremded") { return 1; } else { return 0; } }

430
431
432
433
434
#
# Same for a control node.
#
sub CONTROL()	{ if (-e "$ETCDIR/isctrl") { return 1; } else { return 0; } }

435
436
437
#
# Same for a Windows (CygWinXP) node.
#
438
# XXX  If you change this, look in libtmcc::tmccgetconfig() as well.
439
440
sub WINDOWS()	{ if (-e "$ETCDIR/iscygwin") { return 1; } else { return 0; } }

Kirk Webb's avatar
   
Kirk Webb committed
441
442
443
444
445
#
# Same for a stargate/garcia node.
#
sub STARGATE()  { if (-e "$ETCDIR/isstargate") { return 1; } else { return 0; } }

446
447
448
449
#
# Are we jailed? See above.
#
sub JAILED()	{ if ($injail) { return $vnodeid; } else { return 0; } }
450
sub FAKEJAILED(){ if ($nojail) { return $vnodeid; } else { return 0; } }
451
sub LINUXJAILED(){ if ($injail && $inlinuxjail) { return $vnodeid; } else { return 0; } }
452

453
454
455
456
457
#
# Are we using the generic vm abstraction for this vnode?  See above.
#
sub GENVNODE()  { if ($ingenvnode) { return $vnodeid; } else { return 0; } }

458
459
460
461
462
#
# Are we on plab?
#
sub PLAB()	{ if ($inplab) { return $vnodeid; } else { return 0; } }

463
464
465
466
467
#
# Are we on an IXP
#
sub IXP()	{ if ($inixp) { return $vnodeid; } else { return 0; } }

468
469
470
471
472
#
# Are we a firewall node
#
sub ISFW()	{ if (-e TMFWCONFIG()) { return 1; } else { return 0; } }

473
#
474
# Are we hosting a simulator or maybe just a NSE based trafgen.
475
476
#
sub SIMHOST()   { if ($role eq "simhost") { return 1; } else { return 0; } }
477
sub SIMTRAFGEN(){ if (-e ISSIMTRAFGENPATH())  { return 1; } else { return 0; } }
478

Ryan Jackson's avatar
Ryan Jackson committed
479
480
481
482
483
#
# Are we a subboss?
#
sub SUBBOSS()   { if ($role eq "subboss") { return 1; } else { return 0; } }

484
# A jail host?
485
486
487
488
sub JAILHOST()     { return (($role eq "virthost" ||
			      $role eq "sharedhost") ? 1 : 0); }
sub GENVNODEHOST() { if ($role eq "virthost") { return 1; } else { return 0; }}
sub SHAREDHOST()   { return ($role eq "sharedhost" ? 1 : 0); }
489

490
491
492
# A delay host?  Either a delay node or a node using linkdelays
sub DELAYHOST()	{ if (-e ISDELAYNODEPATH()) { return 1; } else { return 0; } }

493
494
495
# A shadow?
sub SHADOW()	   { return (defined($shadow) ? 1 : 0); }

496
#
497
# Is this node using SFS. Several scripts need to know this.
498
#
499
sub USESFS()	{ if (-e TMUSESFS()) { return 1; } else { return 0; } }
500

501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
#
# What type of fileserver is this node using.  Choices are:
#
# NFS-RACY	FreeBSD NFS server with mountd race (the default)
# NFS		NFS server
# LOCAL		No shared filesystems
#
# XXX should come from tmcd
#
sub FSRVTYPE() {
    if (-e "$BOOTDIR/fileserver") {
	open(FD, "$BOOTDIR/fileserver");
	$fsrvtype = <FD>;
	close(FD);
	chomp($fsrvtype);
    }
    return $fsrvtype;
}

# XXX fer now hack: comes from rc.mounts
sub setFSRVTYPE($) {
    $fsrvtype = shift;
    if (open(FD, ">$BOOTDIR/fileserver")) {
	print FD "$fsrvtype\n";
	close(FD);
    }
}

Mike Hibler's avatar
Mike Hibler committed
529
530
531
532
533
#
# XXX fernow hack so I can readily identify code that is special to Xen VMs
#
sub INXENVM()	{ return ($ingenvnode && GENVNODETYPE() eq "xen"); }

534
535
536
#
# Reset to a moderately clean state.
#
537
538
sub cleanup_node ($) {
    my ($scrub) = @_;
Ryan Jackson's avatar
Ryan Jackson committed
539

540
    print STDOUT "Cleaning node; removing configuration files\n";
541
    unlink TMUSESFS, TMROLE, ISSIMTRAFGENPATH, ISDELAYNODEPATH;
542

543
    #
544
545
    # If scrubbing, also remove the password/group files and DBs so
    # that we revert to base set.
Ryan Jackson's avatar
Ryan Jackson committed
546
    #
547
    if ($scrub) {
548
	unlink TMNICKNAME;
549
550
551
552
    }
}

#
553
554
# Check node allocation. If the nickname file has been created, use
# that to avoid load on tmcd.
555
556
557
558
559
#
# Returns 0 if node is free. Returns list (pid/eid/vname) if allocated.
#
sub check_status ()
{
560
561
562
    my @tmccresults;

    if (tmcc(TMCCCMD_STATUS, undef, \@tmccresults) < 0) {
563
564
	warn("*** WARNING: Could not get status from server!\n");
	return -1;
565
    }
566
567
568
569
    #
    # This is possible if the boss node does not now about us yet.
    # We want to appear free. Specifically, it could happen on the
    # MFS when trying to bring in brand new nodes. tmcd will not know
Ryan Jackson's avatar
Ryan Jackson committed
570
    # anything about us, and return no info.
571
572
573
574
575
    #
    return 0
	if (! @tmccresults);

    my $status = $tmccresults[0];
576

577
    if ($status =~ /^FREE/) {
578
	unlink TMNICKNAME;
579
580
	return 0;
    }
Ryan Jackson's avatar
Ryan Jackson committed
581

582
    if ($status =~ /ALLOCATED=([-\@\w]*)\/([-\@\w]*) NICKNAME=([-\@\w]*)/) {
583
584
585
586
587
588
	$pid   = $1;
	$eid   = $2;
	$vname = $3;
    }
    else {
	warn "*** WARNING: Error getting reservation status\n";
589
	return -1;
590
    }
Ryan Jackson's avatar
Ryan Jackson committed
591

592
593
    #
    # Stick our nickname in a file in case someone wants it.
594
595
    # Do not overwrite; we want to save the original info until later.
    # See bootsetup; indicates project change!
596
    #
597
    if (! -e TMNICKNAME()) {
598
599
	system("echo '$vname.$eid.$pid' > " . TMNICKNAME());
    }
Ryan Jackson's avatar
Ryan Jackson committed
600

601
602
603
604
    return ($pid, $eid, $vname);
}

#
605
606
# Check cached nickname. Its okay if we have been deallocated and the info
# is stale. The node will notice that later.
Ryan Jackson's avatar
Ryan Jackson committed
607
#
608
sub check_nickname()
609
{
610
611
612
    if (-e TMNICKNAME) {
	my $nickfile = TMNICKNAME;
	my $nickinfo = `cat $nickfile`;
613

614
615
616
617
618
619
620
621
622
	if ($nickinfo =~ /([-\@\w]*)\.([-\@\w]*)\.([-\@\w]*)/) {
	    $vname = $1;
	    $eid   = $2;
	    $pid   = $3;

	    return ($pid, $eid, $vname);
	}
    }
    return check_status();
623
624
625
}

#
626
627
# Do SFS hostid setup. If we have an SFS host key and we can get a hostid
# from the SFS daemon, then send it to TMCD.
Austin Clements's avatar
Austin Clements committed
628
#
629
sub initsfs()
Austin Clements's avatar
Austin Clements committed
630
631
632
{
    my $myhostid;

633
634
635
    # Default to no SFS unless we can determine we have it running.
    unlink TMUSESFS()
	if (-e TMUSESFS());
Ryan Jackson's avatar
Ryan Jackson committed
636

637
    # Do I have a host key?
Austin Clements's avatar
Austin Clements committed
638
    if (! -e "/etc/sfs/sfs_host_key") {
639
	return;
Austin Clements's avatar
Austin Clements committed
640
641
642
    }

    # Give hostid to TMCD
643
644
645
646
647
648
    if (-d "/usr/local/lib/sfs-0.6") {
	$myhostid = `sfskey hostid - 2>/dev/null`;
    }
    else {
	$myhostid = `sfskey hostid -s authserv - 2>/dev/null`;
    }
649
    if (! $?) {
Austin Clements's avatar
Austin Clements committed
650
651
652
	if ( $myhostid =~ /^([-\.\w_]*:[a-z0-9]*)$/ ) {
	    $myhostid = $1;
	    print STDOUT "  Hostid: $myhostid\n";
653
	    tmcc(TMCCCMD_SFSHOSTID, "$myhostid");
Austin Clements's avatar
Austin Clements committed
654
	}
655
656
657
	elsif ( $myhostid =~ /^(@[-\.\w_]*,[a-z0-9]*)$/ ) {
	    $myhostid = $1;
	    print STDOUT "  Hostid: $myhostid\n";
658
	    tmcc(TMCCCMD_SFSHOSTID, "$myhostid");
659
	}
Austin Clements's avatar
Austin Clements committed
660
661
	else {
	    warn "*** WARNING: Invalid hostid\n";
662
	    return;
Austin Clements's avatar
Austin Clements committed
663
	}
664
	system("touch " . TMUSESFS());
Austin Clements's avatar
Austin Clements committed
665
666
    }
    else {
667
	warn "*** WARNING: Could not retrieve this node's SFShostid!\n";
Austin Clements's avatar
Austin Clements committed
668
    }
669
670
671
}

#
Ryan Jackson's avatar
Ryan Jackson committed
672
673
# Get the role of the node and stash it for future libsetup load.
#
674
675
676
sub dorole()
{
    my @tmccresults;
Austin Clements's avatar
Austin Clements committed
677

678
679
680
681
682
683
    if (tmcc(TMCCCMD_ROLE, undef, \@tmccresults) < 0) {
	warn("*** WARNING: Could not get role from server!\n");
	return -1;
    }
    return 0
	if (! @tmccresults);
Ryan Jackson's avatar
Ryan Jackson committed
684

685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
    #
    # There should be just one string. Ignore anything else.
    #
    if ($tmccresults[0] =~ /([\w]*)/) {
	# Storing the value into the global variable
	$role = $1;
    }
    else {
	warn "*** WARNING: Bad role line: $tmccresults[0]";
	return -1;
    }
    system("echo '$role' > " . TMROLE());
    if ($?) {
	warn "*** WARNING: Could not write role to " . TMROLE() . "\n";
    }
Austin Clements's avatar
Austin Clements committed
700
701
702
    return 0;
}

703
704
#
# Get the nodeid
Ryan Jackson's avatar
Ryan Jackson committed
705
#
706
707
708
709
710
sub donodeid()
{
    my $nodeid;
    my @tmccresults;

711
712
713
    # Do this too.
    donodeuuid();

714
715
716
717
718
719
    if (tmcc(TMCCCMD_NODEID, undef, \@tmccresults) < 0) {
	warn("*** WARNING: Could not get nodeid from server!\n");
	return -1;
    }
    return 0
	if (! @tmccresults);
Ryan Jackson's avatar
Ryan Jackson committed
720

721
722
723
724
725
726
727
728
729
730
    #
    # There should be just one string. Ignore anything else.
    #
    if ($tmccresults[0] =~ /([-\w]*)/) {
	$nodeid = $1;
    }
    else {
	warn "*** WARNING: Bad nodeid line: $tmccresults[0]";
	return -1;
    }
Ryan Jackson's avatar
Ryan Jackson committed
731

732
733
734
735
736
737
738
    system("echo '$nodeid' > ". TMNODEID);
    if ($?) {
	warn "*** WARNING: Could not write nodeid to " . TMNODEID() . "\n";
    }
    return 0;
}

739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
#
# Get the nodeuuid
#
sub donodeuuid()
{
    my $nodeuuid;
    my @tmccresults;

    if (tmcc(TMCCCMD_NODEUUID, undef, \@tmccresults) < 0) {
	warn("*** WARNING: Could not get nodeuuid from server!\n");
	return -1;
    }
    return 0
	if (! @tmccresults);

    #
    # There should be just one string. Ignore anything else.
    #
    if ($tmccresults[0] =~ /([-\w]*)/) {
	$nodeuuid = $1;
    }
    else {
	warn "*** WARNING: Bad nodeuuid line: $tmccresults[0]";
	return -1;
    }

    system("echo '$nodeuuid' > ". TMNODEUUID);
    if ($?) {
	warn "*** WARNING: Could not write nodeuuid to " . TMNODEUUID() . "\n";
    }
    return 0;
}

772
773
774
775
776
777
778
779
780
781
782
783
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
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
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
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
#
# Get the boot script manifest -- whether scripts are enabled, or hooked, and 
# how and when they or their hooks run!
#
sub getmanifest($;$)
{
    my ($rptr,$nofetch) = @_;
    my @tmccresults;
    my %manifest = ();

    print "Checking manifest...\n";

    if (tmcc(TMCCCMD_MANIFEST, undef, \@tmccresults) < 0) {
	warn("*** WARNING: Could not get manifest from server!\n");
	%$rptr = ();
	return -1;
    }
    if (@tmccresults == 0) {
	%$rptr = ();
	return 0;
    }

    my $servicepat = q(SERVICE NAME=([\w\.\-]+) ENV=(\w+) WHENCE=(\w+));
    $servicepat   .= q( ENABLED=(0|1) HOOKS_ENABLED=(0|1));
    $servicepat   .= q( FATAL=(0|1) BLOBID=([\w\-]*));

    my $hookpat = q(HOOK SERVICE=([\w\.\-]+) ENV=(\w+) WHENCE=(\w+));
    $hookpat   .= q( OP=(\w+) POINT=(\w+));
    $hookpat   .= q( FATAL=(0|1) BLOBID=([\w\-]+));
    $hookpat   .= q( ARGV="([^"]*)");

    my @loadinforesults = ();
    if (tmcc(TMCCCMD_LOADINFO, undef, \@loadinforesults) < 0) {
	warn("*** WARNING: getmanifest could not get loadinfo from server,\n".
	     "             unsure if node is in MFS and reloading, continuing!\n");
    }

    #
    # Are we in a loading environment?  If yes, filter the manifest
    # so that only the service and hook settings that apply to the 
    # loading MFS apply.
    #
    if (@loadinforesults && MFS()) {
	$manifest{'_ENV'} = 'load';
    }
    #
    # Otherwise, if we're not in an MFS, we must be booting!
    # NOTE: we don't do any configuration of the image in the 
    # admin MFS!
    #
    elsif (!MFS()) {
	$manifest{'_ENV'} = 'boot';
    }
    #
    # Otherwise, don't return *anything* -- the admin mfs doesn't do any
    # config of the node.
    #
    else {
	%$rptr = ();
	return 0;
    }

    #
    # Process our results.
    #
    for (my $i = 0; $i < @tmccresults; ++$i) {
	my $line = $tmccresults[$i];
	my %service;

	if ($line =~ /^$servicepat/) {
	    my %service = ( 'ENABLED' => $4,
			    'HOOKS_ENABLED' => $5,
			    'BLOBID' => $7,
			    'WHENCE' => $3,
			    'FATAL' => $6 );
	    #
	    # Filter the service part of the manifest so that only the 
	    # settings that apply here are passed to scripts.
	    #
	    if ($2 eq $manifest{'_ENV'}) {
		# assume that there might be a hook line that applied to this
		# service ahead of the service line!  Other possibility
		# is that there was already a service line for this env...
		# which is a bug -- so just silently stomp it in that case.
		#
		# anyway, just update the results pointer for this service
		foreach my $k (keys(%service)) {
		    $manifest{$1}{$k} = $service{$k};
		}

		if (!exists($manifest{$1}{'_PREHOOKS'})) {
		    $manifest{$1}{'_PREHOOKS'} = [];
		}
		if (!exists($manifest{$1}{'_POSTHOOKS'})) {
		    $manifest{$1}{'_POSTHOOKS'} = [];
		}
	    }
	    # Otherwise just skip this entry -- it doesn't apply to us.
	    else {
		next;
	    }
	}
	elsif ($line =~ /^$hookpat/) {
	    #
	    # Filter the service part of the manifest so that only the 
	    # settings that apply here are passed to scripts.
	    #
	    if ($2 eq $manifest{'_ENV'}) {
		my $hookstr = "_" . uc($5) . "HOOKS";
		if (!exists($manifest{$1}) || !exists($manifest{$1}{$hookstr})) {
		    $manifest{$1}{$hookstr} = [];
		}

		my $hook = { 'BLOBID' => $7,
			     'OP' => $4, 
			     'WHENCE' => $3, 
			     'FATAL' => $6, 
			     'ARGV' => $8 };

		$manifest{$1}{$hookstr}->[@{$manifest{$1}{$hookstr}}] = $hook;
	    }
	    # Otherwise just skip this entry -- it doesn't apply to us.
	    else {
		next;
	    }
	}
	else {
	    warn("*** WARNING: did not recognize manifest line '$line'," . 
		 " continuing!\n");
	}
    }

    my $retval = 0;

    if (!defined($nofetch) || $nofetch != 1) {
	print "Downloading any manifest blobs...\n";
	%$rptr = %manifest;
	$retval = fetchmanifestblobs($rptr,undef,'manifest');
    }

    %$rptr = %manifest;
    return $retval;
}

sub fetchmanifestblobs($;$$)
{
    my ($manifest,$savedir,$basename) = @_;
    if (!defined($savedir)) {
	$savedir = $BLOBDIR;
    }
    if (!defined($basename)) {
	$basename = '';
    }
    my $blobpath = "$savedir/$basename";
    my $retval;
    my $failed = 0;

    foreach my $script (keys(%$manifest)) {
	# first grab the script replacement...
	if (exists($manifest->{$script}{'BLOBID'})
	    && $manifest->{$script}{'BLOBID'} ne '') {
	    my $bpath = $blobpath . "." . $manifest->{$script}{'BLOBID'};
	    $retval = libtmcc::blob::getblob($manifest->{$script}{'BLOBID'},
					     $bpath);
	    if ($retval == -1) {
		print STDERR "ERROR(fetchmanifestblobs): could not fetch " . 
		    $manifest->{$script}{'BLOBID'} . "!\n";
		++$failed;
	    }
	    else {
		$manifest->{$script}{'BLOBPATH'} = $bpath;
		chmod(0755,$bpath);
	    }
	}

	# now do hooks...
	my @hooktypes = ('_PREHOOKS','_POSTHOOKS');
	foreach my $hooktype (@hooktypes) {
	    next 
		if (!exists($manifest->{$script}{$hooktype}));

	    foreach my $hook (@{$manifest->{$script}{$hooktype}}) {
		my $bpath = $blobpath . "." . $hook->{'BLOBID'};
		$retval = libtmcc::blob::getblob($hook->{'BLOBID'},$bpath);
		if ($retval == -1) {
		    print STDERR "ERROR(fetchmanifestblobs): could not fetch " . 
			$hook->{'BLOBID'} . "!\n";
		    ++$failed;
		}
		else {
		    $hook->{'BLOBPATH'} = $bpath;
		    chmod(0755,$bpath);
		}
	    }
	}
		
    }

    return $failed;
}

sub runhooks($$$$)
{
    my ($manifest,$which,$script,$what) = @_;
    my $hookstr = "_".uc($which)."HOOKS";
    my $failed = 0;

    if (exists($manifest->{$script}) 
	# if hooks are enabled because of a service line
	&& ((exists($manifest->{$script}{'HOOKS_ENABLED'}) 
	     && $manifest->{$script}{'HOOKS_ENABLED'} == 1)
	    # or if there was no service line, in which case hooks are
	    # enabled by default
	    || !exists($manifest->{$script}{'HOOKS_ENABLED'}))
	&& exists($manifest->{$script}{$hookstr})) {
	print "  Running $script $which hooks\n"
	    if ($debug);

	for (my $i = 0; $i < @{$manifest->{$script}{$hookstr}}; ++$i) {
	    my $hook = $manifest->{$script}{$hookstr}->[$i];
	    my $blobid = $hook->{'BLOBID'};
	    my $argv = $hook->{'ARGV'};
	    my $hookrunfile = "$VARDIR/db/$script.${which}hook.$blobid.run";

	    # if the path doesn't exist, probably we failed to fetch the blob
	    if (!exists($hook->{'BLOBPATH'})) {
		++$failed;
		if ($hook->{'FATAL'}) {
		    fatal("Failed running $script $which hook $blobid (no blobpath!)");
For faster browsing, not all history is shown. View entire blame