summaryrefslogtreecommitdiff
path: root/nix
diff options
context:
space:
mode:
Diffstat (limited to 'nix')
-rw-r--r--nix/libstore/build.cc146
-rw-r--r--nix/libstore/local-store.cc117
-rw-r--r--nix/libutil/util.cc84
-rw-r--r--nix/libutil/util.hh25
4 files changed, 218 insertions, 154 deletions
diff --git a/nix/libstore/build.cc b/nix/libstore/build.cc
index 29266f1dd6..88f8d11103 100644
--- a/nix/libstore/build.cc
+++ b/nix/libstore/build.cc
@@ -80,12 +80,9 @@ namespace nix {
using std::map;
-static string pathNullDevice = "/dev/null";
-
-
/* Forward definition. */
class Worker;
-struct HookInstance;
+struct Agent;
/* A pointer to a goal. */
@@ -265,7 +262,7 @@ public:
LocalStore & store;
- std::shared_ptr<HookInstance> hook;
+ std::shared_ptr<Agent> hook;
Worker(LocalStore & store);
~Worker();
@@ -397,33 +394,6 @@ void Goal::trace(const format & f)
//////////////////////////////////////////////////////////////////////
-/* Common initialisation performed in child processes. */
-static void commonChildInit(Pipe & logPipe)
-{
- /* Put the child in a separate session (and thus a separate
- process group) so that it has no controlling terminal (meaning
- that e.g. ssh cannot open /dev/tty) and it doesn't receive
- terminal signals. */
- if (setsid() == -1)
- throw SysError(format("creating a new session"));
-
- /* Dup the write side of the logger pipe into stderr. */
- if (dup2(logPipe.writeSide, STDERR_FILENO) == -1)
- throw SysError("cannot pipe standard error into log file");
-
- /* Dup stderr to stdout. */
- if (dup2(STDERR_FILENO, STDOUT_FILENO) == -1)
- throw SysError("cannot dup stderr into stdout");
-
- /* Reroute stdin to /dev/null. */
- int fdDevNull = open(pathNullDevice.c_str(), O_RDWR);
- if (fdDevNull == -1)
- throw SysError(format("cannot open `%1%'") % pathNullDevice);
- if (dup2(fdDevNull, STDIN_FILENO) == -1)
- throw SysError("cannot dup null device into stdin");
- close(fdDevNull);
-}
-
/* Restore default handling of SIGPIPE, otherwise some programs will
randomly say "Broken pipe". */
static void restoreSIGPIPE()
@@ -586,87 +556,6 @@ void UserLock::kill()
killUser(uid);
}
-
-//////////////////////////////////////////////////////////////////////
-
-
-struct HookInstance
-{
- /* Pipes for talking to the build hook. */
- Pipe toHook;
-
- /* Pipe for the hook's standard output/error. */
- Pipe fromHook;
-
- /* Pipe for the builder's standard output/error. */
- Pipe builderOut;
-
- /* The process ID of the hook. */
- Pid pid;
-
- HookInstance();
-
- ~HookInstance();
-};
-
-
-HookInstance::HookInstance()
-{
- debug("starting build hook");
-
- const Path &buildHook = settings.guixProgram;
-
- /* Create a pipe to get the output of the child. */
- fromHook.create();
-
- /* Create the communication pipes. */
- toHook.create();
-
- /* Create a pipe to get the output of the builder. */
- builderOut.create();
-
- /* Fork the hook. */
- pid = startProcess([&]() {
-
- commonChildInit(fromHook);
-
- if (chdir("/") == -1) throw SysError("changing into `/");
-
- /* Dup the communication pipes. */
- if (dup2(toHook.readSide, STDIN_FILENO) == -1)
- throw SysError("dupping to-hook read side");
-
- /* Use fd 4 for the builder's stdout/stderr. */
- if (dup2(builderOut.writeSide, 4) == -1)
- throw SysError("dupping builder's stdout/stderr");
-
- execl(buildHook.c_str(), buildHook.c_str(), "offload",
- settings.thisSystem.c_str(),
- (format("%1%") % settings.maxSilentTime).str().c_str(),
- (format("%1%") % settings.printBuildTrace).str().c_str(),
- (format("%1%") % settings.buildTimeout).str().c_str(),
- NULL);
-
- throw SysError(format("executing `%1% offload'") % buildHook);
- });
-
- pid.setSeparatePG(true);
- fromHook.writeSide.close();
- toHook.readSide.close();
-}
-
-
-HookInstance::~HookInstance()
-{
- try {
- toHook.writeSide.close();
- pid.kill(true);
- } catch (...) {
- ignoreException();
- }
-}
-
-
//////////////////////////////////////////////////////////////////////
@@ -760,7 +649,7 @@ private:
Pipe builderOut;
/* The build hook. */
- std::shared_ptr<HookInstance> hook;
+ std::shared_ptr<Agent> hook;
/* Whether we're currently doing a chroot build. */
bool useChroot;
@@ -1440,7 +1329,7 @@ void DerivationGoal::buildDone()
/* Close the read side of the logger pipe. */
if (hook) {
hook->builderOut.readSide.close();
- hook->fromHook.readSide.close();
+ hook->fromAgent.readSide.close();
}
else builderOut.readSide.close();
@@ -1587,8 +1476,17 @@ HookReply DerivationGoal::tryBuildHook()
{
if (!settings.useBuildHook) return rpDecline;
- if (!worker.hook)
- worker.hook = std::shared_ptr<HookInstance>(new HookInstance);
+ if (!worker.hook) {
+ Strings args = {
+ "offload",
+ settings.thisSystem.c_str(),
+ (format("%1%") % settings.maxSilentTime).str().c_str(),
+ (format("%1%") % settings.printBuildTrace).str().c_str(),
+ (format("%1%") % settings.buildTimeout).str().c_str()
+ };
+
+ worker.hook = std::make_shared<Agent>(settings.guixProgram, args);
+ }
/* Tell the hook about system features (beyond the system type)
required from the build machine. (The hook could parse the
@@ -1597,7 +1495,7 @@ HookReply DerivationGoal::tryBuildHook()
foreach (Strings::iterator, i, features) checkStoreName(*i); /* !!! abuse */
/* Send the request to the hook. */
- writeLine(worker.hook->toHook.writeSide, (format("%1% %2% %3% %4%")
+ writeLine(worker.hook->toAgent.writeSide, (format("%1% %2% %3% %4%")
% (worker.getNrLocalBuilds() < settings.maxBuildJobs ? "1" : "0")
% drv.platform % drvPath % concatStringsSep(",", features)).str());
@@ -1605,7 +1503,7 @@ HookReply DerivationGoal::tryBuildHook()
whether the hook wishes to perform the build. */
string reply;
while (true) {
- string s = readLine(worker.hook->fromHook.readSide);
+ string s = readLine(worker.hook->fromAgent.readSide);
if (string(s, 0, 2) == "# ") {
reply = string(s, 2);
break;
@@ -1637,21 +1535,21 @@ HookReply DerivationGoal::tryBuildHook()
string s;
foreach (PathSet::iterator, i, allInputs) { s += *i; s += ' '; }
- writeLine(hook->toHook.writeSide, s);
+ writeLine(hook->toAgent.writeSide, s);
/* Tell the hooks the missing outputs that have to be copied back
from the remote system. */
s = "";
foreach (PathSet::iterator, i, missingPaths) { s += *i; s += ' '; }
- writeLine(hook->toHook.writeSide, s);
+ writeLine(hook->toAgent.writeSide, s);
- hook->toHook.writeSide.close();
+ hook->toAgent.writeSide.close();
/* Create the log file and pipe. */
Path logFile = openLogFile();
set<int> fds;
- fds.insert(hook->fromHook.readSide);
+ fds.insert(hook->fromAgent.readSide);
fds.insert(hook->builderOut.readSide);
worker.childStarted(shared_from_this(), hook->pid, fds, false, true);
@@ -2785,7 +2683,7 @@ void DerivationGoal::handleChildOutput(int fd, const string & data)
writeFull(fdLogFile, data);
}
- if (hook && fd == hook->fromHook.readSide)
+ if (hook && fd == hook->fromAgent.readSide)
writeToStderr(prefix + data);
}
diff --git a/nix/libstore/local-store.cc b/nix/libstore/local-store.cc
index 7a520925e5..8c479002ec 100644
--- a/nix/libstore/local-store.cc
+++ b/nix/libstore/local-store.cc
@@ -21,6 +21,7 @@
#include <stdio.h>
#include <time.h>
#include <grp.h>
+#include <ctype.h>
#if HAVE_UNSHARE && HAVE_STATVFS && HAVE_SYS_MOUNT_H
#include <sched.h>
@@ -1231,11 +1232,91 @@ static void checkSecrecy(const Path & path)
}
-static std::string runAuthenticationProgram(const Strings & args)
+/* Return the authentication agent, a "guix authenticate" process started
+ lazily. */
+static std::shared_ptr<Agent> authenticationAgent()
{
- Strings fullArgs = { "authenticate" };
- fullArgs.insert(fullArgs.end(), args.begin(), args.end()); // append
- return runProgram(settings.guixProgram, false, fullArgs);
+ static std::shared_ptr<Agent> agent;
+
+ if (!agent) {
+ Strings args = { "authenticate" };
+ agent = std::make_shared<Agent>(settings.guixProgram, args);
+ }
+
+ return agent;
+}
+
+/* Read an integer and the byte that immediately follows it from FD. Return
+ the integer. */
+static int readInteger(int fd)
+{
+ string str;
+
+ while (1) {
+ char ch;
+ ssize_t rd = read(fd, &ch, 1);
+ if (rd == -1) {
+ if (errno != EINTR)
+ throw SysError("reading an integer");
+ } else if (rd == 0)
+ throw EndOfFile("unexpected EOF reading an integer");
+ else {
+ if (isdigit(ch)) {
+ str += ch;
+ } else {
+ break;
+ }
+ }
+ }
+
+ return stoi(str);
+}
+
+/* Read from FD a reply coming from 'guix authenticate'. The reply has the
+ form "CODE LEN:STR". CODE is an integer, where zero indicates success.
+ LEN specifies the length in bytes of the string that immediately
+ follows. */
+static std::string readAuthenticateReply(int fd)
+{
+ int code = readInteger(fd);
+ int len = readInteger(fd);
+
+ string str;
+ str.resize(len);
+ readFull(fd, (unsigned char *) &str[0], len);
+
+ if (code == 0)
+ return str;
+ else
+ throw Error(str);
+}
+
+/* Sign HASH with the key stored in file SECRETKEY. Return the signature as a
+ string, or raise an exception upon error. */
+static std::string signHash(const string &secretKey, const Hash &hash)
+{
+ auto agent = authenticationAgent();
+ auto hexHash = printHash(hash);
+
+ writeLine(agent->toAgent.writeSide,
+ (format("sign %1%:%2% %3%:%4%")
+ % secretKey.size() % secretKey
+ % hexHash.size() % hexHash).str());
+
+ return readAuthenticateReply(agent->fromAgent.readSide);
+}
+
+/* Verify SIGNATURE and return the base16-encoded hash over which it was
+ computed. */
+static std::string verifySignature(const string &signature)
+{
+ auto agent = authenticationAgent();
+
+ writeLine(agent->toAgent.writeSide,
+ (format("verify %1%:%2%")
+ % signature.size() % signature).str());
+
+ return readAuthenticateReply(agent->fromAgent.readSide);
}
void LocalStore::exportPath(const Path & path, bool sign,
@@ -1277,23 +1358,10 @@ void LocalStore::exportPath(const Path & path, bool sign,
writeInt(1, hashAndWriteSink);
- Path tmpDir = createTempDir();
- AutoDelete delTmp(tmpDir);
- Path hashFile = tmpDir + "/hash";
- writeFile(hashFile, printHash(hash));
-
Path secretKey = settings.nixConfDir + "/signing-key.sec";
checkSecrecy(secretKey);
- Strings args;
- args.push_back("rsautl");
- args.push_back("-sign");
- args.push_back("-inkey");
- args.push_back(secretKey);
- args.push_back("-in");
- args.push_back(hashFile);
-
- string signature = runAuthenticationProgram(args);
+ string signature = signHash(secretKey, hash);
writeString(signature, hashAndWriteSink);
@@ -1372,18 +1440,7 @@ Path LocalStore::importPath(bool requireSignature, Source & source)
string signature = readString(hashAndReadSource);
if (requireSignature) {
- Path sigFile = tmpDir + "/sig";
- writeFile(sigFile, signature);
-
- Strings args;
- args.push_back("rsautl");
- args.push_back("-verify");
- args.push_back("-inkey");
- args.push_back(settings.nixConfDir + "/signing-key.pub");
- args.push_back("-pubin");
- args.push_back("-in");
- args.push_back(sigFile);
- string hash2 = runAuthenticationProgram(args);
+ string hash2 = verifySignature(signature);
/* Note: runProgram() throws an exception if the signature
is invalid. */
diff --git a/nix/libutil/util.cc b/nix/libutil/util.cc
index 17d145b4c6..59a2981359 100644
--- a/nix/libutil/util.cc
+++ b/nix/libutil/util.cc
@@ -1142,5 +1142,89 @@ void ignoreException()
}
}
+static const string pathNullDevice = "/dev/null";
+
+/* Common initialisation performed in child processes. */
+void commonChildInit(Pipe & logPipe)
+{
+ /* Put the child in a separate session (and thus a separate
+ process group) so that it has no controlling terminal (meaning
+ that e.g. ssh cannot open /dev/tty) and it doesn't receive
+ terminal signals. */
+ if (setsid() == -1)
+ throw SysError(format("creating a new session"));
+
+ /* Dup the write side of the logger pipe into stderr. */
+ if (dup2(logPipe.writeSide, STDERR_FILENO) == -1)
+ throw SysError("cannot pipe standard error into log file");
+
+ /* Dup stderr to stdout. */
+ if (dup2(STDERR_FILENO, STDOUT_FILENO) == -1)
+ throw SysError("cannot dup stderr into stdout");
+
+ /* Reroute stdin to /dev/null. */
+ int fdDevNull = open(pathNullDevice.c_str(), O_RDWR);
+ if (fdDevNull == -1)
+ throw SysError(format("cannot open `%1%'") % pathNullDevice);
+ if (dup2(fdDevNull, STDIN_FILENO) == -1)
+ throw SysError("cannot dup null device into stdin");
+ close(fdDevNull);
+}
+
+//////////////////////////////////////////////////////////////////////
+
+Agent::Agent(const string &command, const Strings &args)
+{
+ debug(format("starting agent '%1%'") % command);
+
+ /* Create a pipe to get the output of the child. */
+ fromAgent.create();
+
+ /* Create the communication pipes. */
+ toAgent.create();
+
+ /* Create a pipe to get the output of the builder. */
+ builderOut.create();
+
+ /* Fork the hook. */
+ pid = startProcess([&]() {
+
+ commonChildInit(fromAgent);
+
+ if (chdir("/") == -1) throw SysError("changing into `/");
+
+ /* Dup the communication pipes. */
+ if (dup2(toAgent.readSide, STDIN_FILENO) == -1)
+ throw SysError("dupping to-hook read side");
+
+ /* Use fd 4 for the builder's stdout/stderr. */
+ if (dup2(builderOut.writeSide, 4) == -1)
+ throw SysError("dupping builder's stdout/stderr");
+
+ Strings allArgs;
+ allArgs.push_back(command);
+ allArgs.insert(allArgs.end(), args.begin(), args.end()); // append
+
+ execv(command.c_str(), stringsToCharPtrs(allArgs).data());
+
+ throw SysError(format("executing `%1%'") % command);
+ });
+
+ pid.setSeparatePG(true);
+ fromAgent.writeSide.close();
+ toAgent.readSide.close();
+}
+
+
+Agent::~Agent()
+{
+ try {
+ toAgent.writeSide.close();
+ pid.kill(true);
+ } catch (...) {
+ ignoreException();
+ }
+}
+
}
diff --git a/nix/libutil/util.hh b/nix/libutil/util.hh
index 9e3c14bdd4..13cff44316 100644
--- a/nix/libutil/util.hh
+++ b/nix/libutil/util.hh
@@ -264,6 +264,29 @@ public:
void setKillSignal(int signal);
};
+/* An "agent" is a helper program that runs in the background and that we talk
+ to over pipes, such as the "guix offload" program. */
+struct Agent
+{
+ /* Pipes for talking to the agent. */
+ Pipe toAgent;
+
+ /* Pipe for the agent's standard output/error. */
+ Pipe fromAgent;
+
+ /* Pipe for build standard output/error--e.g., for build processes started
+ by "guix offload". */
+ Pipe builderOut;
+
+ /* The process ID of the agent. */
+ Pid pid;
+
+ /* The command and arguments passed to the agent. */
+ Agent(const string &command, const Strings &args);
+
+ ~Agent();
+};
+
/* Kill all processes running under the specified uid by sending them
a SIGKILL. */
@@ -295,6 +318,8 @@ void closeMostFDs(const set<int> & exceptions);
/* Set the close-on-exec flag for the given file descriptor. */
void closeOnExec(int fd);
+/* Common initialisation performed in child processes. */
+void commonChildInit(Pipe & logPipe);
/* User interruption. */