2014-02-08 05:05:46 +00:00
|
|
|
#include "shared.hh"
|
|
|
|
#include "util.hh"
|
|
|
|
#include "serialise.hh"
|
|
|
|
#include "archive.hh"
|
|
|
|
#include "affinity.hh"
|
|
|
|
#include "globals.hh"
|
2014-02-10 12:43:13 +00:00
|
|
|
#include "serve-protocol.hh"
|
2014-02-14 11:31:10 +00:00
|
|
|
#include "worker-protocol.hh"
|
|
|
|
#include "store-api.hh"
|
2014-02-08 05:05:46 +00:00
|
|
|
|
|
|
|
#include <iostream>
|
|
|
|
#include <unistd.h>
|
|
|
|
|
|
|
|
using namespace nix;
|
|
|
|
|
|
|
|
// !!! TODO:
|
|
|
|
// * Respect more than the first host
|
|
|
|
// * use a database
|
|
|
|
// * show progress
|
|
|
|
|
2014-02-14 10:48:42 +00:00
|
|
|
|
|
|
|
static std::pair<FdSink, FdSource> connect(const string & conn)
|
|
|
|
{
|
2014-02-08 05:05:46 +00:00
|
|
|
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);
|
|
|
|
}
|
2014-02-10 11:43:29 +00:00
|
|
|
// If child exits unexpectedly, we'll EPIPE or EOF early.
|
|
|
|
// If we exit unexpectedly, child will EPIPE or EOF early.
|
2014-02-08 05:05:46 +00:00
|
|
|
// So no need to keep track of it.
|
|
|
|
|
2014-02-12 12:22:36 +00:00
|
|
|
return std::pair<FdSink, FdSource>(to.writeSide.borrow(), from.readSide.borrow());
|
2014-02-08 05:05:46 +00:00
|
|
|
}
|
|
|
|
|
2014-02-14 10:48:42 +00:00
|
|
|
|
|
|
|
static void substitute(std::pair<FdSink, FdSource> & pipes, Path storePath, Path destPath)
|
|
|
|
{
|
2014-02-10 12:43:13 +00:00
|
|
|
writeInt(cmdSubstitute, pipes.first);
|
2014-02-08 05:05:46 +00:00
|
|
|
writeString(storePath, pipes.first);
|
|
|
|
pipes.first.flush();
|
|
|
|
restorePath(destPath, pipes.second);
|
2014-02-12 12:22:36 +00:00
|
|
|
std::cout << std::endl;
|
2014-02-08 05:05:46 +00:00
|
|
|
}
|
|
|
|
|
2014-02-14 10:48:42 +00:00
|
|
|
|
|
|
|
static void query(std::pair<FdSink, FdSource> & pipes)
|
|
|
|
{
|
2014-02-10 12:43:13 +00:00
|
|
|
writeInt(cmdQuery, pipes.first);
|
2014-02-12 12:22:36 +00:00
|
|
|
for (string line; getline(std::cin, line);) {
|
2014-02-08 05:05:46 +00:00
|
|
|
Strings tokenized = tokenizeString<Strings>(line);
|
|
|
|
string cmd = tokenized.front();
|
|
|
|
tokenized.pop_front();
|
|
|
|
if (cmd == "have") {
|
2014-02-10 12:43:13 +00:00
|
|
|
writeInt(qCmdHave, pipes.first);
|
|
|
|
writeStrings(tokenized, pipes.first);
|
|
|
|
pipes.first.flush();
|
2014-02-08 05:05:46 +00:00
|
|
|
PathSet paths = readStrings<PathSet>(pipes.second);
|
|
|
|
foreach (PathSet::iterator, i, paths)
|
2014-02-12 12:22:36 +00:00
|
|
|
std::cout << *i << std::endl;
|
2014-02-08 05:05:46 +00:00
|
|
|
} else if (cmd == "info") {
|
2014-02-10 12:43:13 +00:00
|
|
|
writeInt(qCmdInfo, pipes.first);
|
|
|
|
writeStrings(tokenized, pipes.first);
|
|
|
|
pipes.first.flush();
|
2014-02-14 11:31:10 +00:00
|
|
|
while (1) {
|
|
|
|
Path path = readString(pipes.second);
|
|
|
|
if (path.empty()) break;
|
|
|
|
assertStorePath(path);
|
2014-02-12 12:22:36 +00:00
|
|
|
std::cout << path << std::endl;
|
2014-02-14 11:31:10 +00:00
|
|
|
string deriver = readString(pipes.second);
|
|
|
|
if (!deriver.empty()) assertStorePath(deriver);
|
|
|
|
std::cout << deriver << std::endl;
|
|
|
|
PathSet references = readStorePaths<PathSet>(pipes.second);
|
2014-02-12 12:22:36 +00:00
|
|
|
std::cout << references.size() << std::endl;
|
2014-02-08 05:05:46 +00:00
|
|
|
foreach (PathSet::iterator, i, references)
|
2014-02-12 12:22:36 +00:00
|
|
|
std::cout << *i << std::endl;
|
|
|
|
std::cout << readLongLong(pipes.second) << std::endl;
|
|
|
|
std::cout << readLongLong(pipes.second) << std::endl;
|
2014-02-08 05:05:46 +00:00
|
|
|
}
|
|
|
|
} else
|
2014-02-12 12:26:35 +00:00
|
|
|
throw Error(format("unknown substituter query `%1%'") % cmd);
|
2014-02-12 12:22:36 +00:00
|
|
|
std::cout << std::endl;
|
2014-02-08 05:05:46 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-02-14 10:48:42 +00:00
|
|
|
|
2014-02-08 05:05:46 +00:00
|
|
|
void run(Strings args)
|
|
|
|
{
|
|
|
|
if (args.empty())
|
|
|
|
throw UsageError("download-via-ssh requires an argument");
|
|
|
|
|
|
|
|
if (settings.sshSubstituterHosts.empty())
|
|
|
|
return;
|
|
|
|
|
2014-02-12 12:22:36 +00:00
|
|
|
std::cout << std::endl;
|
2014-02-08 05:05:46 +00:00
|
|
|
|
2014-02-14 11:20:12 +00:00
|
|
|
string host = settings.sshSubstituterHosts.front();
|
|
|
|
std::pair<FdSink, FdSource> pipes = connect(host);
|
2014-02-08 05:05:46 +00:00
|
|
|
|
2014-02-10 12:43:13 +00:00
|
|
|
/* Exchange the greeting */
|
|
|
|
writeInt(SERVE_MAGIC_1, pipes.first);
|
|
|
|
pipes.first.flush();
|
|
|
|
unsigned int magic = readInt(pipes.second);
|
|
|
|
if (magic != SERVE_MAGIC_2)
|
|
|
|
throw Error("protocol mismatch");
|
|
|
|
readInt(pipes.second); // Server version, unused for now
|
|
|
|
writeInt(SERVE_PROTOCOL_VERSION, pipes.first);
|
|
|
|
pipes.first.flush();
|
|
|
|
|
2014-02-08 05:05:46 +00:00
|
|
|
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;
|
2014-02-14 11:20:12 +00:00
|
|
|
printMsg(lvlError, format("downloading `%1%' via SSH from `%2%'...") % storePath % host);
|
2014-02-08 05:05:46 +00:00
|
|
|
substitute(pipes, storePath, destPath);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
throw UsageError(format("download-via-ssh: unknown command `%1%'") % *i);
|
|
|
|
}
|
|
|
|
|
2014-02-14 10:48:42 +00:00
|
|
|
|
2014-02-08 05:05:46 +00:00
|
|
|
void printHelp()
|
|
|
|
{
|
|
|
|
std::cerr << "Usage: download-via-ssh --query|--substitute store-path dest-path" << std::endl;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
string programId = "download-via-ssh";
|