imageinfo.in 7.32 KB
Newer Older
1 2
#!/usr/bin/perl -w
#
3
# Copyright (c) 2012-2017 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
#
use English;
use strict;
use Getopt::Std;
use File::stat;
28
use bigint;
29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44

#
# Get basic info for an image.
#
# This is intended for use by non-admin process on boss which might have
# Emulab-permitted download access but not physical access to the image
# (i.e., it is a shared image). Uses the frisbee mserver to get the info.
#

sub usage()
{
    print("Usage: imageinfo [-N nodeid] [-s] [-m] <imageid>\n" .
	  "Options:\n".
	  "   -d        Turn on debug mode\n".
	  "   -N nodeid Use the frisbee master server to get info on behalf of nodeid\n".
	  "   -q        Don't print message on error, just exit non-zero\n".
45 46 47 48
	  "   -s        Print just the size of the image in bytes\n".
	  "   -u        Print just the uncompressed data size of the image (if available)\n".
	  "   -m        Print just the modtime of the image in seconds since epoch\n".
	  "   -r        Print the range of sectors covered by the image\n".
49
          "   -U k|m    Show sizes in KB or MB instead of bytes\n".
50
          "   -x        Show delta image info instead of full image info\n");
51 52
    exit(-1);
}
53
my $optlist   = "dsumN:qrU:";
54 55
my $debug     = 0;
my $quiet     = 0;
56
my $forcedelta= 0;
57 58
my $showall   = 1;
my $showsize  = 0;
59
my $showusize = 0;
60
my $showmtime  = 0;
61
my $showrange = 0;
62
my $units;
63 64 65 66 67 68 69
my $nodeid;

#
# Configure variables
#
my $TB		= "@prefix@";
my $frisbee	= "$TB/sbin/frisbee";
70
my $imagedump	= "$TB/bin/imagedump";
71 72 73 74 75 76 77 78 79 80

# Protos
sub fatal($);

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

81 82 83 84 85 86 87 88
#
# We don't want to run this script unless its the real version.
#
if ($EUID != 0) {
    die("*** $0:\n".
	"    Must be setuid! Maybe its a development version?\n");
}

89 90 91 92 93 94 95 96 97 98
#
# Turn off line buffering on output
#
$| = 1;

#
# Load the Testbed support stuff.
#
use lib "@prefix@/lib";
use Node;
99
use OSImage;
100 101 102 103 104 105 106 107 108 109 110 111 112 113 114

#
# Parse command arguments. Once we return from getopts, all that should be
# left are the required arguments.
#
my %options = ();
if (! getopts($optlist, \%options)) {
    usage();
}
if (defined($options{"d"})) {
    $debug = 1;
}
if (defined($options{"q"})) {
    $quiet = 1;
}
115 116 117
if (defined($options{"x"})) {
    $forcedelta = 1;
}
118 119 120 121 122 123
if (defined($options{"U"})) {
    $units = $options{"U"};
    if ($units ne "k" && $units ne "m") {
	usage();
    }
}
124 125
if (defined($options{"s"})) {
    $showsize = 1;
126 127 128 129 130
    $showall = $showusize = $showmtime = $showrange = 0;
}
if (defined($options{"u"})) {
    $showusize = 1;
    $showall = $showsize = $showmtime = $showrange = 0;
131 132 133
}
if (defined($options{"m"})) {
    $showmtime = 1;
134
    $showall = $showsize = $showusize = $showrange = 0;
135 136 137
}
if (defined($options{"r"})) {
    $showrange = 1;
138
    $showall = $showsize = $showusize = $showmtime = 0;
139 140 141 142 143 144 145 146 147 148 149
}
if (defined($options{"N"})) {
    my $node = Node->Lookup($options{"N"});
    if (!defined($node)) {
	fatal("No such node exists");
    }
    $nodeid = $node->node_id();
}
usage() 
    if (@ARGV != 1);

150
my $image   = OSImage->Lookup($ARGV[0]);
151 152 153 154
if (!defined($image)) {
    fatal("No such image exists");
}
my $imagename = $image->imagename();
155 156 157 158 159 160 161 162 163 164 165 166
my $imagepid  = $image->pid();
my $imagepath;
if ($forcedelta) {
    if (!$image->HaveDeltaImage()) {
	fatal("There is not delta for this image");
    }
    $imagepath = $image->DeltaImageFile();
}
else {
    if ($image->HaveFullImage()) {
	$imagepath = $image->FullImageFile();
    }
167
    elsif ($image->HaveDeltaImage()) {
168
	$imagepath = $image->DeltaImageFile();
169 170 171
    } else {
	# Size may not have been initialized yet, assume full image
	$imagepath = $image->FullImageFile();
172 173
    }
}
174
    
175 176
if (! -R $imagepath) {
    if (!defined($nodeid)) {
177
	fatal("$imagepath: file is not readable, must use -N\n");
178
    }
179 180 181 182
    if ($showusize) {
	fatal("$imagepath: image file is not readable, ".
	      "cannot determine uncompressed size\n");
    }
183
    if ($showrange) {
184
	fatal("$imagepath: image file is not readable, ".
185 186 187 188
	      "cannot determine sector range\n");
    }
}

189
my ($isize, $usize, $imtime, $secmin, $secmax, $relocatable);
190 191

#
192 193
# Use imagedump to get the uncompressed data size, sector range, and whether
# the image is relocatable. It prints:
194
# ...
195
#   1 relocations covering 276 bytes
196 197
#   covered sector range: [0-12305790]
# ...
198 199
#   2.36x compression of allocated data (7636131840 bytes)
# ...    
200 201
# Note that even if an image has no relocations, it might still be relocatable.
# But, this is the best we can do!
202
#
203
$relocatable = 0;
204
if ($showusize || $showrange) {
205 206
    if (-x $imagedump) {
	foreach my $line (`$imagedump $imagepath 2>&1`) {
207 208 209 210
	    if ($line =~ /compression of allocated data \((\d+) bytes\)/) {
		$usize = $1;
		next;
	    }
211 212 213
	    if ($line =~ /covered sector range: \[(\d+)-(\d+)\]/) {
		$secmin = $1;
		$secmax = $2;
214 215 216 217 218 219 220
		next;
	    }
	    if ($line =~ /(\d+) relocations covering/) {
		if ($1 > 0) {
		    $relocatable = 1;
		}
		next;
221 222 223
	    }
	}
    }
224 225 226 227 228
    if ($showsize && !defined($usize)) {
	fatal("$imagepid/$imagename: could not determine uncompressed data size ".
	      "from imagefile $imagepath\n");
    }
    if ($showrange && !defined($secmin)) {
229 230 231
	fatal("$imagepid/$imagename: could not determine sector range ".
	      "from imagefile $imagepath\n");
    }
232 233 234 235 236 237 238 239 240 241
}

if (!defined($nodeid)) {
    print STDERR "Doing stat on $imagepath\n"
	if ($debug);
    $isize = stat($imagepath)->size;
    $imtime = stat($imagepath)->mtime;
} else {
    print STDERR "Using frisbee on $imagepid/$imagename\n"
	if ($debug);
242
    $EUID = $UID;
243 244 245 246 247 248 249 250 251 252 253 254 255
    my $attr = `$frisbee -S localhost -Q $imagepid/$imagename -P $nodeid`;
    if ($attr =~ /error=0/) {
	if ($attr =~ /size=(\d+)/) {
	    $isize = $1;
	}
	if ($attr =~ /sigtype=0x1/ && $attr =~ /sig=(0x[0-9a-f]+)/) {
	    $imtime = hex($1);
	}
    } else {
	fatal("$imagepid/$imagename: access not allowed or image does not exist");
    }
}

256 257 258 259 260 261 262 263 264 265 266
if (defined($units)) {
    if ($units eq "k") {
	$isize = $isize / 1024;
	$usize = $usize / 1024;
    }
    elsif ($units eq "m") {
	$isize = $isize / (1024 * 1024);
	$usize = $usize / (1024 * 1024);
    }
}

267 268 269 270 271 272 273 274 275
if ($showall) {
    if (defined($isize)) {
	printf "size=%lu\n", $isize;
    }
    if (defined($imtime)) {
	printf "mtime=%lu\n", $imtime;
    }
} elsif ($showsize && defined($isize)) {
    printf "%lu\n", $isize;
276 277
} elsif ($showusize && defined($usize)) {
    printf "%lu\n", $usize;
278 279
} elsif ($showmtime && defined($imtime)) {
    printf "%lu\n", $imtime;
280 281 282 283
} elsif ($showrange && defined($secmin)) {
    printf "minsect=%lu\n", $secmin;
    printf "maxsect=%lu\n", $secmax;
    printf "secsize=512\n";
284
    printf "relocatable=%d\n", $relocatable;
285 286 287 288 289 290 291 292 293 294 295 296 297 298
}

exit(0);

sub fatal($)
{
    my ($mesg) = $_[0];

    exit(-1)
	if ($quiet);

    die("*** $0:\n".
	"    $mesg\n");
}