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

#
# EMULAB-COPYRIGHT
5
# Copyright (c) 2003-2008 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 157 158
#
# Named pid file.
#
my $NAMED_PIDFILE = "/var/run/named.pid";

159 160 161
#
# Some directories we care about
#
162 163 164 165 166
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";
167 168
my $PORTSDIR      = "/usr/ports";
my $PORTSMISCDIR  = "$PORTSDIR/misc";
169 170 171
my $MIBPATH       = "/usr/local/share/snmp/mibs";
my $TFTP_DIR      = "$PREFIX/tftpboot";
my $TFTP_PROJ_DIR = "$TFTP_DIR/proj";
172
my $VARRUN        = "/var/run";
173
my $ETCSSH	  = "/etc/ssh";
174 175 176 177 178 179 180 181 182 183 184

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

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

185
my @MOUNTPOINTS = ("$USERSVAR_DIR", "$OPSDIR_DIR",
Leigh B. Stoller's avatar
Leigh B. Stoller committed
186 187 188 189
		   "$USERROOT", "$PROJROOT", "$GROUPROOT");
if ($SHAREDIR) {
    push(@MOUNTPOINTS, "$SHAREROOT");
}
190 191 192
if ($SCRATCHDIR) {
    push(@MOUNTPOINTS, "$SCRATCHROOT");
}
Robert Ricci's avatar
Robert Ricci committed
193

194 195
my @LOGFILES = ("$LOGDIR/bootinfo.log", "$LOGDIR/tmcd.log",
    "$LOGDIR/capture.log", "$LOGDIR/dhcpd.log", "$LOGDIR/capserver.log",
196
    "$LOGDIR/frisbeed.log", "$LOGDIR/proxydhcpd.log",
197
    "$LOGDIR/stated.log", "$LOGDIR/osselect.log",
198
    "$LOGDIR/tftpd.log", "$LOGDIR/sdcollectd.log", "$LOGDIR/genlastlog.log",
199
    "$LOGDIR/sshxmlrpc.log", "$LOGDIR/plabgetfree.log", "$LOGDIR/xmlrpcbag.log",
200
    "$LOGDIR/plabrenew.log", "$LOGDIR/sslxmlrpc.log", "$LOGDIR/pubsubd.log");
201 202

my @CISCO_MIBS = ("CISCO-SMI", "CISCO-TC", "CISCO-VTP-MIB", "CISCO-PAGP-MIB",
203
    "CISCO-PRIVATE-VLAN-MIB", "CISCO-STACK-MIB", "CISCO-VLAN-MEMBERSHIP-MIB",
204
    "CISCO-C2900-MIB", "BRIDGE-MIB", "CISCO-CONFIG-COPY-MIB");
205

206 207 208 209 210 211 212 213 214 215 216 217
# 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"},
218 219
     "nfree-locked"  => {"pid"	       => "emulab-ops",
			 "description" => "Nodes in limbo during nfree"},
220 221 222 223
     "opsnodes"	     => {"pid"	       => "emulab-ops",
			 "description" => "Nodes designated as Ops Nodes"},
    );

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

232 233 234 235 236 237 238 239 240
# 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");
}

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

248 249 250 251 252 253 254 255 256 257 258 259 260 261 262
#
# 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;
263 264 265 266 267 268
use Getopt::Std;

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

my $packagedir = "";
Leigh B. Stoller's avatar
Leigh B. Stoller committed
275
my $batchmode  = 0;
276
my $domakes  = 0;
277
my $password;
278 279
my %opts;

280
if (! getopts("P:p:bsw:", \%opts)) {
Leigh B. Stoller's avatar
Leigh B. Stoller committed
281 282 283 284 285 286 287
    usage();
}
if (defined($opts{p})) {
    $packagedir = $opts{p};
}
if (defined($opts{b})) {
    $batchmode = 1;
288
}
289 290 291
if (defined($opts{s})) {
    $domakes = 1;
}
292 293 294
if (defined($opts{w})) {
    $password = $opts{w};
}
295 296 297
if (defined($opts{P})) {
    $BOSS_PORT = $opts{P};
}
298 299 300 301 302 303 304 305 306
# 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();
}
307 308 309
if (@ARGV) {
    usage();
}
310 311 312 313

#
# Make sure they know what they're getting into...
#
Leigh B. Stoller's avatar
Leigh B. Stoller committed
314 315 316 317 318 319 320 321 322 323
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);
}
324 325 326 327 328

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

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

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
378
Phase "tftp", "Setting up directories for tftp", sub {
379
    Phase "tftpoff", "Disabling BSD tftpd", sub {
380 381
	PhaseSkip("No inetd.conf!?") unless (-e $INETD_CONF);
	PhaseSkip("Already disabled") unless `grep '^tftp' $INETD_CONF`;
382 383 384
	ExecQuietFatal("sed -i .orig -e '/^tftp/s/^/#/' $INETD_CONF");
	HUPDaemon("inetd");
    };
Robert Ricci's avatar
Robert Ricci committed
385 386 387 388 389 390
    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");
    };
391 392 393 394 395 396
    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
397 398 399 400
    Phase "tftplink", "Linking /tftpboot", sub {
	DoneIfExists("/tftpboot");
	ExecQuietFatal("ln -s $TFTP_DIR /tftpboot");
    };
401 402 403
};

Phase "ports", "Installing ports", sub {
404
    Phase "packages", "Installing packages", sub {
405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424
	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");
	    };
425 426 427 428 429 430 431 432 433 434 435 436 437 438
	    # 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");
	    };
439 440 441 442 443 444 445 446
	    # 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");
	    };
447 448
	}
    };
449
    PhaseSkip("Package directory provided; not installing from sources")
Leigh B. Stoller's avatar
Leigh B. Stoller committed
450 451
	if ($packagedir);

452
    Phase "pcopy", "Copying ports into place", sub {
453
	DoneIfExists("$PORTSMISCDIR/emulab-boss");
454 455
	ExecQuietFatal("$SH $SRCDIR/ports/ports-install");
    };
456 457 458 459 460
    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")) {
461
		PhaseSkip("Patch already applied");
462 463 464 465
	    }
	    ExecQuietFatal("$PATCH -f -l -p0 -i $PHP4_PATCH");
	};
    }
466 467
    PhaseFail("Please install ports manually, since some\n of them are " .
	      "interactive. Run: \n" .
468 469
	      "    cd $PORTSMISCDIR/emulab-boss && make install\n" .
	      "    cd $PORTSMISCDIR/emulab-php4 && make install\n" .
470
	      "then re-run this script.");
471 472
};

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

512
Phase "patches", "Applying patches", sub {
513 514 515
    if ($FBSD_VERSION == 4) {
	Phase "g++patch", "Patching g++'s STL", sub {
	    if (!ExecQuiet("$PATCH -C -f -R -p0 -i $STL_PATCH")) {
516
		PhaseSkip("Patch already applied");
517 518 519 520
	    }
	    ExecQuietFatal("$PATCH -f -p0 -i $STL_PATCH");
	};
    }
521
    Phase "m2cryptopatch", "Patching m2crypto", sub {
522 523 524 525 526 527
	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")) {
528
	    PhaseSkip("Patch already applied");
529
	}
530
	ExecQuietFatal("$PATCH -f -l -p0 -i $patchfile");
531
    };
Kevin Atkinson's avatar
 
Kevin Atkinson committed
532 533
    Phase "Mysql.pm.patch", "Patching Mysql.pm", sub {
	my $patchfile = $MYSQL_PM_PATCH;
534
	$patchfile = `realpath $patchfile`;
Kevin Atkinson's avatar
 
Kevin Atkinson committed
535 536 537 538 539 540 541 542 543 544 545 546 547 548
	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")) {
549
	    PhaseSkip("Patch already applied");
Kevin Atkinson's avatar
 
Kevin Atkinson committed
550 551 552
	}
	ExecQuietFatal("$PATCH -d $dir -f -l -i $patchfile");
    };
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 581 582 583 584 585
};

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: $!");
	}
    };
586 587 588 589 590 591
    Phase "my-server2", "Removing $RCDIR/mysql-server", sub {
	DoneIfDoesntExist("$RCDIR/mysql-server");
	if (!unlink "$RCDIR/mysql-server") {
	    PhaseFail("Unable to remove $RCDIR/mysql-server: $!");
	}
    };
592
    Phase "snmpd", "Removing snmpd startup script",  sub {
593 594 595 596 597
	DoneIfDoesntExist("$RCDIR/snmpd.sh");
	if (!unlink "$RCDIR/snmpd.sh") {
	    PhaseFail("Unable to remove $RCDIR/snmpd.sh: $!");
	}
    };
598 599 600 601 602 603 604
    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 {
605 606 607 608 609
	DoneIfDoesntExist("$RCDIR/isc-dhcpd.sh");
	if (!unlink "$RCDIR/isc-dhcpd.sh") {
	    PhaseFail("Unable to remove $RCDIR/isc-dhcpd.sh: $!");
	}
    };
610
    Phase "rc.testbed", "Installing testbed RC scripts", sub {
611
	ExecQuietFatal("$GMAKE -C $TOP_OBJDIR/rc.d install");
612
    };
613 614 615 616 617 618 619 620 621
    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");
        };
    };
622 623
};

624 625 626
Phase "syslog", "Setting up syslog", sub {
    Phase "sysconf", "Editing $SYSLOG_CONF", sub {
	DoneIfEdited($SYSLOG_CONF);
627 628

	#
629 630
	# Modify the /dev/console and /var/log/messages lines to exclude
	# testbed stuff
631 632 633 634 635 636 637 638 639 640
	#
	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) {
641 642 643 644 645 646 647
	    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/;
648 649 650 651 652
	    }
	    print SC $line;
	}
	close(SC);

653 654 655 656 657
	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",
658 659
	    "!proxydhcpd","*.*\t\t\t\t\t\t$LOGDIR/proxydhcpd.log",
	    "!tftpd",     "*.*\t\t\t\t\t\t$LOGDIR/tftpd.log",
660 661
	    "!capserver", "*.*\t\t\t\t\t\t$LOGDIR/capserver.log",
	    "!frisbeed",  "*.*\t\t\t\t\t\t$LOGDIR/frisbeed.log",
662
	    "!pubsubd",    "*.*\t\t\t\t\t\t$LOGDIR/pubsubd.log",
663
	    "!stated",    "*.*\t\t\t\t\t\t$LOGDIR/stated.log",
664 665
	    "!osselect",  "*.*\t\t\t\t\t\t$LOGDIR/osselect.log",
	    "!genlastlog","*.*\t\t\t\t\t\t$LOGDIR/genlastlog.log",
666
	    "!sdcollectd","*.*\t\t\t\t\t\t$LOGDIR/sdcollectd.log",
667
	    "!plabgetfree","*.*\t\t\t\t\t\t$LOGDIR/plabgetfree.log",
668
	    "!plabrenew", "*.*\t\t\t\t\t\t$LOGDIR/plabrenew.log",
669
	    "!xmlrpcbag", "*.*\t\t\t\t\t\t$LOGDIR/xmlrpcbag.log",
670 671
	    "!sshxmlrpc", "*.*\t\t\t\t\t\t$LOGDIR/sshxmlrpc.log",
	    "!sslxmlrpc", "*.*\t\t\t\t\t\t$LOGDIR/sslxmlrpc.log");
672 673 674 675 676 677
    };

    Phase "logdir", "Creating log directory", sub {
	DoneIfExists($LOGDIR);
	mkdir $LOGDIR, 0775 or PhaseFail("Unable to create $LOGDIR : $!");
	ExecQuietFatal("$CHGRP tbadmin $LOGDIR");
678
	ExecQuietFatal("$CHMOD 775 $LOGDIR");
679 680 681 682 683 684
    };

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

    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",
712
	    "$LOGDIR/pubsubd.log            640  5     1000 *     Z",
713
	    "$LOGDIR/suexec.log             640  3     200  *     Z",
714
	    "$LOGDIR/genlastlog.log         640  3     200  *     Z",
715
	    "$LOGDIR/genlastlog             640  3     200  *     Z " .
716 717
	        "/var/run/lastlog_daemon.pid",
	    "$LOGDIR/plabmetrics.log        640  7     1000 *     Z",
718
	    "$LOGDIR/plablinkdata.log       640  7     1000 *     Z",
719
	    "$LOGDIR/xmlrpcbag.log          640  7     300  *     Z",
720 721
	    "$LOGDIR/sshxmlrpc.log          640  7     300  *     Z",
	    "$LOGDIR/sslxmlrpc.log          640  7     300  *     Z");
722 723 724
    };
};

725
Phase "database", "Setting up database", sub {
726 727 728 729 730 731 732
    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
733
    Phase "mysql", "Starting mysqld", sub {
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
	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");
    };
765 766 767 768 769 770 771 772 773 774 775 776
    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
777 778 779 780 781 782 783 784 785 786 787
    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
788 789 790 791 792 793 794 795 796 797 798 799
    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");
    };
800 801 802 803
};

Phase "rc.conf", "Adding testbed content to $RCCONF", sub {
    DoneIfEdited($RCCONF);
804 805 806 807 808 809 810 811 812 813 814 815
    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"|);

816 817
    # Starting at FreeBSD 6 we use the default version of bind, not the port.
    if ($FBSD_VERSION < 6) {
818 819 820 821 822
	push(@strings, qq|named_enable="NO"|);
    }
    else {
	push(@strings, (qq|named_enable="YES"|,
			qq|named_chrootdir=""|,
823
			qq|named_flags=""|,
824 825
			qq|named_uid="root"|,
			qq|named_pidfile="$NAMED_PIDFILE"|));
826 827
    }
    AppendToFileFatal($RCCONF, @strings);
828 829
};

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

847
Phase "hosts", "Adding boss/ops/fs IP addresses to $HOSTS", sub {
848
    DoneIfEdited($HOSTS);
849 850 851 852 853 854
    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);
855 856
};

857 858 859 860 861 862
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
863 864
	Phase $name, $name, sub {
	    if (gethostbyname($name)) {
865
		PhaseSucceed("$name resolves");
Robert Ricci's avatar
Robert Ricci committed
866 867 868 869 870 871 872 873
	    } else {
		PhaseFail("$name does not resolve - please see setup.txt\n" .
		    "for further instructions!");
	    }
	};
    }
};

874 875 876 877 878 879 880 881 882 883 884 885
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);
886 887 888 889 890 891 892 893 894 895 896 897 898 899
	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);
900 901 902 903 904 905 906 907 908
    };
    Phase "mounts", "Mounting NFS filesystems", sub {
	foreach my $dir (@MOUNTPOINTS) {
	    Phase $dir, $dir, sub {
		DoneIfMounted($dir);
		ExecQuietFatal("$MOUNT -o '-R 1' $dir");
	    };
	}
    };
909 910
};

Leigh B. Stoller's avatar
Leigh B. Stoller committed
911 912 913 914 915 916 917 918 919 920 921 922 923 924 925
#
# 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);
	    };
	}
    };
}
926 927 928 929 930

Phase "cron", "Adding cron jobs", sub {
    Phase "crontab", "Editing $CRONTAB", sub {
	DoneIfEdited($CRONTAB);
	AppendToFileFatal($CRONTAB,
931
	    "0  \t6\t*\t*\t*\troot\t$PREFIX/sbin/audit",
932
	    "0  \t1\t*\t*\t*\troot\t$PREFIX/sbin/dbcheck",
933 934 935
	    "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");
936 937 938 939 940
    };
};

Phase "sudoers", "Editing $SUDOERS", sub {
    DoneIfEdited($SUDOERS);
Russ Fish's avatar
Russ Fish committed
941 942 943
    AppendToFileFatal($SUDOERS,
		      "%wheel    ALL=(ALL) NOPASSWD: ALL",
		      "Defaults  logfile=/var/log/sudo.log");
944 945
};

Robert Ricci's avatar
Robert Ricci committed
946 947 948 949 950 951 952 953 954 955 956 957 958 959
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");
};

960
Phase "ssh", "Setting up root ssh from boss to ops", sub {
Robert Ricci's avatar
Robert Ricci committed
961 962
    Phase "keygen", "Creating root private key", sub {
	DoneIfExists($ROOT_PRIVKEY);
963
	ExecQuietFatal("$SSH_KEYGEN -t rsa -P '' -f $ROOT_PRIVKEY");
Robert Ricci's avatar
Robert Ricci committed
964
    };
965 966 967 968 969 970 971 972 973 974 975
    #
    # 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
976 977 978 979 980
    Phase "ssh", "Editing ssh config file", sub {
	DoneIfEdited($SSH_CONFIG);
	AppendToFileFatal($SSH_CONFIG,
	    "Host *",
	    "   StrictHostKeyChecking no",
981
	    "   Protocol 2,1");
Robert Ricci's avatar
Robert Ricci committed
982
    };
983
    Phase "keycopy", "Copy root ssh keys to ops and fs", sub {
Leigh B. Stoller's avatar
Leigh B. Stoller committed
984
	if (! ExecQuiet("$SSH -o 'BatchMode=yes' root\@${USERNODE} pwd")) {
985 986
	    PhaseSkip("Key already copied");
	} else {
987
	    ExecQuietFatal("chmod 400 $INIT_PRIVKEY");
988
	    ExecQuietFatal("$SCP_INIT -i $INIT_PRIVKEY ".
Leigh B. Stoller's avatar
Leigh B. Stoller committed
989 990
			   "$ROOT_PUBKEY ${USERNODE}:$ROOT_AUTHKEY");

991 992 993 994
	    # 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
995
	    if (ExecQuiet("$SSH -o 'BatchMode=yes' root\@${USERNODE} pwd")) {
996 997 998
		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
999 1000
			  "scp $ROOT_PUBKEY ${USERNODE}:$ROOT_AUTHKEY");
	    }
1001
	}
1002
    };
1003 1004 1005 1006 1007 1008 1009
    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 {
1010
	    ExecQuietFatal("$SCP_INIT -i $INIT_PRIVKEY ".
1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024
			   "$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");
	    }
	}
    };
1025 1026
};

1027
Phase "rndc", "Setting up rndc for control of nameserver", sub {
1028 1029 1030 1031 1032 1033 1034 1035
    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";
    }
1036 1037 1038 1039
    DoneIfExists($RNDC_KEY);
    ExecQuietFatal("$RNDC_CONFGEN -a -r /dev/urandom");
};

1040 1041 1042 1043 1044 1045 1046
Phase "loader.conf", "Setting up $LOADER_CONF", sub {
    DoneIfEdited($LOADER_CONF);
    AppendToFileFatal($LOADER_CONF,
	"kern.hz=1000"
    );
};

1047 1048 1049 1050 1051 1052 1053 1054
Phase "sysctl.conf", "Setting up $SYSCTL_CONF", sub {
    DoneIfEdited($SYSCTL_CONF);
    AppendToFileFatal($SYSCTL_CONF,
	"net.local.dgram.maxdgram=65536",
	"net.local.dgram.recvspace=65536"
    );
};

1055 1056 1057 1058 1059 1060 1061 1062 1063
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");
    };
1064
    Phase "sslopscopy", "Copying SSL certificates to ops", sub {
1065 1066
 	ExecQuietFatal("$SSH -o 'BatchMode=yes' root\@${USERNODE} ".
		       "     mkdir -p ${ETC_EMULAB_DIR} ");
1067 1068 1069 1070 1071
 	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} ".
1072 1073
		       "     'chmod 640 ${ETC_EMULAB_DIR}/${CLIENT_PEM}; ".
		       "      chmod 640 ${ETC_EMULAB_DIR}/${EMULAB_PEM}'  ");
1074
    };
1075
    Phase "apache", "Setting up Apache on boss", sub {
1076 1077 1078 1079 1080 1081 1082 1083 1084 1085
	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");
	};