openpkg/mirror.pl

Thu, 04 Oct 2012 20:30:05 +0200

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 04 Oct 2012 20:30:05 +0200
changeset 715
c10fb90893b9
permissions
-rw-r--r--

Correct out of date build configuration, porting to Solaris 11 network
link infrastructure and new libpcap logic. This additionally allows for
device drivers in subdirectories of /dev. Correct packaged nmap
personalities and signatures to work out of the box. Finally, hack
arpd logic to properly close sockets and quit on TERM by repeating
signaling in the run command script. Sadly, all this fails to correct
the run time behaviour of honeyd which fails to bind to the IP layer.

michael@428 1 ##
michael@428 2 ## mirror.pl -- OpenPKG Package Mirroring
michael@428 3 ## Copyright (c) 2011-2012 OpenPKG GmbH <http://openpkg.com/>
michael@428 4 ##
michael@428 5 ## This software is property of the OpenPKG GmbH, DE MUC HRB 160208.
michael@428 6 ## All rights reserved. Licenses which grant limited permission to use,
michael@428 7 ## copy, modify and distribute this software are available from the
michael@428 8 ## OpenPKG GmbH.
michael@428 9 ##
michael@428 10 ## THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
michael@428 11 ## WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
michael@428 12 ## MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
michael@428 13 ## IN NO EVENT SHALL THE AUTHORS AND COPYRIGHT HOLDERS AND THEIR
michael@428 14 ## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
michael@428 15 ## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
michael@428 16 ## LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
michael@428 17 ## USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
michael@428 18 ## ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
michael@428 19 ## OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
michael@428 20 ## OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
michael@428 21 ## SUCH DAMAGE.
michael@428 22 ##
michael@428 23
michael@428 24 require 5.003;
michael@428 25
michael@428 26 # OpenPKG instance prefix and tools
michael@428 27 my $my_prefix = $ENV{'OPENPKG_PREFIX'};
michael@428 28 my $my_rpm = "$my_prefix/bin/openpkg rpm";
michael@428 29 my $my_openssl = "$my_prefix/lib/openpkg/openssl";
michael@428 30 delete $ENV{'OPENPKG_PREFIX'};
michael@428 31
michael@428 32 # program identification
michael@428 33 my $progname = "search";
michael@428 34 my $progvers = "0.1.0";
michael@428 35
michael@428 36 # determine reasonable temporary directory
michael@428 37 my $tmpdir = ($ENV{"TMPDIR"} || "/tmp");
michael@428 38
michael@428 39 # use unbuffered stdout
michael@428 40 $|++;
michael@428 41
michael@428 42 ##
michael@428 43 ## COMMAND-LINE INTERFACE HANDLING
michael@428 44 ##
michael@428 45
michael@428 46 # home-brewn getopt(3) style option parser
michael@428 47 sub getopts ($$@) {
michael@428 48 my ($opt_spec, $opts, @argv_orig) = @_;
michael@428 49 my (%optf) = map { m/(\w)/; $1 => $_ } $opt_spec =~ m/(\w:|\w)/g;
michael@428 50 my (@argv, $optarg);
michael@428 51
michael@428 52 foreach (@argv_orig) {
michael@428 53 if (@argv) {
michael@428 54 push @argv, $_;
michael@428 55 } elsif (defined $optarg) {
michael@428 56 if (exists $opts->{$optarg}) {
michael@428 57 $opts->{$optarg} .= " $_";
michael@428 58 } else {
michael@428 59 $opts->{$optarg} = $_;
michael@428 60 }
michael@428 61 $optarg = undef;
michael@428 62 } elsif (!/^[-]/) {
michael@428 63 push @argv, $_;
michael@428 64 } else {
michael@428 65 while (/^\-(\w)(.*)/) {
michael@428 66 if (exists $optf{$1}) {
michael@428 67 if (length($optf{$1}) > 1) {
michael@428 68 if ($2 ne '') {
michael@428 69 if (exists $opts->{$1}) {
michael@428 70 $opts->{$1} .= " $2";
michael@428 71 } else {
michael@428 72 $opts->{$1} = $2;
michael@428 73 }
michael@428 74 } else {
michael@428 75 $optarg = $1;
michael@428 76 }
michael@428 77 last;
michael@428 78 } else {
michael@428 79 $opts->{$1} = 1;
michael@428 80 }
michael@428 81 } else {
michael@428 82 warn "openpkg:$prog_name:WARNING: unknown option $_\n";
michael@428 83 }
michael@428 84 $_ = "-$2";
michael@428 85 }
michael@428 86 }
michael@428 87 }
michael@428 88 if (defined $optarg) {
michael@428 89 warn "openpkg:$prog_name:WARNING: option $optarg requires an argument\n";
michael@428 90 }
michael@428 91 foreach my $opt (keys %optf) {
michael@428 92 if (not exists $opts->{$opt}) {
michael@428 93 $opts->{$opt} = (length($optf{$opt}) > 1 ? "" : 0);
michael@428 94 }
michael@428 95 }
michael@428 96 return @argv;
michael@428 97 }
michael@428 98
michael@428 99 # parse command line options
michael@428 100 my $opts = {};
michael@428 101 @ARGV = getopts("hfqr:p:C:", $opts, @ARGV);
michael@428 102
michael@428 103 # usage sanity check and usage help
michael@428 104 sub usage {
michael@428 105 my ($rc) = @_;
michael@428 106 my $usage = "openpkg:$prog_name:USAGE: openpkg mirror [-h] [-f] [-C <cache-file>] [-r <remote-url>] [-p <remote-url-prefix>] <local-prefix-dir>\n";
michael@428 107 if ($rc == 0) {
michael@428 108 print STDOUT $usage;
michael@428 109 }
michael@428 110 else {
michael@428 111 print STDERR $usage;
michael@428 112 }
michael@428 113 exit($rc);
michael@428 114 }
michael@428 115 if ($opts->{"h"}) {
michael@428 116 usage(0);
michael@428 117 }
michael@428 118 if (@ARGV != 1) {
michael@428 119 usage(1);
michael@428 120 }
michael@428 121
michael@428 122 # determine arguments
michael@428 123 my ($localprefixdir) = @ARGV;
michael@428 124 if (not -d $localprefixdir) {
michael@428 125 print STDERR "openpkg:$prog_name:ERROR: local directory already has to exist: $localprefixdir\n";
michael@428 126 exit(1);
michael@428 127 }
michael@428 128
michael@428 129 # determine start URL
michael@428 130 my $url = $opts->{"r"} || "";
michael@428 131 if ($url eq "") {
michael@428 132 $url = `$my_prefix/bin/openpkg release --fmt='%u' 2>/dev/null`;
michael@428 133 $url =~ s/^\s+//s;
michael@428 134 $url =~ s/\s+$//s;
michael@428 135 }
michael@428 136 if ($url eq "") {
michael@428 137 print STDERR "openpkg:$prog_name:ERROR: no repository URL known\n";
michael@428 138 exit(1);
michael@428 139 }
michael@428 140
michael@428 141 # determine URL prefix
michael@428 142 my $remoteprefixurl = $opts->{"p"};
michael@428 143 if (not defined $remoteprefixurl) {
michael@428 144 $remoteprefixurl = $url;
michael@428 145 $remoteprefixurl =~ s/\/([^\/]+)$//s;
michael@428 146 }
michael@428 147
michael@428 148 ##
michael@428 149 ## HELPER FUNCTIONS
michael@428 150 ##
michael@428 151
michael@428 152 # determine size and mtime of a file
michael@428 153 sub sizetime ($) {
michael@428 154 my ($file) = @_;
michael@428 155 my @stat = stat($file);
michael@428 156 return ($stat[7], $stat[9]);
michael@428 157 }
michael@428 158
michael@428 159 # determine MD5 of a file
michael@428 160 sub digest ($) {
michael@428 161 my ($file) = @_;
michael@428 162 my $digest = "";
michael@428 163 my $output = `($my_openssl md5 -hex "$file") 2>/dev/null || true`;
michael@428 164 if ($output =~ m/MD5\(.+?\)=\s*([0-9a-fA-F]+)/s) {
michael@428 165 $digest = uc($1);
michael@428 166 }
michael@428 167 return $digest;
michael@428 168 }
michael@428 169
michael@428 170 # print a byte size in human-readable form
michael@428 171 sub humansize ($) {
michael@428 172 my ($bytes) = @_;
michael@428 173 my $size = "";
michael@428 174 if ($bytes > 1024*1024*1024) { $size = sprintf("%.1fGB", $bytes / (1024*1024*1024)); }
michael@428 175 elsif ($bytes > 1024*1024) { $size = sprintf("%.1fMB", $bytes / (1024*1024) ); }
michael@428 176 elsif ($bytes > 1024) { $size = sprintf("%.1fKB", $bytes / (1024) ); }
michael@428 177 else { $size = sprintf("%dB", $bytes ); }
michael@428 178 return $size;
michael@428 179 }
michael@428 180
michael@428 181 # caching support: load cache
michael@428 182 sub cache_load () {
michael@428 183 my $cache = {};
michael@428 184 if ($opts->{"C"}) {
michael@428 185 if (-f $opts->{"C"}) {
michael@428 186 # load cache file
michael@428 187 open(FP, "<" . $opts->{"C"})
michael@428 188 or die "unable to load cache file: " . $opts->{"C"};
michael@428 189 my $txt = ""; $txt .= $_ while (<FP>);
michael@428 190 close(FP);
michael@428 191
michael@428 192 # parse cache file
michael@428 193 $txt =~ s/^\s*(\d+)\s+(\d+)\s+([\da-fA-F]+)\s+(.+)$/
michael@428 194 $cache->{$4} = { size => $1, mtime => $2, md5 => $3 }, ''
michael@428 195 /mge;
michael@428 196 }
michael@428 197 }
michael@428 198 return $cache;
michael@428 199 }
michael@428 200
michael@428 201 # caching support: save cache
michael@428 202 sub cache_save ($) {
michael@428 203 my ($cache) = @_;
michael@428 204 if ($opts->{"C"}) {
michael@428 205 # generate cache file
michael@428 206 my $txt = "";
michael@428 207 foreach my $url (sort { $a cmp $b } keys %{$cache}) {
michael@428 208 $txt .= sprintf("%10d %10d %s %s\n",
michael@428 209 $cache->{$url}->{"size"},
michael@428 210 $cache->{$url}->{"mtime"},
michael@428 211 $cache->{$url}->{"md5"},
michael@428 212 $url
michael@428 213 );
michael@428 214 }
michael@428 215
michael@428 216 # write cache file
michael@428 217 open(FP, ">" . $opts->{"C"})
michael@428 218 or die "unable to save cache file: " . $opts->{"C"};
michael@428 219 print FP $txt;
michael@428 220 close(FP);
michael@428 221 }
michael@428 222 return;
michael@428 223 }
michael@428 224
michael@428 225 ##
michael@428 226 ## DETERMINE LOCAL AND REMOTE FILES
michael@428 227 ##
michael@428 228
michael@428 229 # optionally load the cache
michael@428 230 my $cache = cache_load();
michael@428 231
michael@428 232 # determine local files
michael@428 233 print "++ determining local files\n";
michael@428 234 my @local_files = map {
michael@428 235 s/\r?\n$//s;
michael@428 236 s/\/$//s;
michael@428 237 $_;
michael@428 238 } `find $localprefixdir -depth -print`;
michael@428 239 print "-- local: " . @local_files . " files\n";
michael@428 240
michael@428 241 # determine remote files
michael@428 242 print "++ determining remote files\n";
michael@428 243 sub relurl ($$) {
michael@428 244 my ($url, $suburl) = @_;
michael@428 245 if ($suburl =~ m/^\w+:\/\//) {
michael@428 246 $url = $suburl;
michael@428 247 }
michael@428 248 elsif ($suburl =~ m/^\//) {
michael@428 249 $url = "file://$suburl";
michael@428 250 }
michael@428 251 else {
michael@428 252 $url =~ s/(\/)?\/*[^\/]*$/$1$suburl/;
michael@428 253 }
michael@428 254 1 while ($url =~ s/\/\.\//\//s);
michael@428 255 1 while ($url =~ s/\/[^\/]+\/\.\.\//\//s);
michael@428 256 return $url;
michael@428 257 }
michael@428 258 sub parse_attr ($) {
michael@428 259 my ($xml) = @_;
michael@428 260 my $info = { href => "", size => -1, mtime => -1, md5 => "-" };
michael@428 261 $info->{"href"} = $1 if ($xml =~ m/\bhref="([^"]*)"/s);
michael@428 262 $info->{"size"} = $1 if ($xml =~ m/\bsize="([^"]*)"/s);
michael@428 263 $info->{"mtime"} = $1 if ($xml =~ m/\bmtime="([^"]*)"/s);
michael@428 264 $info->{"md5"} = $1 if ($xml =~ m/\bmd5="([^"]*)"/s);
michael@428 265 return $info;
michael@428 266 }
michael@428 267 sub get_urls ($) {
michael@428 268 my ($info) = @_;
michael@428 269
michael@428 270 # determine URL
michael@428 271 my $url = $info->{"href"};
michael@428 272 if ($url =~ m/^\//) {
michael@428 273 $url = "file://$url";
michael@428 274 }
michael@428 275 if ($url =~ m/\/$/) {
michael@428 276 $url .= "00INDEX.rdf";
michael@428 277 }
michael@428 278 $info->{"href"} = $url;
michael@428 279
michael@428 280 # fetch URL content
michael@428 281 print "-- fetching XML/RDF index: $url\n";
michael@428 282 my $tmpfile = "$tmpdir/openpkg-mirror.tmp";
michael@428 283 unlink($tmpfile);
michael@428 284 my $cmd = "$my_prefix/bin/openpkg curl -k -s -R -o \"$tmpfile\" \"$url\"";
michael@428 285 system($cmd);
michael@428 286 my $xml;
michael@428 287 if ($url =~ m/\.bz2$/) {
michael@428 288 $xml = `$my_prefix/lib/openpkg/bzip2 -d -c <"$tmpfile"`;
michael@428 289 }
michael@428 290 else {
michael@428 291 $xml = `cat "$tmpfile"`;
michael@428 292 }
michael@428 293
michael@428 294 # provide information about this URL
michael@428 295 my @urls = ();
michael@428 296 if ($info->{"size"} == -1 or $info->{"mtime"} == -1) {
michael@428 297 my ($S, $T) = sizetime($tmpfile);
michael@428 298 $info->{"size"} = $S;
michael@428 299 $info->{"mtime"} = $T;
michael@428 300 }
michael@428 301 if ($info->{"md5"} eq "-") {
michael@428 302 $info->{"md5"} = digest($tmpfile);
michael@428 303 }
michael@428 304 push(@urls, $info);
michael@428 305 unlink($tmpfile);
michael@428 306
michael@428 307 # parse references
michael@428 308 my @ref1 = ();
michael@428 309 my @ref2 = ();
michael@428 310 while ($xml =~ m/<Repository\s+([^>]*href=[^>]*)>/gs) {
michael@428 311 push(@ref1, $1);
michael@428 312 }
michael@428 313 while ($xml =~ m/<rdf:Description\s+([^>]*href=[^>]*)>/gs) {
michael@428 314 push(@ref2, $1);
michael@428 315 }
michael@428 316
michael@428 317 # provide information about referenced leave URLs
michael@428 318 foreach my $ref2 (@ref2) {
michael@428 319 my $i = parse_attr($ref2);
michael@428 320 $i->{"href"} = relurl($url, $i->{"href"});
michael@428 321 push(@urls, $i);
michael@428 322 }
michael@428 323
michael@428 324 # provide information about referenced index URLs
michael@428 325 foreach my $ref1 (@ref1) {
michael@428 326 my $i = parse_attr($ref1);
michael@428 327 $i->{"href"} = relurl($url, $i->{"href"});
michael@428 328 push(@urls, get_urls($i)); # RECURSION
michael@428 329 }
michael@428 330
michael@428 331 return @urls;
michael@428 332 }
michael@428 333 my @remote_urls = get_urls({ href => $url, size => -1, mtime => -1, md5 => "-" });
michael@428 334 my $size = 0;
michael@428 335 foreach my $url (@remote_urls) {
michael@428 336 $size += int($url->{"size"}) if ($url->{"size"} > 0);
michael@428 337 }
michael@428 338 print "-- remote: " . @remote_urls . " files (" . humansize($size) . ")\n";
michael@428 339
michael@428 340 ##
michael@428 341 ## DOWNLOAD NEW OR UPDATED REMOTE FILES
michael@428 342 ##
michael@428 343
michael@428 344 # fetching remote files
michael@428 345 print "++ fetching new/updated files\n";
michael@428 346 my $local_files_keep = {};
michael@428 347 sub keep ($) {
michael@428 348 my ($path) = @_;
michael@428 349 # keep path and all its parent paths
michael@428 350 while ($path ne "") {
michael@428 351 $local_files_keep->{$path} = 1;
michael@428 352 $path =~ s/(?:^\/$|\/?[^\/]+$)//s;
michael@428 353 }
michael@428 354 }
michael@428 355 sub mkdir_for_file ($$) {
michael@428 356 my ($prefix, $path) = @_;
michael@428 357 $path =~ s/(?:^\/$|\/?[^\/]+$)//s;
michael@428 358 my @dirs = split("/", $path);
michael@428 359 $path = $prefix;
michael@428 360 $path =~ s/\/$//s;
michael@428 361 foreach my $dir (@dirs) {
michael@428 362 $path .= "/$dir";
michael@428 363 if (not -d $path) {
michael@428 364 print "-- creating: $path\n";
michael@428 365 system("mkdir \"$path\"");
michael@428 366 }
michael@428 367 }
michael@428 368 }
michael@428 369 foreach my $remote_url (@remote_urls) {
michael@428 370 my $href = $remote_url->{"href"};
michael@428 371 if ($href =~ m/^\Q$remoteprefixurl\E(.+)$/) {
michael@428 372 # derive local path
michael@428 373 my $local_path = $1;
michael@428 374 my $local_file = $localprefixdir . $local_path;
michael@428 375 keep($local_file);
michael@428 376
michael@428 377 # determine information about local path
michael@428 378 my $download = 1;
michael@428 379 my $cache_update = 0;
michael@428 380 my ($size, $mtime, $md5);
michael@428 381 if (-f $local_file) {
michael@428 382 my $c = $cache->{$local_file};
michael@428 383 $size = $c->{"size"} if (defined $c and defined $c->{"size"});
michael@428 384 $mtime = $c->{"mtime"} if (defined $c and defined $c->{"mtime"});
michael@428 385 if (not defined $size or not defined $mtime) {
michael@428 386 ($size, $mtime) = sizetime($local_file);
michael@428 387 $cache_update = 1;
michael@428 388 }
michael@428 389 if ( $size == $remote_url->{"size"}
michael@428 390 and $mtime == $remote_url->{"mtime"}) {
michael@428 391 $md5 = $c->{"md5"} if (defined $c and defined $c->{"md5"});
michael@428 392 if (not defined $md5) {
michael@428 393 $md5 = digest($local_file);
michael@428 394 $cache_update = 1;
michael@428 395 }
michael@428 396 if ($md5 eq $remote_url->{"md5"}) {
michael@428 397 $download = 0;
michael@428 398 }
michael@428 399 }
michael@428 400 }
michael@428 401
michael@428 402 # optionally download remote file to local path
michael@428 403 if ($download) {
michael@428 404 print "-- fetching: $local_path\n";
michael@428 405 mkdir_for_file($localprefixdir, $local_path);
michael@428 406 my $cmd = "$my_prefix/bin/openpkg curl";
michael@428 407 $cmd .= " -s" if ( $opts->{"q"});
michael@428 408 $cmd .= " \"-#\"" if (not $opts->{"q"});
michael@428 409 $cmd .= " -k -R -o \"$local_file\" \"$href\"";
michael@428 410 system($cmd);
michael@428 411 ($size, $mtime) = sizetime($local_file);
michael@428 412 $md5 = digest($local_file);
michael@428 413 $cache_update = 1;
michael@428 414 }
michael@428 415
michael@428 416 # optionally update the size/mtime/md5 cache
michael@428 417 if ($opts->{"C"} and $cache_update) {
michael@428 418 $cache->{$local_file} = { size => $size, mtime => $mtime, md5 => $md5 };
michael@428 419 cache_save($cache);
michael@428 420 }
michael@428 421 }
michael@428 422 else {
michael@428 423 print "** WARNING: URL \"$href\" not under prefix \"$remoteprefixurl\": " .
michael@428 424 "unable to derive local path -- skipping URL\n";
michael@428 425 }
michael@428 426 }
michael@428 427
michael@428 428 ##
michael@428 429 ## REMOVE OBSOLETE LOCAL FILES
michael@428 430 ##
michael@428 431
michael@428 432 # remove obsolete files
michael@428 433 print "++ removing obsolete files\n";
michael@428 434 my $lf = @local_files;
michael@428 435 my $lfk = keys %{$local_files_keep};
michael@428 436 if (not $opts->{"f"} and ($lf - $lfk) > ($lf * 0.3)) {
michael@428 437 print "** WARNING: more than 30% of your existing local files would be removed (require option -f)\n";
michael@428 438 }
michael@428 439 else {
michael@428 440 foreach my $local_file (@local_files) {
michael@428 441 if (not exists $local_files_keep->{$local_file}) {
michael@428 442 if (-f $local_file) {
michael@428 443 print "-- removing: $local_file (file)\n";
michael@428 444 system("rm \"$local_file\" >/dev/null 2>&1 || true");
michael@428 445 }
michael@428 446 elsif (-d $local_file) {
michael@428 447 print "-- removing: $local_file (dir)\n";
michael@428 448 system("rmdir \"$local_file\" >/dev/null 2>&1 || true");
michael@428 449 }
michael@428 450 }
michael@428 451 }
michael@428 452 }
michael@428 453
michael@428 454 ##
michael@428 455 ## CLEANUP
michael@428 456 ##
michael@428 457
michael@428 458 # optionally flush the cache
michael@428 459 cache_save($cache);
michael@428 460
michael@428 461 # exit gracefully
michael@428 462 exit(0);
michael@428 463

mercurial