Skip to content

Commit

Permalink
lift the addPermRoot API to GcStore and the daemon protocol
Browse files Browse the repository at this point in the history
  • Loading branch information
mupdt committed Mar 6, 2023
1 parent 0507462 commit 12b2c73
Show file tree
Hide file tree
Showing 11 changed files with 117 additions and 41 deletions.
13 changes: 13 additions & 0 deletions src/libstore/daemon.cc
Original file line number Diff line number Diff line change
Expand Up @@ -660,6 +660,19 @@ static void performOp(TunnelLogger * logger, ref<Store> store,
break;
}

case wopAddPermRoot: {
if (!settings.allowPermRoots)
throw Error("Creating permanent GC roots is not allowed.");
auto storePath = store->parseStorePath(readString(from));
Path gcRoot = absPath(readString(from));
logger->startWork();
auto & gcStore = require<GcStore>(*store);
gcStore.addPermRoot(storePath, gcRoot);
logger->stopWork();
to << gcRoot;
break;
}

case wopAddIndirectRoot: {
Path path = absPath(readString(from));

Expand Down
3 changes: 3 additions & 0 deletions src/libstore/gc-store.hh
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,9 @@ struct GcStore : public virtual Store
`path' has disappeared. */
virtual void addIndirectRoot(const Path & path) = 0;

/* Register a permanent GC root. */
virtual Path addPermRoot(const StorePath & storePath, const Path & gcRoot) = 0;

/* Find the roots of the garbage collector. Each root is a pair
(link, storepath) where `link' is the path of the symlink
outside of the Nix store that point to `storePath'. If
Expand Down
16 changes: 16 additions & 0 deletions src/libstore/globals.hh
Original file line number Diff line number Diff line change
Expand Up @@ -975,6 +975,22 @@ public:
| `~/.nix-channels` | `$XDG_STATE_HOME/nix/channels` |
)"
};

Setting<bool> allowPermRoots{
this, false, "allow-perm-roots",
R"(
If set to `true`, the nix daemon will accept requests to create perm
roots and process them. Default is `false`.
> **Warning**
>
> This allows the nix daemon to create symlinks at any absolute path.
> This setting should not be enabled on multi-user nix daemons as this
> could mean that users could create symlinks anywhere.
>
> This setting is used automatically by the remote out-links feature
> and should never be turned on by the user.
)"};
};


Expand Down
2 changes: 1 addition & 1 deletion src/libstore/local-fs-store.hh
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ public:
ref<FSAccessor> getFSAccessor() override;

/* Register a permanent GC root. */
Path addPermRoot(const StorePath & storePath, const Path & gcRoot);
Path addPermRoot(const StorePath & storePath, const Path & gcRoot) override;

virtual Path getRealStoreDir() { return realStoreDir; }

Expand Down
13 changes: 13 additions & 0 deletions src/libstore/remote-store.cc
Original file line number Diff line number Diff line change
Expand Up @@ -948,6 +948,19 @@ void RemoteStore::addTempRoot(const StorePath & path)
}


Path RemoteStore::addPermRoot(const StorePath & path, const Path & gcRoot)
{
if (!outLinks.get()) {
debug("Not creating permanent GC root '%s'.", gcRoot);
return gcRoot;
}
auto conn(getConnection());
conn->to << wopAddPermRoot << printStorePath(path) << gcRoot;
conn.processStderr();
return readString(conn->from);
}


void RemoteStore::addIndirectRoot(const Path & path)
{
auto conn(getConnection());
Expand Down
5 changes: 5 additions & 0 deletions src/libstore/remote-store.hh
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ struct RemoteStoreConfig : virtual StoreConfig

const Setting<unsigned int> maxConnectionAge{(StoreConfig*) this, std::numeric_limits<unsigned int>::max(),
"max-connection-age", "number of seconds to reuse a connection"};

const Setting<bool> outLinks{(StoreConfig*) this, false,
"out-links", "Indicates whether out-links (permanent GC roots) should be created on the remote."};
};

/* FIXME: RemoteStore is a misnomer - should be something like
Expand Down Expand Up @@ -119,6 +122,8 @@ public:

void addIndirectRoot(const Path & path) override;

Path addPermRoot(const StorePath & storePath, const Path & gcRoot) override;

Roots findRoots(bool censor) override;

void collectGarbage(const GCOptions & options, GCResults & results) override;
Expand Down
10 changes: 9 additions & 1 deletion src/libstore/ssh-store.cc
Original file line number Diff line number Diff line change
Expand Up @@ -85,9 +85,17 @@ class SSHStore : public virtual SSHStoreConfig, public virtual RemoteStore
ref<RemoteStore::Connection> SSHStore::openConnection()
{
auto conn = make_ref<Connection>();

// By default, the daemon we spawn below forwards ops to the final remote
// store. In certain circumstances, however, we require the daemon to
// process the ops and perform some actions before it forwards them.
bool processOpsRemotely = outLinks.get();

conn->sshConn = master.startCommand(
fmt("%s --stdio", remoteProgram)
+ (remoteStore.get() == "" ? "" : " --store " + shellEscape(remoteStore.get())));
+ (remoteStore.get() == "" ? "" : " --store " + shellEscape(remoteStore.get()))
+ (processOpsRemotely ? " --process-ops" : "")
+ (outLinks.get() ? " --allow-perm-roots" : ""));
conn->to = FdSink(conn->sshConn->in.get());
conn->from = FdSource(conn->sshConn->out.get());
return conn;
Expand Down
3 changes: 3 additions & 0 deletions src/libstore/uds-remote-store.hh
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ public:
void narFromPath(const StorePath & path, Sink & sink) override
{ LocalFSStore::narFromPath(path, sink); }

Path addPermRoot(const StorePath & storePath, const Path & gcRoot) override
{ return LocalFSStore::addPermRoot(storePath, gcRoot); }

private:

struct Connection : RemoteStore::Connection
Expand Down
1 change: 1 addition & 0 deletions src/libstore/worker-protocol.hh
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ typedef enum {
wopAddMultipleToStore = 44,
wopAddBuildLog = 45,
wopBuildPathsWithResults = 46,
wopAddPermRoot = 47,
} WorkerOp;


Expand Down
2 changes: 1 addition & 1 deletion src/nix/build.cc
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ struct CmdBuild : InstallablesCommand, MixDryRun, MixJSON, MixProfile
if (json) logger->cout("%s", builtPathsWithResultToJSON(buildables, store).dump());

if (outLink != "")
if (auto store2 = store.dynamic_pointer_cast<LocalFSStore>())
if (auto store2 = store.dynamic_pointer_cast<GcStore>())
for (const auto & [_i, buildable] : enumerate(buildables)) {
auto i = _i;
std::visit(overloaded {
Expand Down
90 changes: 52 additions & 38 deletions src/nix/daemon.cc
Original file line number Diff line number Diff line change
Expand Up @@ -294,45 +294,56 @@ static void daemonLoop()
}
}

static void runDaemon(bool stdio)
static void forwardStdioConnection(RemoteStore& store) {
auto conn = store.openConnectionWrapper();
int from = conn->from.fd;
int to = conn->to.fd;

auto nfds = std::max(from, STDIN_FILENO) + 1;
while (true) {
fd_set fds;
FD_ZERO(&fds);
FD_SET(from, &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(from, &fds)) {
auto res = splice(from, nullptr, STDOUT_FILENO, nullptr, SSIZE_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, to, nullptr, SSIZE_MAX, SPLICE_F_MOVE);
if (res == -1)
throw SysError("splicing data from stdin to daemon socket");
else if (res == 0)
return;
}
}
}

static void processStdioConnection(ref<Store> store)
{
FdSource from(STDIN_FILENO);
FdSink to(STDOUT_FILENO);
/* Auth hook is empty because in this mode we blindly trust the
standard streams. Limiting access to those is explicitly
not `nix-daemon`'s responsibility. */
processConnection(store, from, to, Trusted, NotRecursive);
}

static void runDaemon(bool stdio, bool processOps = false)
{
if (stdio) {
if (auto store = openUncachedStore().dynamic_pointer_cast<RemoteStore>()) {
auto conn = store->openConnectionWrapper();
int from = conn->from.fd;
int to = conn->to.fd;

auto nfds = std::max(from, STDIN_FILENO) + 1;
while (true) {
fd_set fds;
FD_ZERO(&fds);
FD_SET(from, &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(from, &fds)) {
auto res = splice(from, nullptr, STDOUT_FILENO, nullptr, SSIZE_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, to, nullptr, SSIZE_MAX, SPLICE_F_MOVE);
if (res == -1)
throw SysError("splicing data from stdin to daemon socket");
else if (res == 0)
return;
}
}
} else {
FdSource from(STDIN_FILENO);
FdSink to(STDOUT_FILENO);
/* Auth hook is empty because in this mode we blindly trust the
standard streams. Limiting access to those is explicitly
not `nix-daemon`'s responsibility. */
processConnection(openUncachedStore(), from, to, Trusted, NotRecursive);
}
auto store = openUncachedStore();
std::shared_ptr<RemoteStore> remoteStore;

if (!processOps && (remoteStore = store.dynamic_pointer_cast<RemoteStore>()))
forwardStdioConnection(*remoteStore);
else
processStdioConnection(store);
} else
daemonLoop();
}
Expand All @@ -341,6 +352,7 @@ static int main_nix_daemon(int argc, char * * argv)
{
{
auto stdio = false;
auto processOps = false;

parseCmdLine(argc, argv, [&](Strings::iterator & arg, const Strings::iterator & end) {
if (*arg == "--daemon")
Expand All @@ -351,11 +363,13 @@ static int main_nix_daemon(int argc, char * * argv)
printVersion("nix-daemon");
else if (*arg == "--stdio")
stdio = true;
else if (*arg == "--process-ops")
processOps = true;
else return false;
return true;
});

runDaemon(stdio);
runDaemon(stdio, processOps);

return 0;
}
Expand Down

0 comments on commit 12b2c73

Please sign in to comment.