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 /usr.sbin/pkg_add/OpenBSD/PackageLocator.pm | |
parent | 54f12c977cbe82c0a95b797c6c9971492a8e3df7 (diff) |
cut down the Locator code into maintainable chunks.
Diffstat (limited to 'usr.sbin/pkg_add/OpenBSD/PackageLocator.pm')
-rw-r--r-- | usr.sbin/pkg_add/OpenBSD/PackageLocator.pm | 1011 |
1 files changed, 4 insertions, 1007 deletions
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. |