install-rpm 6.04 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12
#!/usr/bin/perl -wT

#
# EMULAB-COPYRIGHT
# Copyright (c) 2000-2003 University of Utah and the Flux Group.
# All rights reserved.
#

use English;
use Getopt::Std;
use POSIX 'setsid';

13 14 15 16
# 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; }

17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
#
# 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
# the RPM is copied over first either via NFS (the default) or tmcc
# (-t option).
#
# Exit Value Matters!: 0 if installed okay
#                      1 if already installed
#                     -1 if something goes wrong.
#
# To ensure safety, the RPM filename must start with /proj, except if
# running with jail option. Must be run as root.
#
sub usage()
{
32
    print STDOUT "Usage: install-rpm [-d] [-ct] [-n nodeid] <filename>\n";
33 34
    exit(-1);
}
35
my $optlist  = "dctn:";
36 37 38 39 40 41 42

#
# Turn off line buffering on output
#
$| = 1;

#
43
# Untaint env.
44 45 46 47 48 49 50
# 
delete @ENV{'IFS', 'CDPATH', 'ENV', 'BASH_ENV'};

#
# No configure vars.
#
my $IDENTFILE      = "/var/db/testbed.rpms";
51 52
my $rpm            = "";
my $usewget	   = 0;
53
my $copymode	   = 0;
54 55 56 57 58 59 60 61 62
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;
63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84

# Protos
sub GetRPMFile($$$);

#
# Must be running as root to work. 
#
if ($EUID != 0) {
    die("Must be run as root! Try using sudo!\n");
}

#
# Parse command arguments. Once we return from getopts, all that should be
# left are the required arguments.
#
%options = ();
if (! getopts($optlist, \%options)) {
    usage();
}
if (defined($options{"c"})) {
    $copymode = 1;
}
85 86 87
if (defined($options{"d"})) {
    $debug = 1;
}
88
if (defined($options{"t"})) {
89
    $usewget = 1;
90 91 92
    $copymode = 1;
}
if (defined($options{"n"})) {
93 94 95
    $nodeid = $options{"n"};
    if ($nodeid =~ /^([-\w]+)$/) {
	$nodeid = $1;
96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145
    }
}
if (@ARGV != 1) {
    usage();
}
$rpm    = $ARGV[0];

#
# Untaint the arguments.
#
# Note different taint check (allow /).
if ($rpm =~ /^([-\w.\/]+)$/) {
    $rpm = $1;
}
else {
    fatal("Tainted filename: $rpm");
}

#
# Check to make sure this RPM has not already been installed. Update
# the file now. If the rpm fails, we got big problems.
#
if (-e $IDENTFILE) {
    if (! system("egrep -q -s '^${rpm}' $IDENTFILE")) {
	print STDOUT "RPM $rpm has already been installed!\n";
	exit(1);
    }
}

#
# Must be able to see the RPM if not copying. The front end
# ensures that its in a reasonable place, but have to make sure here.
#
if (! $copymode) {
    #
    # Make sure its really there.
    #
    if (! -r $rpm) {
	fatal("$rpm does not exist or is not accessible!");
    }
}
else {
    $copyfile = `mktemp /var/tmp/rpm.XXXXXX`;

    if ($copyfile =~ /^([-\@\w\.\/]+)$/) {
	$copyfile = $1;
    }
    else {
	die("Bad data in copyfile name: $copyfile");
    }
146
    GetRPMFile($rpm, $copyfile, $usewget);
147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175
    #
    # Dies on any failure!
    #
}

#
# Add to index first; if fails too bad.
# 
if (system("echo \"$rpm\" >> $IDENTFILE")) {
    fatal("Could not update $IDENTFILE");
}

#
# Run the RPM. 
#
if ($copymode) {
    $rpm = $copyfile;
}
system("rpm -i $rpm");
$exit_status = $? >> 8;
if ($copymode) {
    unlink($copyfile);
}

exit($exit_status);

sub fatal {
    local($msg) = $_[0];

176 177 178 179 180
    if ($copymode && -e $copyfile) {
	unlink($copyfile);
    }
    die("*** $0:\n".
	"    $msg\n");
181 182 183 184 185 186 187
}

#
# Get an RPM from the server via tmcc and stash.
#
sub GetRPMFile($$$)
{
188
    my ($rpm, $copyfile, $usewget) = @_;
189 190 191 192
    my $buf;
    my $bytelen;

    #
193
    # If copying via NFS, must watch for read errors and retry.
194
    #
195
    if (! $usewget) {
196
	open(TMCC, "< $rpm")
197
	    or fatal("Could not open rpm on server!");
198 199 200
	$bytelen = (stat($rpm))[7];

	#
201
	# Open the target file and start dumping the data in.
202
	#
203 204
	open(JFILE, "> $copyfile")
	    or fatal("Could not open local file $copyfile: $!");
205

206 207 208 209 210
	#
	# Deal with NFS read failures
	#
	my $foffset = 0;
	my $retries = 5;
211

212 213
	while ($bytelen) {
	    my $rlen = sysread(TMCC, $buf, 8192);
214

215 216 217 218
	    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.
219 220 221 222 223 224 225
		if ($retries > 0 && sysseek(TMCC, $foffset, 0)) {
		    warn("*** WARNING retrying read of $rpm ".
			 "at offset $foffset\n");
		    $retries--;
		    sleep(2);
		    next;
		}
226 227 228 229 230 231 232
		fatal("Error reading tarball $rpm: $!");
	    }
	    if ($rlen == 0) {
		last;
	    }
	    if (! syswrite(JFILE, $buf)) {
		fatal("Error writing rpm $copyfile: $!");
233
	    }
234 235 236
	    $foffset += $rlen;
	    $bytelen -= $rlen;
	    $retries = 5;
237
	}
238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260
	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;
	    }
261
	}
262 263 264 265 266 267 268 269 270 271 272
	#
	# 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;
273
	}
274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292

	#
	# 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 ".
293
	          ($debug ? "--server-response " : "") .
294 295 296 297 298 299 300 301 302 303
	          "'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 ($?);
304 305 306
    }
    return 0;
}