Add 'nix daemon' command

This commit is contained in:
Eelco Dolstra 2021-01-14 00:05:04 +01:00
parent 28ef6ebf91
commit 7a472a76d4
5 changed files with 98 additions and 44 deletions

View file

@ -13,6 +13,8 @@ namespace nix {
extern std::string programPath; extern std::string programPath;
extern char * * savedArgv;
class EvalState; class EvalState;
struct Pos; struct Pos;
class Store; class Store;

View file

@ -1,3 +1,4 @@
#include "command.hh"
#include "shared.hh" #include "shared.hh"
#include "local-store.hh" #include "local-store.hh"
#include "remote-store.hh" #include "remote-store.hh"
@ -150,7 +151,7 @@ static ref<Store> openUncachedStore()
} }
static void daemonLoop(char * * argv) static void daemonLoop()
{ {
if (chdir("/") == -1) if (chdir("/") == -1)
throw SysError("cannot change current directory"); throw SysError("cannot change current directory");
@ -232,9 +233,9 @@ static void daemonLoop(char * * argv)
setSigChldAction(false); setSigChldAction(false);
// For debugging, stuff the pid into argv[1]. // For debugging, stuff the pid into argv[1].
if (peer.pidKnown && argv[1]) { if (peer.pidKnown && savedArgv[1]) {
string processName = std::to_string(peer.pid); string processName = std::to_string(peer.pid);
strncpy(argv[1], processName.c_str(), strlen(argv[1])); strncpy(savedArgv[1], processName.c_str(), strlen(savedArgv[1]));
} }
// Handle the connection. // Handle the connection.
@ -264,6 +265,48 @@ static void daemonLoop(char * * argv)
} }
} }
static void runDaemon(bool stdio)
{
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, [&](Store & _){});
}
} else
daemonLoop();
}
static int main_nix_daemon(int argc, char * * argv) static int main_nix_daemon(int argc, char * * argv)
{ {
@ -285,49 +328,34 @@ static int main_nix_daemon(int argc, char * * argv)
initPlugins(); initPlugins();
if (stdio) { runDaemon(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 0;
}
}
} 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, [&](Store & _){});
}
} else {
daemonLoop(argv);
}
return 0; return 0;
} }
} }
static RegisterLegacyCommand r_nix_daemon("nix-daemon", main_nix_daemon); static RegisterLegacyCommand r_nix_daemon("nix-daemon", main_nix_daemon);
struct CmdDaemon : StoreCommand
{
std::string description() override
{
return "daemon to perform store operations on behalf of non-root clients";
}
Category category() override { return catUtility; }
std::string doc() override
{
return
#include "daemon.md"
;
}
void run(ref<Store> store) override
{
runDaemon(false);
}
};
static auto rCmdDaemon = registerCommand2<CmdDaemon>({"daemon"});

21
src/nix/daemon.md Normal file
View file

@ -0,0 +1,21 @@
R""(
# Example
* Run the daemon in the foreground:
```console
# nix daemon
```
# Description
This command runs the Nix daemon, which is a required component in
multi-user Nix installations. It performs build actions and other
operations on the Nix store on behalf of non-root users. Usually you
don't run the daemon directly; instead it's managed by a service
management framework such as `systemd`.
Note that this daemon does not fork into the background.
)""

View file

@ -52,6 +52,7 @@ static bool haveInternet()
} }
std::string programPath; std::string programPath;
char * * savedArgv;
struct NixArgs : virtual MultiCommand, virtual MixCommonArgs struct NixArgs : virtual MultiCommand, virtual MixCommonArgs
{ {
@ -232,6 +233,8 @@ static auto rCmdHelp = registerCommand<CmdHelp>("help");
void mainWrapped(int argc, char * * argv) void mainWrapped(int argc, char * * argv)
{ {
savedArgv = argv;
/* The chroot helper needs to be run before any threads have been /* The chroot helper needs to be run before any threads have been
started. */ started. */
if (argc > 0 && argv[0] == chrootHelperName) { if (argc > 0 && argv[0] == chrootHelperName) {

View file

@ -73,7 +73,7 @@ startDaemon() {
# Start the daemon, wait for the socket to appear. !!! # Start the daemon, wait for the socket to appear. !!!
# nix-daemon should have an option to fork into the background. # nix-daemon should have an option to fork into the background.
rm -f $NIX_STATE_DIR/daemon-socket/socket rm -f $NIX_STATE_DIR/daemon-socket/socket
nix-daemon & nix daemon &
for ((i = 0; i < 30; i++)); do for ((i = 0; i < 30; i++)); do
if [ -e $NIX_DAEMON_SOCKET_PATH ]; then break; fi if [ -e $NIX_DAEMON_SOCKET_PATH ]; then break; fi
sleep 1 sleep 1