diff options
Diffstat (limited to 'perl/lib/Nix')
-rw-r--r-- | perl/lib/Nix/CopyClosure.pm | 77 |
1 files changed, 74 insertions, 3 deletions
diff --git a/perl/lib/Nix/CopyClosure.pm b/perl/lib/Nix/CopyClosure.pm index 41ceabd85847..cba365aa1745 100644 --- a/perl/lib/Nix/CopyClosure.pm +++ b/perl/lib/Nix/CopyClosure.pm @@ -4,6 +4,15 @@ use strict; use Nix::Config; use Nix::Store; use List::Util qw(sum); +use IPC::Open2; + + +sub readInt { + my ($from) = @_; + my $resp; + sysread($from, $resp, 8) == 8 or die "did not receive valid reply from remote host\n"; + return unpack("L<x4", $resp); +} sub copyTo { @@ -20,14 +29,76 @@ sub copyTo { # Ignore exit status because this is just an optimisation. } + # Start ‘nix-store --serve’ on the remote host. + my ($from, $to); + my $pid = open2($from, $to, "ssh $sshHost @{$sshOpts} nix-store --serve --write"); + + # Do the handshake. + eval { + my $SERVE_MAGIC_1 = 0x390c9deb; # FIXME + my $clientVersion = 0x200; + syswrite($to, pack("L<x4L<x4", $SERVE_MAGIC_1, $clientVersion)) or die; + die "did not get valid handshake from remote host\n" if readInt($from) != 0x5452eecb; + my $serverVersion = readInt($from); + die "unsupported server version\n" if $serverVersion < 0x200 || $serverVersion >= 0x300; + }; + if ($@) { + chomp $@; + warn "$@; falling back to old closure copying method\n"; + return oldCopyTo(\@closure, @_); + } + + # Send the "query valid paths" command with the "lock" option + # enabled. This prevens a race where the remote host + # garbage-collect paths that are already there. + my $req = pack("L<x4L<x4L<x4", 1, 1, scalar @closure); + for my $s (@closure) { + my $len = length $s; + $req .= pack("L<x4", $len); + $req .= $s; + $req .= "\000" x (8 - $len % 8) if $len % 8; + } + syswrite($to, $req) or die; + + # Get back the set of paths that are already valid on the remote host. + my %present; + my $n = readInt($from); + while ($n--) { + my $len = readInt($from); + my $s; + sysread($from, $s, $len) == $len or die; + $present{$s} = 1; + sysread($from, $s, 8 - $len % 8) if $len % 8; # skip padding + } + + my @missing = grep { !$present{$_} } @closure; + return if !@missing; + + # Send the "import paths" command. + syswrite($to, pack("L<x4", 4)) or die; + exportPaths(fileno($to), $sign, @missing); + readInt($from) == 1 or die; + + # Shut down the server process. + close $to; + waitpid $pid, 0; +} + + +# For backwards compatibility with Nix <= 1.7. Will be removed +# eventually. +sub oldCopyTo { + my ($closure, $sshHost, $sshOpts, $storePaths, $compressor, $decompressor, + $includeOutputs, $dryRun, $sign, $progressViewer, $useSubstitutes) = @_; + # Ask the remote host which paths are invalid. Because of limits # to the command line length, do this in chunks. Eventually, # we'll want to use ‘--from-stdin’, but we can't rely on the # target having this option yet. - my @missing = (); + my @missing; my $missingSize = 0; - while (scalar(@closure) > 0) { - my @ps = splice(@closure, 0, 1500); + while (scalar(@$closure) > 0) { + my @ps = splice(@$closure, 0, 1500); open(READ, "set -f; ssh $sshHost @{$sshOpts} nix-store --check-validity --print-invalid @ps|"); while (<READ>) { chomp; |