Commit c8b47d7d authored by Leigh Stoller's avatar Leigh Stoller

Add makefile support for taking a FreeBSD fixit disk and turning that

into a testbed boot CD in a mostly automated manner. There are still a
few things that need to be done by hand, which are described in the
cdboot/README file.

Add tmcd/freebsd/cdboot directory of little scripts and files that
need to onto the fixit disk.

The install target is cdboot-install (must be run as root) and if you
are brave enough to run it, you better give it a DESTDIR argument or
you will write a bunch of files onto the local node that will cause
mayhem and havoc at the next reboot.
parent b4695a1b
......@@ -29,6 +29,11 @@ SSLFLAGS = -DWITHSSL
TMLIBS += -lssl -lcrypto
SSLOBJ = ssl.o
ifeq ($(SYSTEM),Linux)
NEEDKERB := $(shell nm /usr/lib/libssl.a | grep -q krb; echo $$?)
ifeq ($(NEEDKERB),0)
CFLAGS += `/usr/kerberos/bin/krb5-config --cflags`
TMLIBS += `/usr/kerberos/bin/krb5-config --libs krb5`
endif
TMLIBS += -ldl
MDSUBDIR = linux
endif
......
......@@ -12,6 +12,14 @@
. /etc/emulab/paths.sh
#
# First check for cdboot.
#
if [ -x $BINDIR/rc/rc.cdboot ]; then
$BINDIR/rc/rc.cdboot
exit 0
fi
#
# If on the MFS, skip this stuff and just do the MFS setup stuff.
#
......
......@@ -28,7 +28,7 @@ all: supfile injail $(SCRIPTS)
include $(TESTBED_SRCDIR)/GNUmakerules
DESTDIR =
DESTDIR =
SYSETCDIR = $(DESTDIR)/etc
JAILDIR = $(SYSETCDIR)/jail
ETCDIR = $(DESTDIR)$(CLIENT_ETCDIR)
......@@ -50,6 +50,30 @@ mfs-install: common-install etc-install \
cp $(SRCDIR)/mfs-rc.conf $(SYSETCDIR)/rc.conf
echo >$(ISMFS) "This file indicates its the MFS. DO NOT DELETE!"
cdboot-install: common-install etc-install \
script-install bin-install
$(INSTALL) -m 755 $(SRCDIR)/cdboot/rc.conf $(SYSETCDIR)/rc.conf
$(INSTALL) -m 755 $(SRCDIR)/cdboot/rc.cdboot $(BINDIR)/rc/rc.cdboot
$(INSTALL) -m 755 $(SRCDIR)/cdboot/rc.cdroot $(SYSETCDIR)/rc.cdroot
$(INSTALL) -m 755 $(SRCDIR)/rc.frisbee $(BINDIR)/rc/rc.frisbee
cp $(SRCDIR)/cdboot/fstab $(SYSETCDIR)/fstab
cp $(SRCDIR)/cdboot/fstab.lbs $(SYSETCDIR)/fstab.lbs
cp $(SRCDIR)/cdboot/loader.conf $(DESTDIR)/boot/loader.conf
cp $(SRCDIR)/cdboot/loader.rc.lbs $(DESTDIR)/boot/loader.rc.lbs
cp $(SRCDIR)/cdboot/.profile $(DESTDIR)/.profile
echo >$(ISMFS) "This file indicates its the MFS. DO NOT DELETE!"
(cd ../../sensors/slothd; $(MAKE) DESTDIR=$(DESTDIR) client-install)
(cd ../../pxe; $(MAKE) DESTDIR=$(DESTDIR) client-install)
(cd ../../os; $(MAKE) DESTDIR=$(DESTDIR) client-install)
(cd ../../os/frisbee.redux; $(MAKE) DESTDIR=$(DESTDIR) client-install)
(cd ../../cdrom/tbbootconfig;$(MAKE) DESTDIR=$(DESTDIR) client-install)
(cd ../../tools/teachswitch; $(MAKE) DESTDIR=$(DESTDIR) client-install)
$(INSTALL) -m 755 $(SRCDIR)/../../install/newclient $(BINDIR)/newclient
$(INSTALL) -m 755 -o root -g wheel -d $(DESTDIR)/users
$(INSTALL) -m 755 -o root -g wheel -d $(DESTDIR)/proj
$(INSTALL) -m 755 -o root -g wheel -d $(DESTDIR)/groups
$(INSTALL) -m 755 -o root -g wheel -d $(DESTDIR)/share
control-install: dir-install
cp /dev/null $(ETCDIR)/isctrl
$(INSTALL) -m 755 ../tmcc $(BINDIR)/tmcc
......@@ -82,7 +106,7 @@ dir-install:
ln -s emulab $(DESTDIR)/usr/local/etc/testbed
common-install: dir-install
(cd ../common; $(MAKE) local-install)
(cd ../common; $(MAKE) DESTDIR=$(DESTDIR) local-install)
sup-install: dir-install supfile
$(INSTALL) -m 755 ./supfile $(ETCDIR)/supfile
......
:
# $FreeBSD: src/release/fixit.profile,v 1.8.2.1 2001/11/05 11:22:04 brian Exp $
export BLOCKSIZE=K
export EDITOR=vi
export PAGER=more
alias ls="ls -F"
alias ll="ls -l"
alias m="more -e"
# Make the arrow keys work; everybody will love this.
set -o emacs 2>/dev/null
*** Other stuff that needs to be installed!
* /usr/site/bin/hier
* /root/.ssh
* /etc/ssh
* /usr/local/bin/sudo
* /usr/local/etc/sudoers
*** Stuff to remove
* /usr/share/doc
* /usr/share/man
* /rr_moved
*** New loader that does testbed magic boot stuff
* I grabbed the stuff I did for the RON/Netbed CD's that checks the
magic sector and stuck that (testbed.c) into the boot loader source
from the 4.10 disk. I also grabbed another file from the emuboot
source code; diskboot.c which sets the active partition. This stuff
should be rolled into the source at some point ...
Anyway, I added some code to set the active partition into testbed.c.
See below for how the boot floopy invokes the testbed specific stuff.
This new loader needs to onto the CD *inside* the boot floopy image in
/floppies/boot.flp.
*** Create a bootable CD that does not run from the mini fs.
So, the problem with bootable CDs is that they really boot from a
floopy image on the CD. The mfsroot that the fixit disk runs from is
inside the floopy image. Obviously, thats too small to be very useful,
and you cannot run from the CD since its read-only, and we really want
to go multiuser so that we can use all the good tools including ssh!
We are going to make a minor change to floopy image so that it does not
load the mini mfsroot, and so that it boots directly from the CDROM.
This is okay; it will initially boot up with / read-only.
First off, you need to mount the floopy image from the CD.
cd /FOO/floppies
vnconfig vn0 boot.flp
mount /dev/vn0 /mnt/floopy
Edit /mnt/floopy/boot/loader.rc:
load /kernel
set vfs.root.mountfrom="cd9660:acd0a"
testbed_boot
Compress and copy kernel.GENERIC from the CDROM to the boot floopy image.
cat /FOO/kernel.GENERIC | gzip -9 > /mnt/floopy/kernel.gz
Kill the mfs since it wastes space and it won't be used.
rm -f /mnt/floopy/mfsroot.gz
Copy in the new version of the loader described above:
cp -f loader /mnt/floopy/boot/loader
This is necessary cause something about the original kernel on the boot
floopy does not work correctly when putting /dev on an mfs. Also, it does
not drop into that userconfig interface on boot.
Then unmount/close the boot floopy.
umount /mnt/floopy
vnconfig -u vn0
# Device Mountpoint FStype Options Dump Pass#
/dev/acd0a / cd9660 ro 0 0
proc /proc procfs rw 0 0
# Device Mountpoint FStype Options Dump Pass#
/dev/ad1s1a / ufs ro 0 0
proc /proc procfs rw 0 0
# -- sysinstall generated deltas -- #
userconfig_script_load="YES"
\ Loader.rc
\ $FreeBSD: src/sys/boot/forth/loader.rc,v 1.2 1999/11/24 17:59:37 dcs Exp $
\
\ Includes additional commands
include /boot/loader.4th
\ Reads and processes loader.rc
start
\ Do the testbed boot thing. Note that this file is not actually installed
\ on the CD. Its just for my disk based version. On the CD, loader.rc is
\ modified in the boot floopy image.
testbed_boot
#!/usr/bin/perl -w
#
# EMULAB-COPYRIGHT
# Copyright (c) 2004 University of Utah and the Flux Group.
# All rights reserved.
#
use English;
use Getopt::Std;
use File::Basename;
use Fcntl;
#
# This script is run directly from boot. It should NOT be run after
# that since some stuff is not setup to properly restart yet. For
# general reconfig or update, use rc.config instead.
#
sub usage()
{
print "Usage: " . scriptname() . "boot|shutdown|reconfig|reset\n";
exit(1);
}
my $optlist = "";
my $action = "boot";
my $debug = 1;
# Turn off line buffering on output
$| = 1;
# Drag in path stuff so we can find emulab stuff.
BEGIN { require "/etc/emulab/paths.pm"; import emulabpaths; }
# Only root.
if ($EUID != 0) {
die("*** $0:\n".
" Must be root to run this script!\n");
}
# Script specific goo.
my $RCDIR = "$BINDIR/rc";
#
# Disk related parameters
#
# where to find kernel config output
my $dmesgcmd = "/sbin/dmesg";
my $dmesgfile = "/var/run/dmesg.boot";
# preferred ordering of disks to use
my @preferred = ("ar", "aacd", "amrd", "mlxd", "twed", "ad", "da");
# ordered list of disks found and hash of sizes
my @disklist;
my %disksize;
# min disk size we can use (in MB)
my $MINDISKSIZE = 8000;
my $defrawdisk = "/dev/ad0";
my $rawbootdisk;
#
# Load the OS independent support library. It will load the OS dependent
# library and initialize itself.
#
use libsetup;
use libtmcc;
use librc;
# Protos.
sub doboot();
sub doshutdown();
sub doreconfig();
sub docleanup();
# Allow default above.
if (@ARGV) {
$action = $ARGV[0];
}
# Execute the action.
SWITCH: for ($action) {
/^boot$/i && do {
doboot();
last SWITCH;
};
/^shutdown$/i && do {
doshutdown();
last SWITCH;
};
/^reconfig$/i && do {
doreconfig();
last SWITCH;
};
/^reset$/i && do {
docleanup();
last SWITCH;
};
/^rawdisk$/i && do {
WhichRawDisk();
print "$rawbootdisk\n";
last SWITCH;
};
fatal("Invalid action: $action\n");
}
exit(0);
#
# Boot Action.
#
sub doboot()
{
my $bootdev;
#
# The CD does some different stuff.
#
print("Doing Testbed Setup on a CD\n");
# Get the boss info for below.
my ($bossname, $bossip) = tmccbossinfo();
if (!defined($bossname)) {
fatal("Could not determine the name of the boss server!");
}
# Will not return until it gets something it likes.
$bootdev = WhichRawDisk();
print("Using $bootdev for config sector ...\n");
#
# If this is first install on this disk, or if the disk has just been
# loaded, initialize the magic sector so that it boots from the CD.
# We might reset that below.
#
system("tbbootconfig -v $bootdev");
if ($?) {
print("No valid boot config on $bootdev; initializing ...\n");
system("tbbootconfig -d -f -c 1 -k 0 -m 1 $bootdev");
if ($?) {
print("Error running tbbootconfig; falling back to MFS boot\n");
goto mfs;
}
}
#
# Use the bootinfo client to find out what we should do. Note that
# like the PXE version, this client will block when told to WAIT
# by the bootinfo server, returning only when bootinfo says that the node
# should boot (has been allocated or needs to be reloaded).
#
my $bootwhat = `bootinfoclient -s $bossname`;
if ($?) {
print("Error running bootinfoclient; falling back to MFS boot\n");
goto mfs;
}
chomp($bootwhat);
if ($debug) {
print("Bootinfo returned '$bootwhat'\n");
sleep(30);
}
if ($bootwhat eq "reboot") {
print("Bootinfo says to reboot ... so thats what we gonna do!\n");
system("sync");
system("reboot");
sleep(10000);
}
elsif ($bootwhat =~ /^partition:(\d)$/) {
print("Bootinfo says to boot slice $1!\n");
system("tbbootconfig -d -c 0 -k $1 -m 1 $bootdev");
if ($?) {
print("Error running tbbootconfig; falling back to MFS boot\n");
goto mfs;
}
system("sync");
system("reboot");
sleep(10000);
}
elsif ($bootwhat =~ /^mfs:[-\w\.]*:(.*)$/) {
print("Bootinfo says to boot MFS $1!\n");
my $mfs = basename($1);
#
# We know about a couple of different MFSs, but thats it!
#
if ($mfs eq "newnode") {
system("$BINDIR/newclient");
}
elsif ($mfs eq "frisbee") {
#
# Run the frisbee script. If all goes well it will reboot.
# Any problems we land back here, so go into the MFS so we
# can possibly diagnose the problem.
#
system("$RCDIR/rc.frisbee");
goto mfs;
}
elsif ($mfs eq "freebsd") {
goto mfs;
}
else {
# Default to FreeBSD MFS.
goto mfs;
}
}
#
# At this point, chain over to the MFS boot, since the CD mirrors
# that when doing a standard BSD boot.
#
mfs:
if (-x "$RCDIR/rc.mfs") {
print("Switching over to MFS boot setup\n");
system("$RCDIR/rc.mfs");
# Fall through on failure.
}
}
#
# Shutdown Action.
#
sub doshutdown()
{
}
#
# Node Reconfig Action (without rebooting).
#
sub doreconfig()
{
}
#
# Node cleanup action (node is reset to completely clean state).
#
sub docleanup()
{
}
#
# Which raw disk. Prompt if we cannot come up with a good guess.
# Note: raw and block devices are one in the same now.
#
sub WhichRawDisk()
{
#
# Find the list of configured disks
#
my @list = DiskList();
#
# Search the drives looking for one with a valid header.
#
foreach my $disk (@list) {
my $guess = "/dev/${disk}";
system("tbbootconfig -v $guess");
if (! $?) {
$rawbootdisk = $guess;
goto gotone;
}
}
#
# None with configuration info, just use the first existing disk
# which is large enough and is actually accessible.
#
foreach my $disk (@list) {
my $guess = "/dev/${disk}";
if (DiskSize($disk) >= $MINDISKSIZE && DiskReadable($disk)) {
#
# Allow for overiding the guess, with short timeout.
#
$rawbootdisk = Prompt("Which Disk Device is the boot device?",
"$guess", 10);
goto gotone;
}
}
gotone:
#
# If still not defined, then loop forever.
#
while (!defined($rawbootdisk) || ! -e $rawbootdisk) {
$rawbootdisk = Prompt("Which Disk Device is the boot device?",
$defrawdisk);
}
return $rawbootdisk;
}
#
# Create a list of all disks and their sizes.
#
sub DiskList()
{
if (-x $dmesgcmd) {
GetDisks($dmesgcmd);
}
# if we didn't grab anything there, try the /var/run file
if (@disklist == 0 && -r $dmesgfile) {
GetDisks("cat $dmesgfile");
}
return @disklist;
}
sub DiskSize($)
{
my ($name) = @_;
if (defined($disksize{$name})) {
return $disksize{$name};
}
return 0;
}
sub DiskReadable($)
{
my ($disk) = @_;
my $dev = "/dev/$disk";
if (!system("dd if=$dev of=/dev/null bs=512 count=32 >/dev/null 2>&1")) {
return(1);
}
return(0);
}
sub GetDisks($)
{
my ($cmd) = @_;
my @units = (0, 1, 2, 3);
my @cmdout = `$cmd`;
#
# Arbitrary: we prefer disk type over unit number;
# e.g. ad1 is better than da0.
#
foreach my $disk (@preferred) {
foreach my $unit (@units) {
my $dmesgpat = "^($disk$unit):.* (\\d+)MB.*\$";
foreach my $line (@cmdout) {
if ($line =~ /$dmesgpat/) {
my $name = $1;
my $size = $2;
if (!defined($disksize{$name})) {
push(@disklist, $name);
}
$disksize{$name} = $size;
}
}
}
}
}
#
# Spit out a prompt and a default answer. If optional timeout supplied,
# then wait that long before returning the default. Otherwise, wait forever.
#
sub Prompt($$;$)
{
my ($prompt, $default, $timeout) = @_;
if (!defined($timeout)) {
$timeout = 10000000;
}
print "$prompt";
if (defined($default)) {
print " [$default]";
}
print ": ";
eval {
local $SIG{ALRM} = sub { die "alarm\n" }; # NB: \n required
alarm $timeout;
$_ = <STDIN>;
alarm 0;
};
if ($@) {
if ($@ ne "alarm\n") {
die("Unexpected interrupt in prompt\n");
}
#
# Timed out.
#
print "\n";
return $default;
}
return undef
if (!defined($_));
chomp();
if ($_ eq "") {
return $default;
}
return $_;
}
# Copyright (c) 1999, 2004 Matt Dillon
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
# SUCH DAMAGE.
#
#
# rc.cdroot
#
# chkerr:
#
# Routine to check for error
#
# checks error code and drops into shell on failure.
# if shell exits, terminates script as well as /etc/rc.
#
chkerr() {
case $1 in
0)
;;
*)
echo "$2 failed: dropping into /bin/sh"
/bin/sh
# RESUME
;;
esac
}
#
# Create a real /tmp
#
/sbin/mount_mfs -s 8192 -b 8192 -f 1024 -i 1024 -c 16 -T minimum dummy /tmp
chkerr $? "MFS mount on /tmp"
#
# Duplicate /etc using /tmp as a temporary location.
#
/usr/site/bin/hier cp /etc /tmp/etc
chkerr $? "hier cp /etc to /tmp/etc"
/sbin/mount_mfs -s 8192 -b 8192 -f 1024 -c 16 -T minimum dummy /etc
chkerr $? "MFS mount on /etc"
/usr/site/bin/hier cp /tmp/etc /etc
chkerr $? "hier cp /tmp/etc to /etc"
/bin/rm -rf /tmp/etc
chkerr $? "rm -rf /tmp/etc"
#
# Duplicate /var using /tmp as a temporary location.
#
/usr/site/bin/hier cp /var /tmp/var
chkerr $? "hier cp /var to /tmp/var"
/sbin/mount_mfs -s 8192 -b 8192 -f 1024 -c 16 -T minimum dummy /var
chkerr $? "MFS mount on /var"
/usr/site/bin/hier cp /tmp/var /var
chkerr $? "hier cp /tmp/var to /var"
/bin/rm -rf /tmp/var
chkerr $? "rm -rf /tmp/var"
#
# Duplicate /dev using /tmp as a temporary location.
#
/usr/site/bin/hier cp /dev /tmp/dev
chkerr $? "hier cp /dev to /tmp/dev"
/sbin/mount_mfs -s 8192 -b 8192 -f 1024 -i 1024 -c 16 -T minimum dummy /dev
chkerr $? "MFS mount on /dev"
/usr/site/bin/hier cp /tmp/dev /dev
chkerr $? "hier cp /tmp/dev to /dev"
/bin/rm -rf /tmp/dev
chkerr $? "rm -rf /tmp/dev"
chmod 666 /dev/pty[pqrsPQRS]*
chown root:wheel /dev/pty[pqrsPQRS]*
# Need writable /proj, /users, etc.
/sbin/mount_mfs -s 8192 -b 8192 -f 1024 -c 16 -T minimum dummy /proj
chkerr $? "MFS mount on /proj"
/sbin/mount_mfs -s 8192 -b 8192 -f 1024 -c 16 -T minimum dummy /users
chkerr $? "MFS mount on /users"
exit 0
sendmail_enable="NONE"
sshd_enable="YES"
nfs_client_enable="YES"
usbd_enable="NO"
xntpd_enable="NO"
ntpdate_enable="YES"
ntpdate_flags="-b boss.emulab.net"
syslogd_enable="YES"
inetd_enable="NO"
cron_enable="NO"
ldconfig_paths="/usr/local/lib"
ldconfig_paths_aout=""
blanktime="NO"
update_motd="NO"
# For CD
root_rw_mount="NO"
diskless_mount="/etc/rc.cdroot"
......@@ -117,6 +117,7 @@ if [ x"$ADDRESS" != x ]; then
echo "Image load complete, rebooting ...";
$BINDIR/tmcc state RELOADDONE;
/sbin/reboot;
sleep 100;
;;
*)
echo "Frisbee run failed, status $?"
......
......@@ -94,9 +94,7 @@ sub doboot()
if (-x "$RCDIR/rc.ipod") {
print("Setting up Ping of Death\n");
system("$RCDIR/rc.ipod");
if ($?) {
fatal("Error running $RCDIR/rc.ipod");
}
# This is allowed to fail; ipod might not be supported.
}
# Now we get into the real work.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment