Commit a810febe authored by Leigh B Stoller's avatar Leigh B Stoller

The beginnings of automating the rest of testbed update via perl

fragment scripts, much like we do with the DB updates. The intent
is to simplify testbed update for remote sites.
parent ce23fa1b
# #
# EMULAB-COPYRIGHT # EMULAB-COPYRIGHT
# Copyright (c) 2000-2009 University of Utah and the Flux Group. # Copyright (c) 2000-2010 University of Utah and the Flux Group.
# All rights reserved. # All rights reserved.
# #
...@@ -12,7 +12,8 @@ SUBDIR = install ...@@ -12,7 +12,8 @@ SUBDIR = install
include $(OBJDIR)/Makeconf include $(OBJDIR)/Makeconf
ifeq ($(STANDALONE_CLEARINGHOUSE),0) ifeq ($(STANDALONE_CLEARINGHOUSE),0)
TARGETS = boss-install ops-install fs-install dump-descriptors load-descriptors TARGETS = libinstall.pm boss-install ops-install fs-install dump-descriptors \
load-descriptors update-install update-testbed
else else
TARGETS = clrhouse-install TARGETS = clrhouse-install
endif endif
...@@ -25,7 +26,10 @@ all: $(TARGETS) ...@@ -25,7 +26,10 @@ all: $(TARGETS)
include $(TESTBED_SRCDIR)/GNUmakerules include $(TESTBED_SRCDIR)/GNUmakerules
install: $(INSTALL_LIBDIR)/libinstall.pm install: $(INSTALL_LIBDIR)/libinstall.pm \
$(INSTALL_SBINDIR)/update-install \
$(INSTALL_SBINDIR)/update-testbed
clean: clean:
rm -f boss-install ops-install fs-install rm -f boss-install ops-install fs-install update-install
rm -f update-testbed
...@@ -66,7 +66,6 @@ my $PWD = "/bin/pwd"; ...@@ -66,7 +66,6 @@ my $PWD = "/bin/pwd";
my $PW = "/usr/sbin/pw"; my $PW = "/usr/sbin/pw";
my $PATCH = "/usr/bin/patch"; my $PATCH = "/usr/bin/patch";
my $SSH_KEYGEN = "/usr/bin/ssh-keygen"; my $SSH_KEYGEN = "/usr/bin/ssh-keygen";
my $PKG_INFO = "/usr/sbin/pkg_info";
my $PKG_ADD = "/usr/sbin/pkg_add"; my $PKG_ADD = "/usr/sbin/pkg_add";
my $PKG_DEL = "/usr/sbin/pkg_delete"; my $PKG_DEL = "/usr/sbin/pkg_delete";
my $TOUCH = "/usr/bin/touch"; my $TOUCH = "/usr/bin/touch";
...@@ -87,8 +86,6 @@ my $MYSQLDUMP = "/usr/local/bin/mysqldump"; ...@@ -87,8 +86,6 @@ my $MYSQLDUMP = "/usr/local/bin/mysqldump";
my $MYSQLINSTALL = "/usr/local/bin/mysql_install_db"; my $MYSQLINSTALL = "/usr/local/bin/mysql_install_db";
my $MYSQLDBDIR = "/var/db/mysql"; my $MYSQLDBDIR = "/var/db/mysql";
my $GMAKE = "/usr/local/bin/gmake";
# #
# Some files we edit/create # Some files we edit/create
# #
...@@ -170,17 +167,13 @@ my $NAMED_PIDFILE = "/var/run/named.pid"; ...@@ -170,17 +167,13 @@ my $NAMED_PIDFILE = "/var/run/named.pid";
# #
# Some directories we care about # Some directories we care about
# #
my $LOGDIR = "$PREFIX/log";
my $MYSQL_LOGDIR = "$LOGDIR/mysql"; my $MYSQL_LOGDIR = "$LOGDIR/mysql";
my $RCDIR = "/usr/local/etc/rc.d";
my $USERSVAR_DIR = "$PREFIX/usersvar"; my $USERSVAR_DIR = "$PREFIX/usersvar";
my $OPSDIR_DIR = "$PREFIX/opsdir"; my $OPSDIR_DIR = "$PREFIX/opsdir";
my $PORTSDIR = "/usr/ports";
my $PORTSMISCDIR = "$PORTSDIR/misc"; my $PORTSMISCDIR = "$PORTSDIR/misc";
my $MIBPATH = "/usr/local/share/snmp/mibs"; my $MIBPATH = "/usr/local/share/snmp/mibs";
my $TFTP_DIR = "$PREFIX/tftpboot"; my $TFTP_DIR = "$PREFIX/tftpboot";
my $TFTP_PROJ_DIR = "$TFTP_DIR/proj"; my $TFTP_PROJ_DIR = "$TFTP_DIR/proj";
my $VARRUN = "/var/run";
my $ETCSSH = "/etc/ssh"; my $ETCSSH = "/etc/ssh";
# #
......
...@@ -142,12 +142,10 @@ my $PW = "/usr/sbin/pw"; ...@@ -142,12 +142,10 @@ my $PW = "/usr/sbin/pw";
my $PATCH = "/usr/bin/patch"; my $PATCH = "/usr/bin/patch";
my $NEWALIASES = "/usr/bin/newaliases"; my $NEWALIASES = "/usr/bin/newaliases";
my $SH = "/bin/sh"; my $SH = "/bin/sh";
my $PKG_INFO = "/usr/sbin/pkg_info";
my $PKG_ADD = "/usr/sbin/pkg_add"; my $PKG_ADD = "/usr/sbin/pkg_add";
my $PWD = "/bin/pwd"; my $PWD = "/bin/pwd";
my $CP = "/bin/cp"; my $CP = "/bin/cp";
my $MV = "/bin/mv"; my $MV = "/bin/mv";
my $GMAKE = "/usr/local/bin/gmake";
my $ENV = "/usr/bin/env"; my $ENV = "/usr/bin/env";
my $QUOTAON = "/usr/sbin/quotaon"; my $QUOTAON = "/usr/sbin/quotaon";
...@@ -175,11 +173,8 @@ my $SMBCONF_HEAD = "$SMBCONF_FILE.head"; ...@@ -175,11 +173,8 @@ my $SMBCONF_HEAD = "$SMBCONF_FILE.head";
# #
my $LIST_DIR = "/etc/mail/lists"; my $LIST_DIR = "/etc/mail/lists";
my $TIPLOG_DIR = "/var/log/tiplogs"; my $TIPLOG_DIR = "/var/log/tiplogs";
my $PORTSDIR = "/usr/ports";
my $PORTSMISCDIR = "$PORTSDIR/misc"; my $PORTSMISCDIR = "$PORTSDIR/misc";
my $SRCDIR = '@srcdir@'; my $SRCDIR = '@srcdir@';
my $RCDIR = "/usr/local/etc/rc.d";
my $VARRUN = "/var/run";
# #
# And some lists that we use # And some lists that we use
......
...@@ -4,12 +4,31 @@ ...@@ -4,12 +4,31 @@
# Copyright (c) 2003-2010 University of Utah and the Flux Group. # Copyright (c) 2003-2010 University of Utah and the Flux Group.
# All rights reserved. # All rights reserved.
# #
# #
# A simple library for use in the installation scripts, to make them seem a # A simple library for use in the installation scripts, to make them
# little more legitimate, instead of the quick hacks they are. # seem a little more legitimate, instead of the quick hacks they are.
# #
use POSIX qw(strftime); use POSIX qw(strftime);
use Exporter;
use vars qw(@EXPORT $TOP_OBJDIR
$TBROOT $LOGDIR $MAINSITE $PGENISUPPORT $GMAKE $PKG_INFO
$PORTSDIR $VARRUN $RCDIR);
@EXPORT = qw($TOP_OBJDIR
$TBROOT $LOGDIR $MAINSITE $PGENISUPPORT $GMAKE $PKG_INFO
$PORTSDIR $VARRUN $RCDIR);
# Configure variables
$TBROOT = "@prefix@";
$LOGDIR = "$TBROOT/log";
$MAINSITE = @TBMAINSITE@;
$PGENISUPPORT = @PROTOGENI_SUPPORT@;
$GMAKE = "/usr/local/bin/gmake";
$PKG_INFO = "/usr/sbin/pkg_info";
$PORTSDIR = "/usr/ports";
$VARRUN = "/var/run";
$RCDIR = "/usr/local/etc/rc.d";
# #
# Make sure that output gets printed right away # Make sure that output gets printed right away
...@@ -32,11 +51,24 @@ my $updatemode = 0; ...@@ -32,11 +51,24 @@ my $updatemode = 0;
# Used by update-install to bump the version number. # Used by update-install to bump the version number.
sub SET_TESTBED_VERSION($) { $updatemode = $MAGIC_TESTBED_VERSION = $_[0]; } sub SET_TESTBED_VERSION($) { $updatemode = $MAGIC_TESTBED_VERSION = $_[0]; }
# Set by update-install.
my $impotent = 0;
my $logfp;
sub SET_IMPOTENT_MODE($) { $impotent = 1; $logfp = $_[0]; };
# #
# Some programs we may call # Some programs we may call
# #
my $FETCH = "/usr/bin/fetch"; my $FETCH = "/usr/bin/fetch";
#
# Figure out which directory we live in, so that some stages can do thing
# relative to it.
#
$TOP_OBJDIR = `/usr/bin/dirname $0`;
chomp $TOP_OBJDIR;
$TOP_OBJDIR = "$TOP_OBJDIR/..";
# #
# Let's pretend perl's exception mechanism has a sane name for the function # Let's pretend perl's exception mechanism has a sane name for the function
# that raises an exception # that raises an exception
...@@ -44,7 +76,7 @@ my $FETCH = "/usr/bin/fetch"; ...@@ -44,7 +76,7 @@ my $FETCH = "/usr/bin/fetch";
sub throw(@) { sub throw(@) {
die @_,"\n"; die @_,"\n";
} }
# #
# Start a new installation phase # Start a new installation phase
# #
...@@ -80,12 +112,15 @@ sub Phase($$$) { ...@@ -80,12 +112,15 @@ sub Phase($$$) {
print "\n"; print "\n";
} }
printf "%-50s", $descrstring; printf "%-50s", $descrstring;
printf $logfp "%-50s", $descrstring
if ($impotent);
# #
# Clear these, as we don't want to see the outputs of previous phases # Clear these, as we don't want to see the outputs of previous phases
# #
@libinstall::lastExecOutput = (); @libinstall::lastExecOutput = ();
$libinstall::lastCommand = undef; $libinstall::lastCommand = undef;
@libinstall::loglines = ();
# #
# Cool! TWO levels of Perl Hell just for me! # Cool! TWO levels of Perl Hell just for me!
...@@ -110,6 +145,8 @@ sub Phase($$$) { ...@@ -110,6 +145,8 @@ sub Phase($$$) {
SWITCH: for ($@) { SWITCH: for ($@) {
(/^skip$/) && do { (/^skip$/) && do {
print "[ Skipped ($libinstall::reason) ]\n"; print "[ Skipped ($libinstall::reason) ]\n";
print $logfp "[ Skipped ($libinstall::reason) ]\n"
if ($impotent && defined($logfp));
$$parentSkipped++; $$parentSkipped++;
$libinstall::phaseResults{$name} = $_; $libinstall::phaseResults{$name} = $_;
last SWITCH; last SWITCH;
...@@ -138,10 +175,24 @@ sub Phase($$$) { ...@@ -138,10 +175,24 @@ sub Phase($$$) {
if ($hasSubPhase && $skipped && ($nonSkipped == 0)) { if ($hasSubPhase && $skipped && ($nonSkipped == 0)) {
print "[ Skipped ] ($stamp)\n"; print "[ Skipped ] ($stamp)\n";
print $logfp "[ Skipped ]\n"
if ($impotent && defined($logfp));
$libinstall::phaseResults{$name} = "skip"; $libinstall::phaseResults{$name} = "skip";
$$parentSkipped++; $$parentSkipped++;
} else { } else {
print "[ Succeeded ] ($stamp)\n"; if ($impotent) {
if (defined($logfp)) {
print $logfp "[ $libinstall::reason ]\n";
print $logfp
"> " . join("\n> ", @libinstall::loglines) . "\n"
if (@libinstall::loglines);
}
print "[ $libinstall::reason ]\n";
}
else {
print "[ Succeeded ] ($stamp)\n";
}
$$parentNonSkipped++; $$parentNonSkipped++;
$libinstall::phaseResults{$name} = "succeed"; $libinstall::phaseResults{$name} = "succeed";
} }
...@@ -221,6 +272,38 @@ sub PhaseWasSkipped($) { ...@@ -221,6 +272,38 @@ sub PhaseWasSkipped($) {
($libinstall::phaseResults{$phase} =~ /^skip$/)); ($libinstall::phaseResults{$phase} =~ /^skip$/));
} }
#
# For impotent mode.
#
sub PhaseWouldHave($) {
($libinstall::reason) = (@_);
throw "succeed";
}
#
# Also for impotent mode; detailed logging.
#
sub PhaseLog(@) {
(@libinstall::loglines) = (@_);
}
#
# Also for impotent mode; log differences to a file.
#
sub DiffFiles($$) {
my ($src,$dst) = @_;
return
if (!$impotent);
if (! -e $dst) {
my $stuff = `cat $src`;
PhaseLog(split('\n', $stuff));
}
my $diff = `diff $src $dst`;
PhaseLog(split('\n', $diff));
}
# #
# Check to see if the phase is already done, as evidenced by the existance of # Check to see if the phase is already done, as evidenced by the existance of
# a file # a file
...@@ -272,11 +355,41 @@ sub DoneIfIdentical($$) { ...@@ -272,11 +355,41 @@ sub DoneIfIdentical($$) {
if (!-e $filename1 || !-e $filename2) { if (!-e $filename1 || !-e $filename2) {
return; return;
} }
if (!ExecQuiet("cmp -s $filename1 $filename2")) { system("cmp -s $filename1 $filename2");
if (! $?) {
PhaseSkip("Files $filename1 and $filename2 are identical"); PhaseSkip("Files $filename1 and $filename2 are identical");
} }
} }
#
# Done if package installed.
#
sub DoneIfPackageInstalled($) {
my ($pname) = @_;
my $foo = `$PKG_INFO -x $pname`;
if (! $?) {
PhaseSkip("already installed");
}
}
#
# Backup a file or fail.
#
sub BackUpFileFatal($)
{
my ($filename) = @_;
my $suffix = time();
my $backup = $filename . "-" . $suffix;
PhaseFail("$filename does not exist")
if (! -e $filename);
PhaseFail("$filename already exists")
if (-e $backup);
ExecQuietFatal("/bin/cp -p $filename $backup")
if (!$impotent);
}
# #
# Check to see if filesystem already mounted # Check to see if filesystem already mounted
# #
...@@ -317,6 +430,12 @@ sub DoneIfMounted($) ...@@ -317,6 +430,12 @@ sub DoneIfMounted($)
# #
sub AppendToFile($@) { sub AppendToFile($@) {
my ($filename, @lines) = @_; my ($filename, @lines) = @_;
if ($impotent) {
PhaseLog(@lines);
PhaseWouldHave("append to $filename");
return undef;
}
if (!-e $filename) { if (!-e $filename) {
return "File $filename does not exist"; return "File $filename does not exist";
} }
...@@ -349,6 +468,12 @@ sub AppendToFileFatal($@) { ...@@ -349,6 +468,12 @@ sub AppendToFileFatal($@) {
# #
sub CreateFile($;@) { sub CreateFile($;@) {
my ($filename,@lines) = @_; my ($filename,@lines) = @_;
if ($impotent) {
PhaseLog(@lines);
PhaseWouldHave("create $filename");
return undef;
}
if (-e $filename) { if (-e $filename) {
return "File $filename already exists"; return "File $filename already exists";
} }
...@@ -385,6 +510,12 @@ sub ExecQuiet(@) { ...@@ -385,6 +510,12 @@ sub ExecQuiet(@) {
# Use a pipe read, so that we save away the output # Use a pipe read, so that we save away the output
# #
my $commandstr = join(" ",@_); my $commandstr = join(" ",@_);
if ($impotent) {
PhaseWouldHave("exec($commandstr)");
return 0;
}
my @output = (); my @output = ();
open(PIPE,"$commandstr 2>&1 |") or return -1; open(PIPE,"$commandstr 2>&1 |") or return -1;
while (<PIPE>) { while (<PIPE>) {
...@@ -415,14 +546,14 @@ sub ExecQuietFatal(@) { ...@@ -415,14 +546,14 @@ sub ExecQuietFatal(@) {
} }
# #
# HUP a daemon, if it's PID file exists. If we can't kill it, we assume that # Signal a daemon, if it's PID file exists. If we can't kill it, we assume that
# it's because it wasn't running, and skip the phase. Fails if it has trouble # it's because it wasn't running, and skip the phase. Fails if it has trouble
# reading the pid file. # reading the pid file.
# Takes the name of the daemon as an argument, and assumes # Takes the name of the daemon as an argument, and assumes
# that the pid file is /var/run/$name.pid # that the pid file is /var/run/$name.pid
# #
sub HUPDaemon($) { sub SignalDaemon($$) {
my ($name) = @_; my ($name,$sig) = @_;
my $pidfile = "/var/run/$name.pid"; my $pidfile = "/var/run/$name.pid";
PhaseSkip("$name is not running") unless (-e $pidfile); PhaseSkip("$name is not running") unless (-e $pidfile);
open(PID,$pidfile) or PhaseFail("Unable to open pidfile $pidfile"); open(PID,$pidfile) or PhaseFail("Unable to open pidfile $pidfile");
...@@ -431,17 +562,34 @@ sub HUPDaemon($) { ...@@ -431,17 +562,34 @@ sub HUPDaemon($) {
close PID; close PID;
PhaseFail("Bad pid ($pid) in $pidfile\n") unless ($pid =~ /^\d+$/); PhaseFail("Bad pid ($pid) in $pidfile\n") unless ($pid =~ /^\d+$/);
if (!kill 1, $pid) {
PhaseSkip("$name does not seem to be running"); if ($impotent) {
PhaseWouldHave("signal($sig) $name");
return;
}
if (!kill($sig, $pid)) {
PhaseSkip("$name is not running");
} }
} }
sub HUPDaemon($) {
my ($name) = @_;
SignalDaemon($name, 'HUP');
}
# #
# Fetch a file from the network, using any protocol supported by fetch(1). # Fetch a file from the network, using any protocol supported by fetch(1).
# Arguments are URL and a local filename. Retunrns 1 if succesful, 0 if not. # Arguments are URL and a local filename. Retunrns 1 if succesful, 0 if not.
# #
sub FetchFile($$) { sub FetchFile($$) {
my ($URL, $localname) = @_; my ($URL, $localname) = @_;
if ($impotent) {
PhaseLog("$URL --> $filename");
PhaseWouldHave("fetch $URL");
return 1;
}
if (ExecQuiet("$FETCH -o $localname $URL")) { if (ExecQuiet("$FETCH -o $localname $URL")) {
return 0; return 0;
} else { } else {
......
...@@ -42,7 +42,6 @@ my $CVSSUPPORT = @CVSSUPPORT@; ...@@ -42,7 +42,6 @@ my $CVSSUPPORT = @CVSSUPPORT@;
my $BUGDBSUPPORT= @BUGDBSUPPORT@; my $BUGDBSUPPORT= @BUGDBSUPPORT@;
my $WIKISUPPORT = @WIKISUPPORT@; my $WIKISUPPORT = @WIKISUPPORT@;
my $QUOTA_FSLIST= '@FS_WITH_QUOTAS@'; my $QUOTA_FSLIST= '@FS_WITH_QUOTAS@';
my $LOGDIR = "$PREFIX/log";
my $ETCDIR = "$PREFIX/etc"; my $ETCDIR = "$PREFIX/etc";
my $LIBDIR = "$PREFIX/lib"; my $LIBDIR = "$PREFIX/lib";
my $SCRATCHDIR = '@FSDIR_SCRATCH@'; my $SCRATCHDIR = '@FSDIR_SCRATCH@';
...@@ -183,13 +182,11 @@ my $PW = "/usr/sbin/pw"; ...@@ -183,13 +182,11 @@ my $PW = "/usr/sbin/pw";
my $PATCH = "/usr/bin/patch"; my $PATCH = "/usr/bin/patch";
my $NEWALIASES = "/usr/bin/newaliases"; my $NEWALIASES = "/usr/bin/newaliases";
my $SH = "/bin/sh"; my $SH = "/bin/sh";
my $PKG_INFO = "/usr/sbin/pkg_info";
my $PKG_ADD = "/usr/sbin/pkg_add"; my $PKG_ADD = "/usr/sbin/pkg_add";
my $PKG_DEL = "/usr/sbin/pkg_delete"; my $PKG_DEL = "/usr/sbin/pkg_delete";
my $PWD = "/bin/pwd"; my $PWD = "/bin/pwd";
my $CP = "/bin/cp"; my $CP = "/bin/cp";
my $MV = "/bin/mv"; my $MV = "/bin/mv";
my $GMAKE = "/usr/local/bin/gmake";
my $ENV = "/usr/bin/env"; my $ENV = "/usr/bin/env";
my $MOUNT = "/sbin/mount"; my $MOUNT = "/sbin/mount";
my $TAR = "/usr/bin/tar"; my $TAR = "/usr/bin/tar";
...@@ -234,12 +231,9 @@ my $MYSQL_LOGDIR = "$LOGDIR/mysql"; ...@@ -234,12 +231,9 @@ my $MYSQL_LOGDIR = "$LOGDIR/mysql";
# #
my $LIST_DIR = "/etc/mail/lists"; my $LIST_DIR = "/etc/mail/lists";
my $TIPLOG_DIR = "/var/log/tiplogs"; my $TIPLOG_DIR = "/var/log/tiplogs";
my $PORTSDIR = "/usr/ports";
my $PORTSMISCDIR = "$PORTSDIR/misc"; my $PORTSMISCDIR = "$PORTSDIR/misc";
my $SRCDIR = '@srcdir@'; my $SRCDIR = '@srcdir@';
my $TOP_SRCDIR = "@top_srcdir@"; my $TOP_SRCDIR = "@top_srcdir@";
my $RCDIR = "/usr/local/etc/rc.d";
my $VARRUN = "/var/run";
# #
# And some lists that we use # And some lists that we use
...@@ -840,16 +834,16 @@ Phase "syslog", "Setting up syslog", sub { ...@@ -840,16 +834,16 @@ Phase "syslog", "Setting up syslog", sub {
# Find the cron line, after which we place our auth.info line # Find the cron line, after which we place our auth.info line
# #
if ($line =~ /^cron/) { if ($line =~ /^cron/) {
print SC "# " . MAGIC_TESTBED_START . "\n"; print SC "# " . MAGIC_TESTBED_START() . "\n";
print SC "auth.info\t\t\t\t\t/var/log/logins\n"; print SC "auth.info\t\t\t\t\t/var/log/logins\n";
print SC "# " . MAGIC_TESTBED_END . "\n"; print SC "# " . MAGIC_TESTBED_END() . "\n";
} }
} }
# #
# Put a few more lines at the end # Put a few more lines at the end
# #
print SC "# " . MAGIC_TESTBED_START . "\n"; print SC "# " . MAGIC_TESTBED_START() . "\n";
print SC "!capture\n"; print SC "!capture\n";
print SC "*.*\t\t\t\t\t\t/var/log/tiplogs/capture.log\n"; print SC "*.*\t\t\t\t\t\t/var/log/tiplogs/capture.log\n";
print SC "!mountd\n"; print SC "!mountd\n";
...@@ -858,7 +852,7 @@ Phase "syslog", "Setting up syslog", sub { ...@@ -858,7 +852,7 @@ Phase "syslog", "Setting up syslog", sub {
print SC "*.*\t\t\t\t\t\t${LOGDIR}/pubsubd.log\n"; print SC "*.*\t\t\t\t\t\t${LOGDIR}/pubsubd.log\n";
print SC "!elvin_gateway\n"; print SC "!elvin_gateway\n";
print SC "*.*\t\t\t\t\t\t${LOGDIR}/elvin_gateway.log\n"; print SC "*.*\t\t\t\t\t\t${LOGDIR}/elvin_gateway.log\n";
print SC "# " . MAGIC_TESTBED_END . "\n"; print SC "# " . MAGIC_TESTBED_END() . "\n";
close SC; close SC;
}; };
......
#!/usr/bin/perl -w
#
# EMULAB-COPYRIGHT
# Copyright (c) 2010 University of Utah and the Flux Group.
# All rights reserved.
#
use strict;
use English;
use Getopt::Std;
#
# Update DB.
#
sub usage()
{
print STDERR "Usage: update-install [-v] [-s] [-f] [<version>]\n";
exit(-1);
}
my $optlist = "dsfvp:qa:ci";
my $debug = 0;
my $force = 0;
my $single = 0;
my $verify = 0;
my $quiet = 0;
my $check = 0;
my $impotent= 0;
my $path = ".";
my $phase;
my $version;
my $verify_count = 0;
my $logfp;
#
# Configure variables
#
my $TB = "@prefix@";
my $TBOPS = "@TBOPSEMAIL@";
my $logfile = "/var/tmp/install.$$";
# Protos
sub Fatal($);