Commit 0a259517 authored by Leigh B. Stoller's avatar Leigh B. Stoller

Convert install-rpm/install-tarfile to use the web server instead of

tmcd (which is bad, since tying up the tmcd threads blocks all nodes
in the testbed). The old functionality is left in tmcd for now.

On the server side, a new web page (www/spewrpmtar.php3) receives a
request for a file, along with the nodeid (pcXXX) making the request,
and the secret key that is generated for each new experiment and
transfered to the node via tmcd. If the key matches, the operation is
handed off to tbsetup/spewrpmtar.in which verifies that the file is in
the list of rpm/tar files for that node, and then spits it out to
stdout. The web page uses fpassthru() to send the file out to the
client. The client is using wget, and is required to use https (the
web page checks).

At present, the external script is run as the creator of the
experiment, and gid of the experiment. Perhaps this is not a good
idea. In any event, the file must be in the list of rpm/tarfiles,
either owned by the experiment creator or with a group of the
experiment, and the file must reside in either /proj or /groups.
I use the realpath() function to make sure there are no symlink tricks
pointing to outside those filesystems. I use the standard NFS read goo to
prevent transient mount problems that we all know and love.
parent 00038164
......@@ -1363,6 +1363,7 @@ outfiles="$outfiles Makeconf GNUmakefile \
account/addsfskey account/webaddsfskey \
account/quotamail \
tbsetup/GNUmakefile tbsetup/console_setup tbsetup/spewlogfile \
tbsetup/spewrpmtar \
tbsetup/console_reset tbsetup/bwconfig tbsetup/power_rpc27.pm \
tbsetup/os_load tbsetup/os_setup tbsetup/os_select tbsetup/power \
tbsetup/node_reboot tbsetup/webnscheck tbsetup/nscheck \
......
......@@ -409,6 +409,7 @@ outfiles="$outfiles Makeconf GNUmakefile \
account/addsfskey account/webaddsfskey \
account/quotamail \
tbsetup/GNUmakefile tbsetup/console_setup tbsetup/spewlogfile \
tbsetup/spewrpmtar \
tbsetup/console_reset tbsetup/bwconfig tbsetup/power_rpc27.pm \
tbsetup/os_load tbsetup/os_setup tbsetup/os_select tbsetup/power \
tbsetup/node_reboot tbsetup/webnscheck tbsetup/nscheck \
......
......@@ -10,6 +10,10 @@ use English;
use Getopt::Std;
use POSIX 'setsid';
# Drag in path stuff so we can find emulab stuff.
# XXX Temporary until I have the new tmcc library finished!
BEGIN { require "/etc/emulab/paths.pm"; import emulabpaths; }
#
# Install an RPM. This script is run from the setup code on client nodes.
# By default the RPM is accessed directly via NFS, if '-c' is specified
......@@ -25,10 +29,10 @@ use POSIX 'setsid';
#
sub usage()
{
print STDOUT "Usage: install-rpm [-ct] [-n nodeid] <filename>\n";
print STDOUT "Usage: install-rpm [-d] [-ct] [-n nodeid] <filename>\n";
exit(-1);
}
my $optlist = "ctn:";
my $optlist = "dctn:";
#
# Turn off line buffering on output
......@@ -36,20 +40,26 @@ my $optlist = "ctn:";
$| = 1;
#
# Untaint the path
# Untaint env.
#
$ENV{'PATH'} = "/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/etc/emulab";
delete @ENV{'IFS', 'CDPATH', 'ENV', 'BASH_ENV'};
#
# No configure vars.
#
my $IDENTFILE = "/var/db/testbed.rpms";
my $rpm = "";
my $usetmcc = 0;
my $rpm = "";
my $usewget = 0;
my $copymode = 0;
my $copyfile = "";
my $vnodeid;
my $debug = 0;
my $copyfile;
my $nodeid;
#
# Load the OS independent support library. It will load the OS dependent
# library and initialize itself.
#
use libsetup;
# Protos
sub GetRPMFile($$$);
......@@ -72,14 +82,17 @@ if (! getopts($optlist, \%options)) {
if (defined($options{"c"})) {
$copymode = 1;
}
if (defined($options{"d"})) {
$debug = 1;
}
if (defined($options{"t"})) {
$usetmcc = 1;
$usewget = 1;
$copymode = 1;
}
if (defined($options{"n"})) {
$vnodeid = $options{"n"};
if ($vnodeid =~ /^([-\w]+)$/) {
$vnodeid = $1;
$nodeid = $options{"n"};
if ($nodeid =~ /^([-\w]+)$/) {
$nodeid = $1;
}
}
if (@ARGV != 1) {
......@@ -130,7 +143,7 @@ else {
else {
die("Bad data in copyfile name: $copyfile");
}
GetRPMFile($rpm, $copyfile, $usetmcc);
GetRPMFile($rpm, $copyfile, $usewget);
#
# Dies on any failure!
#
......@@ -140,9 +153,6 @@ else {
# Add to index first; if fails too bad.
#
if (system("echo \"$rpm\" >> $IDENTFILE")) {
if ($copymode) {
unlink($copyfile);
}
fatal("Could not update $IDENTFILE");
}
......@@ -163,8 +173,11 @@ exit($exit_status);
sub fatal {
local($msg) = $_[0];
print STDERR "$msg\n";
exit(-1);
if ($copymode && -e $copyfile) {
unlink($copyfile);
}
die("*** $0:\n".
" $msg\n");
}
#
......@@ -172,66 +185,37 @@ sub fatal {
#
sub GetRPMFile($$$)
{
my ($rpm, $copyfile, $usetmcc) = @_;
my ($rpm, $copyfile, $usewget) = @_;
my $buf;
my $bytelen;
#
# If copying via NFS, open the file. Otherwise pipe from tmcc.
# If copying via NFS, must watch for read errors and retry.
#
if (! $usetmcc) {
if (! $usewget) {
open(TMCC, "< $rpm")
or fatal("Could not open RPM on server!");
or fatal("Could not open rpm on server!");
$bytelen = (stat($rpm))[7];
} else {
my $tmccopt = "-t 300";
if (defined($vnodeid)) {
$tmccopt .= " -n $vnodeid";
}
#
# Protocol is a little odd. First word is the number of bytes
# (yes, limited to 31 bits of length!), then the data. If we do
# not get that much data, we fail. What about timeout option?
# Could take a while to get the entire file, but instead of a
# timeout let Emulab decide when its too long.
# Open the target file and start dumping the data in.
#
open(TMCC, "tmcc $tmccopt rpm $rpm |")
or fatal("Could not download RPM from server!");
# Hokey C struct stuff
my $firstword = pack("i", 0);
if (! sysread(TMCC, $firstword, length($firstword))) {
fatal("Could not read length of RPM from server!");
}
$bytelen = unpack("i", $firstword);
}
if ($bytelen == 0) {
fatal("Zero length of RPM from server!");
}
#
# Open the target file and start dumping the data in.
#
open(JFILE, "> $copyfile")
or fatal("Could not open local file $copyfile: $!");
open(JFILE, "> $copyfile")
or fatal("Could not open local file $copyfile: $!");
#
# Deal with NFS read failures
#
my $foffset = 0;
my $retries = 5;
#
# Deal with NFS read failures
#
my $foffset = 0;
my $retries = 5;
while ($bytelen) {
my $rlen = sysread(TMCC, $buf, 8192);
while ($bytelen) {
my $rlen = sysread(TMCC, $buf, 8192);
if (! defined($rlen)) {
#
# If we are copying the file via NFS, retry a few times
# on error to avoid the changing-exports-file server problem.
#
if (! $usetmcc) {
if (! defined($rlen)) {
#
# If we are copying the file via NFS, retry a few times
# on error to avoid the changing-exports-file server problem.
if ($retries > 0 && sysseek(TMCC, $foffset, 0)) {
warn("*** WARNING retrying read of $rpm ".
"at offset $foffset\n");
......@@ -239,26 +223,83 @@ sub GetRPMFile($$$)
sleep(2);
next;
}
fatal("Error reading tarball $rpm: $!");
}
if ($rlen == 0) {
last;
}
if (! syswrite(JFILE, $buf)) {
fatal("Error writing rpm $copyfile: $!");
}
unlink($copyfile);
fatal("Error reading RPM $rpm: $!");
$foffset += $rlen;
$bytelen -= $rlen;
$retries = 5;
}
if ($rlen == 0) {
last;
close(JFILE);
close(TMCC);
}
else {
#
# Need the nodeid and the keyhash. We allow the nodeid to be
# overridden on the command line, but thats just a debugging
# feature.
#
if (!defined($nodeid)) {
#
# Eventually, use tmcc which will cache the result.
#
open(FD, "< " . TMNODEID()) or
fatal("Could not open ". TMNODEID() . ": $!");
$nodeid = <FD>;
close(FD);
fatal("Could not get our nodeid!")
if (!defined($nodeid));
if ($nodeid =~ /^([-\w]+)$/) {
$nodeid = $1;
}
}
if (! syswrite(JFILE, $buf)) {
unlink($copyfile);
fatal("Error writing RPM $copyfile: $!");
#
# Eventually, use tmcc which will cache the result.
#
open(FD, "< " . TMKEYHASH()) or
fatal("Could not open ". TMKEYHASH() . ": $!");
$keyhash = <FD>;
close(FD);
fatal("Could not get our keyhash!")
if (!defined($keyhash));
if ($keyhash =~ /^([\w]+)$/) {
$keyhash = $1;
}
$foffset += $rlen;
$bytelen -= $rlen;
$retries = 5;
}
if ($bytelen) {
unlink($copyfile);
fatal("Did not get the entire RPM! $bytelen bytes left.");
#
# Lastly, need our boss node.
#
my ($www) = split(" ", `tmcc bossinfo`);
die("Could not get bossinfo!")
if ($?);
if ($www =~ /^[-\w]+\.(.*)$/) {
$www = "www.${1}";
}
else {
fatal("Tainted bossinfo $www!");
}
#
# Okay, run wget with the proper arguments.
#
my $cmd = "wget -q -O $copyfile ".
"'https://${www}/spewrpmtar.php3".
"?nodeid=${nodeid}&file=${rpm}&key=${keyhash}'";
if ($debug) {
print STDERR "$cmd\n";
}
system($cmd);
fatal("Could not retrieve $rpm from $www")
if ($?);
}
close(JFILE);
close(TMCC);
return 0;
}
......@@ -10,6 +10,10 @@ use English;
use Getopt::Std;
use POSIX 'setsid';
# Drag in path stuff so we can find emulab stuff.
# XXX Temporary until I have the new tmcc library finished!
BEGIN { require "/etc/emulab/paths.pm"; import emulabpaths; }
#
# Install a tarfile. This script is run from the setup code on client nodes.
# By default the tarfile is accessed directly via NFS, if '-c' is specified
......@@ -25,10 +29,11 @@ use POSIX 'setsid';
#
sub usage()
{
print STDOUT "Usage: install-tarfile [-ct] [-n nodeid] <installdir> <filename>\n";
print STDOUT "Usage: install-tarfile [-d] [-ct] [-n nodeid] <installdir> ".
"<filename>\n";
exit(-1);
}
my $optlist = "ctn:";
my $optlist = "cdtn:";
#
# Turn off line buffering on output
......@@ -36,9 +41,8 @@ my $optlist = "ctn:";
$| = 1;
#
# Untaint the path
# Untaint env.
#
$ENV{'PATH'} = "/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/etc/emulab";
delete @ENV{'IFS', 'CDPATH', 'ENV', 'BASH_ENV'};
#
......@@ -48,10 +52,18 @@ my $IDENTFILE = "/var/db/testbed.tarfiles";
my $tarfile = "";
my $decompressflag = "";
my $installdir = "/";
my $usetmcc = 0;
my $usewget = 0;
my $copymode = 0;
my $copyfile = "";
my $vnodeid;
my $debug = 0;
my $copyfile;
my $nodeid;
my $keyhash;
#
# Load the OS independent support library. It will load the OS dependent
# library and initialize itself.
#
use libsetup;
# Protos
sub GetTarFile($$$);
......@@ -74,20 +86,26 @@ if (! getopts($optlist, \%options)) {
if (defined($options{"c"})) {
$copymode = 1;
}
if (defined($options{"d"})) {
$debug = 1;
}
if (defined($options{"t"})) {
$usetmcc = 1;
$copymode = 1;
$usewget = 1;
}
if (defined($options{"n"})) {
$vnodeid = $options{"n"};
if ($vnodeid =~ /^([-\w]+)$/) {
$vnodeid = $1;
$nodeid = $options{"n"};
if ($nodeid =~ /^([-\w]+)$/) {
$nodeid = $1;
}
else {
fatal("Tainted nodeid: $nodeid");
}
}
# XXX compat
if (defined($options{"j"})) {
$usetmcc = 1;
$copymode = 1;
$usewget = 1;
}
if (@ARGV != 2) {
usage();
......@@ -151,7 +169,7 @@ else {
else {
die("Bad data in copyfile name: $copyfile");
}
GetTarFile($tarfile, $copyfile, $usetmcc);
GetTarFile($tarfile, $copyfile, $usewget);
#
# Dies on any failure!
#
......@@ -161,9 +179,6 @@ else {
# Add to index first; if fails too bad.
#
if (system("echo \"$tarfile\" >> $IDENTFILE")) {
if ($copymode) {
unlink($copyfile);
}
fatal("Could not update $IDENTFILE");
}
......@@ -181,9 +196,6 @@ SWITCH: for ($tarfile) {
# Install tar file from root?
#
if (! chdir($installdir)) {
if ($copymode) {
unlink($copyfile);
}
fatal("Could not chdir to $installdir: $!\n");
}
......@@ -204,8 +216,11 @@ exit($exit_status);
sub fatal {
local($msg) = $_[0];
print STDERR "$msg\n";
exit(-1);
if ($copymode && -e $copyfile) {
unlink($copyfile);
}
die("*** $0:\n".
" $msg\n");
}
#
......@@ -213,66 +228,37 @@ sub fatal {
#
sub GetTarFile($$$)
{
my ($tarfile, $copyfile, $usetmcc) = @_;
my ($tarfile, $copyfile, $usewget) = @_;
my $buf;
my $bytelen;
#
# If copying via NFS, open the file. Otherwise pipe from tmcc.
# If copying via NFS, must watch for read errors and retry.
#
if (! $usetmcc) {
if (! $usewget) {
open(TMCC, "< $tarfile")
or fatal("Could not open tarfile on server!");
$bytelen = (stat($tarfile))[7];
} else {
my $tmccopt = "-t 300";
if (defined($vnodeid)) {
$tmccopt .= " -n $vnodeid";
}
#
# Protocol is a little odd. First word is the number of bytes
# (yes, limited to 31 bits of length!), then the data. If we do
# not get that much data, we fail. What about timeout option?
# Could take a while to get the entire file, but instead of a
# timeout let Emulab decide when its too long.
# Open the target file and start dumping the data in.
#
open(TMCC, "tmcc $tmccopt tarball $tarfile |")
or fatal("Could not download tarfile from server!");
# Hokey C struct stuff
my $firstword = pack("i", 0);
if (! sysread(TMCC, $firstword, length($firstword))) {
fatal("Could not read length of tarfile from server!");
}
$bytelen = unpack("i", $firstword);
}
if ($bytelen == 0) {
fatal("Zero length of tarfile from server!");
}
open(JFILE, "> $copyfile")
or fatal("Could not open local file $copyfile: $!");
#
# Open the target file and start dumping the data in.
#
open(JFILE, "> $copyfile")
or fatal("Could not open local file $copyfile: $!");
#
# Deal with NFS read failures
#
my $foffset = 0;
my $retries = 5;
#
# Deal with NFS read failures
#
my $foffset = 0;
my $retries = 5;
while ($bytelen) {
my $rlen = sysread(TMCC, $buf, 8192);
while ($bytelen) {
my $rlen = sysread(TMCC, $buf, 8192);
if (! defined($rlen)) {
#
# If we are copying the file via NFS, retry a few times
# on error to avoid the changing-exports-file server problem.
#
if (! $usetmcc) {
if (! defined($rlen)) {
#
# If we are copying the file via NFS, retry a few times
# on error to avoid the changing-exports-file server problem.
if ($retries > 0 && sysseek(TMCC, $foffset, 0)) {
warn("*** WARNING retrying read of $tarfile ".
"at offset $foffset\n");
......@@ -280,26 +266,83 @@ sub GetTarFile($$$)
sleep(2);
next;
}
fatal("Error reading tarball $tarfile: $!");
}
if ($rlen == 0) {
last;
}
if (! syswrite(JFILE, $buf)) {
fatal("Error writing tarfile $copyfile: $!");
}
$foffset += $rlen;
$bytelen -= $rlen;
$retries = 5;
}
close(JFILE);
close(TMCC);
}
else {
#
# Need the nodeid and the keyhash. We allow the nodeid to be
# overridden on the command line, but thats just a debugging
# feature.
#
if (!defined($nodeid)) {
#
# Eventually, use tmcc which will cache the result.
#
open(FD, "< " . TMNODEID()) or
fatal("Could not open ". TMNODEID() . ": $!");
$nodeid = <FD>;
close(FD);
fatal("Could not get our nodeid!")
if (!defined($nodeid));
if ($nodeid =~ /^([-\w]+)$/) {
$nodeid = $1;
}
unlink($copyfile);
fatal("Error reading tarball $tarfile: $!");
}
if ($rlen == 0) {
last;
#
# Eventually, use tmcc which will cache the result.
#
open(FD, "< " . TMKEYHASH()) or
fatal("Could not open ". TMKEYHASH() . ": $!");
$keyhash = <FD>;
close(FD);
fatal("Could not get our keyhash!")
if (!defined($keyhash));
if ($keyhash =~ /^([\w]+)$/) {
$keyhash = $1;
}
if (! syswrite(JFILE, $buf)) {
unlink($copyfile);
fatal("Error writing tarfile $copyfile: $!");
#
# Lastly, need our boss node.
#
my ($www) = split(" ", `tmcc bossinfo`);
die("Could not get bossinfo!")
if ($?);
if ($www =~ /^[-\w]+\.(.*)$/) {
$www = "www.${1}";
}
$foffset += $rlen;
$bytelen -= $rlen;
$retries = 5;
}
if ($bytelen) {
unlink($copyfile);
fatal("Did not get the entire tarball! $bytelen bytes left.");
else {
fatal("Tainted bossinfo $www!");
}
#
# Okay, run wget with the proper arguments.
#
my $cmd = "wget -q -O $copyfile ".
"'https://${www}/spewrpmtar.php3".
"?nodeid=${nodeid}&file=${tarfile}&key=${keyhash}'";
if ($debug) {
print STDERR "$cmd\n";
}
system($cmd);
fatal("Could not retrieve $tarfile from $www")
if ($?);
}
close(JFILE);
close(TMCC);
return 0;
}
......@@ -21,7 +21,7 @@ use Exporter;
TBBackGround TBForkCmd vnodejailsetup plabsetup vnodeplabsetup
dorouterconfig jailsetup dojailconfig JailedMounts findiface
tmccdie tmcctimeout libsetup_getvnodeid dotrafficconfig
ixpsetup
ixpsetup dokeyhash donodeid
OPENTMCC CLOSETMCC RUNTMCC MFS REMOTE JAILED PLAB LOCALROOTFS IXP
......@@ -29,12 +29,14 @@ use Exporter;
TMNICKNAME HOSTSFILE TMSTARTUPCMD FINDIF TMTUNNELCONFIG
TMTRAFFICCONFIG TMROUTECONFIG TMLINKDELAY TMDELMAP TMMOUNTDB
TMPROGAGENTS TMPASSDB TMGROUPDB TMGATEDCONFIG
TMSYNCSERVER TMRCSYNCSERVER TMKEYHASH TMNODEID
TMCCCMD_REBOOT TMCCCMD_STATUS TMCCCMD_IFC TMCCCMD_ACCT TMCCCMD_DELAY
TMCCCMD_HOSTS TMCCCMD_RPM TMCCCMD_TARBALL TMCCCMD_STARTUP
TMCCCMD_DELTA TMCCCMD_STARTSTAT TMCCCMD_READY TMCCCMD_TRAFFIC
TMCCCMD_BOSSINFO TMCCCMD_VNODELIST TMCCCMD_ISALIVE TMCCCMD_LINKDELAYS
TMCCCMD_PROGRAMS TMCCCMD_SUBNODELIST TMCCCMD_SUBCONFIG
TMCCCMD_STATE
TMCCCMD_STATE TMCCCMD_SYNCSERVER TMCCCMD_KEYHASH TMCCCMD_NODEID
);
# Must come after package declaration!
......@@ -210,6 +212,8 @@ sub TMLINKDELAY() { CONFDIR() . "/rc.linkdelay";}
sub TMDELMAP() { CONFDIR() . "/delay_mapping";}
sub TMSYNCSERVER() { CONFDIR() . "/syncserver";}
sub TMRCSYNCSERVER() { CONFDIR() . "/rc.syncserver";}
sub TMKEYHASH() { CONFDIR() . "/keyhash";}
sub TMNODEID() { CONFDIR() . "/nodeid";}
#
# Whether or not to use SFS (the self-certifying file system). If this
......@@ -260,6 +264,8 @@ sub TMCCCMD_SUBCONFIG() { "subconfig"; }
sub TMCCCMD_LINKDELAYS(){ "linkdelays"; }
sub TMCCCMD_PROGRAMS() { "programs"; }
sub TMCCCMD_SYNCSERVER(){ "syncserver"; }
sub TMCCCMD_KEYHASH() { "keyhash"; }
sub TMCCCMD_NODEID() { "nodeid"; }
#
# Some things never change.
......@@ -1759,12 +1765,6 @@ sub dorpms ()
# (install-tarfile knows how to deal with said race when copying).
#
my $installoption = "-c";
if (JAILED() || PLAB()) {
$installoption .= " -t";
if (my $id = PLAB()) {
$installoption .= " -n $id";
}
}
open(RPM, ">" . TMRPM)
or die("Could not open " . TMRPM . ": $!");
......@@ -1814,12 +1814,6 @@ sub dotarballs ()
# (install-tarfile knows how to deal with said race when copying).
#
my $installoption = "-c";
if (JAILED() || PLAB()) {
$installoption .= " -t";
if (my $id = PLAB()) {
$installoption .= " -n $id";
}
}
open(TARBALL, ">" . TMTARBALLS)
or die("Could not open " . TMTARBALLS . ": $!");
......@@ -2303,6 +2297,80 @@ sub dosyncserver()
return 0;
}
#
# Get the hashkey
#
sub dokeyhash()
{
my @configstrings;
my $keyhash;
$TM = OPENTMCC(TMCCCMD_KEYHASH);
while (<$TM>) {
push(@configstrings, $_);
}
CLOSETMCC($TM);
if (! @configstrings) {
return 0;
}
#
# There should be just one string. Ignore anything else.
#
if ($configstrings[0] =~ /KEYHASH HASH=\'([\w]*)\'/) {
$keyhash = $1;
}
else {
warn "*** WARNING: Bad keyhash line: $_";
return 1;
}
#
# Write a file so the node knows the key.
#
my $oldumask = umask(0227);
if (system("echo '$keyhash' > ". TMKEYHASH)) {
warn "*** WARNING: Could not write " . TMKEYHASH . "\n";
}
umask($oldumask);
return 0;
}
#
# Get the nodeid
#
sub donodeid()
{
my @configstrings;
my $nodeid;
$TM = OPENTMCC(TMCCCMD_NODEID);
while (<$TM>) {
push(@configstrings, $_);
}
CLOSETMCC($TM);
if (! @configstrings) {
return 0;
}
#
# There should be just one string. Ignore anything else.
#
if ($configstrings[0] =~ /([-\w]*)/) {
$nodeid = $1;
}