#! @perl@ -w @perlFlags@ use strict; use File::Basename; use Nix::Config; use Nix::Store; my @binaryCacheUrls = split / /, ($ENV{"NIX_BINARY_CACHES"} || ""); sub getInfoFrom { my ($storePath, $pathHash, $binaryCacheUrl) = @_; my $infoUrl = "$binaryCacheUrl/$pathHash.narinfo"; #print STDERR "checking $infoUrl...\n"; my $s = `$Nix::Config::curl --fail --silent --location $infoUrl`; if ($? != 0) { my $status = $? >> 8; print STDERR "could not download ‘$infoUrl’ (curl returned status ", $? >> 8, ")\n" if $status != 22 && $status != 37; return undef; } my ($storePath2, $url, $compression, $fileHash, $fileSize, $narHash, $narSize, $deriver); my @refs; foreach my $line (split "\n", $s) { $line =~ /^(.*): (.*)$/ or return undef; if ($1 eq "StorePath") { $storePath2 = $2; } elsif ($1 eq "URL") { $url = $2; } elsif ($1 eq "Compression") { $compression = $2; } elsif ($1 eq "FileHash") { $fileHash = $2; } elsif ($1 eq "FileSize") { $fileSize = int($2); } elsif ($1 eq "NarHash") { $narHash = $2; } elsif ($1 eq "NarSize") { $narSize = int($2); } elsif ($1 eq "References") { @refs = split / /, $2; } elsif ($1 eq "Deriver") { $deriver = $2; } } if ($storePath ne $storePath2 || !defined $url || !defined $narHash) { print STDERR "bad NAR info file ‘$infoUrl’\n"; return undef; } return { url => $url , compression => ($compression || "bzip2") , fileHash => $fileHash , fileSize => $fileSize , narHash => $narHash , narSize => $narSize , refs => [ map { "$Nix::Config::storeDir/$_" } @refs ] , deriver => defined $deriver ? "$Nix::Config::storeDir/$deriver" : undef } } sub getInfo { my ($storePath) = @_; my $pathHash = substr(basename($storePath), 0, 32); cache: foreach my $binaryCacheUrl (@binaryCacheUrls) { my $info = getInfoFrom($storePath, $pathHash, $binaryCacheUrl); return $info if defined $info; } return undef; } sub downloadBinary { my ($storePath) = @_; my $pathHash = substr(basename($storePath), 0, 32); cache: foreach my $binaryCacheUrl (@binaryCacheUrls) { my $info = getInfoFrom($storePath, $pathHash, $binaryCacheUrl); if (defined $info) { my $decompressor; if ($info->{compression} eq "bzip2") { $decompressor = "$Nix::Config::bzip2 -d"; } elsif ($info->{compression} eq "xz") { $decompressor = "$Nix::Config::xz -d"; } else { print STDERR "unknown compression method ‘$info->{compression}’\n"; next; } if (system("$Nix::Config::curl --fail --location $binaryCacheUrl/$info->{url} | $decompressor | $Nix::Config::binDir/nix-store --restore $storePath") != 0) { die "download of `$info->{url}' failed" . ($! ? ": $!" : "") . "\n" unless $? == 0; next; } # The hash in the manifest can be either in base-16 or # base-32. Handle both. $info->{narHash} =~ /^sha256:(.*)$/ or die "invalid hash"; my $hash = $1; my $hash2 = hashPath("sha256", 1, $storePath); die "hash mismatch in downloaded path ‘$storePath’; expected $hash, got $hash2\n" if $hash ne $hash2; return 1; } } return 0; } if ($ARGV[0] eq "--query") { while () { my $cmd = $_; chomp $cmd; if ($cmd eq "have") { my $storePath = ; chomp $storePath; # FIXME: want to give correct info here, but it's too slow. print "0\n"; #my $info = getInfo($storePath); #if (defined $info) { print "1\n"; } else { print "0\n"; } } elsif ($cmd eq "info") { my $storePath = ; chomp $storePath; my $info = getInfo($storePath); if (defined $info) { print "1\n"; print $info->{deriver} || "", "\n"; print scalar @{$info->{refs}}, "\n"; print "$_\n" foreach @{$info->{refs}}; print $info->{fileSize} || 0, "\n"; print $info->{narSize} || 0, "\n"; } else { print "0\n"; } } else { die "unknown command `$cmd'"; } flush STDOUT; } } elsif ($ARGV[0] eq "--substitute") { my $storePath = $ARGV[1] or die; if (!downloadBinary($storePath)) { print STDERR "could not download ‘$storePath’ from any binary cache\n"; } } else { die; }