summary refs log tree commit diff
path: root/scripts/nix-copy-closure.in
blob: 1b1b3ebc8b25a7246a17cb4de3e0e43b7eceece2 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
#! @perl@ -w

my $binDir = $ENV{"NIX_BIN_DIR"};
$binDir = "@bindir@" unless defined $binDir;


if (scalar @ARGV < 1) {
    print STDERR <<EOF
Usage: nix-copy-closure [--from | --to] HOSTNAME [--sign] PATHS...
EOF
    ;
    exit 1;
}


# Get the target host.
my $sshHost;
my @sshOpts = split ' ', ($ENV{"NIX_SSHOPTS"} or "");

my $sign = 0;

my $compressor = "cat";
my $decompressor = "cat";

my $toMode = 1;


# !!! Copied from nix-pack-closure, should put this in a module.
my @storePaths = ();

while (@ARGV) {
    my $arg = shift @ARGV;
    
    if ($arg eq "--sign") {
        $sign = 1;
    }
    elsif ($arg eq "--gzip") {
        $compressor = "gzip";
        $decompressor = "gunzip";
    }
    elsif ($arg eq "--from") {
        $toMode = 0;
    }
    elsif ($arg eq "--to") {
        $toMode = 1;
    }
    elsif (!defined $sshHost) {
        $sshHost = $arg;
    }
    else {
        push @storePaths, $arg;
    }
}


if ($toMode) { # Copy TO the remote machine.

    my @allStorePaths;
    my %storePathsSeen;

    foreach my $storePath (@storePaths) {
        # $arg might be a symlink to the store, so resolve it.
        my $storePath2 = (`$binDir/nix-store --query --resolve '$storePath'`
            or die "cannot resolve `$storePath'");
        chomp $storePath2;

        # Get the closure of this path.
        my $pid = open(READ,
            "$binDir/nix-store --query --requisites '$storePath2'|") or die;
    
        while (<READ>) {
            chomp;
            die "bad: $_" unless /^\//;
            if (!defined $storePathsSeen{$_}) {
                push @allStorePaths, $_;
                $storePathsSeen{$_} = 1;
            }
        }

        close READ or die "nix-store failed: $?";
    }


    # Ask the remote host which paths are invalid.
    open(READ, "ssh @sshOpts $sshHost nix-store --check-validity --print-invalid @allStorePaths|");
    my @missing = ();
    while (<READ>) {
        chomp;
        print STDERR "target machine needs $_\n";
        push @missing, $_;
    }
    close READ or die;


    # Export the store paths and import them on the remote machine.
    if (scalar @missing > 0) {
        my $extraOpts = "";
        $extraOpts .= "--sign" if $sign == 1;
        system("nix-store --export $extraOpts @missing | $compressor | ssh @sshOpts $sshHost '$decompressor | nix-store --import'") == 0
            or die "copying store paths to remote machine `$sshHost' failed: $?";
    }

}


else { # Copy FROM the remote machine.

    # Query the closure of the given store paths on the remote
    # machine.  Paths are assumed to be store paths; there is no
    # resolution (following of symlinks).
    my $pid = open(READ,
        "ssh @sshOpts $sshHost nix-store --query --requisites @storePaths|") or die;
    
    my @allStorePaths;
    my %storePathsSeen;

    while (<READ>) {
        chomp;
        die "bad: $_" unless /^\//;
        if (!defined $storePathsSeen{$_}) {
            push @allStorePaths, $_;
            $storePathsSeen{$_} = 1;
        }
    }

    close READ or die "nix-store on remote machine `$sshHost' failed: $?";


    # What paths are already valid locally?
    open(READ, "@bindir@/nix-store --check-validity --print-invalid @allStorePaths|");
    my @missing = ();
    while (<READ>) {
        chomp;
        print STDERR "local machine needs $_\n";
        push @missing, $_;
    }
    close READ or die;
    

    # Export the store paths on the remote machine and import them on locally.
    if (scalar @missing > 0) {
        my $extraOpts = "";
        $extraOpts .= "--sign" if $sign == 1;
        system("ssh @sshOpts $sshHost 'nix-store --export $extraOpts @missing | $compressor' | $decompressor | @bindir@/nix-store --import") == 0
            or die "copying store paths to remote machine `$sshHost' failed: $?";
    }

}