boss-install.in 42.2 KB
Newer Older
1 2 3 4
#!/usr/bin/perl -w

#
# EMULAB-COPYRIGHT
5
# Copyright (c) 2003-2007 University of Utah and the Flux Group.
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
# All rights reserved.
#

#
# install-boss.sh - Script to do the initial install of a boss node
#
# The main things it does not do yet:
# * Figure out where to put directories such as /usr/testbed - they must
#   already exist
# * Set up named - we probably have to do that later, when the interfaces table
#   is filled in
# * Set up a sup tree. Not sure what the right thing to do here is!
# * Doesn't do anything about SSL certificates for the web

#
# Configure variables
#
my $PREFIX     = '@prefix@';
my $SRCDIR     = '@srcdir@';
my $TOP_SRCDIR = '@top_srcdir@';
my $DBNAME     = "@TBDBNAME@";
Leigh B. Stoller's avatar
Leigh B. Stoller committed
27
my $ELABINELAB = @ELABINELAB@;
28
my $WINSUPPORT = @WINSUPPORT@;
Leigh B. Stoller's avatar
Leigh B. Stoller committed
29
my $MAILMANSUPPORT = @MAILMANSUPPORT@;
30
my $BUGDBSUPPORT = @BUGDBSUPPORT@;
31
my $ARCHSUPPORT = @ARCHIVESUPPORT@;
32
my $OURDOMAIN  = '@OURDOMAIN@';
33
my $EXPOSETEMPLATES = $ELABINELAB;
34

35 36
my $LOGFACIL   = '@TBLOGFACIL@';

37 38 39
my $USERNODE   = '@USERNODE@';
my $FSNODE     = '@FSNODE@';
my $BOSSNODE   = '@BOSSNODE@';
40 41
my $BOSSNODE_IP= '@BOSSNODE_IP@';
my $USERNODE_IP= '@USERNODE_IP@';
42
my $FSNODE_IP=   '@FSNODE_IP@';
43
my $SCRATCHDIR = '@FSDIR_SCRATCH@';
Leigh B. Stoller's avatar
Leigh B. Stoller committed
44
my $SHAREDIR   = '@FSDIR_SHARE@';
45 46 47 48 49 50 51 52 53

#
# Fixed paths for clients
#
my $PROJROOT    = "@PROJROOT_DIR@";
my $GROUPROOT   = "@GROUPSROOT_DIR@";
my $USERROOT    = "@USERSROOT_DIR@";
my $SCRATCHROOT	= "@SCRATCHROOT_DIR@";
my $SHAREROOT	= "@SHAREROOT_DIR@";
54 55 56 57 58 59 60

#
# Some programs we use
#
my $SH         = "/bin/sh";
my $CHMOD      = "/bin/chmod";
my $CHGRP      = "/usr/bin/chgrp";
61
my $CHOWN      = "/usr/sbin/chown";
62 63 64 65 66
my $PWD        = "/bin/pwd";
my $PW         = "/usr/sbin/pw";
my $PATCH      = "/usr/bin/patch";
my $SSH_KEYGEN = "/usr/bin/ssh-keygen";
my $PKG_INFO   = "/usr/sbin/pkg_info";
67
my $PKG_ADD    = "/usr/sbin/pkg_add";
68
my $PKG_DEL    = "/usr/sbin/pkg_delete";
69
my $TOUCH      = "/usr/bin/touch";
70 71 72 73
my $SSH_INIT   = "/usr/bin/ssh -1";
my $SCP_INIT   = "/usr/bin/scp -1";
my $SSH        = "/usr/bin/ssh -2";
my $SCP        = "/usr/bin/scp -2";
74
my $CP         = "/bin/cp";
75
my $ENV        = "/usr/bin/env";
76
my $MOUNT      = "/sbin/mount";
77
my $SUDO       = "/usr/local/bin/sudo";
78 79 80 81 82 83
my $SUIDPERL   = "/usr/bin/suidperl";

my $MYSQL      = "/usr/local/bin/mysql";
my $MYSQLADMIN = "/usr/local/bin/mysqladmin";
my $MYSQLSHOW  = "/usr/local/bin/mysqlshow";
my $MYSQLDUMP  = "/usr/local/bin/mysqldump";
84 85
my $MYSQLINSTALL = "/usr/local/bin/mysql_install_db";
my $MYSQLDBDIR = "/var/db/mysql";
86 87

my $GMAKE      = "/usr/local/bin/gmake";
88

89 90 91 92
#
# Some files we edit/create
#
my $CRONTAB         = "/etc/crontab";
93
my $HOSTS           = "/etc/hosts";
94 95 96 97
my $FSTAB           = "/etc/fstab";
my $RCCONF          = "/etc/rc.conf";
my $SYSLOG_CONF     = "/etc/syslog.conf";
my $NEWSYSLOG_CONF  = "/etc/newsyslog.conf";
98
my $INETD_CONF      = "/etc/inetd.conf";
99

100 101
my $PROTOUSER       = "elabman";
my $PROTOUSER_KEY   = "$TOP_SRCDIR/install/elabman_dsa.pub";
102
my $ROOT_PRIVKEY    = "/root/.ssh/id_rsa";
103 104 105
my $ROOT_PUBKEY     = "$ROOT_PRIVKEY.pub";
my $ROOT_AUTHKEY    = "/root/.ssh/authorized_keys";

106
my $APACHE_ETCDIR   = "/usr/local/etc/apache";
107
my $SUDOERS         = "/usr/local/etc/sudoers";
108
my $HTTPD_CONF      = "$APACHE_ETCDIR/httpd.conf";
Robert Ricci's avatar
Robert Ricci committed
109
my $PHP_INI         = "/usr/local/etc/php.ini";
110 111 112 113
my $DHCPD_CONF	    = "/usr/local/etc/dhcpd.conf";
my $DHCPD_TEMPLATE  = "/usr/local/etc/dhcpd.conf.template";
my $DHCPD_LEASES    = "/var/db/dhcpd.leases";
my $DHCPD_MAKECONF  = "$PREFIX/sbin/dhcpd_makeconf";
114 115 116
my $BATCHEXP        = "$PREFIX/bin/batchexp";
my $WAP             = "$PREFIX/sbin/withadminprivs";
my $NAMED_SETUP     = "$PREFIX/sbin/named_setup";
117 118
my $ADDPUBKEY       = "$PREFIX/sbin/addpubkey";
my $TBACCT          = "$PREFIX/sbin/tbacct";
119 120 121 122

my $CRACKLIB_DICT   = "/usr/local/lib/pw_dict.pwd";

my $STL_PATCH       = "$TOP_SRCDIR/patches/g++.patch";
123
my $M2CRYPTO_PATCH  = "$TOP_SRCDIR/patches/m2crypto.patch";
Kevin Atkinson's avatar
 
Kevin Atkinson committed
124
my $MYSQL_PM_PATCH  = "$TOP_SRCDIR/patches/Mysql.pm.patch";
125
my $PHP4_PATCH      = "$TOP_SRCDIR/patches/php4-Makefile.patch";
Leigh B. Stoller's avatar
Leigh B. Stoller committed
126
my $INIT_PRIVKEY    = "$TOP_SRCDIR/install/identity";
127

Robert Ricci's avatar
Robert Ricci committed
128 129
my $SSH_CONFIG      = "/etc/ssh/ssh_config";

130 131
my $LOADER_CONF	    = "/boot/loader.conf";

132 133
my $SYSCTL_CONF	    = "/etc/sysctl.conf";

134
my $EMULAB_PEM	    = "emulab.pem";
135 136 137
my $CLIENT_PEM	    = "client.pem";
my $CTRLNODE_PEM    = "ctrlnode.pem";
my $ETC_EMULAB_DIR  = "/etc/emulab";
138 139 140 141 142 143 144 145 146 147

# Apache certs on both boss and ops
my $APACHE_CERTPEM      = "apache_cert.pem";
my $APACHE_KEYPEM       = "apache_key.pem";
my $APACHE_CERTFILE     = "$APACHE_ETCDIR/ssl.crt/www.${OURDOMAIN}.crt";
my $APACHE_KEYFILE      = "$APACHE_ETCDIR/ssl.key/www.${OURDOMAIN}.key";
my $APACHE_CERTPEM_OPS  = "apache-ops_cert.pem";
my $APACHE_KEYPEM_OPS   = "apache-ops_key.pem";
my $APACHE_CERTFILE_OPS = "$APACHE_ETCDIR/ssl.crt/${USERNODE}.crt";
my $APACHE_KEYFILE_OPS  = "$APACHE_ETCDIR/ssl.key/${USERNODE}.key";
148

149 150 151 152 153
#
# XXX temporary for tftp
#
my $TFTPD_PKG	  = "emulab-tftp-hpa-0.48";

154 155 156
#
# Some directories we care about
#
157 158 159 160 161
my $LOGDIR        = "$PREFIX/log";
my $MYSQL_LOGDIR  = "$LOGDIR/mysql";
my $RCDIR         = "/usr/local/etc/rc.d";
my $USERSVAR_DIR  = "$PREFIX/usersvar";
my $OPSDIR_DIR    = "$PREFIX/opsdir";
162 163
my $PORTSDIR      = "/usr/ports";
my $PORTSMISCDIR  = "$PORTSDIR/misc";
164 165 166
my $MIBPATH       = "/usr/local/share/snmp/mibs";
my $TFTP_DIR      = "$PREFIX/tftpboot";
my $TFTP_PROJ_DIR = "$TFTP_DIR/proj";
167
my $VARRUN        = "/var/run";
168
my $ETCSSH	  = "/etc/ssh";
169 170 171 172 173 174 175 176 177 178 179

#
# URLs
#
my $CISCO_MIB_FTP = "ftp://ftp.cisco.com/pub/mibs/v2";

#
# And some lists that we use
#
my @TESTBED_DIRS = ($PREFIX);

180
my @MOUNTPOINTS = ("$USERSVAR_DIR", "$OPSDIR_DIR",
Leigh B. Stoller's avatar
Leigh B. Stoller committed
181 182 183 184
		   "$USERROOT", "$PROJROOT", "$GROUPROOT");
if ($SHAREDIR) {
    push(@MOUNTPOINTS, "$SHAREROOT");
}
185 186 187
if ($SCRATCHDIR) {
    push(@MOUNTPOINTS, "$SCRATCHROOT");
}
Robert Ricci's avatar
Robert Ricci committed
188

189 190
my @LOGFILES = ("$LOGDIR/bootinfo.log", "$LOGDIR/tmcd.log",
    "$LOGDIR/capture.log", "$LOGDIR/dhcpd.log", "$LOGDIR/capserver.log",
191
    "$LOGDIR/frisbeed.log", "$LOGDIR/proxydhcpd.log",
192
    "$LOGDIR/stated.log", "$LOGDIR/osselect.log",
193
    "$LOGDIR/tftpd.log", "$LOGDIR/sdcollectd.log", "$LOGDIR/genlastlog.log",
194
    "$LOGDIR/sshxmlrpc.log", "$LOGDIR/plabgetfree.log", "$LOGDIR/xmlrpcbag.log",
195
    "$LOGDIR/plabrenew.log", "$LOGDIR/sslxmlrpc.log", "$LOGDIR/pubsubd.log");
196 197

my @CISCO_MIBS = ("CISCO-SMI", "CISCO-TC", "CISCO-VTP-MIB", "CISCO-PAGP-MIB",
198 199
    "CISCO-PRIVATE-VLAN-MIB", "CISCO-STACK-MIB", "CISCO-VLAN-MEMBERSHIP-MIB",
    "CISCO-C2900-MIB");
200

201 202 203 204 205 206 207 208 209 210 211 212
# Initial experiments to create. Indexed by eid.
my %EXPERIMENTS =
    ("hwdown"	     => {"pid"	       => "emulab-ops",
			 "description" => "Node reported as down"},
     "reloading"     => {"pid"	       => "emulab-ops",
			 "description" => "Nodes reloading images"},
     "reloadpending" => {"pid"	       => "emulab-ops",
			 "description" => "Nodes waiting for reload"},
     "oldreserved"   => {"pid"	       => "emulab-ops",
			 "description" => "Nodes in limbo during swap modify"},
     "nfree-leases"  => {"pid"	       => "emulab-ops",
			 "description" => "Nodes in limbo during nfree"},
213 214
     "nfree-locked"  => {"pid"	       => "emulab-ops",
			 "description" => "Nodes in limbo during nfree"},
215 216 217 218
     "opsnodes"	     => {"pid"	       => "emulab-ops",
			 "description" => "Nodes designated as Ops Nodes"},
    );

219 220 221 222
#
# List of names that goes into $HOSTS and which must resolve.
# 
my @OPS_NAMES = ($USERNODE, "users", "ops");
223 224 225
if ($USERNODE eq $FSNODE) {
    push(@OPS_NAMES, "fs");
}
Robert Ricci's avatar
Robert Ricci committed
226

227 228 229 230 231 232 233 234 235
# Version of FreeBSD.
my $FBSD_VERSION = 4;
if (`uname -r` =~ /^(\d)/) {
    $FBSD_VERSION = $1;
}
else {
    die("Could not determine what version of FreeBSD you are running!\n");
}

236
#
237 238
# The meta-ports (name and version) that drag in all the dependancies for
# a boss node. These are OS dependent as we upgrade.
239
#
240 241
my $BOSS_PORT = (($FBSD_VERSION == 4) ? "emulab-boss-1.8" : "emulab-boss-2.0");
my $PHP4_PORT = "php4-extensions-1.0";
242

243 244 245 246 247 248 249 250 251 252 253 254 255 256 257
#
# Figure out which directory we live in, so that some stages can do thing
# relative to it.
#
my $OBJDIR = `/usr/bin/dirname $0`;
chomp $OBJDIR;
my $TOP_OBJDIR = "$OBJDIR/..";

#
# Allow this to work if the library is left in the source directory
#
use lib '@srcdir@';
   
use English;
use libinstall;
258 259 260 261 262 263
use Getopt::Std;

#
# Handle command-line options
#
sub usage {
264 265
    print "Usage: boss-install [-b] [-p packagedir] [-s] [-P portname]\n";
    print "  Required: -p (for binary packages) or -s (for source makes.)\n";
266 267 268 269
    exit(1);
}

my $packagedir = "";
Leigh B. Stoller's avatar
Leigh B. Stoller committed
270
my $batchmode  = 0;
271
my $domakes  = 0;
272
my $password;
273 274
my %opts;

275
if (! getopts("P:p:bsw:", \%opts)) {
Leigh B. Stoller's avatar
Leigh B. Stoller committed
276 277 278 279 280 281 282
    usage();
}
if (defined($opts{p})) {
    $packagedir = $opts{p};
}
if (defined($opts{b})) {
    $batchmode = 1;
283
}
284 285 286
if (defined($opts{s})) {
    $domakes = 1;
}
287 288 289
if (defined($opts{w})) {
    $password = $opts{w};
}
290 291 292
if (defined($opts{P})) {
    $BOSS_PORT = $opts{P};
}
293 294 295 296 297 298 299 300 301
# Don't just charge into making ports from source by default.
if ($packagedir eq "" and $domakes eq 0) {
    print "At least one of -p and -s must be given.\n";
    usage();
}
if ($packagedir ne "" and $domakes eq 1) {
    print "Only one of -p and -s can be given.\n";
    usage();
}
302 303 304
if (@ARGV) {
    usage();
}
305 306 307 308

#
# Make sure they know what they're getting into...
#
Leigh B. Stoller's avatar
Leigh B. Stoller committed
309 310 311 312 313 314 315 316 317 318
if (! $batchmode) {
    warn "***** Please run install-ops on ops, and reboot it, before running\n";
    warn "this script!\n\n";
    print STDERR
	"WARNING: This script is ONLY intended to be run on a machine\n";
    print STDERR
	"that is being set up as a dedicated boss node. Continue? [y/N] ";
    my $response = <>;
    die "Installation aborted!\n" unless ($response =~ /^y/i);
}
319 320 321 322 323

if ($UID != 0) {
    die "This script must be run as root.\n";
}

Leigh B. Stoller's avatar
Leigh B. Stoller committed
324
Phase "usersgroups", "Creating users and groups", sub {
325 326
    Phase "tbadmin", "Creating tbadmin group", sub {
	if (getgrnam("tbadmin")) {
327
	    PhaseSkip("Group already exists");
328 329 330 331 332
	}
	ExecQuietFatal("$PW groupadd tbadmin -g 101");
    };
    Phase "root", "Creating root group", sub {
	if (getgrnam("root")) {
333
	    PhaseSkip("Group already exists");
334 335 336
	}
	ExecQuietFatal("$PW groupadd root -g 103");
    };
Leigh B. Stoller's avatar
Leigh B. Stoller committed
337 338 339
    # Added next two cause the mysql package does not do this (port does).
    Phase "mysqlgroup", "Creating mysql group", sub {
	if (getgrnam("mysql")) {
340
	    PhaseSkip("Group already exists");
Leigh B. Stoller's avatar
Leigh B. Stoller committed
341 342 343 344 345
	}
	ExecQuietFatal("$PW groupadd mysql -g 88");
    };
    Phase "mysqluser", "Creating mysql user", sub {
	if (getpwnam("mysql")) {
346
	    PhaseSkip("User already exists");
Leigh B. Stoller's avatar
Leigh B. Stoller committed
347 348
	}
	ExecQuietFatal("$PW useradd mysql -g 88 -g 88 -h - ".
349
		       "-d $MYSQLDBDIR -s /sbin/nologin -c 'MySQL Daemon'");
Leigh B. Stoller's avatar
Leigh B. Stoller committed
350
    };
351 352
    ExecQuietFatal("$CHOWN mysql:mysql $MYSQLDBDIR")
	if (-e $MYSQLDBDIR);
353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372
};

Phase "dirs", "Setting directory permissions", sub {
    foreach my $dir (@TESTBED_DIRS) {
	Phase $dir, $dir, sub {
	    if (!-d $dir) {
		PhaseFail("Directory $dir does not exist");
	    }
	    my ($mode,$group) = (stat($dir))[2,5];
	    # Fix up the mode (strip file type)
	    $mode = $mode & 0777;
	    if ($mode == 0775 && $group eq getgrnam("tbadmin")) {
		PhaseSkip("Already done");
	    }
	    ExecQuietFatal("$CHGRP tbadmin $dir");
	    ExecQuietFatal("$CHMOD 0775 $dir");
	};
    }
};

Robert Ricci's avatar
Robert Ricci committed
373
Phase "tftp", "Setting up directories for tftp", sub {
374
    Phase "tftpoff", "Disabling BSD tftpd", sub {
375 376
	PhaseSkip("No inetd.conf!?") unless (-e $INETD_CONF);
	PhaseSkip("Already disabled") unless `grep '^tftp' $INETD_CONF`;
377 378 379
	ExecQuietFatal("sed -i .orig -e '/^tftp/s/^/#/' $INETD_CONF");
	HUPDaemon("inetd");
    };
Robert Ricci's avatar
Robert Ricci committed
380 381 382 383 384 385
    Phase "tftpboot", "Creating $TFTP_DIR", sub {
	DoneIfExists($TFTP_DIR);
	mkdir $TFTP_DIR,0775 or
	    PhaseFail("Unable to create $TFTP_DIR : $!");
	ExecQuietFatal("$CHGRP tbadmin $TFTP_DIR");
    };
386 387 388 389 390 391
    Phase "tftpproj", "Creating $TFTP_PROJ_DIR", sub {
	DoneIfExists($TFTP_PROJ_DIR);
	mkdir $TFTP_PROJ_DIR,0775 or
	    PhaseFail("Unable to create $TFTP_PROJ_DIR : $!");
	ExecQuietFatal("$CHGRP tbadmin $TFTP_PROJ_DIR");
    };
Robert Ricci's avatar
Robert Ricci committed
392 393 394 395
    Phase "tftplink", "Linking /tftpboot", sub {
	DoneIfExists("/tftpboot");
	ExecQuietFatal("ln -s $TFTP_DIR /tftpboot");
    };
396 397 398
};

Phase "ports", "Installing ports", sub {
399
    Phase "packages", "Installing packages", sub {
400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419
	Phase "main", "Installing main package", sub {
	    if (!ExecQuiet("$PKG_INFO -e $BOSS_PORT")) {
		PhaseSkip("Package already installed");
	    }
	    if (!$packagedir) {
		PhaseSkip("No package directory provided");
	    }
	    ExecQuietFatal("$ENV PKG_PATH=$packagedir $PKG_ADD $BOSS_PORT");
	};
	if ($FBSD_VERSION > 4) {
	    Phase "php4", "Installing php4 package", sub {
		if (!ExecQuiet("$PKG_INFO -e $PHP4_PORT")) {
		    PhaseSkip("Port already installed");
		}
		if (!$packagedir) {
		    PhaseSkip("No package directory provided");
		}
		ExecQuietFatal("$ENV PKG_PATH=$packagedir ".
			       "     $PKG_ADD $PHP4_PORT");
	    };
420 421 422 423 424 425 426 427 428 429 430 431 432 433
	    # XXX temporary: only needed til emulab-boss package updated
	    Phase "tftpd", "Updating tftpd installation", sub {
		my $pname = `$PKG_INFO -E 'emulab-tftp-hpa-*' 2>/dev/null`;
		chomp($pname);
		if ($pname eq $TFTPD_PKG) {
		    PhaseSkip("tftpd already up to date");
		}
		if (!$packagedir) {
		    PhaseSkip("No package directory provided");
		}
		ExecQuietFatal("$ENV PKG_PATH=$packagedir $PKG_DEL -f $pname")
		    if ($pname);
		ExecQuietFatal("$ENV PKG_PATH=$packagedir $PKG_ADD $TFTPD_PKG");
	    };
434 435 436 437 438 439 440 441
	    # XXX Also temporary
	    Phase "pubsub", "Installing pubsub package", sub {
		if (!ExecQuiet("$PKG_INFO -x pubsub")) {
		    PhaseSkip("pubsub already installed");
		}
		my $pname = GetPackage("pubsub", $packagedir);
		ExecQuietFatal("$ENV PKG_PATH=$packagedir $PKG_ADD $pname");
	    };
442 443
	}
    };
444
    PhaseSkip("Package directory provided; not installing from sources")
Leigh B. Stoller's avatar
Leigh B. Stoller committed
445 446
	if ($packagedir);

447
    Phase "pcopy", "Copying ports into place", sub {
448
	DoneIfExists("$PORTSMISCDIR/emulab-boss");
449 450
	ExecQuietFatal("$SH $SRCDIR/ports/ports-install");
    };
451 452 453 454 455
    if ($FBSD_VERSION == 4) {
	# Ick. The php4 port is broken with SSL, so we have to patch
	# it - hopefully it'll get fixed someday, and we remove this
	Phase "php4patch", "Patching php4 port", sub {
	    if (!ExecQuiet("$PATCH -C -f -l -R -p0 -i $PHP4_PATCH")) {
456
		PhaseSkip("Patch already applied");
457 458 459 460
	    }
	    ExecQuietFatal("$PATCH -f -l -p0 -i $PHP4_PATCH");
	};
    }
461 462
    PhaseFail("Please install ports manually, since some\n of them are " .
	      "interactive. Run: \n" .
463 464
	      "    cd $PORTSMISCDIR/emulab-boss && make install\n" .
	      "    cd $PORTSMISCDIR/emulab-php4 && make install\n" .
465
	      "then re-run this script.");
466 467
};

468 469 470 471 472
Phase "portfixup", "Fixing up packages", sub {
    Phase "rsync", "Looking for rsync", sub {
	if (!ExecQuiet("$PKG_INFO -x rsync")) {
	    PhaseSkip("rsync already installed");
	}
473 474
	my $pname = GetPackage("rsync", $packagedir);
	ExecQuietFatal("$ENV PKG_PATH=$packagedir $PKG_ADD $pname");
475
    };
476
    if ($FBSD_VERSION == 6) {
477
	# Temporary for template stuff
478 479 480 481
	Phase "Simple", "Looking for Simple XML Parser", sub {
	    if (!ExecQuiet("$PKG_INFO -x p5-XML-Simple")) {
		PhaseSkip("p5-XML-Simple already installed");
	    }
482 483
	    my $pname = GetPackage("p5-XML-Simple", $packagedir);
	    ExecQuietFatal("$ENV PKG_PATH=$packagedir $PKG_ADD $pname");
484
	};
485 486 487 488 489 490 491 492 493
	if ($ARCHSUPPORT) {
	    Phase "Subversion", "Looking for Subversion", sub {
		if (!ExecQuiet("$PKG_INFO -x subversion")) {
		    PhaseSkip("subversion already installed");
		}
		my $pname = GetPackage("subversion-python", $packagedir);
		ExecQuietFatal("$ENV PKG_PATH=$packagedir $PKG_ADD $pname");
	    };
	}
494
    }
495 496
    if ($WINSUPPORT) {
	Phase "gcc30", "Looking for GCC 3.0 and installing", sub {
497 498
	    if ($FBSD_VERSION > 4 ||
		!ExecQuiet("$PKG_INFO -x gcc30")) {
499 500
		PhaseSkip("GCC 3.0 already installed");
	    }
501 502
	    my $pname = GetPackage("gcc30", $packagedir);
	    ExecQuietFatal("$ENV PKG_PATH=$packagedir $PKG_ADD $pname");
503 504
	};
    }
505 506
};

507
Phase "patches", "Applying patches", sub {
508 509 510
    if ($FBSD_VERSION == 4) {
	Phase "g++patch", "Patching g++'s STL", sub {
	    if (!ExecQuiet("$PATCH -C -f -R -p0 -i $STL_PATCH")) {
511
		PhaseSkip("Patch already applied");
512 513 514 515
	    }
	    ExecQuietFatal("$PATCH -f -p0 -i $STL_PATCH");
	};
    }
516
    Phase "m2cryptopatch", "Patching m2crypto", sub {
517 518 519 520 521 522
	my $patchfile = $M2CRYPTO_PATCH;
	
	if (ExecQuiet("$PKG_INFO -I -x m2crypto | fgrep -q -s '0.13'") == 0) {
	    $patchfile = "${patchfile}-0.13";
	}
	if (!ExecQuiet("$PATCH -C -f -l -R -p0 -i $patchfile")) {
523
	    PhaseSkip("Patch already applied");
524
	}
525
	ExecQuietFatal("$PATCH -f -l -p0 -i $patchfile");
526
    };
Kevin Atkinson's avatar
 
Kevin Atkinson committed
527 528
    Phase "Mysql.pm.patch", "Patching Mysql.pm", sub {
	my $patchfile = $MYSQL_PM_PATCH;
529
	$patchfile = `realpath $patchfile`;
Kevin Atkinson's avatar
 
Kevin Atkinson committed
530 531 532 533 534 535 536 537 538 539 540 541 542 543
	chomp $patchfile;

	my $dir;
	foreach $prefix (@INC) {
	    if (-e "$prefix/Mysql.pm") {
		$dir = $prefix;
		last;
	    }
	}
	if (!defined($dir)) {
	    PhaseFail("Unable to find Mysql.pm");
	}

	if (!ExecQuiet("$PATCH -d $dir -C -f -l -R -i $patchfile")) {
544
	    PhaseSkip("Patch already applied");
Kevin Atkinson's avatar
 
Kevin Atkinson committed
545 546 547
	}
	ExecQuietFatal("$PATCH -d $dir -f -l -i $patchfile");
    };
548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580
};

Phase "cracklib", "Installing cracklib", sub {
    DoneIfExists("$CRACKLIB_DICT");
    my $pwd = `$PWD`;
    chomp $pwd;
    chdir "$TOP_SRCDIR/tbsetup/checkpass/cracklib,2.7" or
	PhaseFail "Unable to change to " .
	    "$TOP_SRCDIR/tbsetup/checkpass/cracklib,2.7: $!";
    ExecQuietFatal("make install clean");
    chdir $pwd;
};

Phase "apache", "Installing apache config file", sub {
    DoneIfEdited("$HTTPD_CONF");
    # ICK!!! If we installed apache AFTER we unpacked the source tarball,
    # make will not properly install the new apache config file! So, we use
    # this shameful hack to force it to do so!
    ExecQuietFatal("$TOUCH -t 01010000 $HTTPD_CONF");
    ExecQuietFatal("$GMAKE -C $TOP_OBJDIR/apache install");
};

Phase "rc.d", "Setting up rc.d scripts", sub {
    Phase "my-client", "Moving $RCDIR/mysql-client.sh", sub {
	DoneIfDoesntExist("$RCDIR/mysql-client.sh");
	ExecQuietFatal("mv $RCDIR/mysql-client.sh $RCDIR/1.mysql-client.sh");
    };
    Phase "my-server", "Removing $RCDIR/mysql-server.sh", sub {
	DoneIfDoesntExist("$RCDIR/mysql-server.sh");
	if (!unlink "$RCDIR/mysql-server.sh") {
	    PhaseFail("Unable to remove $RCDIR/mysql-server.sh: $!");
	}
    };
581 582 583 584 585 586
    Phase "my-server2", "Removing $RCDIR/mysql-server", sub {
	DoneIfDoesntExist("$RCDIR/mysql-server");
	if (!unlink "$RCDIR/mysql-server") {
	    PhaseFail("Unable to remove $RCDIR/mysql-server: $!");
	}
    };
587
    Phase "snmpd", "Removing snmpd startup script",  sub {
588 589 590 591 592
	DoneIfDoesntExist("$RCDIR/snmpd.sh");
	if (!unlink "$RCDIR/snmpd.sh") {
	    PhaseFail("Unable to remove $RCDIR/snmpd.sh: $!");
	}
    };
593 594 595 596 597 598 599
    Phase "rsyncd", "Removing rsyncd startup script",  sub {
	DoneIfDoesntExist("$RCDIR/rsyncd.sh");
	if (!unlink "$RCDIR/rsyncd.sh") {
	    PhaseFail("Unable to remove $RCDIR/rsyncd.sh: $!");
	}
    };
    Phase "dhcpd", "Removing isc-dhcpd startup script",  sub {
600 601 602 603 604
	DoneIfDoesntExist("$RCDIR/isc-dhcpd.sh");
	if (!unlink "$RCDIR/isc-dhcpd.sh") {
	    PhaseFail("Unable to remove $RCDIR/isc-dhcpd.sh: $!");
	}
    };
605
    Phase "rc.testbed", "Installing testbed RC scripts", sub {
606
	ExecQuietFatal("$GMAKE -C $TOP_OBJDIR/rc.d install");
607
    };
608 609 610 611 612 613 614 615 616
    Phase "unelvin", "Removing Elvin related startup scripts", sub {
        Phase "elvind.sh", "Removing elvind startup", sub {
            ExecQuietFatal("/bin/rm -f $RCDIR/elvind.sh $RCDIR/2.elvind.sh");
        };
        Phase "elvin_gateway.sh", "Removing elvin gateway startup", sub {
	    DoneIfDoesntExist("$RCDIR/3.elvin_gateway.sh");
            ExecQuietFatal("/bin/rm -f $RCDIR/3.elvin_gateway.sh");
        };
    };
617 618
};

619 620 621
Phase "syslog", "Setting up syslog", sub {
    Phase "sysconf", "Editing $SYSLOG_CONF", sub {
	DoneIfEdited($SYSLOG_CONF);
622 623

	#
624 625
	# Modify the /dev/console and /var/log/messages lines to exclude
	# testbed stuff
626 627 628 629 630 631 632 633 634 635
	#
	open(SC,"+<$SYSLOG_CONF") or
	    PhaseFail("Unable to open $SYSLOG_CONF : $!");
	my @sc = <SC>;
	if (scalar(grep(/$LOGFACIL/, @sc)) != 0) {
	    PhaseFail("Testbed chosen facility $LOGFACIL already in use in /etc/syslog.conf!");
	}
	seek(SC,0,0);
	truncate(SC,0);
	foreach my $line (@sc) {
636 637 638 639 640 641 642
	    my $cpat = q(\s+/dev/console);
	    my $mpat = q(\s+/var/log/messages);
	    if ($line =~ /^[^#].*$cpat/) {
		$line =~ s/($cpat)/\;$LOGFACIL.none$1/;
	    }
	    elsif ($line =~ /^[^#].*$mpat/) {
		$line =~ s/($mpat)/\;$LOGFACIL.none$1/;
643 644 645 646 647
	    }
	    print SC $line;
	}
	close(SC);

648 649 650 651 652
	AppendToFileFatal($SYSLOG_CONF,
	    "!bootinfo",  "*.*\t\t\t\t\t\t$LOGDIR/bootinfo.log",
	    "!tmcd",      "*.*\t\t\t\t\t\t$LOGDIR/tmcd.log",
	    "!capture",   "*.*\t\t\t\t\t\t$LOGDIR/capture.log",
	    "!dhcpd",     "*.*\t\t\t\t\t\t$LOGDIR/dhcpd.log",
653 654
	    "!proxydhcpd","*.*\t\t\t\t\t\t$LOGDIR/proxydhcpd.log",
	    "!tftpd",     "*.*\t\t\t\t\t\t$LOGDIR/tftpd.log",
655 656
	    "!capserver", "*.*\t\t\t\t\t\t$LOGDIR/capserver.log",
	    "!frisbeed",  "*.*\t\t\t\t\t\t$LOGDIR/frisbeed.log",
657
	    "!pubsubd",    "*.*\t\t\t\t\t\t$LOGDIR/pubsubd.log",
658
	    "!stated",    "*.*\t\t\t\t\t\t$LOGDIR/stated.log",
659 660
	    "!osselect",  "*.*\t\t\t\t\t\t$LOGDIR/osselect.log",
	    "!genlastlog","*.*\t\t\t\t\t\t$LOGDIR/genlastlog.log",
661
	    "!sdcollectd","*.*\t\t\t\t\t\t$LOGDIR/sdcollectd.log",
662
	    "!plabgetfree","*.*\t\t\t\t\t\t$LOGDIR/plabgetfree.log",
663
	    "!plabrenew", "*.*\t\t\t\t\t\t$LOGDIR/plabrenew.log",
664
	    "!xmlrpcbag", "*.*\t\t\t\t\t\t$LOGDIR/xmlrpcbag.log",
665 666
	    "!sshxmlrpc", "*.*\t\t\t\t\t\t$LOGDIR/sshxmlrpc.log",
	    "!sslxmlrpc", "*.*\t\t\t\t\t\t$LOGDIR/sslxmlrpc.log");
667 668 669 670 671 672
    };

    Phase "logdir", "Creating log directory", sub {
	DoneIfExists($LOGDIR);
	mkdir $LOGDIR, 0775 or PhaseFail("Unable to create $LOGDIR : $!");
	ExecQuietFatal("$CHGRP tbadmin $LOGDIR");
673
	ExecQuietFatal("$CHMOD 775 $LOGDIR");
674 675 676 677 678 679
    };

    Phase "logdir", "Creating mysql log directory", sub {
	DoneIfExists($MYSQL_LOGDIR);
	mkdir $MYSQL_LOGDIR, 0775 or
	    PhaseFail("Unable to create $MYSQL_LOGDIR : $!");
680 681
	ExecQuietFatal("$CHOWN mysql:mysql $MYSQL_LOGDIR");
	ExecQuietFatal("$CHMOD 775 $MYSQL_LOGDIR");
682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706
    };

    Phase "logfiles", "Creating log files", sub {
	foreach my $logfile (@LOGFILES) {
	    Phase $logfile, $logfile, sub {
		DoneIfExists($logfile);
		CreateFileFatal($logfile);
		ExecQuietFatal("$CHGRP tbadmin $logfile");
		ExecQuietFatal("$CHMOD 640 $logfile");
	    };
	}
    };

    Phase "newsyslog", "Setting up $NEWSYSLOG_CONF", sub {
	DoneIfEdited($NEWSYSLOG_CONF);
	AppendToFileFatal($NEWSYSLOG_CONF,
	    "$LOGDIR/tmcd.log               640  9     1000 *     Z",
	    "$LOGDIR/stated.log             640  9     300  *     Z",
	    "$LOGDIR/osselect.log           640  9     300  *     Z",
	    "$LOGDIR/power.log              640  7     300  *     Z",
	    "$LOGDIR/frisbeed.log           640  7     300  *     Z",
	    "$LOGDIR/tftpd.log              640  7     200  *     Z",
	    "$LOGDIR/dhcpd.log              640  7     200  *     Z",
	    "$LOGDIR/bootinfo.log           640  7     200  *     Z",
	    "$LOGDIR/capserver.log          640  5     200  *     Z",
707
	    "$LOGDIR/pubsubd.log            640  5     1000 *     Z",
708
	    "$LOGDIR/suexec.log             640  3     200  *     Z",
709
	    "$LOGDIR/genlastlog.log         640  3     200  *     Z",
710
	    "$LOGDIR/genlastlog             640  3     200  *     Z " .
711 712
	        "/var/run/lastlog_daemon.pid",
	    "$LOGDIR/plabmetrics.log        640  7     1000 *     Z",
713
	    "$LOGDIR/plablinkdata.log       640  7     1000 *     Z",
714
	    "$LOGDIR/xmlrpcbag.log          640  7     300  *     Z",
715 716
	    "$LOGDIR/sshxmlrpc.log          640  7     300  *     Z",
	    "$LOGDIR/sslxmlrpc.log          640  7     300  *     Z");
717 718 719
    };
};

720
Phase "database", "Setting up database", sub {
721 722 723 724 725 726 727
    Phase "initialize", "Initializing mysql", sub {
	PhaseSkip("mysqld already initialzed")
	    if (-d "$MYSQLDBDIR/mysql");

	ExecQuietFatal("$MYSQLINSTALL --ldata=${MYSQLDBDIR}");
	ExecQuietFatal("$CHOWN -R mysql:mysql $MYSQLDBDIR");
    };
Leigh B. Stoller's avatar
Leigh B. Stoller committed
728
    Phase "mysql", "Starting mysqld", sub {
729 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
	if (!ExecQuiet("$MYSQLADMIN ping")) {
	    PhaseSkip("mysqld already running");
	}
	ExecQuietFatal("$RCDIR/2.mysql-server.sh start");
	# Give mysqld some time to start, then make sure it did
	sleep 5;
	ExecQuietFatal("$MYSQLADMIN ping");
    };
    Phase "$DBNAME", "Creating $DBNAME", sub {
	if (!ExecQuiet("$MYSQLSHOW $DBNAME")) {
	    PhaseSkip("tbdb already exists");
	}
	ExecQuietFatal("$MYSQLADMIN create $DBNAME");
    };
    Phase "tables", "Creating tables in $DBNAME", sub {
	if (!ExecQuiet("$MYSQLDUMP -d $DBNAME users")) {
	    PhaseSkip("Tables have already been created");
	}
	ExecQuietFatal("$MYSQL $DBNAME < $TOP_SRCDIR/sql/database-create.sql");
    };
    Phase "dbdata", "Filling tables with initial data", sub {
	my ($exitval, @rows) = ExecQuiet("echo 'select * from " .
	    "exported_tables' | $MYSQL -s $DBNAME");
	if ($exitval) {
	    PhaseFail("Error running query");
	}
	if (scalar @rows) {
	    PhaseSkip("Already done");
	}
	ExecQuietFatal("$MYSQL $DBNAME < $TOP_SRCDIR/sql/database-fill.sql");
    };
760 761 762 763 764 765 766 767 768 769 770 771
    Phase "sdbdata", "Filling tables with supplemental data", sub {
	my ($exitval, @rows) = ExecQuiet("echo 'select * from " .
	    "os_info' | $MYSQL -s $DBNAME");
	if ($exitval) {
	    PhaseFail("Error running query");
	}
	if (scalar @rows) {
	    PhaseSkip("Already done");
	}
	ExecQuietFatal("$MYSQL $DBNAME < " .
	    "$TOP_SRCDIR/sql/database-fill-supplemental.sql");
    };
Robert Ricci's avatar
Robert Ricci committed
772 773 774 775 776 777 778 779 780 781 782
    Phase "sitevars", "Setting sitevars to default values", sub {
	my ($exitval, @rows) = ExecQuiet("echo 'select * from " .
	    "sitevariables' | $MYSQL -s $DBNAME");
	if ($exitval) {
	    PhaseFail("Error running query");
	}
	if (scalar @rows) {
	    PhaseSkip("Already done");
	}
	ExecQuietFatal("$MYSQL $DBNAME < $TOP_SRCDIR/sql/sitevars-create.sql");
    };
Leigh B. Stoller's avatar
Leigh B. Stoller committed
783 784 785 786 787 788 789 790 791 792 793 794
    Phase "knowlbase", "Filling knowledge_base_entries table", sub {
	my ($exitval, @rows) = ExecQuiet("echo 'select * from " .
	    "knowledge_base_entries' | $MYSQL -s $DBNAME");
	if ($exitval) {
	    PhaseFail("Error running query");
	}
	if (scalar @rows) {
	    PhaseSkip("Already done");
	}
	ExecQuietFatal("$MYSQL $DBNAME < ".
		       "$TOP_SRCDIR/sql/knowlbase-create.sql");
    };
795 796 797 798
};

Phase "rc.conf", "Adding testbed content to $RCCONF", sub {
    DoneIfEdited($RCCONF);
799 800 801 802 803 804 805 806 807 808 809 810
    my @strings = (qq|rpcbind_enable="YES"|,
		   qq|mountd_enable="YES"|,
		   qq|nfs_server_enable="YES"|,
		   qq|nfs_server_flags="-u -t -n 16"|,
		   qq|nfs_client_enable="YES"|,
		   qq|inetd_enable="YES"|,
		   qq|inetd_flags="-wW -R 0"|,
		   qq|xntpd_enable="YES"|,
		   qq|syslogd_flags=""|,
		   qq|tftpd_flags="-lvvvv -C 40 -s /tftpboot"|,
		   qq|apache_enable="YES"|);

811 812
    # Starting at FreeBSD 6 we use the default version of bind, not the port.
    if ($FBSD_VERSION < 6) {
813 814 815 816 817
	push(@strings, qq|named_enable="NO"|);
    }
    else {
	push(@strings, (qq|named_enable="YES"|,
			qq|named_chrootdir=""|,
818
			qq|named_flags=""|,
819 820 821
			qq|named_uid="root"|));
    }
    AppendToFileFatal($RCCONF, @strings);
822 823
};

824
#
825 826
# New version perl does not appear to require this anymore. In fact, it
# seems to break things if it is!
827 828 829 830 831 832 833
#
if ($FBSD_VERSION == 4) {
    Phase "suidperl", "Setting the suid bit on $SUIDPERL", sub {
	PhaseSkip("Already done") if (-u $SUIDPERL);
	ExecQuietFatal("$CHMOD u+s $SUIDPERL");
    };
}
834 835 836 837 838 839
else {
    Phase "suidperl", "UnSetting the suid bit on $SUIDPERL", sub {
	PhaseSkip("Already done") if (! -u $SUIDPERL);
	ExecQuietFatal("$CHMOD u-s $SUIDPERL");
    };
}
840

841
Phase "hosts", "Adding boss/ops/fs IP addresses to $HOSTS", sub {
842
    DoneIfEdited($HOSTS);
843 844 845 846 847 848
    my $hstr = "${BOSSNODE_IP}\t${BOSSNODE} boss".
	      "\n${USERNODE_IP}\t@OPS_NAMES";
    if ($USERNODE ne $FSNODE) {
	$hstr .= "\n${FSNODE_IP}\t${FSNODE} fs";
    }
    AppendToFileFatal($HOSTS, $hstr);
849 850
};

851 852 853 854 855 856
Phase "resolve", "Checking to make sure names for boss/ops/fs resolve", sub {
    my @hnames = (@OPS_NAMES, ${BOSSNODE}, "boss");
    if ($USERNODE ne $FSNODE) {
	push @hnames, ${FSNODE}, "fs";
    }
    foreach my $name (@hnames) {
Robert Ricci's avatar
Robert Ricci committed
857 858
	Phase $name, $name, sub {
	    if (gethostbyname($name)) {
859
		PhaseSucceed("$name resolves");
Robert Ricci's avatar
Robert Ricci committed
860 861 862 863 864 865 866 867
	    } else {
		PhaseFail("$name does not resolve - please see setup.txt\n" .
		    "for further instructions!");
	    }
	};
    }
};

868 869 870 871 872 873 874 875 876 877 878 879
Phase "NFSmounts", "Setting up NFS mounts", sub {
    Phase "mountpoints", "Creating mountpoints", sub {
	foreach my $dir (@MOUNTPOINTS) {
	    Phase $dir, $dir, sub {
		DoneIfExists($dir);
		mkdir $dir, 0777 or
		    PhaseFail("Unable to create $dir : $!");
	    };
	}
    };
    Phase "fstab", "Adding NFS mounts to $FSTAB", sub {
	DoneIfEdited($FSTAB);
880 881 882 883 884 885 886 887 888 889 890 891 892 893
	my @lines = ("$FSNODE:$USERROOT\t\t$USERROOT\tnfs\trw,nodev,nosuid\t0\t0",
		     "$FSNODE:$PROJROOT\t\t$PROJROOT\tnfs\trw,nodev,nosuid\t0\t0",
		     "$FSNODE:$GROUPROOT\t\t$GROUPROOT\tnfs\trw,nodev,nosuid\t0\t0",
		     "$FSNODE:$SHAREROOT\t\t$SHAREROOT\tnfs\trw,nodev,nosuid\t0\t0");
	if ($SCRATCHDIR) {
	    push(@lines,
		 "$FSNODE:$SCRATCHROOT\t\t$SCRATCHROOT\tnfs\trw,nodev,nosuid\t0\t0");
	}
	push(@lines,
	     "$USERNODE:/usr/testbed\t\t$OPSDIR_DIR\tnfs\trw,soft,".
	     "-b,nodev,nosuid\t0\t0",
	     "$USERNODE:/var\t\t$USERSVAR_DIR\tnfs\tro,soft,".
	     "-b,nodev,nosuid\t0\t0");
	AppendToFileFatal($FSTAB, @lines);
894 895 896 897 898 899 900 901 902
    };
    Phase "mounts", "Mounting NFS filesystems", sub {
	foreach my $dir (@MOUNTPOINTS) {
	    Phase $dir, $dir, sub {
		DoneIfMounted($dir);
		ExecQuietFatal("$MOUNT -o '-R 1' $dir");
	    };
	}
    };
903 904
};

Leigh B. Stoller's avatar
Leigh B. Stoller committed
905 906 907 908 909 910 911 912 913 914 915 916 917 918 919
#
# Not needed in an inner elab, and it takes a long time.
#
if (! $ELABINELAB) {
    Phase "mibs", "Fetching Cisco MIBs", sub {
	foreach my $mib (@CISCO_MIBS) {
	    my $localfile = "$MIBPATH/$mib.txt";
	    my $mibURL = "$CISCO_MIB_FTP/$mib.my";
	    Phase "$mib", "Fetching $mib", sub {
		DoneIfExists($localfile);
		FetchFileFatal($mibURL,$localfile);
	    };
	}
    };
}
920 921 922 923 924

Phase "cron", "Adding cron jobs", sub {
    Phase "crontab", "Editing $CRONTAB", sub {
	DoneIfEdited($CRONTAB);
	AppendToFileFatal($CRONTAB,
925
	    "0  \t6\t*\t*\t*\troot\t$PREFIX/sbin/audit",
926
	    "0  \t1\t*\t*\t*\troot\t$PREFIX/sbin/dbcheck",
927 928 929
	    "45 \t1\t*\t*\t*\troot\t$PREFIX/sbin/backup",
	    "*/5\t*\t*\t*\t*\troot\t$PREFIX/sbin/node_status",
	    "*/5\t*\t*\t*\t*\troot\t$PREFIX/sbin/idlemail");
930 931 932 933 934
    };
};

Phase "sudoers", "Editing $SUDOERS", sub {
    DoneIfEdited($SUDOERS);
Russ Fish's avatar
Russ Fish committed
935 936 937
    AppendToFileFatal($SUDOERS,
		      "%wheel    ALL=(ALL) NOPASSWD: ALL",
		      "Defaults  logfile=/var/log/sudo.log");
938 939
};

Robert Ricci's avatar
Robert Ricci committed
940 941 942 943 944 945 946 947 948 949 950 951 952 953
Phase "php.ini", "Creating php.ini file", sub {
    DoneIfExists($PHP_INI);
    CreateFileFatal($PHP_INI,
	"[PHP]","",
	";",
	"; So that quotes are not escaped. Needed for netbuild application.",
	";", 
	"magic_quotes_gpc        =       Off","",
	";",
	"; Our scripts depend on this!",
	";",
	"register_globals        =       On");
};

954
Phase "ssh", "Setting up root ssh from boss to ops", sub {
Robert Ricci's avatar
Robert Ricci committed
955 956
    Phase "keygen", "Creating root private key", sub {
	DoneIfExists($ROOT_PRIVKEY);
957
	ExecQuietFatal("$SSH_KEYGEN -t rsa -P '' -f $ROOT_PRIVKEY");
Robert Ricci's avatar
Robert Ricci committed
958
    };
959 960 961 962 963 964 965 966 967 968 969
    #
    # Stick it into the DB.
    # WARNING: This sitevar (node/ssh_pubkey) is referenced in tmcd.c
    #
    Phase "sitevar", "Inserting pubkey into DB", sub {
	my $pubkey = `cat $ROOT_PUBKEY`;
	chomp $pubkey;
	ExecQuietFatal("echo \"update sitevariables set value='$pubkey' ".
		       "       where name='node/ssh_pubkey'\" | ".
		       "$MYSQL $DBNAME");
    };
Robert Ricci's avatar
Robert Ricci committed
970 971 972 973 974
    Phase "ssh", "Editing ssh config file", sub {
	DoneIfEdited($SSH_CONFIG);
	AppendToFileFatal($SSH_CONFIG,
	    "Host *",
	    "   StrictHostKeyChecking no",
975
	    "   Protocol 2,1");
Robert Ricci's avatar
Robert Ricci committed
976
    };
977
    Phase "keycopy", "Copy root ssh keys to ops and fs", sub {
Leigh B. Stoller's avatar
Leigh B. Stoller committed
978
	if (! ExecQuiet("$SSH -o 'BatchMode=yes' root\@${USERNODE} pwd")) {
979 980
	    PhaseSkip("Key already copied");
	} else {
981
	    ExecQuietFatal("$SCP_INIT -i $INIT_PRIVKEY ".
Leigh B. Stoller's avatar
Leigh B. Stoller committed
982 983
			   "$ROOT_PUBKEY ${USERNODE}:$ROOT_AUTHKEY");

984 985 986 987
	    # Copy hosts keys to ops.
	    ExecQuietFatal("cat ${ETCSSH}/*.pub | $SSH ${USERNODE} ".
			   "  '(cat > ${ETCSSH}/ssh_known_hosts)'");

Leigh B. Stoller's avatar
Leigh B. Stoller committed
988
	    if (ExecQuiet("$SSH -o 'BatchMode=yes' root\@${USERNODE} pwd")) {
989 990 991
		PhaseFail("You need to manually copy boss's public SSH key\n".
			  "over to $USERNODE so boss can get into it without\n".
			  "a password. Run the following as root:\n" .
Leigh B. Stoller's avatar
Leigh B. Stoller committed
992 993
			  "scp $ROOT_PUBKEY ${USERNODE}:$ROOT_AUTHKEY");
	    }
994
	}
995
    };
996 997 998 999 1000 1001 1002
    Phase "keycopy2", "Copy root ssh keys to fs", sub {
	if ($USERNODE eq $FSNODE) {
	    PhaseSkip("FS node is ops node");
	}
	if (! ExecQuiet("$SSH -o 'BatchMode=yes' root\@${FSNODE} pwd")) {
	    PhaseSkip("Key already copied");
	} else {
1003
	    ExecQuietFatal("$SCP_INIT -i $INIT_PRIVKEY ".
1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017
			   "$ROOT_PUBKEY ${FSNODE}:$ROOT_AUTHKEY");

	    # Copy hosts keys to fs.
	    ExecQuietFatal("cat ${ETCSSH}/*.pub | $SSH ${FSNODE} ".
			   "  '(cat > ${ETCSSH}/ssh_known_hosts)'");

	    if (ExecQuiet("$SSH -o 'BatchMode=yes' root\@${FSNODE} pwd")) {
		PhaseFail("You need to manually copy boss's public SSH key\n".
			  "over to $FSNODE so boss can get into it without\n".
			  "a password. Run the following as root:\n" .
			  "scp $ROOT_PUBKEY ${FSNODE}:$ROOT_AUTHKEY");
	    }
	}
    };
1018 1019
};

1020
Phase "rndc", "Setting up rndc for control of nameserver", sub {
1021 1022 1023 1024 1025 1026 1027 1028
    my $RNDC_KEY     = "/etc/namedb/rndc.key";
    my $RNDC_CONFGEN = "/usr/sbin/rndc-confgen";

    # Bind9 port prior to FreeBSD6
    if ($FBSD_VERSION < 6) {
	$RNDC_KEY     = "/usr/local/etc/rndc.key";
	$RNDC_CONFGEN = "/usr/local/sbin/rndc-confgen";
    }
1029 1030 1031 1032
    DoneIfExists($RNDC_KEY);
    ExecQuietFatal("$RNDC_CONFGEN -a -r /dev/urandom");
};

1033 1034 1035 1036 1037 1038 1039
Phase "loader.conf", "Setting up $LOADER_CONF", sub {
    DoneIfEdited($LOADER_CONF);
    AppendToFileFatal($LOADER_CONF,
	"kern.hz=1000"
    );
};

1040 1041 1042 1043 1044 1045 1046 1047
Phase "sysctl.conf", "Setting up $SYSCTL_CONF", sub {
    DoneIfEdited($SYSCTL_CONF);
    AppendToFileFatal($SYSCTL_CONF,
	"net.local.dgram.maxdgram=65536",
	"net.local.dgram.recvspace=65536"
    );
};

1048 1049 1050 1051 1052 1053 1054 1055 1056
Phase "sslcerts", "Setting up SSL certificates", sub {
    Phase "sslgen", "Generating SSL certificates", sub {
	DoneIfExists("$TOP_OBJDIR/ssl/$EMULAB_PEM");
	ExecQuietFatal("$GMAKE -C $TOP_OBJDIR/ssl remote-site");
    };
    Phase "sslinstall", "Installing SSL certificates", sub {
	DoneIfExists("$PREFIX/etc/$EMULAB_PEM");
	ExecQuietFatal("$GMAKE -C $TOP_OBJDIR/ssl remote-site-boss-install");
    };
1057
    Phase "sslopscopy", "Copying SSL certificates to ops", sub {
1058 1059
 	ExecQuietFatal("$SSH -o 'BatchMode=yes' root\@${USERNODE} ".
		       "     mkdir -p ${ETC_EMULAB_DIR} ");
1060 1061 1062 1063 1064
 	ExecQuietFatal("$SCP $PREFIX/etc/$EMULAB_PEM ".
 		       "     ${USERNODE}:${ETC_EMULAB_DIR}");
 	ExecQuietFatal("$SCP $TOP_OBJDIR/ssl/$CTRLNODE_PEM".
		       "     ${USERNODE}:${ETC_EMULAB_DIR}/${CLIENT_PEM}");
 	ExecQuietFatal("$SSH -o 'BatchMode=yes' root\@${USERNODE} ".
1065 1066
		       "     'chmod 640 ${ETC_EMULAB_DIR}/${CLIENT_PEM}; ".
		       "      chmod 640 ${ETC_EMULAB_DIR}/${EMULAB_PEM}'  ");
1067
    };
1068
    Phase "apache", "Setting up Apache on boss", sub {
1069 1070 1071 1072 1073 1074 1075 1076 1077 1078
	Phase "cert", "Installing Apache SSL certificate", sub {
	    DoneIfExists("$APACHE_CERTFILE");
	    ExecQuietFatal("$CP $TOP_OBJDIR/ssl/$APACHE_CERTPEM ".
			   "    $APACHE_CERTFILE");
	};
	Phase "key", "Installing Apache SSL key", sub {
	    DoneIfExists("$APACHE_KEYFILE");
	    ExecQuietFatal("$CP $TOP_OBJDIR/ssl/$APACHE_KEYPEM ".
			   "    $APACHE_KEYFILE");
	};
1079
	Phase "rc.d", "Installing Apache startup file", sub {
1080 1081
	    DoneIfExists("$RCDIR/apache.sh");
	    ExecQuietFatal("mv $RCDIR/apache.sh.sample $RCDIR/apache.sh");
1082
	    if ($FBSD_VERSION == 6) {
1083 1084
		ExecQuietFatal("sed -i .orig ".
			       "-e 's/^apache_enable/#apache_enable/' ".
1085
			       "$RCDIR/apache.sh");
1086
		ExecQuietFatal("/bin/rm -f $RCDIR/apache.sh.orig");
1087
	    }
1088
	};
1089 1090 1091 1092
	Phase "starting", "Starting Apache server", sub {
	    DoneIfExists("$VARRUN/httpd.pid");
	    ExecQuietFatal("$RCDIR/apache.sh start");
	};
1093
    };
1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104
    if ($FBSD_VERSION > 4) {
	Phase "apache", "Setting up Apache on ops", sub {
	    Phase "cert", "Installing Apache SSL certificate", sub {
		ExecQuietFatal("$SCP $TOP_OBJDIR/ssl/$APACHE_CERTPEM_OPS ".
			       "     ${USERNODE}:$APACHE_CERTFILE_OPS");
	    };
	    Phase "key", "Installing Apache SSL key", sub {
		ExecQuietFatal("$SCP $TOP_OBJDIR/ssl/$APACHE_KEYPEM_OPS ".
			       "     ${USERNODE}:$APACHE_KEYFILE_OPS");
	    };
	    Phase "rc.d", "Installing Apache startup file", sub {
1105 1106
		ExecQuietFatal("$SCP $RCDIR/apache.sh ".
			       "     ${USERNODE}:$RCDIR/apache.sh");
1107 1108 1109
	    };
	};
    }
1110 1111
};

Leigh B. Stoller's avatar
Leigh B. Stoller committed
1112 1113 1114 1115 1116 1117 1118 1119 1120 1121
if ($MAILMANSUPPORT) {
    my $secret = substr(GenSecretKey(), 0, 10);
    
    Phase "Mailman", "Setting up Mailman admin Password", sub {
	ExecQuietFatal("echo \"update sitevariables set value='$secret' ".
		       "       where name='general/mailman/password'\" | ".
		       "$MYSQL $DBNAME");
    };
}

1122 1123 1124 1125 1126 1127 1128 1129 1130 1131
Phase "pubsub", "Setting up pubsub system", sub {
    # Ug, kill off the sync server in elabinelab.
    if ($ELABINELAB) {	
	Phase "syncd", "Stopping syncd", sub {
	    if (ExecQuiet("killall -s emulab-syncd")) {
		PhaseSkip("syncd not running");
	    }
	    ExecQuietFatal("killall emulab-syncd");
	};
    }
1132
    # For elabinelab, be sure to kill off running event system. Harmless.
1133 1134 1135
    Phase "stopping", "Stopping pubsub system", sub {
	if (ExecQuiet("killall -s pubsubd")) {
	    PhaseSkip("pubsubd not running");
1136
	}
1137
	ExecQuietFatal("$RCDIR/2.pubsubd.sh stop");
1138
    };
1139 1140
    Phase "starting", "Starting pubsub system", sub {
	ExecQuietFatal("$RCDIR/2.pubsubd.sh start");
1141
    };
1142 1143
};

1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160
#
# Build and install software. Note that I am not looking to see if its
# been done; I suppose we could touch a file to indicate that build has
# been completed, but maybe that is not such a good idea.
#
Phase "Software", "Building and Installing Software", sub {
    Phase "building", "Building (please be patient)", sub {
	ExecQuietFatal("cd $TOP_OBJDIR; $GMAKE");
    };
    Phase "installing", "Installing (please be patient)", sub {
	ExecQuietFatal("cd $TOP_OBJDIR; $GMAKE boss-install");
    };
    Phase "postinstall", "Post Installing Testbed Software", sub {
	ExecQuietFatal("cd $TOP_OBJDIR; $GMAKE post-install");
    };
};

1161 1162 1163 1164
#
# The next few items must be after the software install since they use
# testbed libraries and such.
#
1165 1166
Phase "dhcpd", "Setting up initial dhcpd configuration", sub {
    Phase "template", "Installing $DHCPD_TEMPLATE", sub {
Leigh B. Stoller's avatar
Leigh B. Stoller committed
1167
	ExecQuietFatal("cd $TOP_OBJDIR/dhcpd; $GMAKE install");
1168 1169
    };
    Phase "config", "Creating $DHCPD_CONF from template", sub {
Leigh B. Stoller's avatar
Leigh B. Stoller committed
1170
	ExecQuietFatal("$DHCPD_MAKECONF -i");
1171 1172 1173 1174 1175 1176 1177 1178
    };
    # How silly is this?
    Phase "leases", "Creating stub leases file", sub {
	DoneIfExists("$DHCPD_LEASES");
	ExecQuietFatal("touch $DHCPD_LEASES");
    };
};

1179 1180 1181 1182 1183 1184 1185 1186 1187 1188
Phase "named", "Setting up initial named configuration", sub {
    Phase "building", "Building named files and templates", sub {
	ExecQuietFatal("cd $TOP_OBJDIR/named; $GMAKE");
    };
    Phase "installing", "Installing named files and templates", sub {
	ExecQuietFatal("cd $TOP_OBJDIR/named; $GMAKE install-real");
    };
    Phase "generating", "Generating named zone files", sub {
	ExecQuietFatal("$NAMED_SETUP -norestart");
    };
1189
    
1190 1191
    # Starting at 6.0 we use the default version of bind, not the port.
    my $named_control = ($FBSD_VERSION < 6 ?
1192 1193
			 "$RCDIR/1.named.sh" : "/etc/rc.d/named");
    
1194 1195 1196 1197
    Phase "stopping", "Stopping named", sub {
	if (ExecQuiet("killall -s named")) {
	    PhaseSkip("named not running");
	}
1198
	ExecQuietFatal("$named_control stop");
1199 1200
    };
    Phase "starting", "Starting named", sub {
1201
	ExecQuietFatal("$named_control start");
1202
    };
1203 1204

    # Lets make sure that old file is gone!
1205
    if ($FBSD_VERSION >= 6) {
1206 1207 1208 1209 1210 1211 1212 1213 1214
	Phase "cleanup", "Cleaning up old files", sub {
	    DoneIfDoesntExist("$RCDIR/1.named.sh");
	
	    if (!unlink "$RCDIR/1.named.sh") {
		PhaseFail("Unable to remove $RCDIR/1.named.sh: $!");
	    }
	};
    }
    
1215 1216 1217 1218
    Phase "copying", "Copying resolv.conf over to ops", sub {
 	ExecQuietFatal("$SCP $TOP_OBJDIR/named/resolv.conf.ops ".
		       "     ${USERNODE}:/etc/resolv.conf");
    };
1219 1220 1221 1222 1223 1224 1225
    Phase "copying2", "Copying resolv.conf over to fs", sub {
	if ($USERNODE eq $FSNODE) {
	    PhaseSkip("FS node is ops node");
	}
 	ExecQuietFatal("$SCP $TOP_OBJDIR/named/resolv.conf.ops ".
		       "     ${FSNODE}:/etc/resolv.conf");
    };
1226 1227
};

1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239
if ($BUGDBSUPPORT) {
    my $BUGDBPROXY  = "$PREFIX/sbin/bugdbproxy";

    Phase "flyspray", "Finalizing flyspray installation", sub {
	PhaseSkip("flyspray not supported")
	    if ($FBSD_VERSION < 6);

 	ExecQuietFatal("$SSH -o 'BatchMode=yes' root\@${USERNODE} ".
		       "     '$BUGDBPROXY setup' ");
    };
}

1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291
Phase "firstuser", "Setting up initial user ($PROTOUSER)", sub {
    Phase "firstuser", "Calling 'firstuser' to create account", sub {
	PhaseSkip("$PROTOUSER already created")
	    if (-d "$USERROOT/$PROTOUSER");
	ExecQuietFatal("perl $TOP_OBJDIR/utils/firstuser -b ".
		       (defined($password) ? " -p $password" : ""));
    };
    Phase "Fixing", "Fixing up DB state for $PROTOUSER", sub {
	my ($exitval, @rows) =
	    ExecQuiet("echo 'select uid from users ".
		      "  where uid=\"$PROTOUSER\" and webonly=0' ".
		      "| $MYSQL -s $DBNAME");
	if ($exitval) {
	    PhaseFail("Error running query");
	}
	if (scalar @rows) {
	    PhaseSkip("Already done");
	}
	ExecQuietFatal("echo 'update users set webonly=0 ".
		       "  where uid=\"$PROTOUSER\"' | $MYSQL -s $DBNAME");
    };
    Phase "Thawing", "Thawing $PROTOUSER", sub {
	my ($exitval, @rows) =
	    ExecQuiet("echo 'select uid from users ".
		      "  where uid=\"$PROTOUSER\" and status=\"active\"' ".
		      "| $MYSQL -s $DBNAME");
	if ($exitval) {
	    PhaseFail("Error running query");
	}
	if (scalar @rows) {
	    PhaseSkip("Already done");
	}
	ExecQuietFatal("echo 'update users set status=\"active\" ".
		       "  where uid=\"$PROTOUSER\"' | $MYSQL -s $DBNAME");
	ExecQuietFatal("$SUDO -u $PROTOUSER $WAP $TBACCT -b thaw $PROTOUSER");
    };
    Phase "DSAKey", "Adding DSA key to $PROTOUSER account", sub {
	my ($exitval, @rows) =
	    ExecQuiet("echo 'select * from user_pubkeys ".
		      " where uid=\"$PROTOUSER\"' | $MYSQL -s $DBNAME");
	if ($exitval) {
	    PhaseFail("Error running query");
	}
	if (scalar @rows) {
	    PhaseSkip("Already done");
	}
	ExecQuietFatal("$SUDO -u $PROTOUSER $WAP ".
		       "  $ADDPUBKEY -f -u $PROTOUSER $PROTOUSER_KEY");
    };
    Phase "authkeys", "Generating authorized_keys for $PROTOUSER", sub {
	ExecQuietFatal("$SUDO -u $PROTOUSER $WAP $ADDPUBKEY -w $PROTOUSER");
    };
1292 1293
};

Timothy Stack's avatar
 
Timothy Stack committed
1294 1295
Phase "chkupuser", "Setting up checkup user (elabckup)", sub {
    PhaseSkip("elabckup already created")
1296
	if (-d "$USERROOT/elabckup");
Timothy Stack's avatar
 
Timothy Stack committed
1297 1298
    ExecQuietFatal("perl $TOP_OBJDIR/utils/firstuser -b ".
		   (defined($password) ? " -p $password" : "").
Timothy Stack's avatar
Timothy Stack committed
1299
		   " -u elabckup -n 'Emulab Checkup User' ".
Timothy Stack's avatar
 
Timothy Stack committed
1300 1301 1302
		   "-e '@TBTESTSUITEEMAIL@'");
};

1303 1304 1305 1306 1307 1308 1309
Phase "experiments", "Setting up system experiments", sub {
    foreach my $eid (keys(%EXPERIMENTS)) {
	my $pid  = $EXPERIMENTS{$eid}->{"pid"};
	my $desc = $EXPERIMENTS{$eid}->{"description"};
	
	Phase "$pid/$eid", "$pid/$eid", sub {
	    PhaseSkip("Experiment Created")
1310
		if (-d "$PROJROOT/$pid/exp/$eid");
1311
	    ExecQuietFatal("$SUDO -u $PROTOUSER $WAP $BATCHEXP ".
1312
			   "  -q -i -w -f -n -S 'System Experiment' ".
1313 1314 1315 1316 1317 1318 1319
			   "  -L 'System Experiment' ".
			   "  -E '$desc - DO NOT DELETE' ".
			   "  -p $pid -e $eid");
	};
    }
};

1320 1321 1322 1323 1324
print "----------------------------------------------------------------------\n";
print "Installation completed succesfully!\n";
print "Please reboot this machine before proceeding with boss setup\n";

exit 0;