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
|
#include "shared.hh"
#include "util.hh"
#include "serialise.hh"
#include "archive.hh"
#include "affinity.hh"
#include "globals.hh"
#include <iostream>
#include <unistd.h>
using namespace nix;
using std::pair;
using std::cout;
using std::endl;
// !!! TODO:
// * Respect more than the first host
// * use a database
// * show progress
static pair<FdSink, FdSource> connect(string conn) {
Pipe to, from;
to.create();
from.create();
pid_t child = fork();
switch (child) {
case -1:
throw SysError("unable to fork");
case 0:
try {
restoreAffinity();
if (dup2(to.readSide, STDIN_FILENO) == -1)
throw SysError("dupping stdin");
if (dup2(from.writeSide, STDOUT_FILENO) == -1)
throw SysError("dupping stdout");
execlp("ssh"
, "ssh"
, "-x"
, "-T"
, conn.c_str()
, "nix-store --serve"
, NULL);
throw SysError("executing ssh");
} catch (std::exception & e) {
std::cerr << "error: " << e.what() << std::endl;
}
_exit(1);
}
// If child exits unexpectedly, we'll EPIPE. If we exit unexpectedly, child will
// So no need to keep track of it.
return pair<FdSink, FdSource>(to.writeSide.borrow(), from.readSide.borrow());
}
static void substitute(pair<FdSink, FdSource> & pipes, Path storePath, Path destPath) {
writeString("substitute", pipes.first);
writeString(storePath, pipes.first);
pipes.first.flush();
restorePath(destPath, pipes.second);
cout << endl;
}
static void query(pair<FdSink, FdSource> & pipes) {
using std::cin;
writeString("query", pipes.first);
for (string line; getline(cin, line);) {
Strings tokenized = tokenizeString<Strings>(line);
string cmd = tokenized.front();
writeString(cmd, pipes.first);
tokenized.pop_front();
foreach (Strings::iterator, i, tokenized)
writeStrings(tokenized, pipes.first);
pipes.first.flush();
if (cmd == "have") {
PathSet paths = readStrings<PathSet>(pipes.second);
foreach (PathSet::iterator, i, paths)
cout << *i << endl;
} else if (cmd == "info") {
for (Path path = readString(pipes.second); !path.empty(); path = readString(pipes.second)) {
cout << path << endl;
cout << readString(pipes.second) << endl;
PathSet references = readStrings<PathSet>(pipes.second);
cout << references.size() << endl;
foreach (PathSet::iterator, i, references)
cout << *i << endl;
cout << readLongLong(pipes.second) << endl;
cout << readLongLong(pipes.second) << endl;
}
} else
throw Error(format("Unknown substituter query `%1%'") % cmd);
cout << endl;
}
writeString("", pipes.first);
}
void run(Strings args)
{
if (args.empty())
throw UsageError("download-via-ssh requires an argument");
if (settings.sshSubstituterHosts.empty())
return;
cout << endl;
pair<FdSink, FdSource> pipes = connect(settings.sshSubstituterHosts.front());
Strings::iterator i = args.begin();
if (*i == "--query")
query(pipes);
else if (*i == "--substitute")
if (args.size() != 3)
throw UsageError("download-via-ssh: --substitute takes exactly two arguments");
else {
Path storePath = *++i;
Path destPath = *++i;
substitute(pipes, storePath, destPath);
}
else
throw UsageError(format("download-via-ssh: unknown command `%1%'") % *i);
}
void printHelp()
{
std::cerr << "Usage: download-via-ssh --query|--substitute store-path dest-path" << std::endl;
}
string programId = "download-via-ssh";
|