tarfiles_setup.in 9.06 KB
Newer Older
1 2
#!/usr/bin/perl -w
#
3
# Copyright (c) 2003-2011 University of Utah and the Flux Group.
4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
# 
# {{{EMULAB-LICENSE
# 
# This file is part of the Emulab network testbed software.
# 
# This file is free software: you can redistribute it and/or modify it
# under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or (at
# your option) any later version.
# 
# This file is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Affero General Public
# License for more details.
# 
# You should have received a copy of the GNU Affero General Public License
# along with this file.  If not, see <http://www.gnu.org/licenses/>.
# 
# }}}
23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
#
use English;
use Getopt::Std;
use Socket;
    
#
# Fetch all tarball(s) (and RPM(s)) for an experiment. Since we don't want to
# give users the chance to exploit bugs or features used in the program to do
# the fetching, we ssh over to ops to do the actual fetching.
# 
# As a side-effect, copies the contents of the tarfiles and rpms fields from
# virt_nodes to the nodes table. Any fetched tarballs (or RPMs) are entered
# into the nodes table as the location on local disk they were fetched to.
#
# Should be run _after_ the experiment has begun swapin - ie. when the
# virt_nodes have already been assigned to physical nodes.
#

sub usage()
{
43
    print "Usage: $0 [-q] pid eid\n";
44 45 46
 
    exit(-1);
}
47 48
my $optlist = "q";
my $quiet   = 0;
49

50 51 52 53 54
#
# Functions
#
sub verifyURL($);

55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82
#
# Configure variables
#
my $TB       = "@prefix@";
my $TBOPS    = "@TBOPSEMAIL@";
my $CONTROL  = "@USERNODE@";
my $TESTMODE = @TESTMODE@;

my $SAVEUID  = $UID;
my $MD5      = "/sbin/md5";

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

#
# Untaint the path
# 
$ENV{'PATH'} = "$TB/bin:$TB/sbin:/bin:/usr/bin:/sbin:/usr/sbin";
delete @ENV{'IFS', 'CDPATH', 'ENV', 'BASH_ENV'};

if ($TESTMODE) {
    # In testmode, drop privs (my devel tree at home is TESTMODE=1)
    $EUID = $UID;
}
elsif ($EUID != 0) {
    # We don't want to run this script unless its the real version.
83
    die("Must be root! Maybe its a development version?");
84 85 86 87 88
}

# This script is setuid, so please do not run it as root. Hard to track
# what has happened.
if ($UID == 0) {
89
    die("Please do not run this as root! Its already setuid!");
90 91 92 93 94 95 96 97
}

#
# Testbed Support libraries
#
use lib "@prefix@/lib";
use libtestbed;
use libdb;
Kevin Atkinson's avatar
Kevin Atkinson committed
98
use libtblog;
99 100
use Template;
use libArchive;
101 102
use Experiment;
use User;
103

104 105 106 107 108 109 110
my %options = ();
if (! getopts($optlist, \%options)) {
    usage();
}
if (defined($options{"q"})) {
    $quiet = 1;
}
111 112 113 114 115
if (@ARGV != 2) {
    usage();
}
my ($pid, $eid) = @ARGV;

116 117
my %tofetch   = ();
my %toarchive = ();
118 119

#
120 121 122 123 124
# Verify user and get his DB uid and other info for later.
#
my $this_user = User->ThisUser();
if (! defined($this_user)) {
    tbdie("You ($UID) do not exist!");
125
}
126 127
my $user_uid   = $this_user->uid();

128 129 130
#
# First, make sure the experiment exists
#
131 132
my $experiment = Experiment->Lookup($pid, $eid);
if (! $experiment) {
Kevin Atkinson's avatar
Kevin Atkinson committed
133
    tbdie("There is no experiment $eid in project $pid");
134 135 136 137 138
}

#
# User must have at least MODIFY permissions to use this script
#
139
if (!$experiment->AccessCheck($this_user, TB_EXPT_MODIFY())) {
Kevin Atkinson's avatar
Kevin Atkinson committed
140
    tbdie("You are not allowed to modify experiment $eid in project $pid");
141 142 143
}

#
Timothy Stack's avatar
Timothy Stack committed
144
# Get the experiment's directory - that's where we'll stash any files we
145 146
# fetch
#
147 148
my $expdir  = $experiment->UserDir();
my $exptidx = $experiment->idx();
149

150 151 152
#
# Get a list of all RPMs and tarballs to fetch
#
153
my $result = $experiment->TableLookUp("virt_nodes", "vname,rpms,tarfiles");
154 155 156 157 158

while (my ($vname, $rpms, $tarfiles) = $result->fetchrow()) {
    #
    # Find out the pnode where the this vnode is mapped, if any
    #
159
    my $physnode = $experiment->VnameToNode($vname);
160 161 162 163
    
    # The if block below is needed for simulated nodes which do not
    # have a reserved table entry. A tarfile specified for a simulated
    # node ends up on its host PC
164 165
    if (! defined($physnode)) {
	$physnode = $experiment->VnameToPmap($vname);
166
    }
167 168 169 170
    $rpms = ""
	if (!defined($rpms));
    $tarfiles = ""
	if (!defined($tarfiles));
171 172 173 174 175

    #
    # Go through the list of RPMs looking for files to fetch
    #
    foreach my $rpm (split(";", $rpms)) {
Timothy Stack's avatar
Timothy Stack committed
176
	if ($rpm =~ /^(http|https|ftp)/) {
177 178 179 180 181
	    #
	    # Veryify that they gave us a legal URL
	    #
	    my $URL = verifyURL($rpm);
	    if (!$URL) {
Kevin Atkinson's avatar
Kevin Atkinson committed
182
		tbdie("Invalid RPM URL given: $rpm");
183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201
	    }

	    #
	    # Build up a local filename using an MD5 hash of the URL, so that
	    # we can uniquely identify it, but don't have to worry about
	    # putting funny characters in filenames.
	    # 
	    my $md5 = `$MD5 -q -s '$URL'`;
	    chomp $md5;
	    # Have to untaint the hash
	    $md5 =~ /^(\w+)$/;
	    $md5 = $1;
	    my $localfile = $expdir . "/" . $md5 . ".rpm";

	    #
	    # Remember this RPM and put the local filename in the string that
	    # will be uploaded to the nodes table
	    #
	    $tofetch{$URL} = $localfile;
202
	    $toarchive{$localfile} = $localfile;
203 204
	    $rpms =~ s/$URL/$localfile/g;
	}
205 206 207 208 209 210 211 212 213
	elsif ($rpm =~ /^xxx:\/\/(.*)$/) {
	    #
	    # XXX (well, what else did you expect!). This is a template
	    # "url" that refers to a file in the template datastore tree.
	    #
	    my $instance = Template::Instance->LookupByExptidx($exptidx);
	    if (!defined($instance)) {
		tbdie("Invalid RPM URL for non-template experiment: $rpm");
	    }
Leigh Stoller's avatar
Leigh Stoller committed
214
	    my $localfile = $instance->path() . "/datastore/" . $1;
215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230

	    tbdie("$rpm cannot be found; $localfile does not exist")
		if (! -e $localfile);

	    # no need to archive these since they are saved in the template
	    $rpms =~ s/$rpm/$localfile/g;
	}
	else {
	    #
	    # Should be a regular path.
	    #
	    tbdie("$rpm cannot be found; local file does not exist")
		if (! -e $rpm);

	    $toarchive{$rpm} = $rpm;
	}
231 232 233 234 235 236 237
    }
    
    #
    # Same as above, for tarballs
    #
    foreach my $tar (split(";", $tarfiles)) {
	my ($dir,$tarfile) = split(" ",$tar);
Timothy Stack's avatar
Timothy Stack committed
238
	if ($tarfile =~ /^(http|https|ftp)/) {
239 240
	    my $URL = verifyURL($tarfile);
	    if (!$URL) {
Kevin Atkinson's avatar
Kevin Atkinson committed
241
		tbdie("Invalid tarball URL given: $tarfile");
242 243 244 245 246
	    }
	    my $md5 = `md5 -q -s '$URL'`;
	    chomp $md5;
	    $md5 =~ /^(\w+)$/;
	    $md5 = $1;
Timothy Stack's avatar
Timothy Stack committed
247 248 249 250 251 252
	    my $ext = $tarfile;
	    # Need to copy the extension from the URL to the file name so
	    # install-tarfile can figure out how to decompress it.
	    $ext =~ /(\.tar|\.tar\.Z|\.tar\.gz|\.tgz|\.tar\.bz2)$/;
	    $ext = $1;
	    my $localfile = $expdir . "/" . $md5 . $ext;
253
	    $toarchive{$localfile} = $localfile;
254 255 256
	    $tofetch{$URL} = $localfile;
	    $tarfiles =~ s/$URL/$localfile/g;
	}
257 258 259 260 261 262 263 264 265 266
	elsif ($tarfile =~ /^xxx:\/\/(.*)$/) {
	    #
	    # XXX (well, what else did you expect!). This is a template
	    # "url" that refers to a file in the template datastore tree.
	    #
	    my $instance = Template::Instance->LookupByExptidx($exptidx);
	    if (!defined($instance)) {
		tbdie("Invalid tarball URL for non-template experiment: ".
		      "$tarfile");
	    }
Leigh Stoller's avatar
Leigh Stoller committed
267
	    my $localfile = $instance->path() . "/datastore/" . $1;
268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283

	    tbdie("$tarfile cannot be found; $localfile does not exist")
		if (! -e $localfile);

	    # no need to archive these since they are saved in the template
	    $tarfiles =~ s/$tarfile/$localfile/g;
	}
	else {
	    #
	    # Should be a regular path.
	    #
	    tbdie("$tarfile cannot be found; local file does not exist")
		if (! -e $tarfile);

	    $toarchive{$tarfile} = $tarfile;
	}
284 285 286 287 288 289 290 291 292 293 294 295
    }

    #
    # Hack, hack, hack! We use ';' as a separator in the virt_nodes table, but
    # ":" in the nodes table. We should fix the latter
    #
    $tarfiles =~ s/;/:/g;
    $rpms =~ s/;/:/g;
    
    #
    # If this virtual node is allocated, update the nodes table
    #
296 297 298
    if (defined($physnode)) {
	$physnode->Update({'tarballs' => $tarfiles, 'rpms' => $rpms}) == 0
	    or tbdie("Could not update tarballs,rpms for $physnode");
299 300 301 302 303 304 305 306 307 308 309 310 311 312
    }
}

#
# In testmode, don't actually fetch anything
#
if ($TESTMODE) {
    exit(0);
}

#
# Actually fetch the tarballs
#
while (my ($URL, $localfile) = each %tofetch) {
313 314
    print "Fetching $URL to $localfile\n"
	if (!$quiet);
315 316 317 318 319

    #
    # Build up a new command line to do the fetch on ops
    #
    my $cmdargs = "$TB/bin/fetchtar.proxy ";
320 321
    $cmdargs .= " -q "
	if ($quiet);
322
    $cmdargs .= " -u $user_uid $URL $localfile ";
323 324 325 326 327 328 329 330 331

    #
    # Must flip to real root for the ssh, and then flip back afterwards.
    # 
    $EUID = $UID = 0;
    system("sshtb -host $CONTROL $cmdargs ");
    $EUID = $UID = $SAVEUID;

    if ($?) {
332
	tbdie("Fetch of Tarball/RPM failed: $URL");
333 334 335
    }
}

336 337 338 339
#
# Now add to the archive.
#
while (my ($localfile, $ignored) = each %toarchive) {
340 341
    # Lets not do this as root. 
    $EUID = $UID;
342 343
    libArchive::TBExperimentArchiveAddFile($pid, $eid, $localfile) == 0 or
	tbdie("Failed to add $localfile to the archive!");
344
    $EUID = 0;
345 346
}

347 348 349 350 351 352
#
# Check to make sure a URL for a tarball or RPM is valid, and return an
# untained version of it. Returns undefined if the URL is not valid.
#
sub verifyURL($) {
    my ($URL) = @_;
353
    if ($URL =~
Timothy Stack's avatar
Timothy Stack committed
354
	    /^((http|https|ftp):\/\/[\w.\-\/\@:~]+(\.tar|\.tar\.Z|\.tar\.gz|\.tgz|\.bz2|\.rpm))$/) {
355 356 357 358 359 360 361
	return $1;
    } else {
	return undef;
    }
}

exit 0;