summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarc Espie <espie@cvs.openbsd.org>2006-03-04 13:13:06 +0000
committerMarc Espie <espie@cvs.openbsd.org>2006-03-04 13:13:06 +0000
commit2671c587bc640f9caf86a61f5d0a8a8cca0eb5ca (patch)
tree90d97a87055042fa85bcae27bbf04bace8edbca4
parent54f12c977cbe82c0a95b797c6c9971492a8e3df7 (diff)
cut down the Locator code into maintainable chunks.
-rw-r--r--usr.sbin/pkg_add/Makefile5
-rw-r--r--usr.sbin/pkg_add/OpenBSD/PackageLocation.pm316
-rw-r--r--usr.sbin/pkg_add/OpenBSD/PackageLocator.pm1011
-rw-r--r--usr.sbin/pkg_add/OpenBSD/PackageRepository.pm650
-rw-r--r--usr.sbin/pkg_add/OpenBSD/PackageRepositoryList.pm104
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;