lix/src/download-via-ssh/download-via-ssh.cc

142 lines
4.6 KiB
C++
Raw Normal View History

#include "shared.hh"
#include "util.hh"
#include "serialise.hh"
#include "archive.hh"
#include "affinity.hh"
#include "globals.hh"
#include "serve-protocol.hh"
2014-02-14 11:31:10 +00:00
#include "worker-protocol.hh"
#include "store-api.hh"
#include <iostream>
2014-12-14 00:51:14 +00:00
#include <cstdlib>
#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)
{
Pipe to, from;
to.create();
from.create();
startProcess([&]() {
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");
});
// If child exits unexpectedly, we'll EPIPE or EOF early.
// If we exit unexpectedly, child will EPIPE or EOF early.
// So no need to keep track of it.
return std::pair<FdSink, FdSource>(to.writeSide.borrow(), from.readSide.borrow());
}
2014-02-14 10:48:42 +00:00
static void substitute(std::pair<FdSink, FdSource> & pipes, Path storePath, Path destPath)
{
2015-07-19 23:16:16 +00:00
pipes.first << cmdDumpStorePath << storePath;
pipes.first.flush();
restorePath(destPath, pipes.second);
std::cout << std::endl;
}
2014-02-14 10:48:42 +00:00
static void query(std::pair<FdSink, FdSource> & pipes)
{
for (string line; getline(std::cin, line);) {
Strings tokenized = tokenizeString<Strings>(line);
string cmd = tokenized.front();
tokenized.pop_front();
if (cmd == "have") {
2015-07-19 23:16:16 +00:00
pipes.first
<< cmdQueryValidPaths
<< 0 // don't lock
<< 0 // don't substitute
<< tokenized;
pipes.first.flush();
PathSet paths = readStrings<PathSet>(pipes.second);
2015-07-17 17:24:28 +00:00
for (auto & i : paths)
std::cout << i << std::endl;
} else if (cmd == "info") {
2015-07-19 23:16:16 +00:00
pipes.first << cmdQueryPathInfos << tokenized;
pipes.first.flush();
2014-02-14 11:31:10 +00:00
while (1) {
Path path = readString(pipes.second);
if (path.empty()) break;
assertStorePath(path);
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);
std::cout << references.size() << std::endl;
2015-07-17 17:24:28 +00:00
for (auto & i : references)
std::cout << i << std::endl;
std::cout << readLongLong(pipes.second) << std::endl;
std::cout << readLongLong(pipes.second) << std::endl;
}
} else
2014-08-20 15:00:17 +00:00
throw Error(format("unknown substituter query %1%") % cmd);
std::cout << std::endl;
}
}
2014-02-14 10:48:42 +00:00
2014-08-13 01:50:44 +00:00
int main(int argc, char * * argv)
{
2014-08-13 01:50:44 +00:00
return handleExceptions(argv[0], [&]() {
if (argc < 2)
throw UsageError("download-via-ssh requires an argument");
initNix();
settings.update();
2014-08-13 01:50:44 +00:00
if (settings.sshSubstituterHosts.empty())
return;
2014-08-13 01:50:44 +00:00
std::cout << std::endl;
2014-08-13 01:50:44 +00:00
/* Pass on the location of the daemon client's SSH
authentication socket. */
2014-09-17 13:18:13 +00:00
string sshAuthSock = settings.get("ssh-auth-sock", string(""));
2014-08-13 01:50:44 +00:00
if (sshAuthSock != "") setenv("SSH_AUTH_SOCK", sshAuthSock.c_str(), 1);
string host = settings.sshSubstituterHosts.front();
std::pair<FdSink, FdSource> pipes = connect(host);
/* Exchange the greeting */
2015-07-19 23:16:16 +00:00
pipes.first << SERVE_MAGIC_1;
2014-08-13 01:50:44 +00:00
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
2015-07-19 23:16:16 +00:00
pipes.first << SERVE_PROTOCOL_VERSION;
2014-08-13 01:50:44 +00:00
pipes.first.flush();
string arg = argv[1];
if (arg == "--query")
query(pipes);
else if (arg == "--substitute") {
if (argc != 4)
throw UsageError("download-via-ssh: --substitute takes exactly two arguments");
Path storePath = argv[2];
Path destPath = argv[3];
2014-08-20 15:00:17 +00:00
printMsg(lvlError, format("downloading %1% via SSH from %2%...") % storePath % host);
substitute(pipes, storePath, destPath);
}
2014-08-13 01:50:44 +00:00
else
2014-08-20 15:00:17 +00:00
throw UsageError(format("download-via-ssh: unknown command %1%") % arg);
2014-08-13 01:50:44 +00:00
});
}