diff options
author | Marc Espie <espie@cvs.openbsd.org> | 2006-03-04 13:13:06 +0000 |
---|---|---|
committer | Marc Espie <espie@cvs.openbsd.org> | 2006-03-04 13:13:06 +0000 |
commit | 2671c587bc640f9caf86a61f5d0a8a8cca0eb5ca (patch) | |
tree | 90d97a87055042fa85bcae27bbf04bace8edbca4 | |
parent | 54f12c977cbe82c0a95b797c6c9971492a8e3df7 (diff) |
cut down the Locator code into maintainable chunks.
-rw-r--r-- | usr.sbin/pkg_add/Makefile | 5 | ||||
-rw-r--r-- | usr.sbin/pkg_add/OpenBSD/PackageLocation.pm | 316 | ||||
-rw-r--r-- | usr.sbin/pkg_add/OpenBSD/PackageLocator.pm | 1011 | ||||
-rw-r--r-- | usr.sbin/pkg_add/OpenBSD/PackageRepository.pm | 650 | ||||
-rw-r--r-- | usr.sbin/pkg_add/OpenBSD/PackageRepositoryList.pm | 104 |
5 files changed, 1078 insertions, 1008 deletions
diff --git a/usr.sbin/pkg_add/Makefile b/usr.sbin/pkg_add/Makefile index dcb1a289832..784174f0905 100644 --- a/usr.sbin/pkg_add/Makefile +++ b/usr.sbin/pkg_add/Makefile @@ -1,4 +1,4 @@ -# $OpenBSD: Makefile,v 1.34 2006/03/04 11:31:18 espie Exp $ +# $OpenBSD: Makefile,v 1.35 2006/03/04 13:13:05 espie Exp $ .include <bsd.own.mk> @@ -21,8 +21,11 @@ PACKAGES= \ OpenBSD/Interactive.pm \ OpenBSD/Mtree.pm \ OpenBSD/PackageInfo.pm \ + OpenBSD/PackageLocation.pm \ OpenBSD/PackageLocator.pm \ OpenBSD/PackageName.pm \ + OpenBSD/PackageRepository.pm \ + OpenBSD/PackageRepositoryList.pm \ OpenBSD/PackingElement.pm \ OpenBSD/PackingList.pm \ OpenBSD/PackingOld.pm \ diff --git a/usr.sbin/pkg_add/OpenBSD/PackageLocation.pm b/usr.sbin/pkg_add/OpenBSD/PackageLocation.pm new file mode 100644 index 00000000000..97920b98bfd --- /dev/null +++ b/usr.sbin/pkg_add/OpenBSD/PackageLocation.pm @@ -0,0 +1,316 @@ +# ex:ts=8 sw=4: +# $OpenBSD: PackageLocation.pm,v 1.1 2006/03/04 13:13:05 espie Exp $ +# +# Copyright (c) 2003-2004 Marc Espie <espie@openbsd.org> +# +# Permission to use, copy, modify, and distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +use strict; +use warnings; + +package OpenBSD::PackageLocation; + +use OpenBSD::PackageInfo; +use OpenBSD::Temp; + +sub new +{ + my ($class, $repository, $name) = @_; + my $self = { repository => $repository, name => $name}; + bless $self, $class; +} + +sub openArchive +{ + my $self = shift; + + my $fh = $self->{repository}->open($self); + if (!defined $fh) { + $self->{repository}->parse_problems($self->{errors}) + if defined $self->{errors}; + undef $self->{errors}; + return undef; + } + require OpenBSD::Ustar; + + my $archive = new OpenBSD::Ustar $fh; + $self->{_archive} = $archive; +} + +sub grabInfoFiles +{ + my $self = shift; + my $dir = $self->{dir} = OpenBSD::Temp::dir(); + + if (defined $self->{contents} && ! -f $dir.CONTENTS) { + open my $fh, '>', $dir.CONTENTS or die "Permission denied"; + print $fh $self->{contents}; + close $fh; + } + + while (my $e = $self->intNext()) { + if ($e->isFile() && is_info_name($e->{name})) { + $e->{name}=$dir.$e->{name}; + eval { $e->create(); }; + if ($@) { + unlink($e->{name}); + $@ =~ s/\s+at.*//; + print STDERR $@; + return 0; + } + } else { + $self->unput(); + last; + } + } + return 1; +} + +sub scanPackage +{ + my $self = shift; + while (my $e = $self->intNext()) { + if ($e->isFile() && is_info_name($e->{name})) { + if ($e->{name} eq CONTENTS && !defined $self->{dir}) { + $self->{contents} = $e->contents(); + last; + } + if (!defined $self->{dir}) { + $self->{dir} = OpenBSD::Temp::dir(); + } + $e->{name}=$self->{dir}.$e->{name}; + eval { $e->create(); }; + if ($@) { + unlink($e->{name}); + $@ =~ s/\s+at.*//; + print STDERR $@; + return 0; + } + } else { + $self->unput(); + last; + } + } + return 1; +} + +sub grabPlist +{ + my ($self, $pkgname, $arch, $code) = @_; + + my $pkg = $self->openPackage($pkgname, $arch); + if (defined $pkg) { + my $plist = $self->plist($code); + $pkg->wipe_info(); + $pkg->close_now(); + return $plist; + } else { + return undef; + } +} + +sub openPackage +{ + my ($self, $pkgname, $arch) = @_; + if (!$self->openArchive()) { + return undef; + } + $self->scanPackage(); + + if (defined $self->{contents}) { + return $self; + } + + # maybe it's a fat package. + while (my $e = $self->intNext()) { + unless ($e->{name} =~ m/\/\+CONTENTS$/) { + last; + } + my $prefix = $`; + my $contents = $e->contents(); + require OpenBSD::PackingList; + + $pkgname =~ s/\.tgz$//; + + my $plist = OpenBSD::PackingList->fromfile(\$contents, + \&OpenBSD::PackingList::FatOnly); + next if defined $pkgname and $plist->pkgname() ne $pkgname; + if ($plist->has('arch')) { + if ($plist->{arch}->check($arch)) { + $self->{filter} = $prefix; + bless $self, "OpenBSD::FatPackageLocation"; + $self->{contents} = $contents; + return $self; + } + } + } + # hopeless + $self->close_with_client_error(); + $self->wipe_info(); + return undef; +} + +sub wipe_info +{ + my $self = shift; + $self->{repository}->wipe_info($self); +} + +sub info +{ + my $self = shift; + if (!defined $self->{dir}) { + $self->grabInfoFiles(); + } + return $self->{dir}; +} + +sub plist +{ + my ($self, $code) = @_; + + require OpenBSD::PackingList; + + if (defined $self->{contents}) { + my $value = $self->{contents}; + return OpenBSD::PackingList->fromfile(\$value, $code); + } elsif (defined $self->{dir} && -f $self->{dir}.CONTENTS) { + return OpenBSD::PackingList->fromfile($self->{dir}.CONTENTS, + $code); + } + # hopeless + $self->close_with_client_error(); + + return undef; +} + +sub close +{ + my ($self, $hint) = @_; + $self->{repository}->close($self, $hint); +} + +sub finish_and_close +{ + my $self = shift; + $self->{repository}->finish_and_close($self); +} + +sub close_now +{ + my $self = shift; + $self->{repository}->close_now($self); +} + +sub close_after_error +{ + my $self = shift; + $self->{repository}->close_after_error($self); +} + +sub close_with_client_error +{ + my $self = shift; + $self->{repository}->close_with_client_error($self); +} + +sub deref +{ + my $self = shift; + $self->{fh} = undef; + $self->{_archive} = undef; +} + +sub reopen +{ + my $self = shift; + if (!$self->openArchive()) { + return undef; + } + while (my $e = $self->{_archive}->next()) { + if ($e->{name} eq $self->{_current}->{name}) { + $self->{_current} = $e; + return $self; + } + } + return undef; +} + +# proxy for archive operations +sub next +{ + my $self = shift; + + if (!defined $self->{dir}) { + $self->grabInfoFiles(); + } + return $self->intNext(); +} + +sub intNext +{ + my $self = shift; + + if (!defined $self->{fh}) { + if (!$self->reopen()) { + return undef; + } + } + if (!$self->{_unput}) { + $self->{_current} = $self->getNext(); + } + $self->{_unput} = 0; + return $self->{_current}; +} + +sub unput +{ + my $self = shift; + $self->{_unput} = 1; +} + +sub getNext +{ + my $self = shift; + + return $self->{_archive}->next(); +} + +sub skip +{ + my $self = shift; + return $self->{_archive}->skip(); +} + +package OpenBSD::FatPackageLocation; +our @ISA=qw(OpenBSD::PackageLocation); + +sub getNext +{ + my $self = shift; + + my $e = $self->SUPER::getNext(); + if ($e->{name} =~ m/^(.*?)\/(.*)$/) { + my ($beg, $name) = ($1, $2); + if (index($beg, $self->{filter}) == -1) { + return $self->next(); + } + $e->{name} = $name; + if ($e->isHardLink()) { + $e->{linkname} =~ s/^(.*?)\///; + } + } + return $e; +} + +1; diff --git a/usr.sbin/pkg_add/OpenBSD/PackageLocator.pm b/usr.sbin/pkg_add/OpenBSD/PackageLocator.pm index 1ad3fbd537f..496e1e6d9c0 100644 --- a/usr.sbin/pkg_add/OpenBSD/PackageLocator.pm +++ b/usr.sbin/pkg_add/OpenBSD/PackageLocator.pm @@ -1,5 +1,5 @@ # ex:ts=8 sw=4: -# $OpenBSD: PackageLocator.pm,v 1.51 2006/03/04 11:28:03 espie Exp $ +# $OpenBSD: PackageLocator.pm,v 1.52 2006/03/04 13:13:05 espie Exp $ # # Copyright (c) 2003-2004 Marc Espie <espie@openbsd.org> # @@ -18,1014 +18,11 @@ use strict; use warnings; -package OpenBSD::PackageRepository; - -sub _new -{ - my ($class, $address) = @_; - bless { baseurl => $address }, $class; -} - -sub new -{ - my ($class, $baseurl) = @_; - if ($baseurl =~ m/^ftp\:/i) { - return OpenBSD::PackageRepository::FTP->_new($baseurl); - } elsif ($baseurl =~ m/^http\:/i) { - return OpenBSD::PackageRepository::HTTP->_new($baseurl); - } elsif ($baseurl =~ m/^scp\:/i) { - return OpenBSD::PackageRepository::SCP->_new($baseurl); - } elsif ($baseurl =~ m/src\:/i) { - return OpenBSD::PackageRepository::Source->_new($baseurl); - } else { - return OpenBSD::PackageRepository::Local->_new($baseurl); - } -} - -sub available -{ - my $self = shift; - - return @{$self->list()}; -} - -sub wipe_info -{ - my ($self, $pkg) = @_; - - require File::Path; - - my $dir = $pkg->{dir}; - if (defined $dir) { - - File::Path::rmtree($dir); - delete $pkg->{dir}; - } -} - -# by default, all objects may exist -sub may_exist -{ - return 1; -} - -# by default, we don't track opened files for this key - -sub opened -{ - undef; -} - -# hint: 0 premature close, 1 real error. undef, normal ! - -sub close -{ - my ($self, $object, $hint) = @_; - close($object->{fh}) if defined $object->{fh}; - $self->parse_problems($object->{errors}, $hint) - if defined $object->{errors}; - undef $object->{errors}; - $object->deref(); -} - -sub finish_and_close -{ - my ($self, $object) = @_; - $self->close($object); -} - -sub close_now -{ - my ($self, $object) = @_; - $self->close($object, 0); -} - -sub close_after_error -{ - my ($self, $object) = @_; - $self->close($object, 1); -} - -sub close_with_client_error -{ - my ($self, $object) = @_; - $self->close($object, 1); -} - -sub make_room -{ - my $self = shift; - - # kill old files if too many - my $already = $self->opened(); - if (defined $already) { - # gc old objects - if (@$already >= $self->maxcount()) { - @$already = grep { defined $_->{fh} } @$already; - } - while (@$already >= $self->maxcount()) { - my $o = shift @$already; - $self->close_now($o); - } - } - return $already; -} - -# open method that tracks opened files per-host. -sub open -{ - my ($self, $object) = @_; - - return undef unless $self->may_exist($object->{name}); - - # kill old files if too many - my $already = $self->make_room(); - my $fh = $self->open_pipe($object); - if (!defined $fh) { - return undef; - } - $object->{fh} = $fh; - if (defined $already) { - push @$already, $object; - } - return $fh; -} - -sub find -{ - my ($repository, $name, $arch, $srcpath) = @_; - $name.=".tgz" unless $name =~ m/\.tgz$/; - my $self = OpenBSD::PackageLocation->new($repository, $name); - - return $self->openPackage($name, $arch); -} - -sub grabPlist -{ - my ($repository, $name, $arch, $code) = @_; - $name.=".tgz" unless $name =~ m/\.tgz$/; - my $self = OpenBSD::PackageLocation->new($repository, $name); - - return $self->grabPlist($name, $arch, $code); -} - -sub parse_problems -{ - my ($self, $filename, $hint) = @_; - CORE::open(my $fh, '<', $filename) or return; - - my $baseurl = $self->{baseurl}; - local $_; - my $notyet = 1; - while(<$fh>) { - next if m/^(?:200|220|221|226|229|230|227|250|331|500|150)[\s\-]/; - next if m/^EPSV command not understood/; - next if m/^Trying [\da-f\.\:]+\.\.\./; - next if m/^Requesting \Q$baseurl\E/; - next if m/^Remote system type is\s+/; - next if m/^Connected to\s+/; - next if m/^remote\:\s+/; - next if m/^Using binary mode to transfer files/; - next if m/^Retrieving\s+/; - next if m/^Succesfully retrieved file/; - next if m/^\d+\s+bytes\s+received\s+in/; - next if m/^ftp: connect to address.*: No route to host/; - - if (defined $hint && $hint == 0) { - next if m/^ftp: -: short write/; - next if m/^421\s+/; - } - if ($notyet) { - print STDERR "Error from $baseurl:\n" if $notyet; - $notyet = 0; - } - if (m/^421\s+/ || - m/^ftp: connect: Connection timed out/ || - m/^ftp: Can't connect or login to host/) { - $self->{lasterror} = 421; - } - if (m/^550\s+/) { - $self->{lasterror} = 550; - } - print STDERR $_; - } - CORE::close($fh); - unlink $filename; -} - -package OpenBSD::PackageRepository::Installed; -our @ISA=qw(OpenBSD::PackageRepository); -use OpenBSD::PackageInfo; - -sub new -{ - bless {}, shift; -} - -sub find -{ - my ($repository, $name, $arch, $srcpath) = @_; - my $self; - - if (is_installed($name)) { - $self = OpenBSD::PackageLocation->new($repository, $name); - $self->{dir} = installed_info($name); - } - return $self; -} - -sub grabPlist -{ - my ($repository, $name, $arch, $code) = @_; - require OpenBSD::PackingList; - return OpenBSD::PackingList->from_installation($name, $code); -} - -sub available -{ - return installed_packages(); -} - -sub list -{ - my @list = installed_packages(); - return \@list; -} - -sub wipe_info -{ -} - -sub may_exist -{ - my ($self, $name) = @_; - return is_installed($name); -} - -package PackageRepository::Source; - -sub find -{ - my ($repository, $name, $arch, $srcpath) = @_; - my $dir; - my $make; - if (defined $ENV{'MAKE'}) { - $make = $ENV{'MAKE'}; - } else { - $make = '/usr/bin/make'; - } - if (defined $repository->{baseurl} && $repository->{baseurl} ne '') { - $dir = $repository->{baseurl} - } elsif (defined $ENV{PORTSDIR}) { - $dir = $ENV{PORTSDIR}; - } else { - $dir = '/usr/ports'; - } - # figure out the repository name and the pkgname - my $pkgfile = `cd $dir && SUBDIR=$srcpath ECHO_MSG=: $make show=PKGFILE`; - chomp $pkgfile; - if (! -f $pkgfile) { - system "cd $dir && SUBDIR=$srcpath $make package BULK=Yes"; - } - if (! -f $pkgfile) { - return undef; - } - $pkgfile =~ m|(.*/)([^/]*)|; - my ($base, $fname) = ($1, $2); - - my $repo = OpenBSD::PackageRepository::Local->_new($base); - return $repo->find($fname); -} - -package OpenBSD::PackageRepository::Local; -our @ISA=qw(OpenBSD::PackageRepository); - -sub open_pipe -{ - my ($self, $object) = @_; - my $pid = open(my $fh, "-|"); - if (!defined $pid) { - die "Cannot fork: $!"; - } - if ($pid) { - return $fh; - } else { - open STDERR, ">/dev/null"; - exec {"/usr/bin/gzip"} - "gzip", - "-d", - "-c", - "-q", - "-f", - $self->{baseurl}.$object->{name} - or die "Can't run gzip"; - } -} - -sub may_exist -{ - my ($self, $name) = @_; - return -r $self->{baseurl}.$name; -} - -sub list -{ - my $self = shift; - my $l = []; - my $dname = $self->{baseurl}; - opendir(my $dir, $dname) or return $l; - while (my $e = readdir $dir) { - next unless $e =~ m/\.tgz$/; - next unless -f "$dname/$e"; - push(@$l, $`); - } - close($dir); - return $l; -} - -package OpenBSD::PackageRepository::Local::Pipe; -our @ISA=qw(OpenBSD::PackageRepository::Local); - -sub may_exist -{ - return 1; -} - -sub open_pipe -{ - my ($self, $object) = @_; - my $fullname = $self->{baseurl}.$object->{name}; - my $pid = open(my $fh, "-|"); - if (!defined $pid) { - die "Cannot fork: $!"; - } - if ($pid) { - return $fh; - } else { - open STDERR, ">/dev/null"; - exec {"/usr/bin/gzip"} - "gzip", - "-d", - "-c", - "-q", - "-f", - "-" - or die "can't run gzip"; - } -} - -package OpenBSD::PackageRepository::Distant; -our @ISA=qw(OpenBSD::PackageRepository); - -my $buffsize = 2 * 1024 * 1024; - -sub pkg_copy -{ - my ($in, $dir, $name) = @_; - - require File::Temp; - my $template = $name; - $template =~ s/\.tgz$/.XXXXXXXX/; - - my ($copy, $filename) = File::Temp::tempfile($template, - DIR => $dir) or die "Can't write copy to cache"; - chmod 0644, $filename; - my $handler = sub { - my ($sig) = @_; - unlink $filename; - $SIG{$sig} = 'DEFAULT'; - kill $sig, $$; - }; - - my $nonempty = 0; - { - - local $SIG{'PIPE'} = $handler; - local $SIG{'INT'} = $handler; - local $SIG{'HUP'} = $handler; - local $SIG{'QUIT'} = $handler; - local $SIG{'KILL'} = $handler; - local $SIG{'TERM'} = $handler; - - my ($buffer, $n); - # copy stuff over - do { - $n = sysread($in, $buffer, $buffsize); - if (!defined $n) { - die "Error reading\n"; - } - if ($n > 0) { - $nonempty = 1; - } - syswrite $copy, $buffer; - syswrite STDOUT, $buffer; - } while ($n != 0); - close($copy); - } - - if ($nonempty) { - rename $filename, "$dir/$name"; - } else { - unlink $filename; - } -} - -sub open_pipe -{ - require OpenBSD::Temp; - - my ($self, $object) = @_; - $object->{errors} = OpenBSD::Temp::file(); - $object->{cache_dir} = $ENV{'PKG_CACHE'}; - my $pid = open(my $fh, "-|"); - if (!defined $pid) { - die "Cannot fork: $!"; - } - if ($pid) { - $object->{pid} = $pid; - return $fh; - } else { - open STDERR, '>', $object->{errors}; - - my $pid2 = open(STDIN, "-|"); - - if (!defined $pid2) { - die "Cannot fork: $!"; - } - if ($pid2) { - exec {"/usr/bin/gzip"} - "gzip", - "-d", - "-c", - "-q", - "-" - or die "can't run gzip"; - } else { - if (defined $object->{cache_dir}) { - my $pid3 = open(my $in, "-|"); - if (!defined $pid3) { - die "Cannot fork: $!"; - } - if ($pid3) { - pkg_copy($in, $object->{cache_dir}, - $object->{name}); - exit(0); - } else { - $self->grab_object($object); - } - } else { - $self->grab_object($object); - } - } - } -} - -sub _list -{ - my ($self, $cmd) = @_; - my $l =[]; - local $_; - open(my $fh, '-|', "$cmd") or return undef; - while(<$fh>) { - chomp; - next if m/^d.*\s+\S/; - next unless m/([^\s]+)\.tgz\s*$/; - push(@$l, $1); - } - close($fh); - return $l; -} - -sub finish_and_close -{ - my ($self, $object) = @_; - if (defined $object->{cache_dir}) { - while (defined $object->intNext()) { - } - } - $self->SUPER::finish_and_close($object); -} - -package OpenBSD::PackageRepository::SCP; -our @ISA=qw(OpenBSD::PackageRepository::Distant); - - -sub grab_object -{ - my ($self, $object) = @_; - - exec {"/usr/bin/scp"} - "scp", - $self->{host}.":".$self->{path}.$object->{name}, - "/dev/stdout" - or die "can't run scp"; -} - -our %distant = (); - -sub maxcount -{ - return 2; -} - -sub opened -{ - my $self = $_[0]; - my $k = $self->{key}; - if (!defined $distant{$k}) { - $distant{$k} = []; - } - return $distant{$k}; -} - -sub _new -{ - my ($class, $baseurl) = @_; - $baseurl =~ s/scp\:\/\///i; - $baseurl =~ m/\//; - bless { host => $`, key => $`, path => "/$'" }, $class; -} - -sub list -{ - my ($self) = @_; - if (!defined $self->{list}) { - my $host = $self->{host}; - my $path = $self->{path}; - $self->{list} = $self->_list("ssh $host ls -l $path"); - } - return $self->{list}; -} - -package OpenBSD::PackageRepository::HTTPorFTP; -our @ISA=qw(OpenBSD::PackageRepository::Distant); - -our %distant = (); - - -sub grab_object -{ - my ($self, $object) = @_; - my $ftp = defined $ENV{'FETCH_CMD'} ? $ENV{'FETCH_CMD'} : "/usr/bin/ftp"; - exec {$ftp} - "ftp", - "-o", - "-", $self->{baseurl}.$object->{name} - or die "can't run ftp"; -} - -sub maxcount -{ - return 1; -} - -sub opened -{ - my $self = $_[0]; - my $k = $self->{key}; - if (!defined $distant{$k}) { - $distant{$k} = []; - } - return $distant{$k}; -} - -sub _new -{ - my ($class, $baseurl) = @_; - my $distant_host; - if ($baseurl =~ m/^(http|ftp)\:\/\/(.*?)\//i) { - $distant_host = $&; - } - bless { baseurl => $baseurl, key => $distant_host }, $class; -} - - -package OpenBSD::PackageRepository::HTTP; -our @ISA=qw(OpenBSD::PackageRepository::HTTPorFTP); - -sub list -{ - my ($self) = @_; - if (!defined $self->{list}) { - my $error = OpenBSD::Temp::file(); - $self->make_room(); - my $fullname = $self->{baseurl}; - my $l = $self->{list} = []; - local $_; - open(my $fh, '-|', "ftp -o - $fullname 2>$error") or return undef; - # XXX assumes a pkg HREF won't cross a line. Is this the case ? - while(<$fh>) { - chomp; - for my $pkg (m/\<A\s+HREF=\"(.*?)\.tgz\"\>/gi) { - next if $pkg =~ m|/|; - push(@$l, $pkg); - } - } - close($fh); - $self->parse_problems($error); - } - return $self->{list}; -} - -package OpenBSD::PackageRepository::FTP; -our @ISA=qw(OpenBSD::PackageRepository::HTTPorFTP); - - -sub list -{ - my ($self) = @_; - if (!defined $self->{list}) { - require OpenBSD::Temp; - - my $error = OpenBSD::Temp::file(); - $self->make_room(); - my $fullname = $self->{baseurl}; - $self->{list} = $self->_list("echo 'nlist *.tgz'|ftp -o - $fullname 2>$error"); - $self->parse_problems($error); - } - return $self->{list}; -} - -package OpenBSD::PackageLocation; - -use OpenBSD::PackageInfo; -use OpenBSD::Temp; - -sub new -{ - my ($class, $repository, $name) = @_; - my $self = { repository => $repository, name => $name}; - bless $self, $class; -} - -sub openArchive -{ - my $self = shift; - - my $fh = $self->{repository}->open($self); - if (!defined $fh) { - $self->{repository}->parse_problems($self->{errors}) - if defined $self->{errors}; - undef $self->{errors}; - return undef; - } - require OpenBSD::Ustar; - - my $archive = new OpenBSD::Ustar $fh; - $self->{_archive} = $archive; -} - -sub grabInfoFiles -{ - my $self = shift; - my $dir = $self->{dir} = OpenBSD::Temp::dir(); - - if (defined $self->{contents} && ! -f $dir.CONTENTS) { - open my $fh, '>', $dir.CONTENTS or die "Permission denied"; - print $fh $self->{contents}; - close $fh; - } - - while (my $e = $self->intNext()) { - if ($e->isFile() && is_info_name($e->{name})) { - $e->{name}=$dir.$e->{name}; - eval { $e->create(); }; - if ($@) { - unlink($e->{name}); - $@ =~ s/\s+at.*//; - print STDERR $@; - return 0; - } - } else { - $self->unput(); - last; - } - } - return 1; -} - -sub scanPackage -{ - my $self = shift; - while (my $e = $self->intNext()) { - if ($e->isFile() && is_info_name($e->{name})) { - if ($e->{name} eq CONTENTS && !defined $self->{dir}) { - $self->{contents} = $e->contents(); - last; - } - if (!defined $self->{dir}) { - $self->{dir} = OpenBSD::Temp::dir(); - } - $e->{name}=$self->{dir}.$e->{name}; - eval { $e->create(); }; - if ($@) { - unlink($e->{name}); - $@ =~ s/\s+at.*//; - print STDERR $@; - return 0; - } - } else { - $self->unput(); - last; - } - } - return 1; -} - -sub grabPlist -{ - my ($self, $pkgname, $arch, $code) = @_; - - my $pkg = $self->openPackage($pkgname, $arch); - if (defined $pkg) { - my $plist = $self->plist($code); - $pkg->wipe_info(); - $pkg->close_now(); - return $plist; - } else { - return undef; - } -} - -sub openPackage -{ - my ($self, $pkgname, $arch) = @_; - if (!$self->openArchive()) { - return undef; - } - $self->scanPackage(); - - if (defined $self->{contents}) { - return $self; - } - - # maybe it's a fat package. - while (my $e = $self->intNext()) { - unless ($e->{name} =~ m/\/\+CONTENTS$/) { - last; - } - my $prefix = $`; - my $contents = $e->contents(); - require OpenBSD::PackingList; - - $pkgname =~ s/\.tgz$//; - - my $plist = OpenBSD::PackingList->fromfile(\$contents, - \&OpenBSD::PackingList::FatOnly); - next if defined $pkgname and $plist->pkgname() ne $pkgname; - if ($plist->has('arch')) { - if ($plist->{arch}->check($arch)) { - $self->{filter} = $prefix; - bless $self, "OpenBSD::FatPackageLocation"; - $self->{contents} = $contents; - return $self; - } - } - } - # hopeless - $self->close_with_client_error(); - $self->wipe_info(); - return undef; -} - -sub wipe_info -{ - my $self = shift; - $self->{repository}->wipe_info($self); -} - -sub info -{ - my $self = shift; - if (!defined $self->{dir}) { - $self->grabInfoFiles(); - } - return $self->{dir}; -} - -sub plist -{ - my ($self, $code) = @_; - - require OpenBSD::PackingList; - - if (defined $self->{contents}) { - my $value = $self->{contents}; - return OpenBSD::PackingList->fromfile(\$value, $code); - } elsif (defined $self->{dir} && -f $self->{dir}.CONTENTS) { - return OpenBSD::PackingList->fromfile($self->{dir}.CONTENTS, - $code); - } - # hopeless - $self->close_with_client_error(); - - return undef; -} - -sub close -{ - my ($self, $hint) = @_; - $self->{repository}->close($self, $hint); -} - -sub finish_and_close -{ - my $self = shift; - $self->{repository}->finish_and_close($self); -} - -sub close_now -{ - my $self = shift; - $self->{repository}->close_now($self); -} - -sub close_after_error -{ - my $self = shift; - $self->{repository}->close_after_error($self); -} - -sub close_with_client_error -{ - my $self = shift; - $self->{repository}->close_with_client_error($self); -} - -sub deref -{ - my $self = shift; - $self->{fh} = undef; - $self->{_archive} = undef; -} - -sub reopen -{ - my $self = shift; - if (!$self->openArchive()) { - return undef; - } - while (my $e = $self->{_archive}->next()) { - if ($e->{name} eq $self->{_current}->{name}) { - $self->{_current} = $e; - return $self; - } - } - return undef; -} - -# proxy for archive operations -sub next -{ - my $self = shift; - - if (!defined $self->{dir}) { - $self->grabInfoFiles(); - } - return $self->intNext(); -} - -sub intNext -{ - my $self = shift; - - if (!defined $self->{fh}) { - if (!$self->reopen()) { - return undef; - } - } - if (!$self->{_unput}) { - $self->{_current} = $self->getNext(); - } - $self->{_unput} = 0; - return $self->{_current}; -} - -sub unput -{ - my $self = shift; - $self->{_unput} = 1; -} - -sub getNext -{ - my $self = shift; - - return $self->{_archive}->next(); -} - -sub skip -{ - my $self = shift; - return $self->{_archive}->skip(); -} - -package OpenBSD::FatPackageLocation; -our @ISA=qw(OpenBSD::PackageLocation); - -sub getNext -{ - my $self = shift; - - my $e = $self->SUPER::getNext(); - if ($e->{name} =~ m/^(.*?)\/(.*)$/) { - my ($beg, $name) = ($1, $2); - if (index($beg, $self->{filter}) == -1) { - return $self->next(); - } - $e->{name} = $name; - if ($e->isHardLink()) { - $e->{linkname} =~ s/^(.*?)\///; - } - } - return $e; -} - -package OpenBSD::PackageRepositoryList; - -sub new -{ - my $class = shift; - return bless {list => [], avail => undef }, $class; -} - -sub add -{ - my $self = shift; - push @{$self->{list}}, @_; - if (@_ > 0) { - $self->{avail} = undef; - } -} - -sub find -{ - my ($self, $pkgname, $arch, $srcpath) = @_; - - for my $repo (@{$self->{list}}) { - my $pkg; - - for (my $retry = 5; $retry < 60; $retry *= 2) { - undef $repo->{lasterror}; - $pkg = $repo->find($pkgname, $arch, $srcpath); - if (!defined $pkg && defined $repo->{lasterror} && - $repo->{lasterror} == 421 && - defined $self->{avail} && - $self->{avail}->{$pkgname} eq $repo) { - print STDERR "Temporary error, sleeping $retry seconds\n"; - sleep($retry); - } else { - last; - } - } - return $pkg if defined $pkg; - } - return undef; -} - -sub grabPlist -{ - my ($self, $pkgname, $arch, $code) = @_; - - for my $repo (@{$self->{list}}) { - my $plist; - - for (my $retry = 5; $retry < 60; $retry *= 2) { - undef $repo->{lasterror}; - $plist = $repo->grabPlist($pkgname, $arch, $code); - if (!defined $plist && defined $repo->{lasterror} && - $repo->{lasterror} == 421 && - defined $self->{avail} && - $self->{avail}->{$pkgname} eq $repo) { - print STDERR "Temporary error, sleeping $retry seconds\n"; - sleep($retry); - } else { - last; - } - } - return $plist if defined $plist; - } - return undef; -} - -sub available -{ - my $self = shift; - - if (!defined $self->{avail}) { - my $available_packages = {}; - foreach my $loc (reverse @{$self->{list}}) { - foreach my $pkg (@{$loc->list()}) { - $available_packages->{$pkg} = $loc; - } - } - $self->{avail} = $available_packages; - } - return keys %{$self->{avail}}; -} - package OpenBSD::PackageLocator; +use OpenBSD::PackageRepositoryList; +use OpenBSD::PackageRepository; + # this returns an archive handle from an uninstalled package name, currently # There is a cache available. diff --git a/usr.sbin/pkg_add/OpenBSD/PackageRepository.pm b/usr.sbin/pkg_add/OpenBSD/PackageRepository.pm new file mode 100644 index 00000000000..a76f6172fbb --- /dev/null +++ b/usr.sbin/pkg_add/OpenBSD/PackageRepository.pm @@ -0,0 +1,650 @@ +# ex:ts=8 sw=4: +# $OpenBSD: PackageRepository.pm,v 1.1 2006/03/04 13:13:05 espie Exp $ +# +# Copyright (c) 2003-2004 Marc Espie <espie@openbsd.org> +# +# Permission to use, copy, modify, and distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +use strict; +use warnings; + +package OpenBSD::PackageRepository; +use OpenBSD::PackageLocation; + +sub _new +{ + my ($class, $address) = @_; + bless { baseurl => $address }, $class; +} + +sub new +{ + my ($class, $baseurl) = @_; + if ($baseurl =~ m/^ftp\:/i) { + return OpenBSD::PackageRepository::FTP->_new($baseurl); + } elsif ($baseurl =~ m/^http\:/i) { + return OpenBSD::PackageRepository::HTTP->_new($baseurl); + } elsif ($baseurl =~ m/^scp\:/i) { + return OpenBSD::PackageRepository::SCP->_new($baseurl); + } elsif ($baseurl =~ m/src\:/i) { + return OpenBSD::PackageRepository::Source->_new($baseurl); + } else { + return OpenBSD::PackageRepository::Local->_new($baseurl); + } +} + +sub available +{ + my $self = shift; + + return @{$self->list()}; +} + +sub wipe_info +{ + my ($self, $pkg) = @_; + + require File::Path; + + my $dir = $pkg->{dir}; + if (defined $dir) { + + File::Path::rmtree($dir); + delete $pkg->{dir}; + } +} + +# by default, all objects may exist +sub may_exist +{ + return 1; +} + +# by default, we don't track opened files for this key + +sub opened +{ + undef; +} + +# hint: 0 premature close, 1 real error. undef, normal ! + +sub close +{ + my ($self, $object, $hint) = @_; + close($object->{fh}) if defined $object->{fh}; + $self->parse_problems($object->{errors}, $hint) + if defined $object->{errors}; + undef $object->{errors}; + $object->deref(); +} + +sub finish_and_close +{ + my ($self, $object) = @_; + $self->close($object); +} + +sub close_now +{ + my ($self, $object) = @_; + $self->close($object, 0); +} + +sub close_after_error +{ + my ($self, $object) = @_; + $self->close($object, 1); +} + +sub close_with_client_error +{ + my ($self, $object) = @_; + $self->close($object, 1); +} + +sub make_room +{ + my $self = shift; + + # kill old files if too many + my $already = $self->opened(); + if (defined $already) { + # gc old objects + if (@$already >= $self->maxcount()) { + @$already = grep { defined $_->{fh} } @$already; + } + while (@$already >= $self->maxcount()) { + my $o = shift @$already; + $self->close_now($o); + } + } + return $already; +} + +# open method that tracks opened files per-host. +sub open +{ + my ($self, $object) = @_; + + return undef unless $self->may_exist($object->{name}); + + # kill old files if too many + my $already = $self->make_room(); + my $fh = $self->open_pipe($object); + if (!defined $fh) { + return undef; + } + $object->{fh} = $fh; + if (defined $already) { + push @$already, $object; + } + return $fh; +} + +sub find +{ + my ($repository, $name, $arch, $srcpath) = @_; + $name.=".tgz" unless $name =~ m/\.tgz$/; + my $self = OpenBSD::PackageLocation->new($repository, $name); + + return $self->openPackage($name, $arch); +} + +sub grabPlist +{ + my ($repository, $name, $arch, $code) = @_; + $name.=".tgz" unless $name =~ m/\.tgz$/; + my $self = OpenBSD::PackageLocation->new($repository, $name); + + return $self->grabPlist($name, $arch, $code); +} + +sub parse_problems +{ + my ($self, $filename, $hint) = @_; + CORE::open(my $fh, '<', $filename) or return; + + my $baseurl = $self->{baseurl}; + local $_; + my $notyet = 1; + while(<$fh>) { + next if m/^(?:200|220|221|226|229|230|227|250|331|500|150)[\s\-]/; + next if m/^EPSV command not understood/; + next if m/^Trying [\da-f\.\:]+\.\.\./; + next if m/^Requesting \Q$baseurl\E/; + next if m/^Remote system type is\s+/; + next if m/^Connected to\s+/; + next if m/^remote\:\s+/; + next if m/^Using binary mode to transfer files/; + next if m/^Retrieving\s+/; + next if m/^Succesfully retrieved file/; + next if m/^\d+\s+bytes\s+received\s+in/; + next if m/^ftp: connect to address.*: No route to host/; + + if (defined $hint && $hint == 0) { + next if m/^ftp: -: short write/; + next if m/^421\s+/; + } + if ($notyet) { + print STDERR "Error from $baseurl:\n" if $notyet; + $notyet = 0; + } + if (m/^421\s+/ || + m/^ftp: connect: Connection timed out/ || + m/^ftp: Can't connect or login to host/) { + $self->{lasterror} = 421; + } + if (m/^550\s+/) { + $self->{lasterror} = 550; + } + print STDERR $_; + } + CORE::close($fh); + unlink $filename; +} + +package OpenBSD::PackageRepository::Installed; +our @ISA=qw(OpenBSD::PackageRepository); +use OpenBSD::PackageInfo; + +sub new +{ + bless {}, shift; +} + +sub find +{ + my ($repository, $name, $arch, $srcpath) = @_; + my $self; + + if (is_installed($name)) { + $self = OpenBSD::PackageLocation->new($repository, $name); + $self->{dir} = installed_info($name); + } + return $self; +} + +sub grabPlist +{ + my ($repository, $name, $arch, $code) = @_; + require OpenBSD::PackingList; + return OpenBSD::PackingList->from_installation($name, $code); +} + +sub available +{ + return installed_packages(); +} + +sub list +{ + my @list = installed_packages(); + return \@list; +} + +sub wipe_info +{ +} + +sub may_exist +{ + my ($self, $name) = @_; + return is_installed($name); +} + +package PackageRepository::Source; + +sub find +{ + my ($repository, $name, $arch, $srcpath) = @_; + my $dir; + my $make; + if (defined $ENV{'MAKE'}) { + $make = $ENV{'MAKE'}; + } else { + $make = '/usr/bin/make'; + } + if (defined $repository->{baseurl} && $repository->{baseurl} ne '') { + $dir = $repository->{baseurl} + } elsif (defined $ENV{PORTSDIR}) { + $dir = $ENV{PORTSDIR}; + } else { + $dir = '/usr/ports'; + } + # figure out the repository name and the pkgname + my $pkgfile = `cd $dir && SUBDIR=$srcpath ECHO_MSG=: $make show=PKGFILE`; + chomp $pkgfile; + if (! -f $pkgfile) { + system "cd $dir && SUBDIR=$srcpath $make package BULK=Yes"; + } + if (! -f $pkgfile) { + return undef; + } + $pkgfile =~ m|(.*/)([^/]*)|; + my ($base, $fname) = ($1, $2); + + my $repo = OpenBSD::PackageRepository::Local->_new($base); + return $repo->find($fname); +} + +package OpenBSD::PackageRepository::Local; +our @ISA=qw(OpenBSD::PackageRepository); + +sub open_pipe +{ + my ($self, $object) = @_; + my $pid = open(my $fh, "-|"); + if (!defined $pid) { + die "Cannot fork: $!"; + } + if ($pid) { + return $fh; + } else { + open STDERR, ">/dev/null"; + exec {"/usr/bin/gzip"} + "gzip", + "-d", + "-c", + "-q", + "-f", + $self->{baseurl}.$object->{name} + or die "Can't run gzip"; + } +} + +sub may_exist +{ + my ($self, $name) = @_; + return -r $self->{baseurl}.$name; +} + +sub list +{ + my $self = shift; + my $l = []; + my $dname = $self->{baseurl}; + opendir(my $dir, $dname) or return $l; + while (my $e = readdir $dir) { + next unless $e =~ m/\.tgz$/; + next unless -f "$dname/$e"; + push(@$l, $`); + } + close($dir); + return $l; +} + +package OpenBSD::PackageRepository::Local::Pipe; +our @ISA=qw(OpenBSD::PackageRepository::Local); + +sub may_exist +{ + return 1; +} + +sub open_pipe +{ + my ($self, $object) = @_; + my $fullname = $self->{baseurl}.$object->{name}; + my $pid = open(my $fh, "-|"); + if (!defined $pid) { + die "Cannot fork: $!"; + } + if ($pid) { + return $fh; + } else { + open STDERR, ">/dev/null"; + exec {"/usr/bin/gzip"} + "gzip", + "-d", + "-c", + "-q", + "-f", + "-" + or die "can't run gzip"; + } +} + +package OpenBSD::PackageRepository::Distant; +our @ISA=qw(OpenBSD::PackageRepository); + +my $buffsize = 2 * 1024 * 1024; + +sub pkg_copy +{ + my ($in, $dir, $name) = @_; + + require File::Temp; + my $template = $name; + $template =~ s/\.tgz$/.XXXXXXXX/; + + my ($copy, $filename) = File::Temp::tempfile($template, + DIR => $dir) or die "Can't write copy to cache"; + chmod 0644, $filename; + my $handler = sub { + my ($sig) = @_; + unlink $filename; + $SIG{$sig} = 'DEFAULT'; + kill $sig, $$; + }; + + my $nonempty = 0; + { + + local $SIG{'PIPE'} = $handler; + local $SIG{'INT'} = $handler; + local $SIG{'HUP'} = $handler; + local $SIG{'QUIT'} = $handler; + local $SIG{'KILL'} = $handler; + local $SIG{'TERM'} = $handler; + + my ($buffer, $n); + # copy stuff over + do { + $n = sysread($in, $buffer, $buffsize); + if (!defined $n) { + die "Error reading\n"; + } + if ($n > 0) { + $nonempty = 1; + } + syswrite $copy, $buffer; + syswrite STDOUT, $buffer; + } while ($n != 0); + close($copy); + } + + if ($nonempty) { + rename $filename, "$dir/$name"; + } else { + unlink $filename; + } +} + +sub open_pipe +{ + require OpenBSD::Temp; + + my ($self, $object) = @_; + $object->{errors} = OpenBSD::Temp::file(); + $object->{cache_dir} = $ENV{'PKG_CACHE'}; + my $pid = open(my $fh, "-|"); + if (!defined $pid) { + die "Cannot fork: $!"; + } + if ($pid) { + $object->{pid} = $pid; + return $fh; + } else { + open STDERR, '>', $object->{errors}; + + my $pid2 = open(STDIN, "-|"); + + if (!defined $pid2) { + die "Cannot fork: $!"; + } + if ($pid2) { + exec {"/usr/bin/gzip"} + "gzip", + "-d", + "-c", + "-q", + "-" + or die "can't run gzip"; + } else { + if (defined $object->{cache_dir}) { + my $pid3 = open(my $in, "-|"); + if (!defined $pid3) { + die "Cannot fork: $!"; + } + if ($pid3) { + pkg_copy($in, $object->{cache_dir}, + $object->{name}); + exit(0); + } else { + $self->grab_object($object); + } + } else { + $self->grab_object($object); + } + } + } +} + +sub _list +{ + my ($self, $cmd) = @_; + my $l =[]; + local $_; + open(my $fh, '-|', "$cmd") or return undef; + while(<$fh>) { + chomp; + next if m/^d.*\s+\S/; + next unless m/([^\s]+)\.tgz\s*$/; + push(@$l, $1); + } + close($fh); + return $l; +} + +sub finish_and_close +{ + my ($self, $object) = @_; + if (defined $object->{cache_dir}) { + while (defined $object->intNext()) { + } + } + $self->SUPER::finish_and_close($object); +} + +package OpenBSD::PackageRepository::SCP; +our @ISA=qw(OpenBSD::PackageRepository::Distant); + + +sub grab_object +{ + my ($self, $object) = @_; + + exec {"/usr/bin/scp"} + "scp", + $self->{host}.":".$self->{path}.$object->{name}, + "/dev/stdout" + or die "can't run scp"; +} + +our %distant = (); + +sub maxcount +{ + return 2; +} + +sub opened +{ + my $self = $_[0]; + my $k = $self->{key}; + if (!defined $distant{$k}) { + $distant{$k} = []; + } + return $distant{$k}; +} + +sub _new +{ + my ($class, $baseurl) = @_; + $baseurl =~ s/scp\:\/\///i; + $baseurl =~ m/\//; + bless { host => $`, key => $`, path => "/$'" }, $class; +} + +sub list +{ + my ($self) = @_; + if (!defined $self->{list}) { + my $host = $self->{host}; + my $path = $self->{path}; + $self->{list} = $self->_list("ssh $host ls -l $path"); + } + return $self->{list}; +} + +package OpenBSD::PackageRepository::HTTPorFTP; +our @ISA=qw(OpenBSD::PackageRepository::Distant); + +our %distant = (); + + +sub grab_object +{ + my ($self, $object) = @_; + my $ftp = defined $ENV{'FETCH_CMD'} ? $ENV{'FETCH_CMD'} : "/usr/bin/ftp"; + exec {$ftp} + "ftp", + "-o", + "-", $self->{baseurl}.$object->{name} + or die "can't run ftp"; +} + +sub maxcount +{ + return 1; +} + +sub opened +{ + my $self = $_[0]; + my $k = $self->{key}; + if (!defined $distant{$k}) { + $distant{$k} = []; + } + return $distant{$k}; +} + +sub _new +{ + my ($class, $baseurl) = @_; + my $distant_host; + if ($baseurl =~ m/^(http|ftp)\:\/\/(.*?)\//i) { + $distant_host = $&; + } + bless { baseurl => $baseurl, key => $distant_host }, $class; +} + + +package OpenBSD::PackageRepository::HTTP; +our @ISA=qw(OpenBSD::PackageRepository::HTTPorFTP); + +sub list +{ + my ($self) = @_; + if (!defined $self->{list}) { + my $error = OpenBSD::Temp::file(); + $self->make_room(); + my $fullname = $self->{baseurl}; + my $l = $self->{list} = []; + local $_; + open(my $fh, '-|', "ftp -o - $fullname 2>$error") or return undef; + # XXX assumes a pkg HREF won't cross a line. Is this the case ? + while(<$fh>) { + chomp; + for my $pkg (m/\<A\s+HREF=\"(.*?)\.tgz\"\>/gi) { + next if $pkg =~ m|/|; + push(@$l, $pkg); + } + } + close($fh); + $self->parse_problems($error); + } + return $self->{list}; +} + +package OpenBSD::PackageRepository::FTP; +our @ISA=qw(OpenBSD::PackageRepository::HTTPorFTP); + + +sub list +{ + my ($self) = @_; + if (!defined $self->{list}) { + require OpenBSD::Temp; + + my $error = OpenBSD::Temp::file(); + $self->make_room(); + my $fullname = $self->{baseurl}; + $self->{list} = $self->_list("echo 'nlist *.tgz'|ftp -o - $fullname 2>$error"); + $self->parse_problems($error); + } + return $self->{list}; +} + +1; diff --git a/usr.sbin/pkg_add/OpenBSD/PackageRepositoryList.pm b/usr.sbin/pkg_add/OpenBSD/PackageRepositoryList.pm new file mode 100644 index 00000000000..c6868ac4099 --- /dev/null +++ b/usr.sbin/pkg_add/OpenBSD/PackageRepositoryList.pm @@ -0,0 +1,104 @@ +# ex:ts=8 sw=4: +# $OpenBSD: PackageRepositoryList.pm,v 1.1 2006/03/04 13:13:05 espie Exp $ +# +# Copyright (c) 2003-2004 Marc Espie <espie@openbsd.org> +# +# Permission to use, copy, modify, and distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +use strict; +use warnings; + +package OpenBSD::PackageRepositoryList; + +sub new +{ + my $class = shift; + return bless {list => [], avail => undef }, $class; +} + +sub add +{ + my $self = shift; + push @{$self->{list}}, @_; + if (@_ > 0) { + $self->{avail} = undef; + } +} + +sub find +{ + my ($self, $pkgname, $arch, $srcpath) = @_; + + for my $repo (@{$self->{list}}) { + my $pkg; + + for (my $retry = 5; $retry < 60; $retry *= 2) { + undef $repo->{lasterror}; + $pkg = $repo->find($pkgname, $arch, $srcpath); + if (!defined $pkg && defined $repo->{lasterror} && + $repo->{lasterror} == 421 && + defined $self->{avail} && + $self->{avail}->{$pkgname} eq $repo) { + print STDERR "Temporary error, sleeping $retry seconds\n"; + sleep($retry); + } else { + last; + } + } + return $pkg if defined $pkg; + } + return undef; +} + +sub grabPlist +{ + my ($self, $pkgname, $arch, $code) = @_; + + for my $repo (@{$self->{list}}) { + my $plist; + + for (my $retry = 5; $retry < 60; $retry *= 2) { + undef $repo->{lasterror}; + $plist = $repo->grabPlist($pkgname, $arch, $code); + if (!defined $plist && defined $repo->{lasterror} && + $repo->{lasterror} == 421 && + defined $self->{avail} && + $self->{avail}->{$pkgname} eq $repo) { + print STDERR "Temporary error, sleeping $retry seconds\n"; + sleep($retry); + } else { + last; + } + } + return $plist if defined $plist; + } + return undef; +} + +sub available +{ + my $self = shift; + + if (!defined $self->{avail}) { + my $available_packages = {}; + foreach my $loc (reverse @{$self->{list}}) { + foreach my $pkg (@{$loc->list()}) { + $available_packages->{$pkg} = $loc; + } + } + $self->{avail} = $available_packages; + } + return keys %{$self->{avail}}; +} + +1; |