libsetup.pm 76.5 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-2011 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
	 TBBackGround 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 gendhcpdconf
26
	 forcecopy
27
         getmanifest fetchmanifestblobs runbootscript runhooks 
28

29
30
	 TBDebugTimeStamp TBDebugTimeStampsOn

31
	 MFS REMOTE REMOTEDED CONTROL WINDOWS JAILED PLAB LOCALROOTFS IXP
32
	 USESFS SHADOW FSRVTYPE PROJDIR EXPDIR
33

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

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

# Must come after package declaration!
use English;

49
my $debug = 0;
50

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

#
# 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!
#
61
sub TMCD_VERSION()	{ 33; };
62
63
64
65
66
libtmcc::configtmcc("version", TMCD_VERSION());

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

67
# Refresh tmcc cache.
68
69
sub libsetup_refresh()	   { libtmcc::tmccgetconfig(); };

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

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

    $vnodeid = $vid;
    libtmcc::configtmcc("subnode", $vnodeid);
89
90
91
92
93
}
sub libsetup_getvnodeid()
{
    return $vnodeid;
}
94

95
#
Ryan Jackson's avatar
Ryan Jackson committed
96
97
# True if running inside a jail. Set just below.
#
98
99
my $injail;

100
101
102
103
104
105
#
# True if $injail == TRUE and running on Linux.
# Right now this means vserves on RHL.
#
my $inlinuxjail;

106
107
108
109
110
#
# True if running inside a vm.
#
my $ingenvnode;

111
112
#
# True if running as a fake jail (no jail, just processes).
Ryan Jackson's avatar
Ryan Jackson committed
113
#
114
115
my $nojail;

116
117
118
119
120
#
# True if running in a Plab vserver.
#
my $inplab;

121
122
123
124
125
126
#
# Ditto for IXP, although currently there is no "in" IXP setup; it
# is all done from outside.
#
my $inixp;

127
128
129
130
131
132
#
# Shadow mode. Run the client side against a remote tmcd.
#
my $shadow;
my $SHADOWDIR = "$VARDIR/shadow";

133
134
135
136
137
138
139
#
# Fileserver type.
# Default is "racy NFS" (the historical only choice) until proven otherwise
# (via "mounts" tmcc call).
#
my $fsrvtype = "NFS-RACY";

140
141
142
#
# The role of this pnode
#
143
my $role;
144

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

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

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

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

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

224
	libsetup_setvnodeid($vid);
225
226
	$inplab = 1;
    }
227
228
229
230
231
232
233
234
235
    elsif (-e "$BOOTDIR/vmname") {
	open(VN, "$BOOTDIR/vmname");
	my $vid = <VN>;
	close(VN);

	libsetup_setvnodeid($vid);
	$ingenvnode = 1;

    }
236

237
    $role = "";
Ryan Jackson's avatar
Ryan Jackson committed
238
    # Get our role.
239
240
241
242
    if (-e "$BOOTDIR/role") {
	open(VN, "$BOOTDIR/role");
	$role = <VN>;
	close(VN);
243
	chomp($role);
244
    }
245
246
}

247
#
Ryan Jackson's avatar
Ryan Jackson committed
248
# This "local" library provides the OS dependent part.
249
#
250
use liblocsetup;
251

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

266
#
267
# This path is valid only *outside* the jail when its setup.
Ryan Jackson's avatar
Ryan Jackson committed
268
#
269
270
sub JAILDIR()		{ "$VARDIR/jails/$vnodeid"; }

271
272
273
274
#
# 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
275
#
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
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;
}

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

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

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

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

# Locals
375
376
377
my $pid		= "";
my $eid		= "";
my $vname	= "";
378
379
380
381
382
383
my $TIMESTAMPS  = 0;

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

385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
#
# 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";
}

409
410
411
# 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.
412
sub MFS()	{ if (-e "$ETCDIR/ismfs") { return 1; } else { return 0; } }
413

414
415
416
#
# Same for a remote node.
#
417
418
sub REMOTE()	{ if (-e "$ETCDIR/isrem") { return 1; } else { return 0; } }

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

424
425
426
427
428
#
# Same for a control node.
#
sub CONTROL()	{ if (-e "$ETCDIR/isctrl") { return 1; } else { return 0; } }

429
430
431
#
# Same for a Windows (CygWinXP) node.
#
432
# XXX  If you change this, look in libtmcc::tmccgetconfig() as well.
433
434
sub WINDOWS()	{ if (-e "$ETCDIR/iscygwin") { return 1; } else { return 0; } }

Kirk Webb's avatar
   
Kirk Webb committed
435
436
437
438
439
#
# Same for a stargate/garcia node.
#
sub STARGATE()  { if (-e "$ETCDIR/isstargate") { return 1; } else { return 0; } }

440
441
442
443
#
# Are we jailed? See above.
#
sub JAILED()	{ if ($injail) { return $vnodeid; } else { return 0; } }
444
sub FAKEJAILED(){ if ($nojail) { return $vnodeid; } else { return 0; } }
445
sub LINUXJAILED(){ if ($injail && $inlinuxjail) { return $vnodeid; } else { return 0; } }
446

447
448
449
450
451
#
# Are we using the generic vm abstraction for this vnode?  See above.
#
sub GENVNODE()  { if ($ingenvnode) { return $vnodeid; } else { return 0; } }

452
453
454
455
456
#
# Are we on plab?
#
sub PLAB()	{ if ($inplab) { return $vnodeid; } else { return 0; } }

457
458
459
460
461
#
# Are we on an IXP
#
sub IXP()	{ if ($inixp) { return $vnodeid; } else { return 0; } }

462
463
464
465
466
#
# Are we a firewall node
#
sub ISFW()	{ if (-e TMFWCONFIG()) { return 1; } else { return 0; } }

467
#
468
# Are we hosting a simulator or maybe just a NSE based trafgen.
469
470
#
sub SIMHOST()   { if ($role eq "simhost") { return 1; } else { return 0; } }
471
sub SIMTRAFGEN(){ if (-e ISSIMTRAFGENPATH())  { return 1; } else { return 0; } }
472

Ryan Jackson's avatar
Ryan Jackson committed
473
474
475
476
477
#
# Are we a subboss?
#
sub SUBBOSS()   { if ($role eq "subboss") { return 1; } else { return 0; } }

478
# A jail host?
479
480
481
482
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); }
483

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

487
488
489
# A shadow?
sub SHADOW()	   { return (defined($shadow) ? 1 : 0); }

490
#
491
# Is this node using SFS. Several scripts need to know this.
492
#
493
sub USESFS()	{ if (-e TMUSESFS()) { return 1; } else { return 0; } }
494

495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
#
# 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
523
524
525
526
527
#
# XXX fernow hack so I can readily identify code that is special to Xen VMs
#
sub INXENVM()	{ return ($ingenvnode && GENVNODETYPE() eq "xen"); }

528
529
530
#
# Reset to a moderately clean state.
#
531
532
sub cleanup_node ($) {
    my ($scrub) = @_;
Ryan Jackson's avatar
Ryan Jackson committed
533

534
    print STDOUT "Cleaning node; removing configuration files\n";
535
    unlink TMUSESFS, TMROLE, ISSIMTRAFGENPATH, ISDELAYNODEPATH;
536

537
    #
538
539
    # If scrubbing, also remove the password/group files and DBs so
    # that we revert to base set.
Ryan Jackson's avatar
Ryan Jackson committed
540
    #
541
    if ($scrub) {
542
	unlink TMNICKNAME;
543
544
545
546
    }
}

#
547
548
# Check node allocation. If the nickname file has been created, use
# that to avoid load on tmcd.
549
550
551
552
553
#
# Returns 0 if node is free. Returns list (pid/eid/vname) if allocated.
#
sub check_status ()
{
554
555
556
    my @tmccresults;

    if (tmcc(TMCCCMD_STATUS, undef, \@tmccresults) < 0) {
557
558
	warn("*** WARNING: Could not get status from server!\n");
	return -1;
559
    }
560
561
562
563
    #
    # 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
564
    # anything about us, and return no info.
565
566
567
568
569
    #
    return 0
	if (! @tmccresults);

    my $status = $tmccresults[0];
570

571
    if ($status =~ /^FREE/) {
572
	unlink TMNICKNAME;
573
574
	return 0;
    }
Ryan Jackson's avatar
Ryan Jackson committed
575

576
    if ($status =~ /ALLOCATED=([-\@\w]*)\/([-\@\w]*) NICKNAME=([-\@\w]*)/) {
577
578
579
580
581
582
	$pid   = $1;
	$eid   = $2;
	$vname = $3;
    }
    else {
	warn "*** WARNING: Error getting reservation status\n";
583
	return -1;
584
    }
Ryan Jackson's avatar
Ryan Jackson committed
585

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

595
596
597
598
    return ($pid, $eid, $vname);
}

#
599
600
# 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
601
#
602
sub check_nickname()
603
{
604
605
606
    if (-e TMNICKNAME) {
	my $nickfile = TMNICKNAME;
	my $nickinfo = `cat $nickfile`;
607

608
609
610
611
612
613
614
615
616
	if ($nickinfo =~ /([-\@\w]*)\.([-\@\w]*)\.([-\@\w]*)/) {
	    $vname = $1;
	    $eid   = $2;
	    $pid   = $3;

	    return ($pid, $eid, $vname);
	}
    }
    return check_status();
617
618
619
}

#
620
621
# 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
622
#
623
sub initsfs()
Austin Clements's avatar
Austin Clements committed
624
625
626
{
    my $myhostid;

627
628
629
    # Default to no SFS unless we can determine we have it running.
    unlink TMUSESFS()
	if (-e TMUSESFS());
Ryan Jackson's avatar
Ryan Jackson committed
630

631
    # Do I have a host key?
Austin Clements's avatar
Austin Clements committed
632
    if (! -e "/etc/sfs/sfs_host_key") {
633
	return;
Austin Clements's avatar
Austin Clements committed
634
635
636
    }

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

#
Ryan Jackson's avatar
Ryan Jackson committed
666
667
# Get the role of the node and stash it for future libsetup load.
#
668
669
670
sub dorole()
{
    my @tmccresults;
Austin Clements's avatar
Austin Clements committed
671

672
673
674
675
676
677
    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
678

679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
    #
    # 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
694
695
696
    return 0;
}

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

    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
711

712
713
714
715
716
717
718
719
720
721
    #
    # 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
722

723
724
725
726
727
728
729
    system("echo '$nodeid' > ". TMNODEID);
    if ($?) {
	warn "*** WARNING: Could not write nodeid to " . TMNODEID() . "\n";
    }
    return 0;
}

730
731
732
733
734
735
736
737
738
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
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
#
# 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!)");
		}
		else {
		    warn("  $script $which hook $blobid failed! (no blobpath!)");
		}
		next;
	    }

	    my $blobpath = $hook->{'BLOBPATH'};

	    # Only run the hook if its operation matches the operation we're
	    # doing (boot,shutdown,reconfig,reset)
	    if ($hook->{'OP'} ne $what) {
		next;
	    }

	    # If this is a first-only hook, skip if we've already done it!
	    if ($hook->{'WHENCE'} eq 'first' && -e $hookrunfile) {
976
977
		print "  Not running $which hook $blobid (first config only)\n" 
		    if ($debug);
978
979
980
		next;
	    }

981
	    print "  Running $script $which hook $blobid\n";
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031

	    # NOTE: the last arg is always $what (boot,shutdown,reconfig,reset)
	    system("$blobpath $argv $what");
	    if ($?) {
		++$failed;
		if ($hook->{'FATAL'} == 1) {
		    fatal("Failed running $script $which hook $blobid");
		}
		else {
		    warn("  $script $which hook $blobid failed! ($?)");
		}
		# Don't write the hook run file if the hook failed!
		next;
	    }

	    open(FD,">$hookrunfile")
		or warn("open($hookrunfile): $!");
	    close(FD);
	}
    }

    return $failed;
}

sub runbootscript($$$$;@)
{
    my ($manifest,$path,$script,$what,@args) = @_;
    my $failed = 0;
    my $runfile = "$VARDIR/db/$script.run";
    # do we have a manifest entry for this script, and is it more than
    # just hooks!
    my $havemanifest = 0;
    if (exists($manifest->{$script}) 
	&& exists($manifest->{$script}{'ENABLED'})) {
	$havemanifest = 1;
    }

    #
    # If the script does not exist, don't run it or any hooks specified for it!
    # If we don't have a path, it's a "virtual" script like TBSETUP or ISUP, so
    # don't skip the hooks.  Only skip the script.
    #
    return 0
	if (defined($path) && ! -x "$path/$script");

    #
    # Don't do anything if the script or its hooks are disabled!
    #
    if ($havemanifest && $manifest->{$script}{'ENABLED'} != 1
	&& $manifest->{$script}{'HOOKS_ENABLED'} != 1) {
1032
	print "Not running $script or hooks (disabled)\n";
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
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
	return 0;
    }

    TBDebugTimeStamp("Executing $script");

    #
    # Handle any pre hooks
    #
    $failed += runhooks($manifest,'pre',$script,$what);

    #
    # Handle the script itself -- if there is a path defined.  If $path is
    # undef, it means that we don't really have a script to run here -- we
    # just want to run pre and post hooks around a bit of code -- like in 
    # rc.bootsetup where we tell tmcd we're in TBSETUP or ISUP.
    #
    if (defined($path) && (!$havemanifest
			   || $manifest->{$script}{'ENABLED'} == 1)) {
	# If this is a first-only script, skip if we've already done it!
	if ($havemanifest && $manifest->{$script}{'WHENCE'} eq 'first' 
	    && -e $runfile) {
	    print "  Not running $script (first config only)\n";
	    return 0;
	}
	
	# If there are no args, we pass $what as the single arg!
	# XXX actually pass user defined args!
	my $argv = "";
	if (@args) {
	    $argv .= " " . join(' ',@args);
	}
	else {
	    $argv .= " $what";
	}
	if ($havemanifest && $manifest->{$script}{'BLOBID'} ne '') {
	    my $blobpath = $manifest->{$script}{'BLOBPATH'};
1069
	    print "  Running $blobpath (instead of $path/$script)\n";
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
	    system("$blobpath $argv");
	}
	else {
	    print "  Running $path/$script\n"
		if ($debug);
	    system("$path/$script $argv");
	}
	if ($?) {
	    ++$failed;
	    if (exists($manifest->{$script}) 
		&& exists($manifest->{$script}{'FATAL'}) 
		&& $manifest->{$script}{'FATAL'} == 1) {
		fatal("  Failed running $script ($?)!");
	    }
	    else {
		warn("  Failed running $script ($?)!");
	    }
	    return 0;
	}

	open(FD,">$runfile")
	    or warn("open($runfile): $!");
	close(FD);
    }

    #
    # Handle any post hooks
    #
    $failed += runhooks($manifest,'post',$script,$what);

    return $failed;
}

1103
#
1104
1105
1106
# Parse the router config and return a hash. This leaves the ugly pattern
# matching stuff here, but lets the caller do whatever with it (as is the
# case for the IXP configuration stuff). This is inconsistent with many
Ryan Jackson's avatar
Ryan Jackson committed
1107
# other config scripts, but at some point that will change.
1108
#
1109
sub getifconfig($;$)
1110
{
1111
    my ($rptr,$nocache) = @_;	# Return list to caller (reference).
1112
    my @tmccresults  = ();
1113
    my @ifacelist    = ();	# To be returned to caller.
1114
    my %ifacehash    = ();
1115

1116
1117
1118
1119
1120
1121
    my %tmccopts = ();
    if ($nocache) {
	$tmccopts{"nocache"} = 1;
    }

    if (tmcc(TMCCCMD_IFC, undef, \@tmccresults, %tmccopts) < 0) {
1122
	warn("*** WARNING: Could not get interface config from server!\n");
1123
	@$rptr = ();
1124
1125
	return -1;
    }
Ryan Jackson's avatar
Ryan Jackson committed
1126

1127
    my $ethpat  = q(INTERFACE IFACETYPE=(\w*) INET=([0-9.]*) MASK=([0-9.]*) );
1128
    $ethpat    .= q(MAC=(\w*) SPEED=(\w*) DUPLEX=(\w*) );
1129
    $ethpat    .= q(IFACE=(\w*) RTABID=(\d*) LAN=([-\w\(\)]*));
1130

1131
1132
    my $vethpat = q(INTERFACE IFACETYPE=(\w*) INET=([0-9.]*) MASK=([0-9.]*) );
    $vethpat   .= q(ID=(\d*) VMAC=(\w*) PMAC=(\w*) RTABID=(\d*) );
1133
    $vethpat   .= q(ENCAPSULATE=(\d*) LAN=([-\w\(\)]*) VTAG=(\d*));
1134
1135
1136

    my $setpat  = q(INTERFACE_SETTING MAC=(\w*) );
    $setpat    .= q(KEY='([-\w\.\:]*)' VAL='([-\w\.\:]*)');
1137

1138
1139
1140
    # XXX see very**3 special hack below
    my $hastvirt = 0;

1141
1142
    foreach my $str (@tmccresults) {
	my $ifconfig = {};
1143
1144
1145
1146
1147

	if ($str =~ /^$setpat/) {
	    my $mac     = $1;
	    my $capkey  = $2;
	    my $capval  = $3;
Ryan Jackson's avatar
Ryan Jackson committed
1148

1149
	    #
Ryan Jackson's avatar
Ryan Jackson committed
1150
	    # Stash the setting into the setting list, but must find the
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
	    #
	    if (!exists($ifacehash{$mac})) {
		warn("*** WARNING: ".
		     "Could not map $mac for its interface settings!\n");
		next;
	    }
	    $ifacehash{$mac}->{"SETTINGS"}->{$capkey} = $capval;
	}
	elsif ($str =~ /$ethpat/) {
	    my $ifacetype= $1;
1161
1162
1163
	    my $inet     = $2;
	    my $mask     = $3;
	    my $mac      = $4;
Ryan Jackson's avatar
Ryan Jackson committed
1164
	    my $speed    = $5;
1165
	    my $duplex   = $6;
1166
1167
1168
	    my $iface    = $7;
	    my $rtabid   = $8;
	    my $lan      = $9;
1169

Kirk Webb's avatar
   
Kirk Webb committed
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
            #
            # XXX GNU Radio hack
            #
            # The GNU Radio interface has a randomly generated MAC addr when
            # it first comes up.  WE have to set it, so just tell the code
            # the name of the interface explicitly to avoid trying to look
            # it up (the iface doesn't even exist yet).
            #
            # We really need another interface flag, like 'ISGNURADIO' since
            # the only current GR iface type is hardwired below (which is bad).
            #
            if ($ifacetype eq "flex900") {
                $iface = "gr0";
            }

1185
1186
1187
1188
1189
	    # The server can specify an iface.
	    if ($iface eq "" &&
		(! ($iface = findiface($mac)))) {
		warn("*** WARNING: Could not map $mac to an interface!\n");
		next;
1190
	    }
1191

1192
	    $ifconfig->{"ISVIRT"}   = 0;
1193
	    $ifconfig->{"TYPE"}     = $ifacetype;
1194
1195
1196
1197
1198
	    $ifconfig->{"IPADDR"}   = $inet;
	    $ifconfig->{"IPMASK"}   = $mask;
	    $ifconfig->{"MAC"}      = $mac;
	    $ifconfig->{"SPEED"}    = $speed;
	    $ifconfig->{"DUPLEX"}   = $duplex;
1199
	    $ifconfig->{"ALIASES"}  = "";	# gone as of version 27
1200
	    $ifconfig->{"IFACE"}    = $iface;
1201
	    $ifconfig->{"VIFACE"}   = $iface;
1202
	    $ifconfig->{"RTABID"}   = $rtabid;
1203
	    $ifconfig->{"LAN"}      = $lan;
1204
	    $ifconfig->{"SETTINGS"} = {};
1205
	    push(@ifacelist, $ifconfig);
1206
	    $ifacehash{$mac}        = $ifconfig;
1207
1208
	}
	elsif ($str =~ /$vethpat/) {
1209
	    my $ifacetype= $1;
1210
1211
1212
1213
	    my $inet     = $2;
	    my $mask     = $3;
	    my $id       = $4;
	    my $vmac     = $5;
1214
1215
1216
	    my $pmac     = $6;
	    my $iface    = undef;
	    my $rtabid   = $7;
1217
	    my $encap    = $8;
1218
	    my $lan      = $9;
1219
	    my $vtag	 = $10;
1220

1221
1222
1223
1224
1225
1226
	    #
	    # Inside a jail, the vmac is really the pmac. That is, when the
	    # veth was created, it was given vmac as its ethernet address.
	    # The pmac refers to the underlying physical interface the veth
	    # is attached to, which we do not see from inside the jail.
	    #
1227
	    if (JAILED() || GENVNODE()) {
1228
1229
1230
		if (! ($iface = findiface($vmac))) {
		    warn("*** WARNING: Could not map $vmac to a veth!\n");
		    next;
1231
		}
1232
1233
1234
	    } else {

		#
1235
1236
1237
1238
1239
		# A veth might not have any underlying physical interface
		# if the link or lan is completely contained on the node.
		# tmcd tells us that by setting the pmac to "none". Note
		# that this obviously is relevant on the physnode, not when
		# called from inside a vnode.
1240
1241
1242
1243
1244
1245
		#
		if ($pmac ne "none") {
		    if (! ($iface = findiface($pmac))) {
			warn("*** WARNING: Could not map $pmac to an iface!\n");
			next;
		    }
1246
		}
1247
	    }
1248

1249
	    $hasvirt++;
1250
1251
	    $ifconfig->{"ISVIRT"}   = 1;
	    $ifconfig->{"ITYPE"}    = $ifacetype;
1252
1253
1254
1255
	    $ifconfig->{"IPADDR"}   = $inet;
	    $ifconfig->{"IPMASK"}