2020-10-11 16:17:24 +00:00
|
|
|
#pragma once
|
|
|
|
|
2019-04-01 19:09:49 +00:00
|
|
|
#include "machines.hh"
|
2020-10-11 16:17:24 +00:00
|
|
|
#include "parsed-derivations.hh"
|
|
|
|
#include "lock.hh"
|
|
|
|
#include "local-store.hh"
|
2006-09-04 21:06:23 +00:00
|
|
|
|
2020-10-11 16:17:24 +00:00
|
|
|
#include <memory>
|
2015-08-04 09:12:31 +00:00
|
|
|
#include <algorithm>
|
|
|
|
#include <iostream>
|
2003-07-20 19:29:38 +00:00
|
|
|
#include <map>
|
2005-10-17 15:33:24 +00:00
|
|
|
#include <sstream>
|
2016-04-29 11:57:08 +00:00
|
|
|
#include <thread>
|
|
|
|
#include <future>
|
2016-12-06 20:58:04 +00:00
|
|
|
#include <chrono>
|
2017-10-25 11:01:50 +00:00
|
|
|
#include <regex>
|
2018-10-22 19:49:56 +00:00
|
|
|
#include <queue>
|
2009-01-12 16:30:32 +00:00
|
|
|
|
2006-09-04 21:06:23 +00:00
|
|
|
namespace nix {
|
|
|
|
|
|
|
|
using std::map;
|
2012-07-27 13:59:18 +00:00
|
|
|
|
2003-07-20 19:29:38 +00:00
|
|
|
|
2004-06-18 18:09:32 +00:00
|
|
|
/* Forward definition. */
|
|
|
|
class Worker;
|
2014-01-21 17:29:55 +00:00
|
|
|
struct HookInstance;
|
2004-06-18 18:09:32 +00:00
|
|
|
|
|
|
|
|
|
|
|
/* A pointer to a goal. */
|
2020-06-17 17:26:37 +00:00
|
|
|
struct Goal;
|
2015-07-20 01:15:45 +00:00
|
|
|
class DerivationGoal;
|
2014-03-29 23:49:23 +00:00
|
|
|
typedef std::shared_ptr<Goal> GoalPtr;
|
|
|
|
typedef std::weak_ptr<Goal> WeakGoalPtr;
|
2004-06-18 18:09:32 +00:00
|
|
|
|
2014-11-24 15:48:04 +00:00
|
|
|
struct CompareGoalPtrs {
|
2017-12-11 18:05:14 +00:00
|
|
|
bool operator() (const GoalPtr & a, const GoalPtr & b) const;
|
2014-11-24 15:48:04 +00:00
|
|
|
};
|
|
|
|
|
2004-06-25 15:36:09 +00:00
|
|
|
/* Set of goals. */
|
2014-11-24 15:48:04 +00:00
|
|
|
typedef set<GoalPtr, CompareGoalPtrs> Goals;
|
2014-03-29 23:49:23 +00:00
|
|
|
typedef list<WeakGoalPtr> WeakGoals;
|
2004-06-18 18:09:32 +00:00
|
|
|
|
|
|
|
/* A map of paths to goals (and the other way around). */
|
2019-12-05 18:11:09 +00:00
|
|
|
typedef std::map<StorePath, WeakGoalPtr> WeakGoalMap;
|
2004-06-18 18:09:32 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
2020-06-15 17:25:35 +00:00
|
|
|
struct Goal : public std::enable_shared_from_this<Goal>
|
2004-06-18 18:09:32 +00:00
|
|
|
{
|
2013-01-02 11:38:28 +00:00
|
|
|
typedef enum {ecBusy, ecSuccess, ecFailed, ecNoSubstituters, ecIncompleteClosure} ExitCode;
|
2012-07-27 13:59:18 +00:00
|
|
|
|
2004-06-18 18:09:32 +00:00
|
|
|
/* Backlink to the worker. */
|
|
|
|
Worker & worker;
|
|
|
|
|
2004-06-25 15:36:09 +00:00
|
|
|
/* Goals that this goal is waiting for. */
|
|
|
|
Goals waitees;
|
|
|
|
|
|
|
|
/* Goals waiting for this one to finish. Must use weak pointers
|
|
|
|
here to prevent cycles. */
|
|
|
|
WeakGoals waiters;
|
2004-06-18 18:09:32 +00:00
|
|
|
|
2004-08-30 11:51:36 +00:00
|
|
|
/* Number of goals we are/were waiting for that have failed. */
|
2004-06-25 10:21:44 +00:00
|
|
|
unsigned int nrFailed;
|
|
|
|
|
2012-07-08 22:39:24 +00:00
|
|
|
/* Number of substitution goals we are/were waiting for that
|
|
|
|
failed because there are no substituters. */
|
|
|
|
unsigned int nrNoSubstituters;
|
|
|
|
|
2013-01-02 11:38:28 +00:00
|
|
|
/* Number of substitution goals we are/were waiting for that
|
|
|
|
failed because othey had unsubstitutable references. */
|
|
|
|
unsigned int nrIncompleteClosure;
|
|
|
|
|
2005-02-18 09:50:20 +00:00
|
|
|
/* Name of this goal for debugging purposes. */
|
|
|
|
string name;
|
|
|
|
|
2005-02-23 11:19:27 +00:00
|
|
|
/* Whether the goal is finished. */
|
|
|
|
ExitCode exitCode;
|
|
|
|
|
2020-06-15 17:25:35 +00:00
|
|
|
/* Exception containing an error message, if any. */
|
|
|
|
std::optional<Error> ex;
|
|
|
|
|
2005-01-19 11:16:11 +00:00
|
|
|
Goal(Worker & worker) : worker(worker)
|
2004-06-18 18:09:32 +00:00
|
|
|
{
|
2013-01-02 11:38:28 +00:00
|
|
|
nrFailed = nrNoSubstituters = nrIncompleteClosure = 0;
|
2005-02-23 11:19:27 +00:00
|
|
|
exitCode = ecBusy;
|
2004-06-18 18:09:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
virtual ~Goal()
|
|
|
|
{
|
2005-02-18 09:50:20 +00:00
|
|
|
trace("goal destroyed");
|
2004-06-18 18:09:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
virtual void work() = 0;
|
|
|
|
|
2004-06-25 15:36:09 +00:00
|
|
|
void addWaitee(GoalPtr waitee);
|
2004-06-18 18:09:32 +00:00
|
|
|
|
2006-12-08 17:26:21 +00:00
|
|
|
virtual void waiteeDone(GoalPtr waitee, ExitCode result);
|
2004-06-25 10:21:44 +00:00
|
|
|
|
2005-10-17 15:33:24 +00:00
|
|
|
virtual void handleChildOutput(int fd, const string & data)
|
|
|
|
{
|
|
|
|
abort();
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual void handleEOF(int fd)
|
2004-06-29 09:41:50 +00:00
|
|
|
{
|
|
|
|
abort();
|
|
|
|
}
|
|
|
|
|
2018-03-14 18:01:22 +00:00
|
|
|
void trace(const FormatOrString & fs);
|
2005-02-18 09:50:20 +00:00
|
|
|
|
|
|
|
string getName()
|
|
|
|
{
|
|
|
|
return name;
|
|
|
|
}
|
2012-07-27 13:59:18 +00:00
|
|
|
|
2015-07-20 01:15:45 +00:00
|
|
|
/* Callback in case of a timeout. It should wake up its waiters,
|
|
|
|
get rid of any running child processes that are being monitored
|
|
|
|
by the worker (important!), etc. */
|
2020-06-15 17:25:35 +00:00
|
|
|
virtual void timedOut(Error && ex) = 0;
|
2006-12-08 17:26:21 +00:00
|
|
|
|
2014-11-24 15:48:04 +00:00
|
|
|
virtual string key() = 0;
|
|
|
|
|
2020-06-15 17:25:35 +00:00
|
|
|
void amDone(ExitCode result, std::optional<Error> ex = {});
|
2004-06-18 18:09:32 +00:00
|
|
|
};
|
|
|
|
|
2016-12-06 20:58:04 +00:00
|
|
|
typedef std::chrono::time_point<std::chrono::steady_clock> steady_time_point;
|
|
|
|
|
|
|
|
|
2004-06-18 18:09:32 +00:00
|
|
|
/* A mapping used to remember for each child process to what goal it
|
2005-10-17 15:33:24 +00:00
|
|
|
belongs, and file descriptors for receiving log data and output
|
|
|
|
path creation commands. */
|
2004-06-19 21:45:04 +00:00
|
|
|
struct Child
|
|
|
|
{
|
2004-06-25 15:36:09 +00:00
|
|
|
WeakGoalPtr goal;
|
2016-08-30 13:45:39 +00:00
|
|
|
Goal * goal2; // ugly hackery
|
2005-10-17 15:33:24 +00:00
|
|
|
set<int> fds;
|
2013-04-23 16:04:59 +00:00
|
|
|
bool respectTimeouts;
|
2004-06-19 21:45:04 +00:00
|
|
|
bool inBuildSlot;
|
2016-12-06 20:58:04 +00:00
|
|
|
steady_time_point lastOutput; /* time we last got output on stdout/stderr */
|
|
|
|
steady_time_point timeStarted;
|
2004-06-19 21:45:04 +00:00
|
|
|
};
|
|
|
|
|
2004-06-18 18:09:32 +00:00
|
|
|
|
|
|
|
/* The worker class. */
|
|
|
|
class Worker
|
|
|
|
{
|
|
|
|
private:
|
|
|
|
|
2004-06-25 15:36:09 +00:00
|
|
|
/* Note: the worker should only have strong pointers to the
|
|
|
|
top-level goals. */
|
|
|
|
|
|
|
|
/* The top-level goals of the worker. */
|
|
|
|
Goals topGoals;
|
2004-06-18 18:09:32 +00:00
|
|
|
|
|
|
|
/* Goals that are ready to do some work. */
|
2004-06-25 15:36:09 +00:00
|
|
|
WeakGoals awake;
|
2004-06-18 18:09:32 +00:00
|
|
|
|
|
|
|
/* Goals waiting for a build slot. */
|
2004-06-25 15:36:09 +00:00
|
|
|
WeakGoals wantingToBuild;
|
2004-06-18 18:09:32 +00:00
|
|
|
|
|
|
|
/* Child processes currently running. */
|
2016-04-29 11:57:08 +00:00
|
|
|
std::list<Child> children;
|
2004-06-18 18:09:32 +00:00
|
|
|
|
2009-03-31 21:14:07 +00:00
|
|
|
/* Number of build slots occupied. This includes local builds and
|
|
|
|
substitutions but not remote builds via the build hook. */
|
|
|
|
unsigned int nrLocalBuilds;
|
2004-06-19 21:45:04 +00:00
|
|
|
|
2005-01-19 11:16:11 +00:00
|
|
|
/* Maps used to prevent multiple instantiations of a goal for the
|
2005-01-20 16:01:07 +00:00
|
|
|
same derivation / path. */
|
2005-01-19 11:16:11 +00:00
|
|
|
WeakGoalMap derivationGoals;
|
2004-06-25 15:36:09 +00:00
|
|
|
WeakGoalMap substitutionGoals;
|
2004-06-18 18:09:32 +00:00
|
|
|
|
2007-08-28 11:36:17 +00:00
|
|
|
/* Goals waiting for busy paths to be unlocked. */
|
|
|
|
WeakGoals waitingForAnyGoal;
|
2012-07-27 13:59:18 +00:00
|
|
|
|
2009-03-23 01:05:54 +00:00
|
|
|
/* Goals sleeping for a few seconds (polling a lock). */
|
|
|
|
WeakGoals waitingForAWhile;
|
|
|
|
|
|
|
|
/* Last time the goals in `waitingForAWhile' where woken up. */
|
2016-12-06 20:58:04 +00:00
|
|
|
steady_time_point lastWokenUp;
|
2011-06-30 15:19:13 +00:00
|
|
|
|
2016-04-08 16:07:13 +00:00
|
|
|
/* Cache for pathContentsGood(). */
|
2019-12-05 18:11:09 +00:00
|
|
|
std::map<StorePath, bool> pathContentsGoodCache;
|
2016-04-08 16:07:13 +00:00
|
|
|
|
2004-06-18 18:09:32 +00:00
|
|
|
public:
|
|
|
|
|
2017-08-14 18:14:55 +00:00
|
|
|
const Activity act;
|
2017-08-15 13:31:59 +00:00
|
|
|
const Activity actDerivations;
|
2017-08-14 20:12:36 +00:00
|
|
|
const Activity actSubstitutions;
|
2017-08-14 18:14:55 +00:00
|
|
|
|
2010-12-13 16:53:23 +00:00
|
|
|
/* Set if at least one derivation had a BuildError (i.e. permanent
|
|
|
|
failure). */
|
|
|
|
bool permanentFailure;
|
|
|
|
|
2014-08-17 17:09:03 +00:00
|
|
|
/* Set if at least one derivation had a timeout. */
|
|
|
|
bool timedOut;
|
|
|
|
|
2019-07-01 22:12:12 +00:00
|
|
|
/* Set if at least one derivation fails with a hash mismatch. */
|
|
|
|
bool hashMismatch;
|
|
|
|
|
|
|
|
/* Set if at least one derivation is not deterministic in check mode. */
|
|
|
|
bool checkMismatch;
|
|
|
|
|
2008-06-09 13:52:45 +00:00
|
|
|
LocalStore & store;
|
|
|
|
|
2017-01-19 14:15:09 +00:00
|
|
|
std::unique_ptr<HookInstance> hook;
|
2012-07-27 13:59:18 +00:00
|
|
|
|
2017-08-15 13:31:59 +00:00
|
|
|
uint64_t expectedBuilds = 0;
|
|
|
|
uint64_t doneBuilds = 0;
|
|
|
|
uint64_t failedBuilds = 0;
|
|
|
|
uint64_t runningBuilds = 0;
|
|
|
|
|
2017-08-14 20:12:36 +00:00
|
|
|
uint64_t expectedSubstitutions = 0;
|
|
|
|
uint64_t doneSubstitutions = 0;
|
2017-08-15 13:31:59 +00:00
|
|
|
uint64_t failedSubstitutions = 0;
|
2017-08-14 20:42:17 +00:00
|
|
|
uint64_t runningSubstitutions = 0;
|
2017-08-14 18:14:55 +00:00
|
|
|
uint64_t expectedDownloadSize = 0;
|
|
|
|
uint64_t doneDownloadSize = 0;
|
|
|
|
uint64_t expectedNarSize = 0;
|
|
|
|
uint64_t doneNarSize = 0;
|
|
|
|
|
2017-10-24 09:00:16 +00:00
|
|
|
/* Whether to ask the build hook if it can build a derivation. If
|
|
|
|
it answers with "decline-permanently", we don't try again. */
|
|
|
|
bool tryBuildHook = true;
|
|
|
|
|
2008-06-09 13:52:45 +00:00
|
|
|
Worker(LocalStore & store);
|
2004-06-18 18:09:32 +00:00
|
|
|
~Worker();
|
|
|
|
|
2004-06-25 15:36:09 +00:00
|
|
|
/* Make a goal (with caching). */
|
2020-08-22 20:44:47 +00:00
|
|
|
|
|
|
|
/* derivation goal */
|
|
|
|
private:
|
|
|
|
std::shared_ptr<DerivationGoal> makeDerivationGoalCommon(
|
|
|
|
const StorePath & drvPath, const StringSet & wantedOutputs,
|
|
|
|
std::function<std::shared_ptr<DerivationGoal>()> mkDrvGoal);
|
|
|
|
public:
|
|
|
|
std::shared_ptr<DerivationGoal> makeDerivationGoal(
|
|
|
|
const StorePath & drvPath,
|
|
|
|
const StringSet & wantedOutputs, BuildMode buildMode = bmNormal);
|
|
|
|
std::shared_ptr<DerivationGoal> makeBasicDerivationGoal(
|
|
|
|
const StorePath & drvPath, const BasicDerivation & drv,
|
|
|
|
const StringSet & wantedOutputs, BuildMode buildMode = bmNormal);
|
|
|
|
|
|
|
|
/* substitution goal */
|
2020-06-22 17:08:11 +00:00
|
|
|
GoalPtr makeSubstitutionGoal(const StorePath & storePath, RepairFlag repair = NoRepair, std::optional<ContentAddress> ca = std::nullopt);
|
2004-06-18 18:09:32 +00:00
|
|
|
|
2004-06-25 15:36:09 +00:00
|
|
|
/* Remove a dead goal. */
|
2004-06-18 18:09:32 +00:00
|
|
|
void removeGoal(GoalPtr goal);
|
|
|
|
|
|
|
|
/* Wake up a goal (i.e., there is something for it to do). */
|
|
|
|
void wakeUp(GoalPtr goal);
|
|
|
|
|
2009-03-31 21:14:07 +00:00
|
|
|
/* Return the number of local build and substitution processes
|
|
|
|
currently running (but not remote builds via the build
|
|
|
|
hook). */
|
|
|
|
unsigned int getNrLocalBuilds();
|
2004-06-18 18:09:32 +00:00
|
|
|
|
2006-12-08 17:26:21 +00:00
|
|
|
/* Registers a running child process. `inBuildSlot' means that
|
|
|
|
the process counts towards the jobs limit. */
|
2016-04-29 11:57:08 +00:00
|
|
|
void childStarted(GoalPtr goal, const set<int> & fds,
|
|
|
|
bool inBuildSlot, bool respectTimeouts);
|
2006-12-08 17:26:21 +00:00
|
|
|
|
|
|
|
/* Unregisters a running child process. `wakeSleepers' should be
|
|
|
|
false if there is no sense in waking up goals that are sleeping
|
|
|
|
because they can't run yet (e.g., there is no free build slot,
|
|
|
|
or the hook would still say `postpone'). */
|
2016-08-30 13:45:39 +00:00
|
|
|
void childTerminated(Goal * goal, bool wakeSleepers = true);
|
2004-06-18 18:09:32 +00:00
|
|
|
|
2006-12-08 17:26:21 +00:00
|
|
|
/* Put `goal' to sleep until a build slot becomes available (which
|
|
|
|
might be right away). */
|
|
|
|
void waitForBuildSlot(GoalPtr goal);
|
|
|
|
|
2007-08-28 11:36:17 +00:00
|
|
|
/* Wait for any goal to finish. Pretty indiscriminate way to
|
|
|
|
wait for some resource that some other goal is holding. */
|
|
|
|
void waitForAnyGoal(GoalPtr goal);
|
2012-07-27 13:59:18 +00:00
|
|
|
|
2009-03-23 01:05:54 +00:00
|
|
|
/* Wait for a few seconds and then retry this goal. Used when
|
|
|
|
waiting for a lock held by another process. This kind of
|
|
|
|
polling is inefficient, but POSIX doesn't really provide a way
|
|
|
|
to wait for multiple locks in the main select() loop. */
|
|
|
|
void waitForAWhile(GoalPtr goal);
|
2012-07-27 13:59:18 +00:00
|
|
|
|
2005-02-23 11:19:27 +00:00
|
|
|
/* Loop until the specified top-level goals have finished. */
|
|
|
|
void run(const Goals & topGoals);
|
2004-06-18 18:09:32 +00:00
|
|
|
|
|
|
|
/* Wait for input to become available. */
|
|
|
|
void waitForInput();
|
2010-12-13 16:53:23 +00:00
|
|
|
|
|
|
|
unsigned int exitStatus();
|
2016-04-08 16:07:13 +00:00
|
|
|
|
|
|
|
/* Check whether the given valid path exists and has the right
|
|
|
|
contents. */
|
2019-12-05 18:11:09 +00:00
|
|
|
bool pathContentsGood(const StorePath & path);
|
2016-04-08 16:07:13 +00:00
|
|
|
|
2020-06-16 20:20:18 +00:00
|
|
|
void markContentsGood(const StorePath & path);
|
2017-08-14 18:14:55 +00:00
|
|
|
|
|
|
|
void updateProgress()
|
|
|
|
{
|
2017-08-15 13:31:59 +00:00
|
|
|
actDerivations.progress(doneBuilds, expectedBuilds + doneBuilds, runningBuilds, failedBuilds);
|
|
|
|
actSubstitutions.progress(doneSubstitutions, expectedSubstitutions + doneSubstitutions, runningSubstitutions, failedSubstitutions);
|
2020-04-06 21:43:43 +00:00
|
|
|
act.setExpected(actFileTransfer, expectedDownloadSize + doneDownloadSize);
|
2017-08-16 14:38:23 +00:00
|
|
|
act.setExpected(actCopyPath, expectedNarSize + doneNarSize);
|
2017-08-14 18:14:55 +00:00
|
|
|
}
|
2004-06-18 18:09:32 +00:00
|
|
|
};
|
|
|
|
|
2010-08-25 20:44:28 +00:00
|
|
|
typedef enum {rpAccept, rpDecline, rpPostpone} HookReply;
|
|
|
|
|
2012-07-08 22:39:24 +00:00
|
|
|
class SubstitutionGoal;
|
|
|
|
|
2020-09-15 15:19:45 +00:00
|
|
|
/* Unless we are repairing, we don't both to test validity and just assume it,
|
|
|
|
so the choices are `Absent` or `Valid`. */
|
|
|
|
enum struct PathStatus {
|
|
|
|
Corrupt,
|
|
|
|
Absent,
|
|
|
|
Valid,
|
|
|
|
};
|
|
|
|
|
2020-09-04 15:15:51 +00:00
|
|
|
struct InitialOutputStatus {
|
2020-08-07 19:09:26 +00:00
|
|
|
StorePath path;
|
2020-09-15 15:19:45 +00:00
|
|
|
PathStatus status;
|
2020-08-07 19:09:26 +00:00
|
|
|
/* Valid in the store, and additionally non-corrupt if we are repairing */
|
|
|
|
bool isValid() const {
|
2020-09-15 15:19:45 +00:00
|
|
|
return status == PathStatus::Valid;
|
|
|
|
}
|
|
|
|
/* Merely present, allowed to be corrupt */
|
|
|
|
bool isPresent() const {
|
|
|
|
return status == PathStatus::Corrupt
|
|
|
|
|| status == PathStatus::Valid;
|
2020-08-07 19:09:26 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2020-09-04 15:15:51 +00:00
|
|
|
struct InitialOutput {
|
2020-08-07 19:09:26 +00:00
|
|
|
bool wanted;
|
2020-09-04 15:15:51 +00:00
|
|
|
std::optional<InitialOutputStatus> known;
|
2020-08-07 19:09:26 +00:00
|
|
|
};
|
|
|
|
|
2005-01-19 11:16:11 +00:00
|
|
|
class DerivationGoal : public Goal
|
2004-05-11 18:05:44 +00:00
|
|
|
{
|
2004-06-18 18:09:32 +00:00
|
|
|
private:
|
Allow remote builds without sending the derivation closure
Previously, to build a derivation remotely, we had to copy the entire
closure of the .drv file to the remote machine, even though we only
need the top-level derivation. This is very wasteful: the closure can
contain thousands of store paths, and in some Hydra use cases, include
source paths that are very large (e.g. Git/Mercurial checkouts).
So now there is a new operation, StoreAPI::buildDerivation(), that
performs a build from an in-memory representation of a derivation
(BasicDerivation) rather than from a on-disk .drv file. The only files
that need to be in the Nix store are the sources of the derivation
(drv.inputSrcs), and the needed output paths of the dependencies (as
described by drv.inputDrvs). "nix-store --serve" exposes this
interface.
Note that this is a privileged operation, because you can construct a
derivation that builds any store path whatsoever. Fixing this will
require changing the hashing scheme (i.e., the output paths should be
computed from the other fields in BasicDerivation, allowing them to be
verified without access to other derivations). However, this would be
quite nice because it would allow .drv-free building (e.g. "nix-env
-i" wouldn't have to write any .drv files to disk).
Fixes #173.
2015-07-17 15:57:40 +00:00
|
|
|
/* Whether to use an on-disk .drv file. */
|
|
|
|
bool useDerivation;
|
|
|
|
|
2005-01-20 16:01:07 +00:00
|
|
|
/* The path of the derivation. */
|
2019-12-05 18:11:09 +00:00
|
|
|
StorePath drvPath;
|
2004-05-11 18:05:44 +00:00
|
|
|
|
2012-11-26 16:15:09 +00:00
|
|
|
/* The specific outputs that we need to build. Empty means all of
|
|
|
|
them. */
|
|
|
|
StringSet wantedOutputs;
|
|
|
|
|
|
|
|
/* Whether additional wanted outputs have been added. */
|
Allow remote builds without sending the derivation closure
Previously, to build a derivation remotely, we had to copy the entire
closure of the .drv file to the remote machine, even though we only
need the top-level derivation. This is very wasteful: the closure can
contain thousands of store paths, and in some Hydra use cases, include
source paths that are very large (e.g. Git/Mercurial checkouts).
So now there is a new operation, StoreAPI::buildDerivation(), that
performs a build from an in-memory representation of a derivation
(BasicDerivation) rather than from a on-disk .drv file. The only files
that need to be in the Nix store are the sources of the derivation
(drv.inputSrcs), and the needed output paths of the dependencies (as
described by drv.inputDrvs). "nix-store --serve" exposes this
interface.
Note that this is a privileged operation, because you can construct a
derivation that builds any store path whatsoever. Fixing this will
require changing the hashing scheme (i.e., the output paths should be
computed from the other fields in BasicDerivation, allowing them to be
verified without access to other derivations). However, this would be
quite nice because it would allow .drv-free building (e.g. "nix-env
-i" wouldn't have to write any .drv files to disk).
Fixes #173.
2015-07-17 15:57:40 +00:00
|
|
|
bool needRestart = false;
|
2012-11-26 16:15:09 +00:00
|
|
|
|
2013-01-02 11:38:28 +00:00
|
|
|
/* Whether to retry substituting the outputs after building the
|
|
|
|
inputs. */
|
2018-06-05 14:04:41 +00:00
|
|
|
bool retrySubstitution;
|
2013-01-02 11:38:28 +00:00
|
|
|
|
2005-01-20 16:01:07 +00:00
|
|
|
/* The derivation stored at drvPath. */
|
Allow remote builds without sending the derivation closure
Previously, to build a derivation remotely, we had to copy the entire
closure of the .drv file to the remote machine, even though we only
need the top-level derivation. This is very wasteful: the closure can
contain thousands of store paths, and in some Hydra use cases, include
source paths that are very large (e.g. Git/Mercurial checkouts).
So now there is a new operation, StoreAPI::buildDerivation(), that
performs a build from an in-memory representation of a derivation
(BasicDerivation) rather than from a on-disk .drv file. The only files
that need to be in the Nix store are the sources of the derivation
(drv.inputSrcs), and the needed output paths of the dependencies (as
described by drv.inputDrvs). "nix-store --serve" exposes this
interface.
Note that this is a privileged operation, because you can construct a
derivation that builds any store path whatsoever. Fixing this will
require changing the hashing scheme (i.e., the output paths should be
computed from the other fields in BasicDerivation, allowing them to be
verified without access to other derivations). However, this would be
quite nice because it would allow .drv-free building (e.g. "nix-env
-i" wouldn't have to write any .drv files to disk).
Fixes #173.
2015-07-17 15:57:40 +00:00
|
|
|
std::unique_ptr<BasicDerivation> drv;
|
2012-07-27 13:59:18 +00:00
|
|
|
|
2018-09-28 12:31:16 +00:00
|
|
|
std::unique_ptr<ParsedDerivation> parsedDrv;
|
2018-09-28 10:43:01 +00:00
|
|
|
|
2004-05-11 18:05:44 +00:00
|
|
|
/* The remainder is state held during the build. */
|
|
|
|
|
2020-08-07 19:09:26 +00:00
|
|
|
/* Locks on (fixed) output paths. */
|
2004-05-11 18:05:44 +00:00
|
|
|
PathLocks outputLocks;
|
|
|
|
|
2005-01-19 11:16:11 +00:00
|
|
|
/* All input paths (that is, the union of FS closures of the
|
|
|
|
immediate input paths). */
|
2019-12-05 18:11:09 +00:00
|
|
|
StorePathSet inputPaths;
|
2004-05-11 18:05:44 +00:00
|
|
|
|
2020-09-04 15:15:51 +00:00
|
|
|
std::map<std::string, InitialOutput> initialOutputs;
|
2013-06-13 14:43:20 +00:00
|
|
|
|
2005-10-17 15:33:24 +00:00
|
|
|
/* User selected for running the builder. */
|
2017-01-25 11:45:38 +00:00
|
|
|
std::unique_ptr<UserLock> buildUser;
|
2005-10-17 15:33:24 +00:00
|
|
|
|
2004-05-11 18:05:44 +00:00
|
|
|
/* The process ID of the builder. */
|
2004-06-22 09:51:44 +00:00
|
|
|
Pid pid;
|
2004-05-11 18:05:44 +00:00
|
|
|
|
|
|
|
/* The temporary directory. */
|
|
|
|
Path tmpDir;
|
|
|
|
|
2015-12-02 13:59:07 +00:00
|
|
|
/* The path of the temporary directory in the sandbox. */
|
|
|
|
Path tmpDirInSandbox;
|
|
|
|
|
2004-05-11 18:05:44 +00:00
|
|
|
/* File descriptor for the log file. */
|
2012-07-17 13:40:12 +00:00
|
|
|
AutoCloseFD fdLogFile;
|
2016-05-04 13:46:25 +00:00
|
|
|
std::shared_ptr<BufferedSink> logFileSink, logSink;
|
2004-05-11 18:05:44 +00:00
|
|
|
|
2013-09-02 09:58:18 +00:00
|
|
|
/* Number of bytes received from the builder's stdout/stderr. */
|
|
|
|
unsigned long logSize;
|
|
|
|
|
2016-04-25 14:47:46 +00:00
|
|
|
/* The most recent log lines. */
|
|
|
|
std::list<std::string> logTail;
|
|
|
|
|
|
|
|
std::string currentLogLine;
|
2016-04-28 12:27:00 +00:00
|
|
|
size_t currentLogLinePos = 0; // to handle carriage return
|
2016-04-25 14:47:46 +00:00
|
|
|
|
2017-10-24 11:41:52 +00:00
|
|
|
std::string currentHookLine;
|
|
|
|
|
2004-05-11 18:05:44 +00:00
|
|
|
/* Pipe for the builder's standard output/error. */
|
2010-08-30 14:53:03 +00:00
|
|
|
Pipe builderOut;
|
2004-05-13 19:14:49 +00:00
|
|
|
|
Recursive Nix support
This allows Nix builders to call Nix to build derivations, with some
limitations.
Example:
let nixpkgs = fetchTarball channel:nixos-18.03; in
with import <nixpkgs> {};
runCommand "foo"
{
buildInputs = [ nix jq ];
NIX_PATH = "nixpkgs=${nixpkgs}";
}
''
hello=$(nix-build -E '(import <nixpkgs> {}).hello.overrideDerivation (args: { name = "hello-3.5"; })')
$hello/bin/hello
mkdir -p $out/bin
ln -s $hello/bin/hello $out/bin/hello
nix path-info -r --json $hello | jq .
''
This derivation makes a recursive Nix call to build GNU Hello and
symlinks it from its $out, i.e.
# ll ./result/bin/
lrwxrwxrwx 1 root root 63 Jan 1 1970 hello -> /nix/store/s0awxrs71gickhaqdwxl506hzccb30y5-hello-3.5/bin/hello
# nix-store -qR ./result
/nix/store/hwwqshlmazzjzj7yhrkyjydxamvvkfd3-glibc-2.26-131
/nix/store/s0awxrs71gickhaqdwxl506hzccb30y5-hello-3.5
/nix/store/sgmvvyw8vhfqdqb619bxkcpfn9lvd8ss-foo
This is implemented as follows:
* Before running the outer builder, Nix creates a Unix domain socket
'.nix-socket' in the builder's temporary directory and sets
$NIX_REMOTE to point to it. It starts a thread to process
connections to this socket. (Thus you don't need to have nix-daemon
running.)
* The daemon thread uses a wrapper store (RestrictedStore) to keep
track of paths added through recursive Nix calls, to implement some
restrictions (see below), and to do some censorship (e.g. for
purity, queryPathInfo() won't return impure information such as
signatures and timestamps).
* After the build finishes, the output paths are scanned for
references to the paths added through recursive Nix calls (in
addition to the inputs closure). Thus, in the example above, $out
has a reference to $hello.
The main restriction on recursive Nix calls is that they cannot do
arbitrary substitutions. For example, doing
nix-store -r /nix/store/kmwd1hq55akdb9sc7l3finr175dajlby-hello-2.10
is forbidden unless /nix/store/kmwd... is in the inputs closure or
previously built by a recursive Nix call. This is to prevent
irreproducible derivations that have hidden dependencies on
substituters or the current store contents. Building a derivation is
fine, however, and Nix will use substitutes if available. In other
words, the builder has to present proof that it knows how to build a
desired store path from scratch by constructing a derivation graph for
that path.
Probably we should also disallow instantiating/building fixed-output
derivations (specifically, those that access the network, but
currently we have no way to mark fixed-output derivations that don't
access the network). Otherwise sandboxed derivations can bypass
sandbox restrictions and access the network.
When sandboxing is enabled, we make paths appear in the sandbox of the
builder by entering the mount namespace of the builder and
bind-mounting each path. This is tricky because we do a pivot_root()
in the builder to change the root directory of its mount namespace,
and thus the host /nix/store is not visible in the mount namespace of
the builder. To get around this, just before doing pivot_root(), we
branch a second mount namespace that shares its /nix/store mountpoint
with the parent.
Recursive Nix currently doesn't work on macOS in sandboxed mode
(because we can't change the sandbox policy of a running build) and in
non-root mode (because setns() barfs).
2018-10-02 14:01:26 +00:00
|
|
|
/* Pipe for synchronising updates to the builder namespaces. */
|
2016-06-09 16:27:39 +00:00
|
|
|
Pipe userNamespaceSync;
|
|
|
|
|
Recursive Nix support
This allows Nix builders to call Nix to build derivations, with some
limitations.
Example:
let nixpkgs = fetchTarball channel:nixos-18.03; in
with import <nixpkgs> {};
runCommand "foo"
{
buildInputs = [ nix jq ];
NIX_PATH = "nixpkgs=${nixpkgs}";
}
''
hello=$(nix-build -E '(import <nixpkgs> {}).hello.overrideDerivation (args: { name = "hello-3.5"; })')
$hello/bin/hello
mkdir -p $out/bin
ln -s $hello/bin/hello $out/bin/hello
nix path-info -r --json $hello | jq .
''
This derivation makes a recursive Nix call to build GNU Hello and
symlinks it from its $out, i.e.
# ll ./result/bin/
lrwxrwxrwx 1 root root 63 Jan 1 1970 hello -> /nix/store/s0awxrs71gickhaqdwxl506hzccb30y5-hello-3.5/bin/hello
# nix-store -qR ./result
/nix/store/hwwqshlmazzjzj7yhrkyjydxamvvkfd3-glibc-2.26-131
/nix/store/s0awxrs71gickhaqdwxl506hzccb30y5-hello-3.5
/nix/store/sgmvvyw8vhfqdqb619bxkcpfn9lvd8ss-foo
This is implemented as follows:
* Before running the outer builder, Nix creates a Unix domain socket
'.nix-socket' in the builder's temporary directory and sets
$NIX_REMOTE to point to it. It starts a thread to process
connections to this socket. (Thus you don't need to have nix-daemon
running.)
* The daemon thread uses a wrapper store (RestrictedStore) to keep
track of paths added through recursive Nix calls, to implement some
restrictions (see below), and to do some censorship (e.g. for
purity, queryPathInfo() won't return impure information such as
signatures and timestamps).
* After the build finishes, the output paths are scanned for
references to the paths added through recursive Nix calls (in
addition to the inputs closure). Thus, in the example above, $out
has a reference to $hello.
The main restriction on recursive Nix calls is that they cannot do
arbitrary substitutions. For example, doing
nix-store -r /nix/store/kmwd1hq55akdb9sc7l3finr175dajlby-hello-2.10
is forbidden unless /nix/store/kmwd... is in the inputs closure or
previously built by a recursive Nix call. This is to prevent
irreproducible derivations that have hidden dependencies on
substituters or the current store contents. Building a derivation is
fine, however, and Nix will use substitutes if available. In other
words, the builder has to present proof that it knows how to build a
desired store path from scratch by constructing a derivation graph for
that path.
Probably we should also disallow instantiating/building fixed-output
derivations (specifically, those that access the network, but
currently we have no way to mark fixed-output derivations that don't
access the network). Otherwise sandboxed derivations can bypass
sandbox restrictions and access the network.
When sandboxing is enabled, we make paths appear in the sandbox of the
builder by entering the mount namespace of the builder and
bind-mounting each path. This is tricky because we do a pivot_root()
in the builder to change the root directory of its mount namespace,
and thus the host /nix/store is not visible in the mount namespace of
the builder. To get around this, just before doing pivot_root(), we
branch a second mount namespace that shares its /nix/store mountpoint
with the parent.
Recursive Nix currently doesn't work on macOS in sandboxed mode
(because we can't change the sandbox policy of a running build) and in
non-root mode (because setns() barfs).
2018-10-02 14:01:26 +00:00
|
|
|
/* The mount namespace of the builder, used to add additional
|
|
|
|
paths to the sandbox as a result of recursive Nix calls. */
|
|
|
|
AutoCloseFD sandboxMountNamespace;
|
|
|
|
|
2020-10-07 20:02:36 +00:00
|
|
|
/* On Linux, whether we're doing the build in its own user
|
|
|
|
namespace. */
|
|
|
|
bool usingUserNamespace = true;
|
|
|
|
|
2010-08-25 20:44:28 +00:00
|
|
|
/* The build hook. */
|
2017-01-19 14:15:09 +00:00
|
|
|
std::unique_ptr<HookInstance> hook;
|
2012-07-27 13:59:18 +00:00
|
|
|
|
2007-10-27 00:46:59 +00:00
|
|
|
/* Whether we're currently doing a chroot build. */
|
Allow remote builds without sending the derivation closure
Previously, to build a derivation remotely, we had to copy the entire
closure of the .drv file to the remote machine, even though we only
need the top-level derivation. This is very wasteful: the closure can
contain thousands of store paths, and in some Hydra use cases, include
source paths that are very large (e.g. Git/Mercurial checkouts).
So now there is a new operation, StoreAPI::buildDerivation(), that
performs a build from an in-memory representation of a derivation
(BasicDerivation) rather than from a on-disk .drv file. The only files
that need to be in the Nix store are the sources of the derivation
(drv.inputSrcs), and the needed output paths of the dependencies (as
described by drv.inputDrvs). "nix-store --serve" exposes this
interface.
Note that this is a privileged operation, because you can construct a
derivation that builds any store path whatsoever. Fixing this will
require changing the hashing scheme (i.e., the output paths should be
computed from the other fields in BasicDerivation, allowing them to be
verified without access to other derivations). However, this would be
quite nice because it would allow .drv-free building (e.g. "nix-env
-i" wouldn't have to write any .drv files to disk).
Fixes #173.
2015-07-17 15:57:40 +00:00
|
|
|
bool useChroot = false;
|
2012-07-27 13:59:18 +00:00
|
|
|
|
2008-12-11 18:57:10 +00:00
|
|
|
Path chrootRootDir;
|
2007-10-27 00:46:59 +00:00
|
|
|
|
2008-12-11 17:00:12 +00:00
|
|
|
/* RAII object to delete the chroot directory. */
|
2014-03-29 23:49:23 +00:00
|
|
|
std::shared_ptr<AutoDelete> autoDelChroot;
|
2009-03-25 21:05:42 +00:00
|
|
|
|
2020-03-15 06:23:17 +00:00
|
|
|
/* The sort of derivation we are building. */
|
|
|
|
DerivationType derivationType;
|
2012-07-27 13:59:18 +00:00
|
|
|
|
2016-06-03 13:45:11 +00:00
|
|
|
/* Whether to run the build in a private network namespace. */
|
|
|
|
bool privateNetwork = false;
|
|
|
|
|
2005-01-19 11:16:11 +00:00
|
|
|
typedef void (DerivationGoal::*GoalState)();
|
2004-06-18 18:09:32 +00:00
|
|
|
GoalState state;
|
2012-07-27 13:59:18 +00:00
|
|
|
|
2012-06-25 19:45:16 +00:00
|
|
|
/* Stuff we need to pass to initChild(). */
|
2016-10-31 16:09:52 +00:00
|
|
|
struct ChrootPath {
|
|
|
|
Path source;
|
|
|
|
bool optional;
|
|
|
|
ChrootPath(Path source = "", bool optional = false)
|
|
|
|
: source(source), optional(optional)
|
|
|
|
{ }
|
|
|
|
};
|
|
|
|
typedef map<Path, ChrootPath> DirsInChroot; // maps target path to source path
|
2012-12-29 22:04:02 +00:00
|
|
|
DirsInChroot dirsInChroot;
|
2017-01-25 11:00:28 +00:00
|
|
|
|
2012-06-25 19:45:16 +00:00
|
|
|
typedef map<string, string> Environment;
|
|
|
|
Environment env;
|
2015-12-03 15:30:19 +00:00
|
|
|
|
|
|
|
#if __APPLE__
|
2015-11-13 03:00:16 +00:00
|
|
|
typedef string SandboxProfile;
|
|
|
|
SandboxProfile additionalSandboxProfile;
|
2015-11-15 11:08:50 +00:00
|
|
|
#endif
|
2012-06-25 19:45:16 +00:00
|
|
|
|
2012-09-11 22:39:22 +00:00
|
|
|
/* Hash rewriting. */
|
2018-03-29 22:56:13 +00:00
|
|
|
StringMap inputRewrites, outputRewrites;
|
2019-12-05 18:11:09 +00:00
|
|
|
typedef map<StorePath, StorePath> RedirectedOutputs;
|
2014-02-18 00:01:14 +00:00
|
|
|
RedirectedOutputs redirectedOutputs;
|
2012-09-11 22:39:22 +00:00
|
|
|
|
2020-08-07 19:09:26 +00:00
|
|
|
/* The outputs paths used during the build.
|
|
|
|
|
|
|
|
- Input-addressed derivations or fixed content-addressed outputs are
|
|
|
|
sometimes built when some of their outputs already exist, and can not
|
|
|
|
be hidden via sandboxing. We use temporary locations instead and
|
|
|
|
rewrite after the build. Otherwise the regular predetermined paths are
|
|
|
|
put here.
|
|
|
|
|
|
|
|
- Floating content-addressed derivations do not know their final build
|
|
|
|
output paths until the outputs are hashed, so random locations are
|
|
|
|
used, and then renamed. The randomness helps guard against hidden
|
|
|
|
self-references.
|
|
|
|
*/
|
|
|
|
OutputPathMap scratchOutputs;
|
|
|
|
|
|
|
|
/* The final output paths of the build.
|
|
|
|
|
|
|
|
- For input-addressed derivations, always the precomputed paths
|
|
|
|
|
|
|
|
- For content-addressed derivations, calcuated from whatever the hash
|
|
|
|
ends up being. (Note that fixed outputs derivations that produce the
|
|
|
|
"wrong" output still install that data under its true content-address.)
|
|
|
|
*/
|
2020-08-11 20:49:10 +00:00
|
|
|
OutputPathMap finalOutputs;
|
2020-08-07 19:09:26 +00:00
|
|
|
|
2014-02-17 22:04:52 +00:00
|
|
|
BuildMode buildMode;
|
|
|
|
|
|
|
|
/* If we're repairing without a chroot, there may be outputs that
|
|
|
|
are valid but corrupt. So we redirect these outputs to
|
|
|
|
temporary paths. */
|
2019-12-05 18:11:09 +00:00
|
|
|
StorePathSet redirectedBadOutputs;
|
2012-10-02 21:13:46 +00:00
|
|
|
|
2015-07-20 01:15:45 +00:00
|
|
|
BuildResult result;
|
|
|
|
|
2015-11-09 22:16:24 +00:00
|
|
|
/* The current round, if we're building multiple times. */
|
2018-05-02 11:56:34 +00:00
|
|
|
size_t curRound = 1;
|
2015-11-09 22:16:24 +00:00
|
|
|
|
2018-05-02 11:56:34 +00:00
|
|
|
size_t nrRounds;
|
2015-11-09 22:16:24 +00:00
|
|
|
|
|
|
|
/* Path registration info from the previous round, if we're
|
|
|
|
building multiple times. Since this contains the hash, it
|
|
|
|
allows us to compare whether two rounds produced the same
|
|
|
|
result. */
|
2018-10-22 19:49:56 +00:00
|
|
|
std::map<Path, ValidPathInfo> prevInfos;
|
2015-11-09 22:16:24 +00:00
|
|
|
|
2020-10-07 20:46:01 +00:00
|
|
|
uid_t sandboxUid() { return usingUserNamespace ? 1000 : buildUser->getUID(); }
|
|
|
|
gid_t sandboxGid() { return usingUserNamespace ? 100 : buildUser->getGID(); }
|
2016-12-19 10:52:57 +00:00
|
|
|
|
2017-01-25 11:00:28 +00:00
|
|
|
const static Path homeDir;
|
|
|
|
|
2017-08-15 13:31:59 +00:00
|
|
|
std::unique_ptr<MaintainCount<uint64_t>> mcExpectedBuilds, mcRunningBuilds;
|
|
|
|
|
|
|
|
std::unique_ptr<Activity> act;
|
|
|
|
|
2020-06-15 14:03:29 +00:00
|
|
|
/* Activity that denotes waiting for a lock. */
|
|
|
|
std::unique_ptr<Activity> actLock;
|
|
|
|
|
2017-08-21 10:01:21 +00:00
|
|
|
std::map<ActivityId, Activity> builderActivities;
|
|
|
|
|
2017-10-24 12:24:57 +00:00
|
|
|
/* The remote machine on which we're building. */
|
|
|
|
std::string machineName;
|
|
|
|
|
Recursive Nix support
This allows Nix builders to call Nix to build derivations, with some
limitations.
Example:
let nixpkgs = fetchTarball channel:nixos-18.03; in
with import <nixpkgs> {};
runCommand "foo"
{
buildInputs = [ nix jq ];
NIX_PATH = "nixpkgs=${nixpkgs}";
}
''
hello=$(nix-build -E '(import <nixpkgs> {}).hello.overrideDerivation (args: { name = "hello-3.5"; })')
$hello/bin/hello
mkdir -p $out/bin
ln -s $hello/bin/hello $out/bin/hello
nix path-info -r --json $hello | jq .
''
This derivation makes a recursive Nix call to build GNU Hello and
symlinks it from its $out, i.e.
# ll ./result/bin/
lrwxrwxrwx 1 root root 63 Jan 1 1970 hello -> /nix/store/s0awxrs71gickhaqdwxl506hzccb30y5-hello-3.5/bin/hello
# nix-store -qR ./result
/nix/store/hwwqshlmazzjzj7yhrkyjydxamvvkfd3-glibc-2.26-131
/nix/store/s0awxrs71gickhaqdwxl506hzccb30y5-hello-3.5
/nix/store/sgmvvyw8vhfqdqb619bxkcpfn9lvd8ss-foo
This is implemented as follows:
* Before running the outer builder, Nix creates a Unix domain socket
'.nix-socket' in the builder's temporary directory and sets
$NIX_REMOTE to point to it. It starts a thread to process
connections to this socket. (Thus you don't need to have nix-daemon
running.)
* The daemon thread uses a wrapper store (RestrictedStore) to keep
track of paths added through recursive Nix calls, to implement some
restrictions (see below), and to do some censorship (e.g. for
purity, queryPathInfo() won't return impure information such as
signatures and timestamps).
* After the build finishes, the output paths are scanned for
references to the paths added through recursive Nix calls (in
addition to the inputs closure). Thus, in the example above, $out
has a reference to $hello.
The main restriction on recursive Nix calls is that they cannot do
arbitrary substitutions. For example, doing
nix-store -r /nix/store/kmwd1hq55akdb9sc7l3finr175dajlby-hello-2.10
is forbidden unless /nix/store/kmwd... is in the inputs closure or
previously built by a recursive Nix call. This is to prevent
irreproducible derivations that have hidden dependencies on
substituters or the current store contents. Building a derivation is
fine, however, and Nix will use substitutes if available. In other
words, the builder has to present proof that it knows how to build a
desired store path from scratch by constructing a derivation graph for
that path.
Probably we should also disallow instantiating/building fixed-output
derivations (specifically, those that access the network, but
currently we have no way to mark fixed-output derivations that don't
access the network). Otherwise sandboxed derivations can bypass
sandbox restrictions and access the network.
When sandboxing is enabled, we make paths appear in the sandbox of the
builder by entering the mount namespace of the builder and
bind-mounting each path. This is tricky because we do a pivot_root()
in the builder to change the root directory of its mount namespace,
and thus the host /nix/store is not visible in the mount namespace of
the builder. To get around this, just before doing pivot_root(), we
branch a second mount namespace that shares its /nix/store mountpoint
with the parent.
Recursive Nix currently doesn't work on macOS in sandboxed mode
(because we can't change the sandbox policy of a running build) and in
non-root mode (because setns() barfs).
2018-10-02 14:01:26 +00:00
|
|
|
/* The recursive Nix daemon socket. */
|
|
|
|
AutoCloseFD daemonSocket;
|
|
|
|
|
|
|
|
/* The daemon main thread. */
|
|
|
|
std::thread daemonThread;
|
|
|
|
|
2019-11-04 13:27:28 +00:00
|
|
|
/* The daemon worker threads. */
|
|
|
|
std::vector<std::thread> daemonWorkerThreads;
|
|
|
|
|
Recursive Nix support
This allows Nix builders to call Nix to build derivations, with some
limitations.
Example:
let nixpkgs = fetchTarball channel:nixos-18.03; in
with import <nixpkgs> {};
runCommand "foo"
{
buildInputs = [ nix jq ];
NIX_PATH = "nixpkgs=${nixpkgs}";
}
''
hello=$(nix-build -E '(import <nixpkgs> {}).hello.overrideDerivation (args: { name = "hello-3.5"; })')
$hello/bin/hello
mkdir -p $out/bin
ln -s $hello/bin/hello $out/bin/hello
nix path-info -r --json $hello | jq .
''
This derivation makes a recursive Nix call to build GNU Hello and
symlinks it from its $out, i.e.
# ll ./result/bin/
lrwxrwxrwx 1 root root 63 Jan 1 1970 hello -> /nix/store/s0awxrs71gickhaqdwxl506hzccb30y5-hello-3.5/bin/hello
# nix-store -qR ./result
/nix/store/hwwqshlmazzjzj7yhrkyjydxamvvkfd3-glibc-2.26-131
/nix/store/s0awxrs71gickhaqdwxl506hzccb30y5-hello-3.5
/nix/store/sgmvvyw8vhfqdqb619bxkcpfn9lvd8ss-foo
This is implemented as follows:
* Before running the outer builder, Nix creates a Unix domain socket
'.nix-socket' in the builder's temporary directory and sets
$NIX_REMOTE to point to it. It starts a thread to process
connections to this socket. (Thus you don't need to have nix-daemon
running.)
* The daemon thread uses a wrapper store (RestrictedStore) to keep
track of paths added through recursive Nix calls, to implement some
restrictions (see below), and to do some censorship (e.g. for
purity, queryPathInfo() won't return impure information such as
signatures and timestamps).
* After the build finishes, the output paths are scanned for
references to the paths added through recursive Nix calls (in
addition to the inputs closure). Thus, in the example above, $out
has a reference to $hello.
The main restriction on recursive Nix calls is that they cannot do
arbitrary substitutions. For example, doing
nix-store -r /nix/store/kmwd1hq55akdb9sc7l3finr175dajlby-hello-2.10
is forbidden unless /nix/store/kmwd... is in the inputs closure or
previously built by a recursive Nix call. This is to prevent
irreproducible derivations that have hidden dependencies on
substituters or the current store contents. Building a derivation is
fine, however, and Nix will use substitutes if available. In other
words, the builder has to present proof that it knows how to build a
desired store path from scratch by constructing a derivation graph for
that path.
Probably we should also disallow instantiating/building fixed-output
derivations (specifically, those that access the network, but
currently we have no way to mark fixed-output derivations that don't
access the network). Otherwise sandboxed derivations can bypass
sandbox restrictions and access the network.
When sandboxing is enabled, we make paths appear in the sandbox of the
builder by entering the mount namespace of the builder and
bind-mounting each path. This is tricky because we do a pivot_root()
in the builder to change the root directory of its mount namespace,
and thus the host /nix/store is not visible in the mount namespace of
the builder. To get around this, just before doing pivot_root(), we
branch a second mount namespace that shares its /nix/store mountpoint
with the parent.
Recursive Nix currently doesn't work on macOS in sandboxed mode
(because we can't change the sandbox policy of a running build) and in
non-root mode (because setns() barfs).
2018-10-02 14:01:26 +00:00
|
|
|
/* Paths that were added via recursive Nix calls. */
|
2019-12-05 18:11:09 +00:00
|
|
|
StorePathSet addedPaths;
|
Recursive Nix support
This allows Nix builders to call Nix to build derivations, with some
limitations.
Example:
let nixpkgs = fetchTarball channel:nixos-18.03; in
with import <nixpkgs> {};
runCommand "foo"
{
buildInputs = [ nix jq ];
NIX_PATH = "nixpkgs=${nixpkgs}";
}
''
hello=$(nix-build -E '(import <nixpkgs> {}).hello.overrideDerivation (args: { name = "hello-3.5"; })')
$hello/bin/hello
mkdir -p $out/bin
ln -s $hello/bin/hello $out/bin/hello
nix path-info -r --json $hello | jq .
''
This derivation makes a recursive Nix call to build GNU Hello and
symlinks it from its $out, i.e.
# ll ./result/bin/
lrwxrwxrwx 1 root root 63 Jan 1 1970 hello -> /nix/store/s0awxrs71gickhaqdwxl506hzccb30y5-hello-3.5/bin/hello
# nix-store -qR ./result
/nix/store/hwwqshlmazzjzj7yhrkyjydxamvvkfd3-glibc-2.26-131
/nix/store/s0awxrs71gickhaqdwxl506hzccb30y5-hello-3.5
/nix/store/sgmvvyw8vhfqdqb619bxkcpfn9lvd8ss-foo
This is implemented as follows:
* Before running the outer builder, Nix creates a Unix domain socket
'.nix-socket' in the builder's temporary directory and sets
$NIX_REMOTE to point to it. It starts a thread to process
connections to this socket. (Thus you don't need to have nix-daemon
running.)
* The daemon thread uses a wrapper store (RestrictedStore) to keep
track of paths added through recursive Nix calls, to implement some
restrictions (see below), and to do some censorship (e.g. for
purity, queryPathInfo() won't return impure information such as
signatures and timestamps).
* After the build finishes, the output paths are scanned for
references to the paths added through recursive Nix calls (in
addition to the inputs closure). Thus, in the example above, $out
has a reference to $hello.
The main restriction on recursive Nix calls is that they cannot do
arbitrary substitutions. For example, doing
nix-store -r /nix/store/kmwd1hq55akdb9sc7l3finr175dajlby-hello-2.10
is forbidden unless /nix/store/kmwd... is in the inputs closure or
previously built by a recursive Nix call. This is to prevent
irreproducible derivations that have hidden dependencies on
substituters or the current store contents. Building a derivation is
fine, however, and Nix will use substitutes if available. In other
words, the builder has to present proof that it knows how to build a
desired store path from scratch by constructing a derivation graph for
that path.
Probably we should also disallow instantiating/building fixed-output
derivations (specifically, those that access the network, but
currently we have no way to mark fixed-output derivations that don't
access the network). Otherwise sandboxed derivations can bypass
sandbox restrictions and access the network.
When sandboxing is enabled, we make paths appear in the sandbox of the
builder by entering the mount namespace of the builder and
bind-mounting each path. This is tricky because we do a pivot_root()
in the builder to change the root directory of its mount namespace,
and thus the host /nix/store is not visible in the mount namespace of
the builder. To get around this, just before doing pivot_root(), we
branch a second mount namespace that shares its /nix/store mountpoint
with the parent.
Recursive Nix currently doesn't work on macOS in sandboxed mode
(because we can't change the sandbox policy of a running build) and in
non-root mode (because setns() barfs).
2018-10-02 14:01:26 +00:00
|
|
|
|
|
|
|
/* Recursive Nix calls are only allowed to build or realize paths
|
|
|
|
in the original input closure or added via a recursive Nix call
|
|
|
|
(so e.g. you can't do 'nix-store -r /nix/store/<bla>' where
|
|
|
|
/nix/store/<bla> is some arbitrary path in a binary cache). */
|
2019-12-05 18:11:09 +00:00
|
|
|
bool isAllowed(const StorePath & path)
|
Recursive Nix support
This allows Nix builders to call Nix to build derivations, with some
limitations.
Example:
let nixpkgs = fetchTarball channel:nixos-18.03; in
with import <nixpkgs> {};
runCommand "foo"
{
buildInputs = [ nix jq ];
NIX_PATH = "nixpkgs=${nixpkgs}";
}
''
hello=$(nix-build -E '(import <nixpkgs> {}).hello.overrideDerivation (args: { name = "hello-3.5"; })')
$hello/bin/hello
mkdir -p $out/bin
ln -s $hello/bin/hello $out/bin/hello
nix path-info -r --json $hello | jq .
''
This derivation makes a recursive Nix call to build GNU Hello and
symlinks it from its $out, i.e.
# ll ./result/bin/
lrwxrwxrwx 1 root root 63 Jan 1 1970 hello -> /nix/store/s0awxrs71gickhaqdwxl506hzccb30y5-hello-3.5/bin/hello
# nix-store -qR ./result
/nix/store/hwwqshlmazzjzj7yhrkyjydxamvvkfd3-glibc-2.26-131
/nix/store/s0awxrs71gickhaqdwxl506hzccb30y5-hello-3.5
/nix/store/sgmvvyw8vhfqdqb619bxkcpfn9lvd8ss-foo
This is implemented as follows:
* Before running the outer builder, Nix creates a Unix domain socket
'.nix-socket' in the builder's temporary directory and sets
$NIX_REMOTE to point to it. It starts a thread to process
connections to this socket. (Thus you don't need to have nix-daemon
running.)
* The daemon thread uses a wrapper store (RestrictedStore) to keep
track of paths added through recursive Nix calls, to implement some
restrictions (see below), and to do some censorship (e.g. for
purity, queryPathInfo() won't return impure information such as
signatures and timestamps).
* After the build finishes, the output paths are scanned for
references to the paths added through recursive Nix calls (in
addition to the inputs closure). Thus, in the example above, $out
has a reference to $hello.
The main restriction on recursive Nix calls is that they cannot do
arbitrary substitutions. For example, doing
nix-store -r /nix/store/kmwd1hq55akdb9sc7l3finr175dajlby-hello-2.10
is forbidden unless /nix/store/kmwd... is in the inputs closure or
previously built by a recursive Nix call. This is to prevent
irreproducible derivations that have hidden dependencies on
substituters or the current store contents. Building a derivation is
fine, however, and Nix will use substitutes if available. In other
words, the builder has to present proof that it knows how to build a
desired store path from scratch by constructing a derivation graph for
that path.
Probably we should also disallow instantiating/building fixed-output
derivations (specifically, those that access the network, but
currently we have no way to mark fixed-output derivations that don't
access the network). Otherwise sandboxed derivations can bypass
sandbox restrictions and access the network.
When sandboxing is enabled, we make paths appear in the sandbox of the
builder by entering the mount namespace of the builder and
bind-mounting each path. This is tricky because we do a pivot_root()
in the builder to change the root directory of its mount namespace,
and thus the host /nix/store is not visible in the mount namespace of
the builder. To get around this, just before doing pivot_root(), we
branch a second mount namespace that shares its /nix/store mountpoint
with the parent.
Recursive Nix currently doesn't work on macOS in sandboxed mode
(because we can't change the sandbox policy of a running build) and in
non-root mode (because setns() barfs).
2018-10-02 14:01:26 +00:00
|
|
|
{
|
|
|
|
return inputPaths.count(path) || addedPaths.count(path);
|
|
|
|
}
|
|
|
|
|
2019-12-05 18:11:09 +00:00
|
|
|
friend struct RestrictedStore;
|
Recursive Nix support
This allows Nix builders to call Nix to build derivations, with some
limitations.
Example:
let nixpkgs = fetchTarball channel:nixos-18.03; in
with import <nixpkgs> {};
runCommand "foo"
{
buildInputs = [ nix jq ];
NIX_PATH = "nixpkgs=${nixpkgs}";
}
''
hello=$(nix-build -E '(import <nixpkgs> {}).hello.overrideDerivation (args: { name = "hello-3.5"; })')
$hello/bin/hello
mkdir -p $out/bin
ln -s $hello/bin/hello $out/bin/hello
nix path-info -r --json $hello | jq .
''
This derivation makes a recursive Nix call to build GNU Hello and
symlinks it from its $out, i.e.
# ll ./result/bin/
lrwxrwxrwx 1 root root 63 Jan 1 1970 hello -> /nix/store/s0awxrs71gickhaqdwxl506hzccb30y5-hello-3.5/bin/hello
# nix-store -qR ./result
/nix/store/hwwqshlmazzjzj7yhrkyjydxamvvkfd3-glibc-2.26-131
/nix/store/s0awxrs71gickhaqdwxl506hzccb30y5-hello-3.5
/nix/store/sgmvvyw8vhfqdqb619bxkcpfn9lvd8ss-foo
This is implemented as follows:
* Before running the outer builder, Nix creates a Unix domain socket
'.nix-socket' in the builder's temporary directory and sets
$NIX_REMOTE to point to it. It starts a thread to process
connections to this socket. (Thus you don't need to have nix-daemon
running.)
* The daemon thread uses a wrapper store (RestrictedStore) to keep
track of paths added through recursive Nix calls, to implement some
restrictions (see below), and to do some censorship (e.g. for
purity, queryPathInfo() won't return impure information such as
signatures and timestamps).
* After the build finishes, the output paths are scanned for
references to the paths added through recursive Nix calls (in
addition to the inputs closure). Thus, in the example above, $out
has a reference to $hello.
The main restriction on recursive Nix calls is that they cannot do
arbitrary substitutions. For example, doing
nix-store -r /nix/store/kmwd1hq55akdb9sc7l3finr175dajlby-hello-2.10
is forbidden unless /nix/store/kmwd... is in the inputs closure or
previously built by a recursive Nix call. This is to prevent
irreproducible derivations that have hidden dependencies on
substituters or the current store contents. Building a derivation is
fine, however, and Nix will use substitutes if available. In other
words, the builder has to present proof that it knows how to build a
desired store path from scratch by constructing a derivation graph for
that path.
Probably we should also disallow instantiating/building fixed-output
derivations (specifically, those that access the network, but
currently we have no way to mark fixed-output derivations that don't
access the network). Otherwise sandboxed derivations can bypass
sandbox restrictions and access the network.
When sandboxing is enabled, we make paths appear in the sandbox of the
builder by entering the mount namespace of the builder and
bind-mounting each path. This is tricky because we do a pivot_root()
in the builder to change the root directory of its mount namespace,
and thus the host /nix/store is not visible in the mount namespace of
the builder. To get around this, just before doing pivot_root(), we
branch a second mount namespace that shares its /nix/store mountpoint
with the parent.
Recursive Nix currently doesn't work on macOS in sandboxed mode
(because we can't change the sandbox policy of a running build) and in
non-root mode (because setns() barfs).
2018-10-02 14:01:26 +00:00
|
|
|
|
2004-06-18 18:09:32 +00:00
|
|
|
public:
|
2020-08-22 20:44:47 +00:00
|
|
|
DerivationGoal(const StorePath & drvPath,
|
|
|
|
const StringSet & wantedOutputs, Worker & worker,
|
|
|
|
BuildMode buildMode = bmNormal);
|
2020-06-16 20:20:18 +00:00
|
|
|
DerivationGoal(const StorePath & drvPath, const BasicDerivation & drv,
|
2020-08-22 20:44:47 +00:00
|
|
|
const StringSet & wantedOutputs, Worker & worker,
|
|
|
|
BuildMode buildMode = bmNormal);
|
2005-01-19 11:16:11 +00:00
|
|
|
~DerivationGoal();
|
2004-05-11 18:05:44 +00:00
|
|
|
|
2019-05-12 20:47:41 +00:00
|
|
|
/* Whether we need to perform hash rewriting if there are valid output paths. */
|
|
|
|
bool needsHashRewrite();
|
|
|
|
|
2020-06-15 17:25:35 +00:00
|
|
|
void timedOut(Error && ex) override;
|
2012-07-27 13:59:18 +00:00
|
|
|
|
2015-09-17 23:22:06 +00:00
|
|
|
string key() override
|
2014-11-24 15:48:04 +00:00
|
|
|
{
|
|
|
|
/* Ensure that derivations get built in order of their name,
|
|
|
|
i.e. a derivation named "aardvark" always comes before
|
|
|
|
"baboon". And substitution goals always happen before
|
|
|
|
derivation goals (due to "b$"). */
|
2019-12-05 18:11:09 +00:00
|
|
|
return "b$" + std::string(drvPath.name()) + "$" + worker.store.printStorePath(drvPath);
|
2014-11-24 15:48:04 +00:00
|
|
|
}
|
|
|
|
|
2015-09-17 23:22:06 +00:00
|
|
|
void work() override;
|
2004-06-19 21:45:04 +00:00
|
|
|
|
2019-12-05 18:11:09 +00:00
|
|
|
StorePath getDrvPath()
|
2005-02-23 11:19:27 +00:00
|
|
|
{
|
2020-06-16 20:20:18 +00:00
|
|
|
return drvPath;
|
2005-02-23 11:19:27 +00:00
|
|
|
}
|
2008-01-15 04:32:08 +00:00
|
|
|
|
2012-11-26 16:15:09 +00:00
|
|
|
/* Add wanted outputs to an already existing derivation goal. */
|
|
|
|
void addWantedOutputs(const StringSet & outputs);
|
|
|
|
|
2015-07-20 01:15:45 +00:00
|
|
|
BuildResult getResult() { return result; }
|
|
|
|
|
2004-06-19 21:45:04 +00:00
|
|
|
private:
|
2004-06-18 18:09:32 +00:00
|
|
|
/* The states. */
|
Allow remote builds without sending the derivation closure
Previously, to build a derivation remotely, we had to copy the entire
closure of the .drv file to the remote machine, even though we only
need the top-level derivation. This is very wasteful: the closure can
contain thousands of store paths, and in some Hydra use cases, include
source paths that are very large (e.g. Git/Mercurial checkouts).
So now there is a new operation, StoreAPI::buildDerivation(), that
performs a build from an in-memory representation of a derivation
(BasicDerivation) rather than from a on-disk .drv file. The only files
that need to be in the Nix store are the sources of the derivation
(drv.inputSrcs), and the needed output paths of the dependencies (as
described by drv.inputDrvs). "nix-store --serve" exposes this
interface.
Note that this is a privileged operation, because you can construct a
derivation that builds any store path whatsoever. Fixing this will
require changing the hashing scheme (i.e., the output paths should be
computed from the other fields in BasicDerivation, allowing them to be
verified without access to other derivations). However, this would be
quite nice because it would allow .drv-free building (e.g. "nix-env
-i" wouldn't have to write any .drv files to disk).
Fixes #173.
2015-07-17 15:57:40 +00:00
|
|
|
void getDerivation();
|
|
|
|
void loadDerivation();
|
2006-12-07 23:58:36 +00:00
|
|
|
void haveDerivation();
|
2020-08-07 19:09:26 +00:00
|
|
|
void outputsSubstitutionTried();
|
|
|
|
void gaveUpOnSubstitution();
|
2012-10-03 14:38:09 +00:00
|
|
|
void closureRepaired();
|
2005-01-19 11:16:11 +00:00
|
|
|
void inputsRealised();
|
2004-06-18 18:09:32 +00:00
|
|
|
void tryToBuild();
|
2020-05-14 14:00:54 +00:00
|
|
|
void tryLocalBuild();
|
2004-06-18 18:09:32 +00:00
|
|
|
void buildDone();
|
|
|
|
|
2020-08-22 20:44:47 +00:00
|
|
|
void resolvedFinished();
|
|
|
|
|
2004-06-19 21:45:04 +00:00
|
|
|
/* Is the build hook willing to perform the build? */
|
|
|
|
HookReply tryBuildHook();
|
|
|
|
|
2004-06-18 18:09:32 +00:00
|
|
|
/* Start building a derivation. */
|
|
|
|
void startBuilder();
|
|
|
|
|
2017-01-25 11:00:28 +00:00
|
|
|
/* Fill in the environment for the builder. */
|
|
|
|
void initEnv();
|
|
|
|
|
2019-10-12 23:02:57 +00:00
|
|
|
/* Setup tmp dir location. */
|
|
|
|
void initTmpDir();
|
|
|
|
|
Add support for passing structured data to builders
Previously, all derivation attributes had to be coerced into strings
so that they could be passed via the environment. This is lossy
(e.g. lists get flattened, necessitating configureFlags
vs. configureFlagsArray, of which the latter cannot be specified as an
attribute), doesn't support attribute sets at all, and has size
limitations (necessitating hacks like passAsFile).
This patch adds a new mode for passing attributes to builders, namely
encoded as a JSON file ".attrs.json" in the current directory of the
builder. This mode is activated via the special attribute
__structuredAttrs = true;
(The idea is that one day we can set this in stdenv.mkDerivation.)
For example,
stdenv.mkDerivation {
__structuredAttrs = true;
name = "foo";
buildInputs = [ pkgs.hello pkgs.cowsay ];
doCheck = true;
hardening.format = false;
}
results in a ".attrs.json" file containing (sans the indentation):
{
"buildInputs": [],
"builder": "/nix/store/ygl61ycpr2vjqrx775l1r2mw1g2rb754-bash-4.3-p48/bin/bash",
"configureFlags": [
"--with-foo",
"--with-bar=1 2"
],
"doCheck": true,
"hardening": {
"format": false
},
"name": "foo",
"nativeBuildInputs": [
"/nix/store/10h6li26i7g6z3mdpvra09yyf10mmzdr-hello-2.10",
"/nix/store/4jnvjin0r6wp6cv1hdm5jbkx3vinlcvk-cowsay-3.03"
],
"propagatedBuildInputs": [],
"propagatedNativeBuildInputs": [],
"stdenv": "/nix/store/f3hw3p8armnzy6xhd4h8s7anfjrs15n2-stdenv",
"system": "x86_64-linux"
}
"passAsFile" is ignored in this mode because it's not needed - large
strings are included directly in the JSON representation.
It is up to the builder to do something with the JSON
representation. For example, in bash-based builders, lists/attrsets of
string values could be mapped to bash (associative) arrays.
2017-01-25 15:42:07 +00:00
|
|
|
/* Write a JSON file containing the derivation attributes. */
|
|
|
|
void writeStructuredAttrs();
|
|
|
|
|
Recursive Nix support
This allows Nix builders to call Nix to build derivations, with some
limitations.
Example:
let nixpkgs = fetchTarball channel:nixos-18.03; in
with import <nixpkgs> {};
runCommand "foo"
{
buildInputs = [ nix jq ];
NIX_PATH = "nixpkgs=${nixpkgs}";
}
''
hello=$(nix-build -E '(import <nixpkgs> {}).hello.overrideDerivation (args: { name = "hello-3.5"; })')
$hello/bin/hello
mkdir -p $out/bin
ln -s $hello/bin/hello $out/bin/hello
nix path-info -r --json $hello | jq .
''
This derivation makes a recursive Nix call to build GNU Hello and
symlinks it from its $out, i.e.
# ll ./result/bin/
lrwxrwxrwx 1 root root 63 Jan 1 1970 hello -> /nix/store/s0awxrs71gickhaqdwxl506hzccb30y5-hello-3.5/bin/hello
# nix-store -qR ./result
/nix/store/hwwqshlmazzjzj7yhrkyjydxamvvkfd3-glibc-2.26-131
/nix/store/s0awxrs71gickhaqdwxl506hzccb30y5-hello-3.5
/nix/store/sgmvvyw8vhfqdqb619bxkcpfn9lvd8ss-foo
This is implemented as follows:
* Before running the outer builder, Nix creates a Unix domain socket
'.nix-socket' in the builder's temporary directory and sets
$NIX_REMOTE to point to it. It starts a thread to process
connections to this socket. (Thus you don't need to have nix-daemon
running.)
* The daemon thread uses a wrapper store (RestrictedStore) to keep
track of paths added through recursive Nix calls, to implement some
restrictions (see below), and to do some censorship (e.g. for
purity, queryPathInfo() won't return impure information such as
signatures and timestamps).
* After the build finishes, the output paths are scanned for
references to the paths added through recursive Nix calls (in
addition to the inputs closure). Thus, in the example above, $out
has a reference to $hello.
The main restriction on recursive Nix calls is that they cannot do
arbitrary substitutions. For example, doing
nix-store -r /nix/store/kmwd1hq55akdb9sc7l3finr175dajlby-hello-2.10
is forbidden unless /nix/store/kmwd... is in the inputs closure or
previously built by a recursive Nix call. This is to prevent
irreproducible derivations that have hidden dependencies on
substituters or the current store contents. Building a derivation is
fine, however, and Nix will use substitutes if available. In other
words, the builder has to present proof that it knows how to build a
desired store path from scratch by constructing a derivation graph for
that path.
Probably we should also disallow instantiating/building fixed-output
derivations (specifically, those that access the network, but
currently we have no way to mark fixed-output derivations that don't
access the network). Otherwise sandboxed derivations can bypass
sandbox restrictions and access the network.
When sandboxing is enabled, we make paths appear in the sandbox of the
builder by entering the mount namespace of the builder and
bind-mounting each path. This is tricky because we do a pivot_root()
in the builder to change the root directory of its mount namespace,
and thus the host /nix/store is not visible in the mount namespace of
the builder. To get around this, just before doing pivot_root(), we
branch a second mount namespace that shares its /nix/store mountpoint
with the parent.
Recursive Nix currently doesn't work on macOS in sandboxed mode
(because we can't change the sandbox policy of a running build) and in
non-root mode (because setns() barfs).
2018-10-02 14:01:26 +00:00
|
|
|
void startDaemon();
|
|
|
|
|
|
|
|
void stopDaemon();
|
|
|
|
|
|
|
|
/* Add 'path' to the set of paths that may be referenced by the
|
|
|
|
outputs, and make it appear in the sandbox. */
|
2019-12-05 18:11:09 +00:00
|
|
|
void addDependency(const StorePath & path);
|
Recursive Nix support
This allows Nix builders to call Nix to build derivations, with some
limitations.
Example:
let nixpkgs = fetchTarball channel:nixos-18.03; in
with import <nixpkgs> {};
runCommand "foo"
{
buildInputs = [ nix jq ];
NIX_PATH = "nixpkgs=${nixpkgs}";
}
''
hello=$(nix-build -E '(import <nixpkgs> {}).hello.overrideDerivation (args: { name = "hello-3.5"; })')
$hello/bin/hello
mkdir -p $out/bin
ln -s $hello/bin/hello $out/bin/hello
nix path-info -r --json $hello | jq .
''
This derivation makes a recursive Nix call to build GNU Hello and
symlinks it from its $out, i.e.
# ll ./result/bin/
lrwxrwxrwx 1 root root 63 Jan 1 1970 hello -> /nix/store/s0awxrs71gickhaqdwxl506hzccb30y5-hello-3.5/bin/hello
# nix-store -qR ./result
/nix/store/hwwqshlmazzjzj7yhrkyjydxamvvkfd3-glibc-2.26-131
/nix/store/s0awxrs71gickhaqdwxl506hzccb30y5-hello-3.5
/nix/store/sgmvvyw8vhfqdqb619bxkcpfn9lvd8ss-foo
This is implemented as follows:
* Before running the outer builder, Nix creates a Unix domain socket
'.nix-socket' in the builder's temporary directory and sets
$NIX_REMOTE to point to it. It starts a thread to process
connections to this socket. (Thus you don't need to have nix-daemon
running.)
* The daemon thread uses a wrapper store (RestrictedStore) to keep
track of paths added through recursive Nix calls, to implement some
restrictions (see below), and to do some censorship (e.g. for
purity, queryPathInfo() won't return impure information such as
signatures and timestamps).
* After the build finishes, the output paths are scanned for
references to the paths added through recursive Nix calls (in
addition to the inputs closure). Thus, in the example above, $out
has a reference to $hello.
The main restriction on recursive Nix calls is that they cannot do
arbitrary substitutions. For example, doing
nix-store -r /nix/store/kmwd1hq55akdb9sc7l3finr175dajlby-hello-2.10
is forbidden unless /nix/store/kmwd... is in the inputs closure or
previously built by a recursive Nix call. This is to prevent
irreproducible derivations that have hidden dependencies on
substituters or the current store contents. Building a derivation is
fine, however, and Nix will use substitutes if available. In other
words, the builder has to present proof that it knows how to build a
desired store path from scratch by constructing a derivation graph for
that path.
Probably we should also disallow instantiating/building fixed-output
derivations (specifically, those that access the network, but
currently we have no way to mark fixed-output derivations that don't
access the network). Otherwise sandboxed derivations can bypass
sandbox restrictions and access the network.
When sandboxing is enabled, we make paths appear in the sandbox of the
builder by entering the mount namespace of the builder and
bind-mounting each path. This is tricky because we do a pivot_root()
in the builder to change the root directory of its mount namespace,
and thus the host /nix/store is not visible in the mount namespace of
the builder. To get around this, just before doing pivot_root(), we
branch a second mount namespace that shares its /nix/store mountpoint
with the parent.
Recursive Nix currently doesn't work on macOS in sandboxed mode
(because we can't change the sandbox policy of a running build) and in
non-root mode (because setns() barfs).
2018-10-02 14:01:26 +00:00
|
|
|
|
2017-01-25 11:00:28 +00:00
|
|
|
/* Make a file owned by the builder. */
|
|
|
|
void chownToBuilder(const Path & path);
|
|
|
|
|
2014-12-10 16:25:12 +00:00
|
|
|
/* Run the builder's process. */
|
|
|
|
void runChild();
|
2012-06-25 19:45:16 +00:00
|
|
|
|
|
|
|
friend int childEntry(void *);
|
|
|
|
|
2014-02-17 21:25:15 +00:00
|
|
|
/* Check that the derivation outputs all exist and register them
|
|
|
|
as valid. */
|
|
|
|
void registerOutputs();
|
2004-06-18 18:09:32 +00:00
|
|
|
|
2018-10-22 19:49:56 +00:00
|
|
|
/* Check that an output meets the requirements specified by the
|
|
|
|
'outputChecks' attribute (or the legacy
|
|
|
|
'{allowed,disallowed}{References,Requisites}' attributes). */
|
|
|
|
void checkOutputs(const std::map<std::string, ValidPathInfo> & outputs);
|
|
|
|
|
2004-06-18 18:09:32 +00:00
|
|
|
/* Open a log file and a pipe to it. */
|
2008-11-12 11:08:27 +00:00
|
|
|
Path openLogFile();
|
2004-06-18 18:09:32 +00:00
|
|
|
|
2012-05-30 14:12:29 +00:00
|
|
|
/* Close the log file. */
|
|
|
|
void closeLogFile();
|
|
|
|
|
2004-06-18 18:09:32 +00:00
|
|
|
/* Delete the temporary directory, if we have one. */
|
2004-05-11 18:05:44 +00:00
|
|
|
void deleteTmpDir(bool force);
|
2004-06-18 18:09:32 +00:00
|
|
|
|
2004-06-29 09:41:50 +00:00
|
|
|
/* Callback used by the worker to write to the log. */
|
2015-09-17 23:22:06 +00:00
|
|
|
void handleChildOutput(int fd, const string & data) override;
|
|
|
|
void handleEOF(int fd) override;
|
2016-04-25 14:47:46 +00:00
|
|
|
void flushLine();
|
2004-06-29 09:41:50 +00:00
|
|
|
|
2020-08-07 19:09:26 +00:00
|
|
|
/* Wrappers around the corresponding Store methods that first consult the
|
|
|
|
derivation. This is currently needed because when there is no drv file
|
|
|
|
there also is no DB entry. */
|
2020-08-20 18:14:12 +00:00
|
|
|
std::map<std::string, std::optional<StorePath>> queryPartialDerivationOutputMap();
|
|
|
|
OutputPathMap queryDerivationOutputMap();
|
2020-08-07 19:09:26 +00:00
|
|
|
|
2005-01-25 10:55:33 +00:00
|
|
|
/* Return the set of (in)valid paths. */
|
2020-08-07 19:09:26 +00:00
|
|
|
void checkPathValidity();
|
2009-03-25 21:05:42 +00:00
|
|
|
|
2006-12-08 18:41:48 +00:00
|
|
|
/* Forcibly kill the child process, if any. */
|
|
|
|
void killChild();
|
2012-10-02 21:13:46 +00:00
|
|
|
|
2020-09-04 15:15:51 +00:00
|
|
|
/* Create alternative path calculated from but distinct from the
|
|
|
|
input, so we can avoid overwriting outputs (or other store paths)
|
2020-08-07 19:09:26 +00:00
|
|
|
that already exist. */
|
|
|
|
StorePath makeFallbackPath(const StorePath & path);
|
2020-09-04 15:15:51 +00:00
|
|
|
/* Make a path to another based on the output name along with the
|
|
|
|
derivation hash. */
|
|
|
|
/* FIXME add option to randomize, so we can audit whether our
|
|
|
|
rewrites caught everything */
|
2020-08-11 20:49:10 +00:00
|
|
|
StorePath makeFallbackPath(std::string_view outputName);
|
2012-10-03 14:38:09 +00:00
|
|
|
|
|
|
|
void repairClosure();
|
2015-07-20 01:15:45 +00:00
|
|
|
|
2020-05-14 14:00:54 +00:00
|
|
|
void started();
|
|
|
|
|
2020-06-15 17:25:35 +00:00
|
|
|
void done(
|
|
|
|
BuildResult::Status status,
|
|
|
|
std::optional<Error> ex = {});
|
2018-04-17 10:03:27 +00:00
|
|
|
|
2019-12-05 18:11:09 +00:00
|
|
|
StorePathSet exportReferences(const StorePathSet & storePaths);
|
2004-05-11 18:05:44 +00:00
|
|
|
};
|
|
|
|
|
2020-10-11 16:17:24 +00:00
|
|
|
class SubstitutionGoal : public Goal
|
2004-05-11 18:05:44 +00:00
|
|
|
{
|
2020-10-11 16:17:24 +00:00
|
|
|
friend class Worker;
|
2010-08-25 20:44:28 +00:00
|
|
|
|
2020-10-11 16:17:24 +00:00
|
|
|
private:
|
|
|
|
/* The store path that should be realised through a substitute. */
|
|
|
|
StorePath storePath;
|
2019-05-12 20:47:41 +00:00
|
|
|
|
2020-10-11 16:17:24 +00:00
|
|
|
/* The path the substituter refers to the path as. This will be
|
|
|
|
* different when the stores have different names. */
|
|
|
|
std::optional<StorePath> subPath;
|
2019-05-12 20:47:41 +00:00
|
|
|
|
2020-10-11 16:17:24 +00:00
|
|
|
/* The remaining substituters. */
|
|
|
|
std::list<ref<Store>> subs;
|
2010-08-25 20:44:28 +00:00
|
|
|
|
2020-10-11 16:17:24 +00:00
|
|
|
/* The current substituter. */
|
|
|
|
std::shared_ptr<Store> sub;
|
2006-12-08 18:41:48 +00:00
|
|
|
|
2020-10-11 16:17:24 +00:00
|
|
|
/* Whether a substituter failed. */
|
|
|
|
bool substituterFailed = false;
|
2006-12-08 18:41:48 +00:00
|
|
|
|
2020-10-11 16:17:24 +00:00
|
|
|
/* Path info returned by the substituter's query info operation. */
|
|
|
|
std::shared_ptr<const ValidPathInfo> info;
|
2006-12-08 18:41:48 +00:00
|
|
|
|
2020-10-11 16:17:24 +00:00
|
|
|
/* Pipe for the substituter's standard output. */
|
|
|
|
Pipe outPipe;
|
2006-12-08 18:41:48 +00:00
|
|
|
|
2020-10-11 16:17:24 +00:00
|
|
|
/* The substituter thread. */
|
|
|
|
std::thread thr;
|
2004-05-11 18:05:44 +00:00
|
|
|
|
2020-10-11 16:17:24 +00:00
|
|
|
std::promise<void> promise;
|
2004-05-11 18:05:44 +00:00
|
|
|
|
2020-10-11 16:17:24 +00:00
|
|
|
/* Whether to try to repair a valid path. */
|
|
|
|
RepairFlag repair;
|
2012-11-26 16:15:09 +00:00
|
|
|
|
2020-10-11 16:17:24 +00:00
|
|
|
/* Location where we're downloading the substitute. Differs from
|
|
|
|
storePath when doing a repair. */
|
|
|
|
Path destPath;
|
2012-11-26 16:15:09 +00:00
|
|
|
|
2020-10-11 16:17:24 +00:00
|
|
|
std::unique_ptr<MaintainCount<uint64_t>> maintainExpectedSubstitutions,
|
|
|
|
maintainRunningSubstitutions, maintainExpectedNar, maintainExpectedDownload;
|
2014-11-24 15:44:35 +00:00
|
|
|
|
2020-10-11 16:17:24 +00:00
|
|
|
typedef void (SubstitutionGoal::*GoalState)();
|
|
|
|
GoalState state;
|
2003-08-01 14:11:19 +00:00
|
|
|
|
2020-10-11 16:17:24 +00:00
|
|
|
/* Content address for recomputing store path */
|
|
|
|
std::optional<ContentAddress> ca;
|
2004-05-11 18:05:44 +00:00
|
|
|
|
2020-10-11 16:17:24 +00:00
|
|
|
public:
|
|
|
|
SubstitutionGoal(const StorePath & storePath, Worker & worker, RepairFlag repair = NoRepair, std::optional<ContentAddress> ca = std::nullopt);
|
|
|
|
~SubstitutionGoal();
|
2004-05-11 18:05:44 +00:00
|
|
|
|
2020-10-11 16:17:24 +00:00
|
|
|
void timedOut(Error && ex) override { abort(); };
|
2004-05-11 18:05:44 +00:00
|
|
|
|
2020-10-11 16:17:24 +00:00
|
|
|
string key() override
|
|
|
|
{
|
|
|
|
/* "a$" ensures substitution goals happen before derivation
|
|
|
|
goals. */
|
|
|
|
return "a$" + std::string(storePath.name()) + "$" + worker.store.printStorePath(storePath);
|
2004-06-25 10:21:44 +00:00
|
|
|
}
|
|
|
|
|
2020-10-11 16:17:24 +00:00
|
|
|
void work() override;
|
2003-07-20 19:29:38 +00:00
|
|
|
|
2020-10-11 16:17:24 +00:00
|
|
|
/* The states. */
|
|
|
|
void init();
|
|
|
|
void tryNext();
|
|
|
|
void gotInfo();
|
|
|
|
void referencesValid();
|
|
|
|
void tryToRun();
|
|
|
|
void finished();
|
Allow remote builds without sending the derivation closure
Previously, to build a derivation remotely, we had to copy the entire
closure of the .drv file to the remote machine, even though we only
need the top-level derivation. This is very wasteful: the closure can
contain thousands of store paths, and in some Hydra use cases, include
source paths that are very large (e.g. Git/Mercurial checkouts).
So now there is a new operation, StoreAPI::buildDerivation(), that
performs a build from an in-memory representation of a derivation
(BasicDerivation) rather than from a on-disk .drv file. The only files
that need to be in the Nix store are the sources of the derivation
(drv.inputSrcs), and the needed output paths of the dependencies (as
described by drv.inputDrvs). "nix-store --serve" exposes this
interface.
Note that this is a privileged operation, because you can construct a
derivation that builds any store path whatsoever. Fixing this will
require changing the hashing scheme (i.e., the output paths should be
computed from the other fields in BasicDerivation, allowing them to be
verified without access to other derivations). However, this would be
quite nice because it would allow .drv-free building (e.g. "nix-env
-i" wouldn't have to write any .drv files to disk).
Fixes #173.
2015-07-17 15:57:40 +00:00
|
|
|
|
2020-10-11 16:17:24 +00:00
|
|
|
/* Callback used by the worker to write to the log. */
|
|
|
|
void handleChildOutput(int fd, const string & data) override;
|
|
|
|
void handleEOF(int fd) override;
|
2004-05-11 18:05:44 +00:00
|
|
|
|
2020-10-11 16:17:24 +00:00
|
|
|
StorePath getStorePath() { return storePath; }
|
|
|
|
};
|
Allow remote builds without sending the derivation closure
Previously, to build a derivation remotely, we had to copy the entire
closure of the .drv file to the remote machine, even though we only
need the top-level derivation. This is very wasteful: the closure can
contain thousands of store paths, and in some Hydra use cases, include
source paths that are very large (e.g. Git/Mercurial checkouts).
So now there is a new operation, StoreAPI::buildDerivation(), that
performs a build from an in-memory representation of a derivation
(BasicDerivation) rather than from a on-disk .drv file. The only files
that need to be in the Nix store are the sources of the derivation
(drv.inputSrcs), and the needed output paths of the dependencies (as
described by drv.inputDrvs). "nix-store --serve" exposes this
interface.
Note that this is a privileged operation, because you can construct a
derivation that builds any store path whatsoever. Fixing this will
require changing the hashing scheme (i.e., the output paths should be
computed from the other fields in BasicDerivation, allowing them to be
verified without access to other derivations). However, this would be
quite nice because it would allow .drv-free building (e.g. "nix-env
-i" wouldn't have to write any .drv files to disk).
Fixes #173.
2015-07-17 15:57:40 +00:00
|
|
|
|
2020-10-11 16:17:24 +00:00
|
|
|
struct HookInstance
|
Allow remote builds without sending the derivation closure
Previously, to build a derivation remotely, we had to copy the entire
closure of the .drv file to the remote machine, even though we only
need the top-level derivation. This is very wasteful: the closure can
contain thousands of store paths, and in some Hydra use cases, include
source paths that are very large (e.g. Git/Mercurial checkouts).
So now there is a new operation, StoreAPI::buildDerivation(), that
performs a build from an in-memory representation of a derivation
(BasicDerivation) rather than from a on-disk .drv file. The only files
that need to be in the Nix store are the sources of the derivation
(drv.inputSrcs), and the needed output paths of the dependencies (as
described by drv.inputDrvs). "nix-store --serve" exposes this
interface.
Note that this is a privileged operation, because you can construct a
derivation that builds any store path whatsoever. Fixing this will
require changing the hashing scheme (i.e., the output paths should be
computed from the other fields in BasicDerivation, allowing them to be
verified without access to other derivations). However, this would be
quite nice because it would allow .drv-free building (e.g. "nix-env
-i" wouldn't have to write any .drv files to disk).
Fixes #173.
2015-07-17 15:57:40 +00:00
|
|
|
{
|
2020-10-11 16:17:24 +00:00
|
|
|
/* Pipes for talking to the build hook. */
|
|
|
|
Pipe toHook;
|
2018-09-28 10:43:01 +00:00
|
|
|
|
2020-10-11 16:17:24 +00:00
|
|
|
/* Pipe for the hook's standard output/error. */
|
|
|
|
Pipe fromHook;
|
2018-09-28 10:43:01 +00:00
|
|
|
|
2020-10-11 16:17:24 +00:00
|
|
|
/* Pipe for the builder's standard output/error. */
|
|
|
|
Pipe builderOut;
|
2005-01-25 10:55:33 +00:00
|
|
|
|
2020-10-11 16:17:24 +00:00
|
|
|
/* The process ID of the hook. */
|
|
|
|
Pid pid;
|
2005-01-25 10:55:33 +00:00
|
|
|
|
2020-10-11 16:17:24 +00:00
|
|
|
FdSink sink;
|
2005-01-25 10:55:33 +00:00
|
|
|
|
2020-10-11 16:17:24 +00:00
|
|
|
std::map<ActivityId, Activity> activities;
|
2005-01-25 13:00:12 +00:00
|
|
|
|
2020-10-11 16:17:24 +00:00
|
|
|
HookInstance();
|
2013-01-02 11:38:28 +00:00
|
|
|
|
2020-10-11 16:17:24 +00:00
|
|
|
~HookInstance();
|
|
|
|
};
|
2005-01-25 13:00:12 +00:00
|
|
|
|
2012-10-02 18:08:59 +00:00
|
|
|
|
2020-10-11 16:17:24 +00:00
|
|
|
void addToWeakGoals(WeakGoals & goals, GoalPtr p);
|
2012-10-02 18:08:59 +00:00
|
|
|
|
2006-09-04 21:06:23 +00:00
|
|
|
}
|