Merge branch 'ssh-store' of https://github.com/shlevy/nix
This commit is contained in:
commit
4b8f1b0ec0
15 changed files with 465 additions and 91 deletions
|
@ -6,9 +6,9 @@
|
||||||
#include "globals.hh"
|
#include "globals.hh"
|
||||||
#include "nar-info.hh"
|
#include "nar-info.hh"
|
||||||
#include "sync.hh"
|
#include "sync.hh"
|
||||||
#include "worker-protocol.hh"
|
#include "remote-fs-accessor.hh"
|
||||||
#include "nar-accessor.hh"
|
|
||||||
#include "nar-info-disk-cache.hh"
|
#include "nar-info-disk-cache.hh"
|
||||||
|
#include "nar-accessor.hh"
|
||||||
#include "json.hh"
|
#include "json.hh"
|
||||||
|
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
|
@ -379,8 +379,7 @@ Path BinaryCacheStore::addTextToStore(const string & name, const string & s,
|
||||||
|
|
||||||
ref<FSAccessor> BinaryCacheStore::getFSAccessor()
|
ref<FSAccessor> BinaryCacheStore::getFSAccessor()
|
||||||
{
|
{
|
||||||
return make_ref<BinaryCacheStoreAccessor>(ref<BinaryCacheStore>(
|
return make_ref<RemoteFSAccessor>(ref<Store>(shared_from_this()));
|
||||||
std::dynamic_pointer_cast<BinaryCacheStore>(shared_from_this())));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,7 +37,8 @@ namespace nix {
|
||||||
|
|
||||||
|
|
||||||
LocalStore::LocalStore(const Params & params)
|
LocalStore::LocalStore(const Params & params)
|
||||||
: LocalFSStore(params)
|
: Store(params)
|
||||||
|
, LocalFSStore(params)
|
||||||
, realStoreDir(get(params, "real", rootDir != "" ? rootDir + "/nix/store" : storeDir))
|
, realStoreDir(get(params, "real", rootDir != "" ? rootDir + "/nix/store" : storeDir))
|
||||||
, dbDir(stateDir + "/db")
|
, dbDir(stateDir + "/db")
|
||||||
, linksDir(realStoreDir + "/.links")
|
, linksDir(realStoreDir + "/.links")
|
||||||
|
|
57
src/libstore/remote-fs-accessor.cc
Normal file
57
src/libstore/remote-fs-accessor.cc
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
#include "remote-fs-accessor.hh"
|
||||||
|
#include "nar-accessor.hh"
|
||||||
|
|
||||||
|
namespace nix {
|
||||||
|
|
||||||
|
|
||||||
|
RemoteFSAccessor::RemoteFSAccessor(ref<Store> store)
|
||||||
|
: store(store)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
std::pair<ref<FSAccessor>, Path> RemoteFSAccessor::fetch(const Path & path_)
|
||||||
|
{
|
||||||
|
auto path = canonPath(path_);
|
||||||
|
|
||||||
|
auto storePath = store->toStorePath(path);
|
||||||
|
std::string restPath = std::string(path, storePath.size());
|
||||||
|
|
||||||
|
if (!store->isValidPath(storePath))
|
||||||
|
throw InvalidPath(format("path ‘%1%’ is not a valid store path") % storePath);
|
||||||
|
|
||||||
|
auto i = nars.find(storePath);
|
||||||
|
if (i != nars.end()) return {i->second, restPath};
|
||||||
|
|
||||||
|
StringSink sink;
|
||||||
|
store->narFromPath(storePath, sink);
|
||||||
|
|
||||||
|
auto accessor = makeNarAccessor(sink.s);
|
||||||
|
nars.emplace(storePath, accessor);
|
||||||
|
return {accessor, restPath};
|
||||||
|
}
|
||||||
|
|
||||||
|
FSAccessor::Stat RemoteFSAccessor::stat(const Path & path)
|
||||||
|
{
|
||||||
|
auto res = fetch(path);
|
||||||
|
return res.first->stat(res.second);
|
||||||
|
}
|
||||||
|
|
||||||
|
StringSet RemoteFSAccessor::readDirectory(const Path & path)
|
||||||
|
{
|
||||||
|
auto res = fetch(path);
|
||||||
|
return res.first->readDirectory(res.second);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string RemoteFSAccessor::readFile(const Path & path)
|
||||||
|
{
|
||||||
|
auto res = fetch(path);
|
||||||
|
return res.first->readFile(res.second);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string RemoteFSAccessor::readLink(const Path & path)
|
||||||
|
{
|
||||||
|
auto res = fetch(path);
|
||||||
|
return res.first->readLink(res.second);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
29
src/libstore/remote-fs-accessor.hh
Normal file
29
src/libstore/remote-fs-accessor.hh
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "fs-accessor.hh"
|
||||||
|
#include "ref.hh"
|
||||||
|
#include "store-api.hh"
|
||||||
|
|
||||||
|
namespace nix {
|
||||||
|
|
||||||
|
class RemoteFSAccessor : public FSAccessor
|
||||||
|
{
|
||||||
|
ref<Store> store;
|
||||||
|
|
||||||
|
std::map<Path, ref<FSAccessor>> nars;
|
||||||
|
|
||||||
|
std::pair<ref<FSAccessor>, Path> fetch(const Path & path_);
|
||||||
|
public:
|
||||||
|
|
||||||
|
RemoteFSAccessor(ref<Store> store);
|
||||||
|
|
||||||
|
Stat stat(const Path & path) override;
|
||||||
|
|
||||||
|
StringSet readDirectory(const Path & path) override;
|
||||||
|
|
||||||
|
std::string readFile(const Path & path) override;
|
||||||
|
|
||||||
|
std::string readLink(const Path & path) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -38,9 +38,9 @@ template<class T> T readStorePaths(Store & store, Source & from)
|
||||||
|
|
||||||
template PathSet readStorePaths(Store & store, Source & from);
|
template PathSet readStorePaths(Store & store, Source & from);
|
||||||
|
|
||||||
|
/* TODO: Separate these store impls into different files, give them better names */
|
||||||
RemoteStore::RemoteStore(const Params & params, size_t maxConnections)
|
RemoteStore::RemoteStore(const Params & params, size_t maxConnections)
|
||||||
: LocalFSStore(params)
|
: Store(params)
|
||||||
, connections(make_ref<Pool<Connection>>(
|
, connections(make_ref<Pool<Connection>>(
|
||||||
maxConnections,
|
maxConnections,
|
||||||
[this]() { return openConnection(); },
|
[this]() { return openConnection(); },
|
||||||
|
@ -50,13 +50,21 @@ RemoteStore::RemoteStore(const Params & params, size_t maxConnections)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
std::string RemoteStore::getUri()
|
UDSRemoteStore::UDSRemoteStore(const Params & params, size_t maxConnections)
|
||||||
|
: Store(params)
|
||||||
|
, LocalFSStore(params)
|
||||||
|
, RemoteStore(params, maxConnections)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
std::string UDSRemoteStore::getUri()
|
||||||
{
|
{
|
||||||
return "daemon";
|
return "daemon";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
ref<RemoteStore::Connection> RemoteStore::openConnection()
|
ref<RemoteStore::Connection> UDSRemoteStore::openConnection()
|
||||||
{
|
{
|
||||||
auto conn = make_ref<Connection>();
|
auto conn = make_ref<Connection>();
|
||||||
|
|
||||||
|
@ -84,46 +92,52 @@ ref<RemoteStore::Connection> RemoteStore::openConnection()
|
||||||
conn->from.fd = conn->fd.get();
|
conn->from.fd = conn->fd.get();
|
||||||
conn->to.fd = conn->fd.get();
|
conn->to.fd = conn->fd.get();
|
||||||
|
|
||||||
|
initConnection(*conn);
|
||||||
|
|
||||||
|
return conn;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void RemoteStore::initConnection(Connection & conn)
|
||||||
|
{
|
||||||
/* Send the magic greeting, check for the reply. */
|
/* Send the magic greeting, check for the reply. */
|
||||||
try {
|
try {
|
||||||
conn->to << WORKER_MAGIC_1;
|
conn.to << WORKER_MAGIC_1;
|
||||||
conn->to.flush();
|
conn.to.flush();
|
||||||
unsigned int magic = readInt(conn->from);
|
unsigned int magic = readInt(conn.from);
|
||||||
if (magic != WORKER_MAGIC_2) throw Error("protocol mismatch");
|
if (magic != WORKER_MAGIC_2) throw Error("protocol mismatch");
|
||||||
|
|
||||||
conn->daemonVersion = readInt(conn->from);
|
conn.daemonVersion = readInt(conn.from);
|
||||||
if (GET_PROTOCOL_MAJOR(conn->daemonVersion) != GET_PROTOCOL_MAJOR(PROTOCOL_VERSION))
|
if (GET_PROTOCOL_MAJOR(conn.daemonVersion) != GET_PROTOCOL_MAJOR(PROTOCOL_VERSION))
|
||||||
throw Error("Nix daemon protocol version not supported");
|
throw Error("Nix daemon protocol version not supported");
|
||||||
if (GET_PROTOCOL_MINOR(conn->daemonVersion) < 10)
|
if (GET_PROTOCOL_MINOR(conn.daemonVersion) < 10)
|
||||||
throw Error("the Nix daemon version is too old");
|
throw Error("the Nix daemon version is too old");
|
||||||
conn->to << PROTOCOL_VERSION;
|
conn.to << PROTOCOL_VERSION;
|
||||||
|
|
||||||
if (GET_PROTOCOL_MINOR(conn->daemonVersion) >= 14) {
|
if (GET_PROTOCOL_MINOR(conn.daemonVersion) >= 14) {
|
||||||
int cpu = settings.lockCPU ? lockToCurrentCPU() : -1;
|
int cpu = settings.lockCPU ? lockToCurrentCPU() : -1;
|
||||||
if (cpu != -1)
|
if (cpu != -1)
|
||||||
conn->to << 1 << cpu;
|
conn.to << 1 << cpu;
|
||||||
else
|
else
|
||||||
conn->to << 0;
|
conn.to << 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (GET_PROTOCOL_MINOR(conn->daemonVersion) >= 11)
|
if (GET_PROTOCOL_MINOR(conn.daemonVersion) >= 11)
|
||||||
conn->to << false;
|
conn.to << false;
|
||||||
|
|
||||||
conn->processStderr();
|
conn.processStderr();
|
||||||
}
|
}
|
||||||
catch (Error & e) {
|
catch (Error & e) {
|
||||||
throw Error(format("cannot start daemon worker: %1%") % e.msg());
|
throw Error(format("cannot start daemon worker: %1%") % e.msg());
|
||||||
}
|
}
|
||||||
|
|
||||||
setOptions(conn);
|
setOptions(conn);
|
||||||
|
|
||||||
return conn;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void RemoteStore::setOptions(ref<Connection> conn)
|
void RemoteStore::setOptions(Connection & conn)
|
||||||
{
|
{
|
||||||
conn->to << wopSetOptions
|
conn.to << wopSetOptions
|
||||||
<< settings.keepFailed
|
<< settings.keepFailed
|
||||||
<< settings.keepGoing
|
<< settings.keepGoing
|
||||||
<< settings.tryFallback
|
<< settings.tryFallback
|
||||||
|
@ -137,16 +151,16 @@ void RemoteStore::setOptions(ref<Connection> conn)
|
||||||
<< settings.buildCores
|
<< settings.buildCores
|
||||||
<< settings.useSubstitutes;
|
<< settings.useSubstitutes;
|
||||||
|
|
||||||
if (GET_PROTOCOL_MINOR(conn->daemonVersion) >= 12) {
|
if (GET_PROTOCOL_MINOR(conn.daemonVersion) >= 12) {
|
||||||
Settings::SettingsMap overrides = settings.getOverrides();
|
Settings::SettingsMap overrides = settings.getOverrides();
|
||||||
if (overrides["ssh-auth-sock"] == "")
|
if (overrides["ssh-auth-sock"] == "")
|
||||||
overrides["ssh-auth-sock"] = getEnv("SSH_AUTH_SOCK");
|
overrides["ssh-auth-sock"] = getEnv("SSH_AUTH_SOCK");
|
||||||
conn->to << overrides.size();
|
conn.to << overrides.size();
|
||||||
for (auto & i : overrides)
|
for (auto & i : overrides)
|
||||||
conn->to << i.first << i.second;
|
conn.to << i.first << i.second;
|
||||||
}
|
}
|
||||||
|
|
||||||
conn->processStderr();
|
conn.processStderr();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -336,27 +350,39 @@ void RemoteStore::addToStore(const ValidPathInfo & info, const ref<std::string>
|
||||||
bool repair, bool dontCheckSigs, std::shared_ptr<FSAccessor> accessor)
|
bool repair, bool dontCheckSigs, std::shared_ptr<FSAccessor> accessor)
|
||||||
{
|
{
|
||||||
auto conn(connections->get());
|
auto conn(connections->get());
|
||||||
conn->to << wopImportPaths;
|
|
||||||
|
|
||||||
StringSink sink;
|
if (GET_PROTOCOL_MINOR(conn->daemonVersion) < 18) {
|
||||||
sink << 1 // == path follows
|
conn->to << wopImportPaths;
|
||||||
;
|
|
||||||
assert(nar->size() % 8 == 0);
|
|
||||||
sink((unsigned char *) nar->data(), nar->size());
|
|
||||||
sink
|
|
||||||
<< exportMagic
|
|
||||||
<< info.path
|
|
||||||
<< info.references
|
|
||||||
<< info.deriver
|
|
||||||
<< 0 // == no legacy signature
|
|
||||||
<< 0 // == no path follows
|
|
||||||
;
|
|
||||||
|
|
||||||
StringSource source(*sink.s);
|
StringSink sink;
|
||||||
conn->processStderr(0, &source);
|
sink << 1 // == path follows
|
||||||
|
;
|
||||||
|
assert(nar->size() % 8 == 0);
|
||||||
|
sink((unsigned char *) nar->data(), nar->size());
|
||||||
|
sink
|
||||||
|
<< exportMagic
|
||||||
|
<< info.path
|
||||||
|
<< info.references
|
||||||
|
<< info.deriver
|
||||||
|
<< 0 // == no legacy signature
|
||||||
|
<< 0 // == no path follows
|
||||||
|
;
|
||||||
|
|
||||||
auto importedPaths = readStorePaths<PathSet>(*this, conn->from);
|
StringSource source(*sink.s);
|
||||||
assert(importedPaths.size() <= 1);
|
conn->processStderr(0, &source);
|
||||||
|
|
||||||
|
auto importedPaths = readStorePaths<PathSet>(*this, conn->from);
|
||||||
|
assert(importedPaths.size() <= 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
else {
|
||||||
|
conn->to << wopAddToStoreNar
|
||||||
|
<< info.path << info.deriver << printHash(info.narHash)
|
||||||
|
<< info.references << info.registrationTime << info.narSize
|
||||||
|
<< info.ultimate << info.sigs << *nar << repair << dontCheckSigs;
|
||||||
|
// FIXME: don't send nar as a string
|
||||||
|
conn->processStderr();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -553,7 +579,6 @@ RemoteStore::Connection::~Connection()
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
to.flush();
|
to.flush();
|
||||||
fd = -1;
|
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
ignoreException();
|
ignoreException();
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,7 @@ template<typename T> class Pool;
|
||||||
|
|
||||||
/* FIXME: RemoteStore is a misnomer - should be something like
|
/* FIXME: RemoteStore is a misnomer - should be something like
|
||||||
DaemonStore. */
|
DaemonStore. */
|
||||||
class RemoteStore : public LocalFSStore
|
class RemoteStore : public virtual Store
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
@ -26,8 +26,6 @@ public:
|
||||||
|
|
||||||
/* Implementations of abstract store API methods. */
|
/* Implementations of abstract store API methods. */
|
||||||
|
|
||||||
std::string getUri() override;
|
|
||||||
|
|
||||||
bool isValidPathUncached(const Path & path) override;
|
bool isValidPathUncached(const Path & path) override;
|
||||||
|
|
||||||
PathSet queryValidPaths(const PathSet & paths) override;
|
PathSet queryValidPaths(const PathSet & paths) override;
|
||||||
|
@ -87,25 +85,46 @@ public:
|
||||||
|
|
||||||
void addSignatures(const Path & storePath, const StringSet & sigs) override;
|
void addSignatures(const Path & storePath, const StringSet & sigs) override;
|
||||||
|
|
||||||
private:
|
protected:
|
||||||
|
|
||||||
struct Connection
|
struct Connection
|
||||||
{
|
{
|
||||||
AutoCloseFD fd;
|
|
||||||
FdSink to;
|
FdSink to;
|
||||||
FdSource from;
|
FdSource from;
|
||||||
unsigned int daemonVersion;
|
unsigned int daemonVersion;
|
||||||
|
|
||||||
~Connection();
|
virtual ~Connection();
|
||||||
|
|
||||||
void processStderr(Sink * sink = 0, Source * source = 0);
|
void processStderr(Sink * sink = 0, Source * source = 0);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
virtual ref<Connection> openConnection() = 0;
|
||||||
|
|
||||||
|
void initConnection(Connection & conn);
|
||||||
|
|
||||||
ref<Pool<Connection>> connections;
|
ref<Pool<Connection>> connections;
|
||||||
|
|
||||||
ref<Connection> openConnection();
|
private:
|
||||||
|
|
||||||
void setOptions(ref<Connection> conn);
|
void setOptions(Connection & conn);
|
||||||
|
};
|
||||||
|
|
||||||
|
class UDSRemoteStore : public LocalFSStore, public RemoteStore
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
UDSRemoteStore(const Params & params, size_t maxConnections = std::numeric_limits<size_t>::max());
|
||||||
|
|
||||||
|
std::string getUri() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
struct Connection : RemoteStore::Connection
|
||||||
|
{
|
||||||
|
AutoCloseFD fd;
|
||||||
|
};
|
||||||
|
|
||||||
|
ref<RemoteStore::Connection> openConnection() override;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
130
src/libstore/ssh-store.cc
Normal file
130
src/libstore/ssh-store.cc
Normal file
|
@ -0,0 +1,130 @@
|
||||||
|
#include "store-api.hh"
|
||||||
|
#include "remote-store.hh"
|
||||||
|
#include "remote-fs-accessor.hh"
|
||||||
|
#include "archive.hh"
|
||||||
|
#include "worker-protocol.hh"
|
||||||
|
#include "pool.hh"
|
||||||
|
|
||||||
|
namespace nix {
|
||||||
|
|
||||||
|
class SSHStore : public RemoteStore
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
SSHStore(string uri, const Params & params, size_t maxConnections = std::numeric_limits<size_t>::max());
|
||||||
|
|
||||||
|
std::string getUri() override;
|
||||||
|
|
||||||
|
void narFromPath(const Path & path, Sink & sink) override;
|
||||||
|
|
||||||
|
ref<FSAccessor> getFSAccessor() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
struct Connection : RemoteStore::Connection
|
||||||
|
{
|
||||||
|
Pid sshPid;
|
||||||
|
AutoCloseFD out;
|
||||||
|
AutoCloseFD in;
|
||||||
|
};
|
||||||
|
|
||||||
|
ref<RemoteStore::Connection> openConnection() override;
|
||||||
|
|
||||||
|
AutoDelete tmpDir;
|
||||||
|
|
||||||
|
Path socketPath;
|
||||||
|
|
||||||
|
Pid sshMaster;
|
||||||
|
|
||||||
|
string uri;
|
||||||
|
|
||||||
|
Path key;
|
||||||
|
};
|
||||||
|
|
||||||
|
SSHStore::SSHStore(string uri, const Params & params, size_t maxConnections)
|
||||||
|
: Store(params)
|
||||||
|
, RemoteStore(params, maxConnections)
|
||||||
|
, tmpDir(createTempDir("", "nix", true, true, 0700))
|
||||||
|
, socketPath((Path) tmpDir + "/ssh.sock")
|
||||||
|
, uri(std::move(uri))
|
||||||
|
, key(get(params, "ssh-key", ""))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
string SSHStore::getUri()
|
||||||
|
{
|
||||||
|
return "ssh://" + uri;
|
||||||
|
}
|
||||||
|
|
||||||
|
class ForwardSource : public Source
|
||||||
|
{
|
||||||
|
Source & readSource;
|
||||||
|
Sink & writeSink;
|
||||||
|
public:
|
||||||
|
ForwardSource(Source & readSource, Sink & writeSink) : readSource(readSource), writeSink(writeSink) {}
|
||||||
|
size_t read(unsigned char * data, size_t len) override
|
||||||
|
{
|
||||||
|
auto res = readSource.read(data, len);
|
||||||
|
writeSink(data, len);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
void SSHStore::narFromPath(const Path & path, Sink & sink)
|
||||||
|
{
|
||||||
|
auto conn(connections->get());
|
||||||
|
conn->to << wopNarFromPath << path;
|
||||||
|
conn->processStderr();
|
||||||
|
ParseSink ps;
|
||||||
|
auto fwd = ForwardSource(conn->from, sink);
|
||||||
|
parseDump(ps, fwd);
|
||||||
|
}
|
||||||
|
|
||||||
|
ref<FSAccessor> SSHStore::getFSAccessor()
|
||||||
|
{
|
||||||
|
return make_ref<RemoteFSAccessor>(ref<Store>(shared_from_this()));
|
||||||
|
}
|
||||||
|
|
||||||
|
ref<RemoteStore::Connection> SSHStore::openConnection()
|
||||||
|
{
|
||||||
|
if ((pid_t) sshMaster == -1) {
|
||||||
|
sshMaster = startProcess([&]() {
|
||||||
|
if (key.empty())
|
||||||
|
execlp("ssh", "ssh", "-N", "-M", "-S", socketPath.c_str(), uri.c_str(), NULL);
|
||||||
|
else
|
||||||
|
execlp("ssh", "ssh", "-N", "-M", "-S", socketPath.c_str(), "-i", key.c_str(), uri.c_str(), NULL);
|
||||||
|
throw SysError("starting ssh master");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
auto conn = make_ref<Connection>();
|
||||||
|
Pipe in, out;
|
||||||
|
in.create();
|
||||||
|
out.create();
|
||||||
|
conn->sshPid = startProcess([&]() {
|
||||||
|
if (dup2(in.readSide.get(), STDIN_FILENO) == -1)
|
||||||
|
throw SysError("duping over STDIN");
|
||||||
|
if (dup2(out.writeSide.get(), STDOUT_FILENO) == -1)
|
||||||
|
throw SysError("duping over STDOUT");
|
||||||
|
execlp("ssh", "ssh", "-S", socketPath.c_str(), uri.c_str(), "nix-daemon", "--stdio", NULL);
|
||||||
|
throw SysError("executing nix-daemon --stdio over ssh");
|
||||||
|
});
|
||||||
|
in.readSide = -1;
|
||||||
|
out.writeSide = -1;
|
||||||
|
conn->out = std::move(out.readSide);
|
||||||
|
conn->in = std::move(in.writeSide);
|
||||||
|
conn->to = FdSink(conn->in.get());
|
||||||
|
conn->from = FdSource(conn->out.get());
|
||||||
|
initConnection(*conn);
|
||||||
|
return conn;
|
||||||
|
}
|
||||||
|
|
||||||
|
static RegisterStoreImplementation regStore([](
|
||||||
|
const std::string & uri, const Store::Params & params)
|
||||||
|
-> std::shared_ptr<Store>
|
||||||
|
{
|
||||||
|
if (std::string(uri, 0, 6) != "ssh://") return 0;
|
||||||
|
return std::make_shared<SSHStore>(uri.substr(6), params);
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
|
@ -606,7 +606,7 @@ namespace nix {
|
||||||
RegisterStoreImplementation::Implementations * RegisterStoreImplementation::implementations = 0;
|
RegisterStoreImplementation::Implementations * RegisterStoreImplementation::implementations = 0;
|
||||||
|
|
||||||
|
|
||||||
ref<Store> openStoreAt(const std::string & uri_)
|
ref<Store> openStore(const std::string & uri_)
|
||||||
{
|
{
|
||||||
auto uri(uri_);
|
auto uri(uri_);
|
||||||
Store::Params params;
|
Store::Params params;
|
||||||
|
@ -629,9 +629,22 @@ ref<Store> openStoreAt(const std::string & uri_)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
ref<Store> openStore()
|
StoreType getStoreType(const std::string & uri, const std::string & stateDir)
|
||||||
{
|
{
|
||||||
return openStoreAt(getEnv("NIX_REMOTE"));
|
if (uri == "daemon") {
|
||||||
|
return tDaemon;
|
||||||
|
} else if (uri == "local") {
|
||||||
|
return tLocal;
|
||||||
|
} else if (uri == "") {
|
||||||
|
if (access(stateDir.c_str(), R_OK | W_OK) == 0)
|
||||||
|
return tLocal;
|
||||||
|
else if (pathExists(settings.nixDaemonSocketFile))
|
||||||
|
return tDaemon;
|
||||||
|
else
|
||||||
|
return tLocal;
|
||||||
|
} else {
|
||||||
|
return tOther;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -639,26 +652,14 @@ static RegisterStoreImplementation regStore([](
|
||||||
const std::string & uri, const Store::Params & params)
|
const std::string & uri, const Store::Params & params)
|
||||||
-> std::shared_ptr<Store>
|
-> std::shared_ptr<Store>
|
||||||
{
|
{
|
||||||
enum { mDaemon, mLocal, mAuto } mode;
|
switch (getStoreType(uri, get(params, "state", settings.nixStateDir))) {
|
||||||
|
case tDaemon:
|
||||||
if (uri == "daemon") mode = mDaemon;
|
return std::shared_ptr<Store>(std::make_shared<UDSRemoteStore>(params));
|
||||||
else if (uri == "local") mode = mLocal;
|
case tLocal:
|
||||||
else if (uri == "") mode = mAuto;
|
return std::shared_ptr<Store>(std::make_shared<LocalStore>(params));
|
||||||
else return 0;
|
default:
|
||||||
|
return nullptr;
|
||||||
if (mode == mAuto) {
|
|
||||||
auto stateDir = get(params, "state", settings.nixStateDir);
|
|
||||||
if (access(stateDir.c_str(), R_OK | W_OK) == 0)
|
|
||||||
mode = mLocal;
|
|
||||||
else if (pathExists(settings.nixDaemonSocketFile))
|
|
||||||
mode = mDaemon;
|
|
||||||
else
|
|
||||||
mode = mLocal;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return mode == mDaemon
|
|
||||||
? std::shared_ptr<Store>(std::make_shared<RemoteStore>(params))
|
|
||||||
: std::shared_ptr<Store>(std::make_shared<LocalStore>(params));
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
@ -679,7 +680,7 @@ std::list<ref<Store>> getDefaultSubstituters()
|
||||||
auto addStore = [&](const std::string & uri) {
|
auto addStore = [&](const std::string & uri) {
|
||||||
if (done.count(uri)) return;
|
if (done.count(uri)) return;
|
||||||
done.insert(uri);
|
done.insert(uri);
|
||||||
state->stores.push_back(openStoreAt(uri));
|
state->stores.push_back(openStore(uri));
|
||||||
};
|
};
|
||||||
|
|
||||||
for (auto uri : settings.get("substituters", Strings()))
|
for (auto uri : settings.get("substituters", Strings()))
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
#include "crypto.hh"
|
#include "crypto.hh"
|
||||||
#include "lru-cache.hh"
|
#include "lru-cache.hh"
|
||||||
#include "sync.hh"
|
#include "sync.hh"
|
||||||
|
#include "globals.hh"
|
||||||
|
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <limits>
|
#include <limits>
|
||||||
|
@ -537,7 +538,7 @@ protected:
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
class LocalFSStore : public Store
|
class LocalFSStore : public virtual Store
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
const Path rootDir;
|
const Path rootDir;
|
||||||
|
@ -604,13 +605,18 @@ void removeTempRoots();
|
||||||
If ‘uri’ is empty, it defaults to ‘direct’ or ‘daemon’ depending on
|
If ‘uri’ is empty, it defaults to ‘direct’ or ‘daemon’ depending on
|
||||||
whether the user has write access to the local Nix store/database.
|
whether the user has write access to the local Nix store/database.
|
||||||
set to true *unless* you're going to collect garbage. */
|
set to true *unless* you're going to collect garbage. */
|
||||||
ref<Store> openStoreAt(const std::string & uri);
|
ref<Store> openStore(const std::string & uri = getEnv("NIX_REMOTE"));
|
||||||
|
|
||||||
|
|
||||||
/* Open the store indicated by the ‘NIX_REMOTE’ environment variable. */
|
enum StoreType {
|
||||||
ref<Store> openStore();
|
tDaemon,
|
||||||
|
tLocal,
|
||||||
|
tOther
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
StoreType getStoreType(const std::string & uri = getEnv("NIX_REMOTE"), const std::string & stateDir = settings.nixStateDir);
|
||||||
|
|
||||||
/* Return the default substituter stores, defined by the
|
/* Return the default substituter stores, defined by the
|
||||||
‘substituters’ option and various legacy options like
|
‘substituters’ option and various legacy options like
|
||||||
‘binary-caches’. */
|
‘binary-caches’. */
|
||||||
|
|
|
@ -6,7 +6,7 @@ namespace nix {
|
||||||
#define WORKER_MAGIC_1 0x6e697863
|
#define WORKER_MAGIC_1 0x6e697863
|
||||||
#define WORKER_MAGIC_2 0x6478696f
|
#define WORKER_MAGIC_2 0x6478696f
|
||||||
|
|
||||||
#define PROTOCOL_VERSION 0x111
|
#define PROTOCOL_VERSION 0x112
|
||||||
#define GET_PROTOCOL_MAJOR(x) ((x) & 0xff00)
|
#define GET_PROTOCOL_MAJOR(x) ((x) & 0xff00)
|
||||||
#define GET_PROTOCOL_MINOR(x) ((x) & 0x00ff)
|
#define GET_PROTOCOL_MINOR(x) ((x) & 0x00ff)
|
||||||
|
|
||||||
|
@ -46,6 +46,8 @@ typedef enum {
|
||||||
wopVerifyStore = 35,
|
wopVerifyStore = 35,
|
||||||
wopBuildDerivation = 36,
|
wopBuildDerivation = 36,
|
||||||
wopAddSignatures = 37,
|
wopAddSignatures = 37,
|
||||||
|
wopNarFromPath = 38,
|
||||||
|
wopAddToStoreNar = 39
|
||||||
} WorkerOp;
|
} WorkerOp;
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <pwd.h>
|
#include <pwd.h>
|
||||||
#include <grp.h>
|
#include <grp.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
|
||||||
#if __APPLE__ || __FreeBSD__
|
#if __APPLE__ || __FreeBSD__
|
||||||
#include <sys/ucred.h>
|
#include <sys/ucred.h>
|
||||||
|
@ -29,6 +30,25 @@
|
||||||
|
|
||||||
using namespace nix;
|
using namespace nix;
|
||||||
|
|
||||||
|
#ifndef __linux__
|
||||||
|
#define SPLICE_F_MOVE 0
|
||||||
|
static ssize_t splice(int fd_in, loff_t *off_in, int fd_out, loff_t *off_out, size_t len, unsigned int flags)
|
||||||
|
{
|
||||||
|
/* We ignore most parameters, we just have them for conformance with the linux syscall */
|
||||||
|
char buf[8192];
|
||||||
|
auto read_count = read(fd_in, buf, sizeof(buf));
|
||||||
|
if (read_count == -1)
|
||||||
|
return read_count;
|
||||||
|
auto write_count = decltype<read_count>(0);
|
||||||
|
while (write_count < read_count) {
|
||||||
|
auto res = write(fd_out, buf + write_count, read_count - write_count);
|
||||||
|
if (res == -1)
|
||||||
|
return res;
|
||||||
|
write_count += res;
|
||||||
|
}
|
||||||
|
return read_count;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
static FdSource from(STDIN_FILENO);
|
static FdSource from(STDIN_FILENO);
|
||||||
static FdSink to(STDOUT_FILENO);
|
static FdSink to(STDOUT_FILENO);
|
||||||
|
@ -556,6 +576,37 @@ static void performOp(ref<LocalStore> store, bool trusted, unsigned int clientVe
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case wopNarFromPath: {
|
||||||
|
auto path = readStorePath(*store, from);
|
||||||
|
startWork();
|
||||||
|
stopWork();
|
||||||
|
dumpPath(path, to);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case wopAddToStoreNar: {
|
||||||
|
ValidPathInfo info;
|
||||||
|
info.path = readStorePath(*store, from);
|
||||||
|
info.deriver = readString(from);
|
||||||
|
if (!info.deriver.empty())
|
||||||
|
store->assertStorePath(info.deriver);
|
||||||
|
info.narHash = parseHash(htSHA256, readString(from));
|
||||||
|
info.references = readStorePaths<PathSet>(*store, from);
|
||||||
|
info.registrationTime = readInt(from);
|
||||||
|
info.narSize = readLongLong(from);
|
||||||
|
info.ultimate = readLongLong(from);
|
||||||
|
info.sigs = readStrings<StringSet>(from);
|
||||||
|
auto nar = make_ref<std::string>(readString(from));
|
||||||
|
auto repair = readInt(from) ? true : false;
|
||||||
|
auto dontCheckSigs = readInt(from) ? true : false;
|
||||||
|
if (!trusted && dontCheckSigs)
|
||||||
|
dontCheckSigs = false;
|
||||||
|
startWork();
|
||||||
|
store->addToStore(info, nar, repair, dontCheckSigs, nullptr);
|
||||||
|
stopWork();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw Error(format("invalid operation %1%") % op);
|
throw Error(format("invalid operation %1%") % op);
|
||||||
}
|
}
|
||||||
|
@ -885,6 +936,8 @@ int main(int argc, char * * argv)
|
||||||
return handleExceptions(argv[0], [&]() {
|
return handleExceptions(argv[0], [&]() {
|
||||||
initNix();
|
initNix();
|
||||||
|
|
||||||
|
auto stdio = false;
|
||||||
|
|
||||||
parseCmdLine(argc, argv, [&](Strings::iterator & arg, const Strings::iterator & end) {
|
parseCmdLine(argc, argv, [&](Strings::iterator & arg, const Strings::iterator & end) {
|
||||||
if (*arg == "--daemon")
|
if (*arg == "--daemon")
|
||||||
; /* ignored for backwards compatibility */
|
; /* ignored for backwards compatibility */
|
||||||
|
@ -892,10 +945,62 @@ int main(int argc, char * * argv)
|
||||||
showManPage("nix-daemon");
|
showManPage("nix-daemon");
|
||||||
else if (*arg == "--version")
|
else if (*arg == "--version")
|
||||||
printVersion("nix-daemon");
|
printVersion("nix-daemon");
|
||||||
|
else if (*arg == "--stdio")
|
||||||
|
stdio = true;
|
||||||
else return false;
|
else return false;
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
|
||||||
daemonLoop(argv);
|
if (stdio) {
|
||||||
|
if (getStoreType() == tDaemon) {
|
||||||
|
/* Forward on this connection to the real daemon */
|
||||||
|
auto socketPath = settings.nixDaemonSocketFile;
|
||||||
|
auto s = socket(PF_UNIX, SOCK_STREAM, 0);
|
||||||
|
if (s == -1)
|
||||||
|
throw SysError("creating Unix domain socket");
|
||||||
|
|
||||||
|
auto socketDir = dirOf(socketPath);
|
||||||
|
if (chdir(socketDir.c_str()) == -1)
|
||||||
|
throw SysError(format("changing to socket directory ‘%1%’") % socketDir);
|
||||||
|
|
||||||
|
auto socketName = baseNameOf(socketPath);
|
||||||
|
auto addr = sockaddr_un{};
|
||||||
|
addr.sun_family = AF_UNIX;
|
||||||
|
if (socketName.size() + 1 >= sizeof(addr.sun_path))
|
||||||
|
throw Error(format("socket name %1% is too long") % socketName);
|
||||||
|
strcpy(addr.sun_path, socketName.c_str());
|
||||||
|
|
||||||
|
if (connect(s, (struct sockaddr *) &addr, sizeof(addr)) == -1)
|
||||||
|
throw SysError(format("cannot connect to daemon at %1%") % socketPath);
|
||||||
|
|
||||||
|
auto nfds = (s > STDIN_FILENO ? s : STDIN_FILENO) + 1;
|
||||||
|
while (true) {
|
||||||
|
fd_set fds;
|
||||||
|
FD_ZERO(&fds);
|
||||||
|
FD_SET(s, &fds);
|
||||||
|
FD_SET(STDIN_FILENO, &fds);
|
||||||
|
if (select(nfds, &fds, nullptr, nullptr, nullptr) == -1)
|
||||||
|
throw SysError("waiting for data from client or server");
|
||||||
|
if (FD_ISSET(s, &fds)) {
|
||||||
|
auto res = splice(s, nullptr, STDOUT_FILENO, nullptr, SIZE_MAX, SPLICE_F_MOVE);
|
||||||
|
if (res == -1)
|
||||||
|
throw SysError("splicing data from daemon socket to stdout");
|
||||||
|
else if (res == 0)
|
||||||
|
throw EndOfFile("unexpected EOF from daemon socket");
|
||||||
|
}
|
||||||
|
if (FD_ISSET(STDIN_FILENO, &fds)) {
|
||||||
|
auto res = splice(STDIN_FILENO, nullptr, s, nullptr, SIZE_MAX, SPLICE_F_MOVE);
|
||||||
|
if (res == -1)
|
||||||
|
throw SysError("splicing data from stdin to daemon socket");
|
||||||
|
else if (res == 0)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
processConnection(true);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
daemonLoop(argv);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -81,7 +81,7 @@ StoreCommand::StoreCommand()
|
||||||
|
|
||||||
void StoreCommand::run()
|
void StoreCommand::run()
|
||||||
{
|
{
|
||||||
run(openStoreAt(storeUri));
|
run(openStore(storeUri));
|
||||||
}
|
}
|
||||||
|
|
||||||
StorePathsCommand::StorePathsCommand()
|
StorePathsCommand::StorePathsCommand()
|
||||||
|
|
|
@ -43,8 +43,8 @@ struct CmdCopy : StorePathsCommand
|
||||||
if (srcUri.empty() && dstUri.empty())
|
if (srcUri.empty() && dstUri.empty())
|
||||||
throw UsageError("you must pass ‘--from’ and/or ‘--to’");
|
throw UsageError("you must pass ‘--from’ and/or ‘--to’");
|
||||||
|
|
||||||
ref<Store> srcStore = srcUri.empty() ? store : openStoreAt(srcUri);
|
ref<Store> srcStore = srcUri.empty() ? store : openStore(srcUri);
|
||||||
ref<Store> dstStore = dstUri.empty() ? store : openStoreAt(dstUri);
|
ref<Store> dstStore = dstUri.empty() ? store : openStore(dstUri);
|
||||||
|
|
||||||
std::string copiedLabel = "copied";
|
std::string copiedLabel = "copied";
|
||||||
|
|
||||||
|
|
|
@ -35,7 +35,7 @@ struct CmdCopySigs : StorePathsCommand
|
||||||
// FIXME: factor out commonality with MixVerify.
|
// FIXME: factor out commonality with MixVerify.
|
||||||
std::vector<ref<Store>> substituters;
|
std::vector<ref<Store>> substituters;
|
||||||
for (auto & s : substituterUris)
|
for (auto & s : substituterUris)
|
||||||
substituters.push_back(openStoreAt(s));
|
substituters.push_back(openStore(s));
|
||||||
|
|
||||||
ThreadPool pool;
|
ThreadPool pool;
|
||||||
|
|
||||||
|
|
|
@ -52,7 +52,7 @@ struct CmdVerify : StorePathsCommand
|
||||||
{
|
{
|
||||||
std::vector<ref<Store>> substituters;
|
std::vector<ref<Store>> substituters;
|
||||||
for (auto & s : substituterUris)
|
for (auto & s : substituterUris)
|
||||||
substituters.push_back(openStoreAt(s));
|
substituters.push_back(openStore(s));
|
||||||
|
|
||||||
auto publicKeys = getDefaultPublicKeys();
|
auto publicKeys = getDefaultPublicKeys();
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue