3 #include "references.hh"
4 #include "pathlocks.hh"
7 #include "local-store.hh"
10 #include "affinity.hh"
20 #include <sys/types.h>
22 #include <sys/utsname.h>
34 /* Includes required for chroot support. */
36 #include <sys/param.h>
39 #include <sys/mount.h>
41 #if HAVE_SYS_SYSCALL_H
42 #include <sys/syscall.h>
48 /* In GNU libc 2.11, <sys/mount.h> does not define `MS_PRIVATE', but
50 #if !defined MS_PRIVATE && defined HAVE_LINUX_FS_H
54 #define CHROOT_ENABLED HAVE_CHROOT && HAVE_SYS_MOUNT_H && defined(MS_BIND) && defined(MS_PRIVATE) && defined(CLONE_NEWNS) && defined(SYS_pivot_root)
57 #include <sys/socket.h>
58 #include <sys/ioctl.h>
60 #include <netinet/ip.h>
64 #include <sys/personality.h>
68 #include <sys/statvfs.h>
77 static string pathNullDevice
= "/dev/null";
80 /* Forward definition. */
85 /* A pointer to a goal. */
88 typedef std::shared_ptr
<Goal
> GoalPtr
;
89 typedef std::weak_ptr
<Goal
> WeakGoalPtr
;
91 struct CompareGoalPtrs
{
92 bool operator() (const GoalPtr
& a
, const GoalPtr
& b
);
96 typedef set
<GoalPtr
, CompareGoalPtrs
> Goals
;
97 typedef list
<WeakGoalPtr
> WeakGoals
;
99 /* A map of paths to goals (and the other way around). */
100 typedef map
<Path
, WeakGoalPtr
> WeakGoalMap
;
104 class Goal
: public std::enable_shared_from_this
<Goal
>
107 typedef enum {ecBusy
, ecSuccess
, ecFailed
, ecNoSubstituters
, ecIncompleteClosure
} ExitCode
;
111 /* Backlink to the worker. */
114 /* Goals that this goal is waiting for. */
117 /* Goals waiting for this one to finish. Must use weak pointers
118 here to prevent cycles. */
121 /* Number of goals we are/were waiting for that have failed. */
122 unsigned int nrFailed
;
124 /* Number of substitution goals we are/were waiting for that
125 failed because there are no substituters. */
126 unsigned int nrNoSubstituters
;
128 /* Number of substitution goals we are/were waiting for that
129 failed because othey had unsubstitutable references. */
130 unsigned int nrIncompleteClosure
;
132 /* Name of this goal for debugging purposes. */
135 /* Whether the goal is finished. */
138 Goal(Worker
& worker
) : worker(worker
)
140 nrFailed
= nrNoSubstituters
= nrIncompleteClosure
= 0;
146 trace("goal destroyed");
150 virtual void work() = 0;
152 void addWaitee(GoalPtr waitee
);
154 virtual void waiteeDone(GoalPtr waitee
, ExitCode result
);
156 virtual void handleChildOutput(int fd
, const string
& data
)
161 virtual void handleEOF(int fd
)
166 void trace(const format
& f
);
173 ExitCode
getExitCode()
178 /* Callback in case of a timeout. It should wake up its waiters,
179 get rid of any running child processes that are being monitored
180 by the worker (important!), etc. */
181 virtual void timedOut() = 0;
183 virtual string
key() = 0;
186 void amDone(ExitCode result
);
190 bool CompareGoalPtrs::operator() (const GoalPtr
& a
, const GoalPtr
& b
) {
191 string s1
= a
->key();
192 string s2
= b
->key();
197 /* A mapping used to remember for each child process to what goal it
198 belongs, and file descriptors for receiving log data and output
199 path creation commands. */
204 bool respectTimeouts
;
206 time_t lastOutput
; /* time we last got output on stdout/stderr */
210 typedef map
<pid_t
, Child
> Children
;
213 /* The worker class. */
218 /* Note: the worker should only have strong pointers to the
221 /* The top-level goals of the worker. */
224 /* Goals that are ready to do some work. */
227 /* Goals waiting for a build slot. */
228 WeakGoals wantingToBuild
;
230 /* Child processes currently running. */
233 /* Number of build slots occupied. This includes local builds and
234 substitutions but not remote builds via the build hook. */
235 unsigned int nrLocalBuilds
;
237 /* Maps used to prevent multiple instantiations of a goal for the
238 same derivation / path. */
239 WeakGoalMap derivationGoals
;
240 WeakGoalMap substitutionGoals
;
242 /* Goals waiting for busy paths to be unlocked. */
243 WeakGoals waitingForAnyGoal
;
245 /* Goals sleeping for a few seconds (polling a lock). */
246 WeakGoals waitingForAWhile
;
248 /* Last time the goals in `waitingForAWhile' where woken up. */
253 /* Set if at least one derivation had a BuildError (i.e. permanent
255 bool permanentFailure
;
257 /* Set if at least one derivation had a timeout. */
262 std::shared_ptr
<HookInstance
> hook
;
264 Worker(LocalStore
& store
);
267 /* Make a goal (with caching). */
268 GoalPtr
makeDerivationGoal(const Path
& drvPath
, const StringSet
& wantedOutputs
, BuildMode buildMode
= bmNormal
);
269 GoalPtr
makeSubstitutionGoal(const Path
& storePath
, bool repair
= false);
271 /* Remove a dead goal. */
272 void removeGoal(GoalPtr goal
);
274 /* Wake up a goal (i.e., there is something for it to do). */
275 void wakeUp(GoalPtr goal
);
277 /* Return the number of local build and substitution processes
278 currently running (but not remote builds via the build
280 unsigned int getNrLocalBuilds();
282 /* Registers a running child process. `inBuildSlot' means that
283 the process counts towards the jobs limit. */
284 void childStarted(GoalPtr goal
, pid_t pid
,
285 const set
<int> & fds
, bool inBuildSlot
, bool respectTimeouts
);
287 /* Unregisters a running child process. `wakeSleepers' should be
288 false if there is no sense in waking up goals that are sleeping
289 because they can't run yet (e.g., there is no free build slot,
290 or the hook would still say `postpone'). */
291 void childTerminated(pid_t pid
, bool wakeSleepers
= true);
293 /* Put `goal' to sleep until a build slot becomes available (which
294 might be right away). */
295 void waitForBuildSlot(GoalPtr goal
);
297 /* Wait for any goal to finish. Pretty indiscriminate way to
298 wait for some resource that some other goal is holding. */
299 void waitForAnyGoal(GoalPtr goal
);
301 /* Wait for a few seconds and then retry this goal. Used when
302 waiting for a lock held by another process. This kind of
303 polling is inefficient, but POSIX doesn't really provide a way
304 to wait for multiple locks in the main select() loop. */
305 void waitForAWhile(GoalPtr goal
);
307 /* Loop until the specified top-level goals have finished. */
308 void run(const Goals
& topGoals
);
310 /* Wait for input to become available. */
313 unsigned int exitStatus();
317 //////////////////////////////////////////////////////////////////////
320 void addToWeakGoals(WeakGoals
& goals
, GoalPtr p
)
324 foreach (WeakGoals::iterator
, i
, goals
)
325 if (i
->lock() == p
) return;
330 void Goal::addWaitee(GoalPtr waitee
)
332 waitees
.insert(waitee
);
333 addToWeakGoals(waitee
->waiters
, shared_from_this());
337 void Goal::waiteeDone(GoalPtr waitee
, ExitCode result
)
339 assert(waitees
.find(waitee
) != waitees
.end());
340 waitees
.erase(waitee
);
342 trace(format("waitee `%1%' done; %2% left") %
343 waitee
->name
% waitees
.size());
345 if (result
== ecFailed
|| result
== ecNoSubstituters
|| result
== ecIncompleteClosure
) ++nrFailed
;
347 if (result
== ecNoSubstituters
) ++nrNoSubstituters
;
349 if (result
== ecIncompleteClosure
) ++nrIncompleteClosure
;
351 if (waitees
.empty() || (result
== ecFailed
&& !settings
.keepGoing
)) {
353 /* If we failed and keepGoing is not set, we remove all
354 remaining waitees. */
355 foreach (Goals::iterator
, i
, waitees
) {
358 foreach (WeakGoals::iterator
, j
, goal
->waiters
)
359 if (j
->lock() != shared_from_this()) waiters2
.push_back(*j
);
360 goal
->waiters
= waiters2
;
364 worker
.wakeUp(shared_from_this());
369 void Goal::amDone(ExitCode result
)
372 assert(exitCode
== ecBusy
);
373 assert(result
== ecSuccess
|| result
== ecFailed
|| result
== ecNoSubstituters
|| result
== ecIncompleteClosure
);
375 foreach (WeakGoals::iterator
, i
, waiters
) {
376 GoalPtr goal
= i
->lock();
377 if (goal
) goal
->waiteeDone(shared_from_this(), result
);
380 worker
.removeGoal(shared_from_this());
384 void Goal::trace(const format
& f
)
386 debug(format("%1%: %2%") % name
% f
);
391 //////////////////////////////////////////////////////////////////////
394 /* Common initialisation performed in child processes. */
395 static void commonChildInit(Pipe
& logPipe
)
397 /* Put the child in a separate session (and thus a separate
398 process group) so that it has no controlling terminal (meaning
399 that e.g. ssh cannot open /dev/tty) and it doesn't receive
402 throw SysError(format("creating a new session"));
404 /* Dup the write side of the logger pipe into stderr. */
405 if (dup2(logPipe
.writeSide
, STDERR_FILENO
) == -1)
406 throw SysError("cannot pipe standard error into log file");
408 /* Dup stderr to stdout. */
409 if (dup2(STDERR_FILENO
, STDOUT_FILENO
) == -1)
410 throw SysError("cannot dup stderr into stdout");
412 /* Reroute stdin to /dev/null. */
413 int fdDevNull
= open(pathNullDevice
.c_str(), O_RDWR
);
415 throw SysError(format("cannot open `%1%'") % pathNullDevice
);
416 if (dup2(fdDevNull
, STDIN_FILENO
) == -1)
417 throw SysError("cannot dup null device into stdin");
421 /* Restore default handling of SIGPIPE, otherwise some programs will
422 randomly say "Broken pipe". */
423 static void restoreSIGPIPE()
425 struct sigaction act
, oact
;
426 act
.sa_handler
= SIG_DFL
;
428 sigemptyset(&act
.sa_mask
);
429 if (sigaction(SIGPIPE
, &act
, &oact
)) throw SysError("resetting SIGPIPE");
433 //////////////////////////////////////////////////////////////////////
439 /* POSIX locks suck. If we have a lock on a file, and we open and
440 close that file again (without closing the original file
441 descriptor), we lose the lock. So we have to be *very* careful
442 not to open a lock file on which we are holding a lock. */
443 static PathSet lockedPaths
; /* !!! not thread-safe */
446 AutoCloseFD fdUserLock
;
451 std::vector
<gid_t
> supplementaryGIDs
;
462 string
getUser() { return user
; }
463 uid_t
getUID() { return uid
; }
464 uid_t
getGID() { return gid
; }
465 std::vector
<gid_t
> getSupplementaryGIDs() { return supplementaryGIDs
; }
467 bool enabled() { return uid
!= 0; }
472 PathSet
UserLock::lockedPaths
;
481 UserLock::~UserLock()
487 void UserLock::acquire()
491 assert(settings
.buildUsersGroup
!= "");
493 /* Get the members of the build-users-group. */
494 struct group
* gr
= getgrnam(settings
.buildUsersGroup
.c_str());
496 throw Error(format("the group `%1%' specified in `build-users-group' does not exist")
497 % settings
.buildUsersGroup
);
500 /* Copy the result of getgrnam. */
502 for (char * * p
= gr
->gr_mem
; *p
; ++p
) {
503 debug(format("found build user `%1%'") % *p
);
508 throw Error(format("the build users group `%1%' has no members")
509 % settings
.buildUsersGroup
);
511 /* Find a user account that isn't currently in use for another
513 foreach (Strings::iterator
, i
, users
) {
514 debug(format("trying user `%1%'") % *i
);
516 struct passwd
* pw
= getpwnam(i
->c_str());
518 throw Error(format("the user `%1%' in the group `%2%' does not exist")
519 % *i
% settings
.buildUsersGroup
);
521 createDirs(settings
.nixStateDir
+ "/userpool");
523 fnUserLock
= (format("%1%/userpool/%2%") % settings
.nixStateDir
% pw
->pw_uid
).str();
525 if (lockedPaths
.find(fnUserLock
) != lockedPaths
.end())
526 /* We already have a lock on this one. */
529 AutoCloseFD fd
= open(fnUserLock
.c_str(), O_RDWR
| O_CREAT
, 0600);
531 throw SysError(format("opening user lock `%1%'") % fnUserLock
);
534 if (lockFile(fd
, ltWrite
, false)) {
535 fdUserLock
= fd
.borrow();
536 lockedPaths
.insert(fnUserLock
);
540 /* Sanity check... */
541 if (uid
== getuid() || uid
== geteuid())
542 throw Error(format("the Nix user should not be a member of `%1%'")
543 % settings
.buildUsersGroup
);
545 /* Get the list of supplementary groups of this build user. This
546 is usually either empty or contains a group such as "kvm". */
547 supplementaryGIDs
.resize(10);
548 int ngroups
= supplementaryGIDs
.size();
549 int err
= getgrouplist(pw
->pw_name
, pw
->pw_gid
,
550 supplementaryGIDs
.data(), &ngroups
);
552 throw Error(format("failed to get list of supplementary groups for ‘%1%’") % pw
->pw_name
);
554 supplementaryGIDs
.resize(ngroups
);
560 throw Error(format("all build users are currently in use; "
561 "consider creating additional users and adding them to the `%1%' group")
562 % settings
.buildUsersGroup
);
566 void UserLock::release()
568 if (uid
== 0) return;
569 fdUserLock
.close(); /* releases lock */
570 assert(lockedPaths
.find(fnUserLock
) != lockedPaths
.end());
571 lockedPaths
.erase(fnUserLock
);
577 void UserLock::kill()
584 //////////////////////////////////////////////////////////////////////
589 /* Pipes for talking to the build hook. */
592 /* Pipe for the hook's standard output/error. */
595 /* Pipe for the builder's standard output/error. */
598 /* The process ID of the hook. */
607 HookInstance::HookInstance()
609 debug("starting build hook");
611 Path buildHook
= getEnv("NIX_BUILD_HOOK");
612 if (string(buildHook
, 0, 1) != "/") buildHook
= settings
.nixLibexecDir
+ "/nix/" + buildHook
;
613 buildHook
= canonPath(buildHook
);
615 /* Create a pipe to get the output of the child. */
618 /* Create the communication pipes. */
621 /* Create a pipe to get the output of the builder. */
625 pid
= startProcess([&]() {
627 commonChildInit(fromHook
);
629 if (chdir("/") == -1) throw SysError("changing into `/");
631 /* Dup the communication pipes. */
632 if (dup2(toHook
.readSide
, STDIN_FILENO
) == -1)
633 throw SysError("dupping to-hook read side");
635 /* Use fd 4 for the builder's stdout/stderr. */
636 if (dup2(builderOut
.writeSide
, 4) == -1)
637 throw SysError("dupping builder's stdout/stderr");
639 execl(buildHook
.c_str(), buildHook
.c_str(), settings
.thisSystem
.c_str(),
640 (format("%1%") % settings
.maxSilentTime
).str().c_str(),
641 (format("%1%") % settings
.printBuildTrace
).str().c_str(),
642 (format("%1%") % settings
.buildTimeout
).str().c_str(),
645 throw SysError(format("executing `%1%'") % buildHook
);
648 pid
.setSeparatePG(true);
649 fromHook
.writeSide
.close();
650 toHook
.readSide
.close();
654 HookInstance::~HookInstance()
657 toHook
.writeSide
.close();
665 //////////////////////////////////////////////////////////////////////
668 typedef map
<string
, string
> HashRewrites
;
671 string
rewriteHashes(string s
, const HashRewrites
& rewrites
)
673 foreach (HashRewrites::const_iterator
, i
, rewrites
) {
674 assert(i
->first
.size() == i
->second
.size());
676 while ((j
= s
.find(i
->first
, j
)) != string::npos
) {
677 debug(format("rewriting @ %1%") % j
);
678 s
.replace(j
, i
->second
.size(), i
->second
);
685 //////////////////////////////////////////////////////////////////////
688 typedef enum {rpAccept
, rpDecline
, rpPostpone
} HookReply
;
690 class SubstitutionGoal
;
692 class DerivationGoal
: public Goal
695 /* The path of the derivation. */
698 /* The specific outputs that we need to build. Empty means all of
700 StringSet wantedOutputs
;
702 /* Whether additional wanted outputs have been added. */
705 /* Whether to retry substituting the outputs after building the
707 bool retrySubstitution
;
709 /* The derivation stored at drvPath. */
712 /* The remainder is state held during the build. */
714 /* Locks on the output paths. */
715 PathLocks outputLocks
;
717 /* All input paths (that is, the union of FS closures of the
718 immediate input paths). */
721 /* Referenceable paths (i.e., input and output paths). */
724 /* Outputs that are already valid. If we're repairing, these are
725 the outputs that are valid *and* not corrupt. */
728 /* Outputs that are corrupt or not valid. */
729 PathSet missingPaths
;
731 /* User selected for running the builder. */
734 /* The process ID of the builder. */
737 /* The temporary directory. */
740 /* The path of the temporary directory in the sandbox. */
741 Path tmpDirInSandbox
;
743 /* File descriptor for the log file. */
746 AutoCloseFD fdLogFile
;
748 /* Number of bytes received from the builder's stdout/stderr. */
749 unsigned long logSize
;
751 /* Pipe for the builder's standard output/error. */
754 /* The build hook. */
755 std::shared_ptr
<HookInstance
> hook
;
757 /* Whether we're currently doing a chroot build. */
762 /* RAII object to delete the chroot directory. */
763 std::shared_ptr
<AutoDelete
> autoDelChroot
;
765 /* All inputs that are regular files. */
766 PathSet regularInputPaths
;
768 /* Whether this is a fixed-output derivation. */
771 typedef void (DerivationGoal::*GoalState
)();
774 /* Stuff we need to pass to runChild(). */
775 typedef map
<Path
, Path
> DirsInChroot
; // maps target path to source path
776 DirsInChroot dirsInChroot
;
777 typedef map
<string
, string
> Environment
;
780 /* Hash rewriting. */
781 HashRewrites rewritesToTmp
, rewritesFromTmp
;
782 typedef map
<Path
, Path
> RedirectedOutputs
;
783 RedirectedOutputs redirectedOutputs
;
787 /* If we're repairing without a chroot, there may be outputs that
788 are valid but corrupt. So we redirect these outputs to
790 PathSet redirectedBadOutputs
;
792 /* The current round, if we're building multiple times. */
793 unsigned int curRound
= 1;
795 unsigned int nrRounds
;
797 /* Path registration info from the previous round, if we're
798 building multiple times. Since this contains the hash, it
799 allows us to compare whether two rounds produced the same
801 ValidPathInfos prevInfos
;
806 DerivationGoal(const Path
& drvPath
, const StringSet
& wantedOutputs
, Worker
& worker
, BuildMode buildMode
= bmNormal
);
809 void timedOut() override
;
813 /* Ensure that derivations get built in order of their name,
814 i.e. a derivation named "aardvark" always comes before
815 "baboon". And substitution goals always happen before
816 derivation goals (due to "b$"). */
817 return "b$" + storePathToName(drvPath
) + "$" + drvPath
;
827 /* Add wanted outputs to an already existing derivation goal. */
828 void addWantedOutputs(const StringSet
& outputs
);
830 BuildResult
getResult() { return result
; }
835 void haveDerivation();
836 void outputsSubstituted();
837 void closureRepaired();
838 void inputsRealised();
842 /* Is the build hook willing to perform the build? */
843 HookReply
tryBuildHook();
845 /* Start building a derivation. */
848 /* Run the builder's process. */
851 friend int childEntry(void *);
853 /* Check that the derivation outputs all exist and register them
855 void registerOutputs();
857 /* Open a log file and a pipe to it. */
860 /* Close the log file. */
863 /* Delete the temporary directory, if we have one. */
864 void deleteTmpDir(bool force
);
866 /* Callback used by the worker to write to the log. */
867 void handleChildOutput(int fd
, const string
& data
);
868 void handleEOF(int fd
);
870 /* Return the set of (in)valid paths. */
871 PathSet
checkPathValidity(bool returnValid
, bool checkHash
);
873 /* Abort the goal if `path' failed to build. */
874 bool pathFailed(const Path
& path
);
876 /* Forcibly kill the child process, if any. */
879 Path
addHashRewrite(const Path
& path
);
881 void repairClosure();
883 void done(BuildResult::Status status
, const string
& msg
= "");
887 DerivationGoal::DerivationGoal(const Path
& drvPath
, const StringSet
& wantedOutputs
, Worker
& worker
, BuildMode buildMode
)
889 , wantedOutputs(wantedOutputs
)
891 , retrySubstitution(false)
895 , buildMode(buildMode
)
897 this->drvPath
= drvPath
;
898 state
= &DerivationGoal::init
;
899 name
= (format("building of `%1%'") % drvPath
).str();
902 /* Prevent the .chroot directory from being
903 garbage-collected. (See isActiveTempFile() in gc.cc.) */
904 worker
.store
.addTempRoot(drvPath
);
908 DerivationGoal::~DerivationGoal()
910 /* Careful: we should never ever throw an exception from a
912 try { killChild(); } catch (...) { ignoreException(); }
913 try { deleteTmpDir(false); } catch (...) { ignoreException(); }
914 try { closeLogFile(); } catch (...) { ignoreException(); }
918 void DerivationGoal::killChild()
921 worker
.childTerminated(pid
);
923 if (buildUser
.enabled()) {
924 /* If we're using a build user, then there is a tricky
925 race condition: if we kill the build user before the
926 child has done its setuid() to the build user uid, then
927 it won't be killed, and we'll potentially lock up in
928 pid.wait(). So also send a conventional kill to the
930 ::kill(-pid
, SIGKILL
); /* ignore the result */
943 void DerivationGoal::timedOut()
945 if (settings
.printBuildTrace
)
946 printMsg(lvlError
, format("@ build-failed %1% - timeout") % drvPath
);
948 done(BuildResult::TimedOut
);
952 void DerivationGoal::work()
958 void DerivationGoal::addWantedOutputs(const StringSet
& outputs
)
960 /* If we already want all outputs, there is nothing to do. */
961 if (wantedOutputs
.empty()) return;
963 if (outputs
.empty()) {
964 wantedOutputs
.clear();
967 foreach (StringSet::const_iterator
, i
, outputs
)
968 if (wantedOutputs
.find(*i
) == wantedOutputs
.end()) {
969 wantedOutputs
.insert(*i
);
975 void DerivationGoal::init()
979 if (settings
.readOnlyMode
)
980 throw Error(format("cannot build derivation `%1%' - no write access to the Nix store") % drvPath
);
982 /* The first thing to do is to make sure that the derivation
983 exists. If it doesn't, it may be created through a
985 if (buildMode
== bmNormal
&& worker
.store
.isValidPath(drvPath
)) {
990 addWaitee(worker
.makeSubstitutionGoal(drvPath
));
992 state
= &DerivationGoal::haveDerivation
;
996 void DerivationGoal::haveDerivation()
998 trace("loading derivation");
1000 if (nrFailed
!= 0) {
1001 printMsg(lvlError
, format("cannot build missing derivation ‘%1%’") % drvPath
);
1002 done(BuildResult::MiscFailure
);
1006 /* `drvPath' should already be a root, but let's be on the safe
1007 side: if the user forgot to make it a root, we wouldn't want
1008 things being garbage collected while we're busy. */
1009 worker
.store
.addTempRoot(drvPath
);
1011 assert(worker
.store
.isValidPath(drvPath
));
1013 /* Get the derivation. */
1014 drv
= derivationFromPath(worker
.store
, drvPath
);
1016 foreach (DerivationOutputs::iterator
, i
, drv
.outputs
)
1017 worker
.store
.addTempRoot(i
->second
.path
);
1019 /* Check what outputs paths are not already valid. */
1020 PathSet invalidOutputs
= checkPathValidity(false, buildMode
== bmRepair
);
1022 /* If they are all valid, then we're done. */
1023 if (invalidOutputs
.size() == 0 && buildMode
== bmNormal
) {
1024 done(BuildResult::AlreadyValid
);
1028 /* Check whether any output previously failed to build. If so,
1030 foreach (PathSet::iterator
, i
, invalidOutputs
)
1031 if (pathFailed(*i
)) return;
1033 /* We are first going to try to create the invalid output paths
1034 through substitutes. If that doesn't work, we'll build
1036 if (settings
.useSubstitutes
&& substitutesAllowed(drv
))
1037 foreach (PathSet::iterator
, i
, invalidOutputs
)
1038 addWaitee(worker
.makeSubstitutionGoal(*i
, buildMode
== bmRepair
));
1040 if (waitees
.empty()) /* to prevent hang (no wake-up event) */
1041 outputsSubstituted();
1043 state
= &DerivationGoal::outputsSubstituted
;
1047 void DerivationGoal::outputsSubstituted()
1049 trace("all outputs substituted (maybe)");
1051 if (nrFailed
> 0 && nrFailed
> nrNoSubstituters
+ nrIncompleteClosure
&& !settings
.tryFallback
)
1052 throw Error(format("some substitutes for the outputs of derivation `%1%' failed (usually happens due to networking issues); try `--fallback' to build derivation from source ") % drvPath
);
1054 /* If the substitutes form an incomplete closure, then we should
1055 build the dependencies of this derivation, but after that, we
1056 can still use the substitutes for this derivation itself. */
1057 if (nrIncompleteClosure
> 0 && !retrySubstitution
) retrySubstitution
= true;
1059 nrFailed
= nrNoSubstituters
= nrIncompleteClosure
= 0;
1062 needRestart
= false;
1067 unsigned int nrInvalid
= checkPathValidity(false, buildMode
== bmRepair
).size();
1068 if (buildMode
== bmNormal
&& nrInvalid
== 0) {
1069 done(BuildResult::Substituted
);
1072 if (buildMode
== bmRepair
&& nrInvalid
== 0) {
1076 if (buildMode
== bmCheck
&& nrInvalid
> 0)
1077 throw Error(format("some outputs of `%1%' are not valid, so checking is not possible") % drvPath
);
1079 /* Otherwise, at least one of the output paths could not be
1080 produced using a substitute. So we have to build instead. */
1082 /* Make sure checkPathValidity() from now on checks all
1084 wantedOutputs
= PathSet();
1086 /* The inputs must be built before we can build this goal. */
1087 foreach (DerivationInputs::iterator
, i
, drv
.inputDrvs
)
1088 addWaitee(worker
.makeDerivationGoal(i
->first
, i
->second
, buildMode
== bmRepair
? bmRepair
: bmNormal
));
1090 foreach (PathSet::iterator
, i
, drv
.inputSrcs
)
1091 addWaitee(worker
.makeSubstitutionGoal(*i
));
1093 if (waitees
.empty()) /* to prevent hang (no wake-up event) */
1096 state
= &DerivationGoal::inputsRealised
;
1100 void DerivationGoal::repairClosure()
1102 /* If we're repairing, we now know that our own outputs are valid.
1103 Now check whether the other paths in the outputs closure are
1104 good. If not, then start derivation goals for the derivations
1105 that produced those outputs. */
1107 /* Get the output closure. */
1108 PathSet outputClosure
;
1109 foreach (DerivationOutputs::iterator
, i
, drv
.outputs
)
1110 computeFSClosure(worker
.store
, i
->second
.path
, outputClosure
);
1112 /* Filter out our own outputs (which we have already checked). */
1113 foreach (DerivationOutputs::iterator
, i
, drv
.outputs
)
1114 outputClosure
.erase(i
->second
.path
);
1116 /* Get all dependencies of this derivation so that we know which
1117 derivation is responsible for which path in the output
1119 PathSet inputClosure
;
1120 computeFSClosure(worker
.store
, drvPath
, inputClosure
);
1121 std::map
<Path
, Path
> outputsToDrv
;
1122 foreach (PathSet::iterator
, i
, inputClosure
)
1123 if (isDerivation(*i
)) {
1124 Derivation drv
= derivationFromPath(worker
.store
, *i
);
1125 foreach (DerivationOutputs::iterator
, j
, drv
.outputs
)
1126 outputsToDrv
[j
->second
.path
] = *i
;
1129 /* Check each path (slow!). */
1131 foreach (PathSet::iterator
, i
, outputClosure
) {
1132 if (worker
.store
.pathContentsGood(*i
)) continue;
1133 printMsg(lvlError
, format("found corrupted or missing path `%1%' in the output closure of `%2%'") % *i
% drvPath
);
1134 Path drvPath2
= outputsToDrv
[*i
];
1136 addWaitee(worker
.makeSubstitutionGoal(*i
, true));
1138 addWaitee(worker
.makeDerivationGoal(drvPath2
, PathSet(), bmRepair
));
1141 if (waitees
.empty()) {
1142 done(BuildResult::AlreadyValid
);
1146 state
= &DerivationGoal::closureRepaired
;
1150 void DerivationGoal::closureRepaired()
1152 trace("closure repaired");
1154 throw Error(format("some paths in the output closure of derivation ‘%1%’ could not be repaired") % drvPath
);
1155 done(BuildResult::AlreadyValid
);
1159 void DerivationGoal::inputsRealised()
1161 trace("all inputs realised");
1163 if (nrFailed
!= 0) {
1165 format("cannot build derivation `%1%': %2% dependencies couldn't be built")
1166 % drvPath
% nrFailed
);
1167 done(BuildResult::DependencyFailed
);
1171 if (retrySubstitution
) {
1176 /* Gather information necessary for computing the closure and/or
1177 running the build hook. */
1179 /* The outputs are referenceable paths. */
1180 foreach (DerivationOutputs::iterator
, i
, drv
.outputs
) {
1181 debug(format("building path `%1%'") % i
->second
.path
);
1182 allPaths
.insert(i
->second
.path
);
1185 /* Determine the full set of input paths. */
1187 /* First, the input derivations. */
1188 foreach (DerivationInputs::iterator
, i
, drv
.inputDrvs
) {
1189 /* Add the relevant output closures of the input derivation
1190 `*i' as input paths. Only add the closures of output paths
1191 that are specified as inputs. */
1192 assert(worker
.store
.isValidPath(i
->first
));
1193 Derivation inDrv
= derivationFromPath(worker
.store
, i
->first
);
1194 foreach (StringSet::iterator
, j
, i
->second
)
1195 if (inDrv
.outputs
.find(*j
) != inDrv
.outputs
.end())
1196 computeFSClosure(worker
.store
, inDrv
.outputs
[*j
].path
, inputPaths
);
1199 format("derivation `%1%' requires non-existent output `%2%' from input derivation `%3%'")
1200 % drvPath
% *j
% i
->first
);
1203 /* Second, the input sources. */
1204 foreach (PathSet::iterator
, i
, drv
.inputSrcs
)
1205 computeFSClosure(worker
.store
, *i
, inputPaths
);
1207 debug(format("added input paths %1%") % showPaths(inputPaths
));
1209 allPaths
.insert(inputPaths
.begin(), inputPaths
.end());
1211 /* Is this a fixed-output derivation? */
1213 for (auto & i
: drv
.outputs
)
1214 if (i
.second
.hash
== "") fixedOutput
= false;
1216 /* Don't repeat fixed-output derivations since they're already
1217 verified by their output hash.*/
1218 nrRounds
= fixedOutput
? 1 : settings
.get("build-repeat", 0) + 1;
1220 /* Okay, try to build. Note that here we don't wait for a build
1221 slot to become available, since we don't need one if there is a
1223 state
= &DerivationGoal::tryToBuild
;
1224 worker
.wakeUp(shared_from_this());
1228 static bool canBuildLocally(const string
& platform
)
1230 return platform
== settings
.thisSystem
1232 || (platform
== "i686-linux" && settings
.thisSystem
== "x86_64-linux")
1238 static string
get(const StringPairs
& map
, const string
& key
, const string
& def
= "")
1240 StringPairs::const_iterator i
= map
.find(key
);
1241 return i
== map
.end() ? def
: i
->second
;
1245 bool willBuildLocally(const Derivation
& drv
)
1247 return get(drv
.env
, "preferLocalBuild") == "1" && canBuildLocally(drv
.platform
);
1251 bool substitutesAllowed(const Derivation
& drv
)
1253 return get(drv
.env
, "allowSubstitutes", "1") == "1";
1257 void DerivationGoal::tryToBuild()
1259 trace("trying to build");
1261 /* Check for the possibility that some other goal in this process
1262 has locked the output since we checked in haveDerivation().
1263 (It can't happen between here and the lockPaths() call below
1264 because we're not allowing multi-threading.) If so, put this
1265 goal to sleep until another goal finishes, then try again. */
1266 foreach (DerivationOutputs::iterator
, i
, drv
.outputs
)
1267 if (pathIsLockedByMe(i
->second
.path
)) {
1268 debug(format("putting derivation `%1%' to sleep because `%2%' is locked by another goal")
1269 % drvPath
% i
->second
.path
);
1270 worker
.waitForAnyGoal(shared_from_this());
1274 /* Obtain locks on all output paths. The locks are automatically
1275 released when we exit this function or Nix crashes. If we
1276 can't acquire the lock, then continue; hopefully some other
1277 goal can start a build, and if not, the main loop will sleep a
1278 few seconds and then retry this goal. */
1279 if (!outputLocks
.lockPaths(outputPaths(drv
), "", false)) {
1280 worker
.waitForAWhile(shared_from_this());
1284 /* Now check again whether the outputs are valid. This is because
1285 another process may have started building in parallel. After
1286 it has finished and released the locks, we can (and should)
1287 reuse its results. (Strictly speaking the first check can be
1288 omitted, but that would be less efficient.) Note that since we
1289 now hold the locks on the output paths, no other process can
1290 build this derivation, so no further checks are necessary. */
1291 validPaths
= checkPathValidity(true, buildMode
== bmRepair
);
1292 assert(buildMode
!= bmCheck
|| validPaths
.size() == drv
.outputs
.size());
1293 if (buildMode
!= bmCheck
&& validPaths
.size() == drv
.outputs
.size()) {
1294 debug(format("skipping build of derivation `%1%', someone beat us to it") % drvPath
);
1295 outputLocks
.setDeletion(true);
1296 done(BuildResult::AlreadyValid
);
1300 missingPaths
= outputPaths(drv
);
1301 if (buildMode
!= bmCheck
)
1302 foreach (PathSet::iterator
, i
, validPaths
) missingPaths
.erase(*i
);
1304 /* If any of the outputs already exist but are not valid, delete
1306 foreach (DerivationOutputs::iterator
, i
, drv
.outputs
) {
1307 Path path
= i
->second
.path
;
1308 if (worker
.store
.isValidPath(path
)) continue;
1309 if (!pathExists(path
)) continue;
1310 debug(format("removing invalid path `%1%'") % path
);
1314 /* Check again whether any output previously failed to build,
1315 because some other process may have tried and failed before we
1316 acquired the lock. */
1317 foreach (DerivationOutputs::iterator
, i
, drv
.outputs
)
1318 if (pathFailed(i
->second
.path
)) return;
1320 /* Don't do a remote build if the derivation has the attribute
1321 `preferLocalBuild' set. Also, check and repair modes are only
1322 supported for local builds. */
1323 bool buildLocally
= buildMode
!= bmNormal
|| willBuildLocally(drv
);
1325 /* Is the build hook willing to accept this job? */
1326 if (!buildLocally
) {
1327 switch (tryBuildHook()) {
1329 /* Yes, it has started doing so. Wait until we get
1330 EOF from the hook. */
1331 state
= &DerivationGoal::buildDone
;
1334 /* Not now; wait until at least one child finishes or
1335 the wake-up timeout expires. */
1336 worker
.waitForAWhile(shared_from_this());
1337 outputLocks
.unlock();
1340 /* We should do it ourselves. */
1345 /* Make sure that we are allowed to start a build. If this
1346 derivation prefers to be done locally, do it even if
1347 maxBuildJobs is 0. */
1348 unsigned int curBuilds
= worker
.getNrLocalBuilds();
1349 if (curBuilds
>= settings
.maxBuildJobs
&& !(buildLocally
&& curBuilds
== 0)) {
1350 worker
.waitForBuildSlot(shared_from_this());
1351 outputLocks
.unlock();
1357 /* Okay, we have to build. */
1360 } catch (BuildError
& e
) {
1361 printMsg(lvlError
, e
.msg());
1362 outputLocks
.unlock();
1363 buildUser
.release();
1364 if (settings
.printBuildTrace
)
1365 printMsg(lvlError
, format("@ build-failed %1% - %2% %3%")
1366 % drvPath
% 0 % e
.msg());
1367 worker
.permanentFailure
= true;
1368 done(BuildResult::InputRejected
, e
.msg());
1372 /* This state will be reached when we get EOF on the child's
1374 state
= &DerivationGoal::buildDone
;
1378 void replaceValidPath(const Path
& storePath
, const Path tmpPath
)
1380 /* We can't atomically replace storePath (the original) with
1381 tmpPath (the replacement), so we have to move it out of the
1382 way first. We'd better not be interrupted here, because if
1383 we're repairing (say) Glibc, we end up with a broken system. */
1384 Path oldPath
= (format("%1%.old-%2%-%3%") % storePath
% getpid() % rand()).str();
1385 if (pathExists(storePath
))
1386 rename(storePath
.c_str(), oldPath
.c_str());
1387 if (rename(tmpPath
.c_str(), storePath
.c_str()) == -1)
1388 throw SysError(format("moving `%1%' to `%2%'") % tmpPath
% storePath
);
1389 if (pathExists(oldPath
))
1390 deletePath(oldPath
);
1394 MakeError(NotDeterministic
, BuildError
)
1397 void DerivationGoal::buildDone()
1399 trace("build done");
1401 /* Since we got an EOF on the logger pipe, the builder is presumed
1402 to have terminated. In fact, the builder could also have
1403 simply have closed its end of the pipe --- just don't do that
1408 savedPid
= hook
->pid
;
1409 status
= hook
->pid
.wait(true);
1411 /* !!! this could block! security problem! solution: kill the
1414 status
= pid
.wait(true);
1417 debug(format("builder process for `%1%' finished") % drvPath
);
1419 /* So the child is gone now. */
1420 worker
.childTerminated(savedPid
);
1422 /* Close the read side of the logger pipe. */
1424 hook
->builderOut
.readSide
.close();
1425 hook
->fromHook
.readSide
.close();
1427 else builderOut
.readSide
.close();
1429 /* Close the log file. */
1432 /* When running under a build user, make sure that all processes
1433 running under that uid are gone. This is to prevent a
1434 malicious user from leaving behind a process that keeps files
1435 open and modifies them after they have been chown'ed to
1437 if (buildUser
.enabled()) buildUser
.kill();
1439 bool diskFull
= false;
1443 /* Check the exit status. */
1444 if (!statusOk(status
)) {
1446 /* Heuristically check whether the build failure may have
1447 been caused by a disk full condition. We have no way
1448 of knowing whether the build actually got an ENOSPC.
1449 So instead, check if the disk is (nearly) full now. If
1450 so, we don't mark this build as a permanent failure. */
1452 unsigned long long required
= 8ULL * 1024 * 1024; // FIXME: make configurable
1454 if (statvfs(settings
.nixStore
.c_str(), &st
) == 0 &&
1455 (unsigned long long) st
.f_bavail
* st
.f_bsize
< required
)
1457 if (statvfs(tmpDir
.c_str(), &st
) == 0 &&
1458 (unsigned long long) st
.f_bavail
* st
.f_bsize
< required
)
1462 deleteTmpDir(false);
1464 /* Move paths out of the chroot for easier debugging of
1466 if (useChroot
&& buildMode
== bmNormal
)
1467 foreach (PathSet::iterator
, i
, missingPaths
)
1468 if (pathExists(chrootRootDir
+ *i
))
1469 rename((chrootRootDir
+ *i
).c_str(), i
->c_str());
1472 printMsg(lvlError
, "note: build failure may have been caused by lack of free disk space");
1474 throw BuildError(format("builder for `%1%' %2%")
1475 % drvPath
% statusToString(status
));
1478 /* Compute the FS closure of the outputs and register them as
1482 if (buildMode
== bmCheck
) {
1483 done(BuildResult::Built
);
1487 /* Delete unused redirected outputs (when doing hash rewriting). */
1488 foreach (RedirectedOutputs::iterator
, i
, redirectedOutputs
)
1489 if (pathExists(i
->second
)) deletePath(i
->second
);
1491 /* Delete the chroot (if we were using one). */
1492 autoDelChroot
.reset(); /* this runs the destructor */
1496 /* Repeat the build if necessary. */
1497 if (curRound
++ < nrRounds
) {
1498 outputLocks
.unlock();
1499 buildUser
.release();
1500 state
= &DerivationGoal::tryToBuild
;
1501 worker
.wakeUp(shared_from_this());
1505 /* It is now safe to delete the lock files, since all future
1506 lockers will see that the output paths are valid; they will
1507 not create new lock files with the same names as the old
1508 (unlinked) lock files. */
1509 outputLocks
.setDeletion(true);
1510 outputLocks
.unlock();
1512 } catch (BuildError
& e
) {
1514 printMsg(lvlError
, e
.msg());
1515 outputLocks
.unlock();
1516 buildUser
.release();
1518 BuildResult::Status st
= BuildResult::MiscFailure
;
1520 if (hook
&& WIFEXITED(status
) && WEXITSTATUS(status
) == 101) {
1521 if (settings
.printBuildTrace
)
1522 printMsg(lvlError
, format("@ build-failed %1% - timeout") % drvPath
);
1523 st
= BuildResult::TimedOut
;
1526 else if (hook
&& (!WIFEXITED(status
) || WEXITSTATUS(status
) != 100)) {
1527 if (settings
.printBuildTrace
)
1528 printMsg(lvlError
, format("@ hook-failed %1% - %2% %3%")
1529 % drvPath
% status
% e
.msg());
1533 if (settings
.printBuildTrace
)
1534 printMsg(lvlError
, format("@ build-failed %1% - %2% %3%")
1535 % drvPath
% 1 % e
.msg());
1538 statusOk(status
) ? BuildResult::OutputRejected
:
1539 fixedOutput
|| diskFull
? BuildResult::TransientFailure
:
1540 BuildResult::PermanentFailure
;
1542 /* Register the outputs of this build as "failed" so we
1543 won't try to build them again (negative caching).
1544 However, don't do this for fixed-output derivations,
1545 since they're likely to fail for transient reasons
1546 (e.g., fetchurl not being able to access the network).
1547 Hook errors (like communication problems with the
1548 remote machine) shouldn't be cached either. */
1549 if (settings
.cacheFailure
&& !fixedOutput
&& !diskFull
)
1550 foreach (DerivationOutputs::iterator
, i
, drv
.outputs
)
1551 worker
.store
.registerFailedPath(i
->second
.path
);
1558 /* Release the build user, if applicable. */
1559 buildUser
.release();
1561 if (settings
.printBuildTrace
)
1562 printMsg(lvlError
, format("@ build-succeeded %1% -") % drvPath
);
1564 done(BuildResult::Built
);
1568 HookReply
DerivationGoal::tryBuildHook()
1570 if (!settings
.useBuildHook
|| getEnv("NIX_BUILD_HOOK") == "") return rpDecline
;
1573 worker
.hook
= std::shared_ptr
<HookInstance
>(new HookInstance
);
1575 /* Tell the hook about system features (beyond the system type)
1576 required from the build machine. (The hook could parse the
1577 drv file itself, but this is easier.) */
1578 Strings features
= tokenizeString
<Strings
>(get(drv
.env
, "requiredSystemFeatures"));
1579 foreach (Strings::iterator
, i
, features
) checkStoreName(*i
); /* !!! abuse */
1581 /* Send the request to the hook. */
1582 writeLine(worker
.hook
->toHook
.writeSide
, (format("%1% %2% %3% %4%")
1583 % (worker
.getNrLocalBuilds() < settings
.maxBuildJobs
? "1" : "0")
1584 % drv
.platform
% drvPath
% concatStringsSep(",", features
)).str());
1586 /* Read the first line of input, which should be a word indicating
1587 whether the hook wishes to perform the build. */
1590 string s
= readLine(worker
.hook
->fromHook
.readSide
);
1591 if (string(s
, 0, 2) == "# ") {
1592 reply
= string(s
, 2);
1599 debug(format("hook reply is `%1%'") % reply
);
1601 if (reply
== "decline" || reply
== "postpone")
1602 return reply
== "decline" ? rpDecline
: rpPostpone
;
1603 else if (reply
!= "accept")
1604 throw Error(format("bad hook reply `%1%'") % reply
);
1606 printMsg(lvlTalkative
, format("using hook to build path(s) %1%") % showPaths(missingPaths
));
1609 worker
.hook
.reset();
1611 /* Tell the hook all the inputs that have to be copied to the
1612 remote system. This unfortunately has to contain the entire
1613 derivation closure to ensure that the validity invariant holds
1614 on the remote system. (I.e., it's unfortunate that we have to
1615 list it since the remote system *probably* already has it.) */
1617 allInputs
.insert(inputPaths
.begin(), inputPaths
.end());
1618 computeFSClosure(worker
.store
, drvPath
, allInputs
);
1621 foreach (PathSet::iterator
, i
, allInputs
) { s
+= *i
; s
+= ' '; }
1622 writeLine(hook
->toHook
.writeSide
, s
);
1624 /* Tell the hooks the missing outputs that have to be copied back
1625 from the remote system. */
1627 foreach (PathSet::iterator
, i
, missingPaths
) { s
+= *i
; s
+= ' '; }
1628 writeLine(hook
->toHook
.writeSide
, s
);
1630 hook
->toHook
.writeSide
.close();
1632 /* Create the log file and pipe. */
1633 Path logFile
= openLogFile();
1636 fds
.insert(hook
->fromHook
.readSide
);
1637 fds
.insert(hook
->builderOut
.readSide
);
1638 worker
.childStarted(shared_from_this(), hook
->pid
, fds
, false, false);
1640 if (settings
.printBuildTrace
)
1641 printMsg(lvlError
, format("@ build-started %1% - %2% %3%")
1642 % drvPath
% drv
.platform
% logFile
);
1648 void chmod_(const Path
& path
, mode_t mode
)
1650 if (chmod(path
.c_str(), mode
) == -1)
1651 throw SysError(format("setting permissions on `%1%'") % path
);
1655 int childEntry(void * arg
)
1657 ((DerivationGoal
*) arg
)->runChild();
1662 void DerivationGoal::startBuilder()
1665 buildMode
== bmRepair
? "repairing path(s) %1%" :
1666 buildMode
== bmCheck
? "checking path(s) %1%" :
1667 nrRounds
> 1 ? "building path(s) %1% (round %2%/%3%)" :
1668 "building path(s) %1%");
1669 f
.exceptions(boost::io::all_error_bits
^ boost::io::too_many_args_bit
);
1670 startNest(nest
, lvlInfo
, f
% showPaths(missingPaths
) % curRound
% nrRounds
);
1672 /* Right platform? */
1673 if (!canBuildLocally(drv
.platform
)) {
1674 if (settings
.printBuildTrace
)
1675 printMsg(lvlError
, format("@ unsupported-platform %1% %2%") % drvPath
% drv
.platform
);
1677 format("a `%1%' is required to build `%3%', but I am a `%2%'")
1678 % drv
.platform
% settings
.thisSystem
% drvPath
);
1681 useChroot
= settings
.useChroot
;
1683 /* Construct the environment passed to the builder. */
1686 /* Most shells initialise PATH to some default (/bin:/usr/bin:...) when
1687 PATH is not set. We don't want this, so we fill it in with some dummy
1689 env
["PATH"] = "/path-not-set";
1691 /* Set HOME to a non-existing path to prevent certain programs from using
1692 /etc/passwd (or NIS, or whatever) to locate the home directory (for
1693 example, wget looks for ~/.wgetrc). I.e., these tools use /etc/passwd
1694 if HOME is not set, but they will just assume that the settings file
1695 they are looking for does not exist if HOME is set but points to some
1696 non-existing path. */
1697 Path homeDir
= "/homeless-shelter";
1698 env
["HOME"] = homeDir
;
1700 /* Tell the builder where the Nix store is. Usually they
1701 shouldn't care, but this is useful for purity checking (e.g.,
1702 the compiler or linker might only want to accept paths to files
1703 in the store or in the build directory). */
1704 env
["NIX_STORE"] = settings
.nixStore
;
1706 /* The maximum number of cores to utilize for parallel building. */
1707 env
["NIX_BUILD_CORES"] = (format("%d") % settings
.buildCores
).str();
1709 /* Add all bindings specified in the derivation. */
1710 foreach (StringPairs::iterator
, i
, drv
.env
)
1711 env
[i
->first
] = i
->second
;
1713 /* Create a temporary directory where the build will take
1715 auto drvName
= storePathToName(drvPath
);
1716 tmpDir
= createTempDir("", "guix-build-" + drvName
, false, false, 0700);
1718 /* In a sandbox, for determinism, always use the same temporary
1720 tmpDirInSandbox
= useChroot
? "/tmp/guix-build-" + drvName
+ "-0" : tmpDir
;
1722 /* For convenience, set an environment pointing to the top build
1724 env
["NIX_BUILD_TOP"] = tmpDirInSandbox
;
1726 /* Also set TMPDIR and variants to point to this directory. */
1727 env
["TMPDIR"] = env
["TEMPDIR"] = env
["TMP"] = env
["TEMP"] = tmpDirInSandbox
;
1729 /* Explicitly set PWD to prevent problems with chroot builds. In
1730 particular, dietlibc cannot figure out the cwd because the
1731 inode of the current directory doesn't appear in .. (because
1732 getdents returns the inode of the mount point). */
1733 env
["PWD"] = tmpDirInSandbox
;
1735 /* Compatibility hack with Nix <= 0.7: if this is a fixed-output
1736 derivation, tell the builder, so that for instance `fetchurl'
1737 can skip checking the output. On older Nixes, this environment
1738 variable won't be set, so `fetchurl' will do the check. */
1739 if (fixedOutput
) env
["NIX_OUTPUT_CHECKED"] = "1";
1741 /* *Only* if this is a fixed-output derivation, propagate the
1742 values of the environment variables specified in the
1743 `impureEnvVars' attribute to the builder. This allows for
1744 instance environment variables for proxy configuration such as
1745 `http_proxy' to be easily passed to downloaders like
1746 `fetchurl'. Passing such environment variables from the caller
1747 to the builder is generally impure, but the output of
1748 fixed-output derivations is by definition pure (since we
1749 already know the cryptographic hash of the output). */
1751 Strings varNames
= tokenizeString
<Strings
>(get(drv
.env
, "impureEnvVars"));
1752 foreach (Strings::iterator
, i
, varNames
) env
[*i
] = getEnv(*i
);
1755 /* The `exportReferencesGraph' feature allows the references graph
1756 to be passed to a builder. This attribute should be a list of
1757 pairs [name1 path1 name2 path2 ...]. The references graph of
1758 each `pathN' will be stored in a text file `nameN' in the
1759 temporary build directory. The text files have the format used
1760 by `nix-store --register-validity'. However, the deriver
1761 fields are left empty. */
1762 string s
= get(drv
.env
, "exportReferencesGraph");
1763 Strings ss
= tokenizeString
<Strings
>(s
);
1764 if (ss
.size() % 2 != 0)
1765 throw BuildError(format("odd number of tokens in `exportReferencesGraph': `%1%'") % s
);
1766 for (Strings::iterator i
= ss
.begin(); i
!= ss
.end(); ) {
1767 string fileName
= *i
++;
1768 checkStoreName(fileName
); /* !!! abuse of this function */
1770 /* Check that the store path is valid. */
1771 Path storePath
= *i
++;
1772 if (!isInStore(storePath
))
1773 throw BuildError(format("`exportReferencesGraph' contains a non-store path `%1%'")
1775 storePath
= toStorePath(storePath
);
1776 if (!worker
.store
.isValidPath(storePath
))
1777 throw BuildError(format("`exportReferencesGraph' contains an invalid path `%1%'")
1780 /* If there are derivations in the graph, then include their
1781 outputs as well. This is useful if you want to do things
1782 like passing all build-time dependencies of some path to a
1783 derivation that builds a NixOS DVD image. */
1784 PathSet paths
, paths2
;
1785 computeFSClosure(worker
.store
, storePath
, paths
);
1788 foreach (PathSet::iterator
, j
, paths2
) {
1789 if (isDerivation(*j
)) {
1790 Derivation drv
= derivationFromPath(worker
.store
, *j
);
1791 foreach (DerivationOutputs::iterator
, k
, drv
.outputs
)
1792 computeFSClosure(worker
.store
, k
->second
.path
, paths
);
1796 /* Write closure info to `fileName'. */
1797 writeFile(tmpDir
+ "/" + fileName
,
1798 worker
.store
.makeValidityRegistration(paths
, false, false));
1802 /* If `build-users-group' is not empty, then we have to build as
1803 one of the members of that group. */
1804 if (settings
.buildUsersGroup
!= "") {
1805 buildUser
.acquire();
1806 assert(buildUser
.getUID() != 0);
1807 assert(buildUser
.getGID() != 0);
1809 /* Make sure that no other processes are executing under this
1813 /* Change ownership of the temporary build directory. */
1814 if (chown(tmpDir
.c_str(), buildUser
.getUID(), buildUser
.getGID()) == -1)
1815 throw SysError(format("cannot change ownership of '%1%'") % tmpDir
);
1820 /* Create a temporary directory in which we set up the chroot
1821 environment using bind-mounts. We put it in the Nix store
1822 to ensure that we can create hard-links to non-directory
1823 inputs in the fake Nix store in the chroot (see below). */
1824 chrootRootDir
= drvPath
+ ".chroot";
1825 if (pathExists(chrootRootDir
)) deletePath(chrootRootDir
);
1827 /* Clean up the chroot directory automatically. */
1828 autoDelChroot
= std::shared_ptr
<AutoDelete
>(new AutoDelete(chrootRootDir
));
1830 printMsg(lvlChatty
, format("setting up chroot environment in `%1%'") % chrootRootDir
);
1832 if (mkdir(chrootRootDir
.c_str(), 0750) == -1)
1833 throw SysError(format("cannot create ‘%1%’") % chrootRootDir
);
1835 if (chown(chrootRootDir
.c_str(), 0, buildUser
.getGID()) == -1)
1836 throw SysError(format("cannot change ownership of ‘%1%’") % chrootRootDir
);
1838 /* Create a writable /tmp in the chroot. Many builders need
1839 this. (Of course they should really respect $TMPDIR
1841 Path chrootTmpDir
= chrootRootDir
+ "/tmp";
1842 createDirs(chrootTmpDir
);
1843 chmod_(chrootTmpDir
, 01777);
1845 /* Create a /etc/passwd with entries for the build user and the
1846 nobody account. The latter is kind of a hack to support
1848 createDirs(chrootRootDir
+ "/etc");
1850 writeFile(chrootRootDir
+ "/etc/passwd",
1852 "nixbld:x:%1%:%2%:Nix build user:/:/noshell\n"
1853 "nobody:x:65534:65534:Nobody:/:/noshell\n")
1854 % (buildUser
.enabled() ? buildUser
.getUID() : getuid())
1855 % (buildUser
.enabled() ? buildUser
.getGID() : getgid())).str());
1857 /* Declare the build user's group so that programs get a consistent
1858 view of the system (e.g., "id -gn"). */
1859 writeFile(chrootRootDir
+ "/etc/group",
1860 (format("nixbld:!:%1%:\n")
1861 % (buildUser
.enabled() ? buildUser
.getGID() : getgid())).str());
1863 /* Create /etc/hosts with localhost entry. */
1865 writeFile(chrootRootDir
+ "/etc/hosts", "127.0.0.1 localhost\n");
1867 /* Bind-mount a user-configurable set of directories from the
1868 host file system. */
1869 PathSet dirs
= tokenizeString
<StringSet
>(settings
.get("build-chroot-dirs", string(DEFAULT_CHROOT_DIRS
)));
1870 PathSet dirs2
= tokenizeString
<StringSet
>(settings
.get("build-extra-chroot-dirs", string("")));
1871 dirs
.insert(dirs2
.begin(), dirs2
.end());
1872 for (auto & i
: dirs
) {
1873 size_t p
= i
.find('=');
1874 if (p
== string::npos
)
1875 dirsInChroot
[i
] = i
;
1877 dirsInChroot
[string(i
, 0, p
)] = string(i
, p
+ 1);
1879 dirsInChroot
[tmpDirInSandbox
] = tmpDir
;
1881 /* Make the closure of the inputs available in the chroot,
1882 rather than the whole Nix store. This prevents any access
1883 to undeclared dependencies. Directories are bind-mounted,
1884 while other inputs are hard-linked (since only directories
1885 can be bind-mounted). !!! As an extra security
1886 precaution, make the fake Nix store only writable by the
1888 Path chrootStoreDir
= chrootRootDir
+ settings
.nixStore
;
1889 createDirs(chrootStoreDir
);
1890 chmod_(chrootStoreDir
, 01775);
1892 if (chown(chrootStoreDir
.c_str(), 0, buildUser
.getGID()) == -1)
1893 throw SysError(format("cannot change ownership of ‘%1%’") % chrootStoreDir
);
1895 foreach (PathSet::iterator
, i
, inputPaths
) {
1897 if (lstat(i
->c_str(), &st
))
1898 throw SysError(format("getting attributes of path `%1%'") % *i
);
1899 if (S_ISDIR(st
.st_mode
))
1900 dirsInChroot
[*i
] = *i
;
1902 Path p
= chrootRootDir
+ *i
;
1903 if (link(i
->c_str(), p
.c_str()) == -1) {
1904 /* Hard-linking fails if we exceed the maximum
1905 link count on a file (e.g. 32000 of ext3),
1906 which is quite possible after a `nix-store
1908 if (errno
!= EMLINK
)
1909 throw SysError(format("linking `%1%' to `%2%'") % p
% *i
);
1912 StringSource
source(sink
.s
);
1913 restorePath(p
, source
);
1916 regularInputPaths
.insert(*i
);
1920 /* If we're repairing, checking or rebuilding part of a
1921 multiple-outputs derivation, it's possible that we're
1922 rebuilding a path that is in settings.dirsInChroot
1923 (typically the dependencies of /bin/sh). Throw them
1925 for (auto & i
: drv
.outputs
)
1926 dirsInChroot
.erase(i
.second
.path
);
1929 throw Error("chroot builds are not supported on this platform");
1935 if (pathExists(homeDir
))
1936 throw Error(format("directory `%1%' exists; please remove it") % homeDir
);
1938 /* We're not doing a chroot build, but we have some valid
1939 output paths. Since we can't just overwrite or delete
1940 them, we have to do hash rewriting: i.e. in the
1941 environment/arguments passed to the build, we replace the
1942 hashes of the valid outputs with unique dummy strings;
1943 after the build, we discard the redirected outputs
1944 corresponding to the valid outputs, and rewrite the
1945 contents of the new outputs to replace the dummy strings
1946 with the actual hashes. */
1947 if (validPaths
.size() > 0)
1948 foreach (PathSet::iterator
, i
, validPaths
)
1951 /* If we're repairing, then we don't want to delete the
1952 corrupt outputs in advance. So rewrite them as well. */
1953 if (buildMode
== bmRepair
)
1954 foreach (PathSet::iterator
, i
, missingPaths
)
1955 if (worker
.store
.isValidPath(*i
) && pathExists(*i
)) {
1957 redirectedBadOutputs
.insert(*i
);
1962 /* Run the builder. */
1963 printMsg(lvlChatty
, format("executing builder `%1%'") % drv
.builder
);
1965 /* Create the log file. */
1966 Path logFile
= openLogFile();
1968 /* Create a pipe to get the output of the builder. */
1969 builderOut
.create();
1971 /* Fork a child to build the package. Note that while we
1972 currently use forks to run and wait for the children, it
1973 shouldn't be hard to use threads for this on systems where
1974 fork() is unavailable or inefficient.
1976 If we're building in a chroot, then also set up private
1977 namespaces for the build:
1979 - The PID namespace causes the build to start as PID 1.
1980 Processes outside of the chroot are not visible to those on
1981 the inside, but processes inside the chroot are visible from
1982 the outside (though with different PIDs).
1984 - The private mount namespace ensures that all the bind mounts
1985 we do will only show up in this process and its children, and
1986 will disappear automatically when we're done.
1988 - The private network namespace ensures that the builder cannot
1989 talk to the outside world (or vice versa). It only has a
1990 private loopback interface.
1992 - The IPC namespace prevents the builder from communicating
1993 with outside processes using SysV IPC mechanisms (shared
1994 memory, message queues, semaphores). It also ensures that
1995 all IPC objects are destroyed when the builder exits.
1997 - The UTS namespace ensures that builders see a hostname of
1998 localhost rather than the actual hostname.
2002 char stack
[32 * 1024];
2003 int flags
= CLONE_NEWPID
| CLONE_NEWNS
| CLONE_NEWIPC
| CLONE_NEWUTS
| SIGCHLD
;
2004 if (!fixedOutput
) flags
|= CLONE_NEWNET
;
2005 pid
= clone(childEntry
, stack
+ sizeof(stack
) - 8, flags
, this);
2007 throw SysError("cloning builder process");
2012 if (pid
== 0) runChild();
2015 if (pid
== -1) throw SysError("unable to fork");
2018 pid
.setSeparatePG(true);
2019 builderOut
.writeSide
.close();
2020 worker
.childStarted(shared_from_this(), pid
,
2021 singleton
<set
<int> >(builderOut
.readSide
), true, true);
2023 /* Check if setting up the build environment failed. */
2024 string msg
= readLine(builderOut
.readSide
);
2025 if (!msg
.empty()) throw Error(msg
);
2027 if (settings
.printBuildTrace
) {
2028 printMsg(lvlError
, format("@ build-started %1% - %2% %3%")
2029 % drvPath
% drv
.platform
% logFile
);
2035 void DerivationGoal::runChild()
2037 /* Warning: in the child we should absolutely not make any SQLite
2046 commonChildInit(builderOut
);
2050 /* Initialise the loopback interface. */
2051 AutoCloseFD
fd(socket(PF_INET
, SOCK_DGRAM
, IPPROTO_IP
));
2052 if (fd
== -1) throw SysError("cannot open IP socket");
2055 strcpy(ifr
.ifr_name
, "lo");
2056 ifr
.ifr_flags
= IFF_UP
| IFF_LOOPBACK
| IFF_RUNNING
;
2057 if (ioctl(fd
, SIOCSIFFLAGS
, &ifr
) == -1)
2058 throw SysError("cannot set loopback interface flags");
2062 /* Set the hostname etc. to fixed values. */
2063 char hostname
[] = "localhost";
2064 if (sethostname(hostname
, sizeof(hostname
)) == -1)
2065 throw SysError("cannot set host name");
2066 char domainname
[] = "(none)"; // kernel default
2067 if (setdomainname(domainname
, sizeof(domainname
)) == -1)
2068 throw SysError("cannot set domain name");
2070 /* Make all filesystems private. This is necessary
2071 because subtrees may have been mounted as "shared"
2072 (MS_SHARED). (Systemd does this, for instance.) Even
2073 though we have a private mount namespace, mounting
2074 filesystems on top of a shared subtree still propagates
2075 outside of the namespace. Making a subtree private is
2076 local to the namespace, though, so setting MS_PRIVATE
2077 does not affect the outside world. */
2078 Strings mounts
= tokenizeString
<Strings
>(readFile("/proc/self/mountinfo", true), "\n");
2079 foreach (Strings::iterator
, i
, mounts
) {
2080 vector
<string
> fields
= tokenizeString
<vector
<string
> >(*i
, " ");
2081 string fs
= decodeOctalEscaped(fields
.at(4));
2082 if (mount(0, fs
.c_str(), 0, MS_PRIVATE
, 0) == -1)
2083 throw SysError(format("unable to make filesystem `%1%' private") % fs
);
2086 /* Bind-mount chroot directory to itself, to treat it as a
2087 different filesystem from /, as needed for pivot_root. */
2088 if (mount(chrootRootDir
.c_str(), chrootRootDir
.c_str(), 0, MS_BIND
, 0) == -1)
2089 throw SysError(format("unable to bind mount ‘%1%’") % chrootRootDir
);
2091 /* Set up a nearly empty /dev, unless the user asked to
2092 bind-mount the host /dev. */
2094 if (dirsInChroot
.find("/dev") == dirsInChroot
.end()) {
2095 createDirs(chrootRootDir
+ "/dev/shm");
2096 createDirs(chrootRootDir
+ "/dev/pts");
2097 ss
.push_back("/dev/full");
2099 if (pathExists("/dev/kvm"))
2100 ss
.push_back("/dev/kvm");
2102 ss
.push_back("/dev/null");
2103 ss
.push_back("/dev/random");
2104 ss
.push_back("/dev/tty");
2105 ss
.push_back("/dev/urandom");
2106 ss
.push_back("/dev/zero");
2107 createSymlink("/proc/self/fd", chrootRootDir
+ "/dev/fd");
2108 createSymlink("/proc/self/fd/0", chrootRootDir
+ "/dev/stdin");
2109 createSymlink("/proc/self/fd/1", chrootRootDir
+ "/dev/stdout");
2110 createSymlink("/proc/self/fd/2", chrootRootDir
+ "/dev/stderr");
2113 /* Fixed-output derivations typically need to access the
2114 network, so give them access to /etc/resolv.conf and so
2117 ss
.push_back("/etc/resolv.conf");
2118 ss
.push_back("/etc/nsswitch.conf");
2119 ss
.push_back("/etc/services");
2120 ss
.push_back("/etc/hosts");
2123 for (auto & i
: ss
) dirsInChroot
[i
] = i
;
2125 /* Bind-mount all the directories from the "host"
2126 filesystem that we want in the chroot
2128 foreach (DirsInChroot::iterator
, i
, dirsInChroot
) {
2130 Path source
= i
->second
;
2131 Path target
= chrootRootDir
+ i
->first
;
2132 if (source
== "/proc") continue; // backwards compatibility
2133 debug(format("bind mounting `%1%' to `%2%'") % source
% target
);
2134 if (stat(source
.c_str(), &st
) == -1)
2135 throw SysError(format("getting attributes of path `%1%'") % source
);
2136 if (S_ISDIR(st
.st_mode
))
2139 createDirs(dirOf(target
));
2140 writeFile(target
, "");
2142 if (mount(source
.c_str(), target
.c_str(), "", MS_BIND
, 0) == -1)
2143 throw SysError(format("bind mount from `%1%' to `%2%' failed") % source
% target
);
2146 /* Bind a new instance of procfs on /proc to reflect our
2147 private PID namespace. */
2148 createDirs(chrootRootDir
+ "/proc");
2149 if (mount("none", (chrootRootDir
+ "/proc").c_str(), "proc", 0, 0) == -1)
2150 throw SysError("mounting /proc");
2152 /* Mount a new tmpfs on /dev/shm to ensure that whatever
2153 the builder puts in /dev/shm is cleaned up automatically. */
2154 if (pathExists("/dev/shm") && mount("none", (chrootRootDir
+ "/dev/shm").c_str(), "tmpfs", 0, 0) == -1)
2155 throw SysError("mounting /dev/shm");
2157 /* Mount a new devpts on /dev/pts. Note that this
2158 requires the kernel to be compiled with
2159 CONFIG_DEVPTS_MULTIPLE_INSTANCES=y (which is the case
2160 if /dev/ptx/ptmx exists). */
2161 if (pathExists("/dev/pts/ptmx") &&
2162 !pathExists(chrootRootDir
+ "/dev/ptmx")
2163 && dirsInChroot
.find("/dev/pts") == dirsInChroot
.end())
2165 if (mount("none", (chrootRootDir
+ "/dev/pts").c_str(), "devpts", 0, "newinstance,mode=0620") == -1)
2166 throw SysError("mounting /dev/pts");
2167 createSymlink("/dev/pts/ptmx", chrootRootDir
+ "/dev/ptmx");
2169 /* Make sure /dev/pts/ptmx is world-writable. With some
2170 Linux versions, it is created with permissions 0. */
2171 chmod_(chrootRootDir
+ "/dev/pts/ptmx", 0666);
2174 /* Do the chroot(). */
2175 if (chdir(chrootRootDir
.c_str()) == -1)
2176 throw SysError(format("cannot change directory to '%1%'") % chrootRootDir
);
2178 if (mkdir("real-root", 0) == -1)
2179 throw SysError("cannot create real-root directory");
2181 #define pivot_root(new_root, put_old) (syscall(SYS_pivot_root, new_root, put_old))
2182 if (pivot_root(".", "real-root") == -1)
2183 throw SysError(format("cannot pivot old root directory onto '%1%'") % (chrootRootDir
+ "/real-root"));
2186 if (chroot(".") == -1)
2187 throw SysError(format("cannot change root directory to '%1%'") % chrootRootDir
);
2189 if (umount2("real-root", MNT_DETACH
) == -1)
2190 throw SysError("cannot unmount real root filesystem");
2192 if (rmdir("real-root") == -1)
2193 throw SysError("cannot remove real-root directory");
2197 if (chdir(tmpDirInSandbox
.c_str()) == -1)
2198 throw SysError(format("changing into `%1%'") % tmpDir
);
2200 /* Close all other file descriptors. */
2201 closeMostFDs(set
<int>());
2204 /* Change the personality to 32-bit if we're doing an
2205 i686-linux build on an x86_64-linux machine. */
2206 struct utsname utsbuf
;
2208 if (drv
.platform
== "i686-linux" &&
2209 (settings
.thisSystem
== "x86_64-linux" ||
2210 (!strcmp(utsbuf
.sysname
, "Linux") && !strcmp(utsbuf
.machine
, "x86_64")))) {
2211 if (personality(PER_LINUX32
) == -1)
2212 throw SysError("cannot set i686-linux personality");
2215 /* Impersonate a Linux 2.6 machine to get some determinism in
2216 builds that depend on the kernel version. */
2217 if ((drv
.platform
== "i686-linux" || drv
.platform
== "x86_64-linux") && settings
.impersonateLinux26
) {
2218 int cur
= personality(0xffffffff);
2219 if (cur
!= -1) personality(cur
| 0x0020000 /* == UNAME26 */);
2222 /* Disable address space randomization for improved
2224 int cur
= personality(0xffffffff);
2225 if (cur
!= -1) personality(cur
| ADDR_NO_RANDOMIZE
);
2228 /* Fill in the environment. */
2230 foreach (Environment::const_iterator
, i
, env
)
2231 envStrs
.push_back(rewriteHashes(i
->first
+ "=" + i
->second
, rewritesToTmp
));
2233 /* If we are running in `build-users' mode, then switch to the
2234 user we allocated above. Make sure that we drop all root
2235 privileges. Note that above we have closed all file
2236 descriptors except std*, so that's safe. Also note that
2237 setuid() when run as root sets the real, effective and
2239 if (buildUser
.enabled()) {
2240 /* Preserve supplementary groups of the build user, to allow
2241 admins to specify groups such as "kvm". */
2242 if (setgroups(buildUser
.getSupplementaryGIDs().size(),
2243 buildUser
.getSupplementaryGIDs().data()) == -1)
2244 throw SysError("cannot set supplementary groups of build user");
2246 if (setgid(buildUser
.getGID()) == -1 ||
2247 getgid() != buildUser
.getGID() ||
2248 getegid() != buildUser
.getGID())
2249 throw SysError("setgid failed");
2251 if (setuid(buildUser
.getUID()) == -1 ||
2252 getuid() != buildUser
.getUID() ||
2253 geteuid() != buildUser
.getUID())
2254 throw SysError("setuid failed");
2257 /* Fill in the arguments. */
2259 string builderBasename
= baseNameOf(drv
.builder
);
2260 args
.push_back(builderBasename
);
2261 foreach (Strings::iterator
, i
, drv
.args
)
2262 args
.push_back(rewriteHashes(*i
, rewritesToTmp
));
2266 /* Indicate that we managed to set up the build environment. */
2267 writeFull(STDERR_FILENO
, "\n");
2269 /* Execute the program. This should not return. */
2270 execve(drv
.builder
.c_str(), stringsToCharPtrs(args
).data(), stringsToCharPtrs(envStrs
).data());
2272 throw SysError(format("executing `%1%'") % drv
.builder
);
2274 } catch (std::exception
& e
) {
2275 writeFull(STDERR_FILENO
, "while setting up the build environment: " + string(e
.what()) + "\n");
2279 abort(); /* never reached */
2283 /* Parse a list of reference specifiers. Each element must either be
2284 a store path, or the symbolic name of the output of the derivation
2286 PathSet
parseReferenceSpecifiers(const Derivation
& drv
, string attr
)
2289 Paths paths
= tokenizeString
<Paths
>(attr
);
2290 foreach (Strings::iterator
, i
, paths
) {
2291 if (isStorePath(*i
))
2293 else if (drv
.outputs
.find(*i
) != drv
.outputs
.end())
2294 result
.insert(drv
.outputs
.find(*i
)->second
.path
);
2295 else throw BuildError(
2296 format("derivation contains an illegal reference specifier `%1%'")
2303 void DerivationGoal::registerOutputs()
2305 /* When using a build hook, the build hook can register the output
2306 as valid (by doing `nix-store --import'). If so we don't have
2307 to do anything here. */
2309 bool allValid
= true;
2310 foreach (DerivationOutputs::iterator
, i
, drv
.outputs
)
2311 if (!worker
.store
.isValidPath(i
->second
.path
)) allValid
= false;
2312 if (allValid
) return;
2315 ValidPathInfos infos
;
2317 /* Set of inodes seen during calls to canonicalisePathMetaData()
2318 for this build's outputs. This needs to be shared between
2319 outputs to allow hard links between outputs. */
2320 InodesSeen inodesSeen
;
2322 /* Check whether the output paths were created, and grep each
2323 output path to determine what other paths it references. Also make all
2324 output paths read-only. */
2325 foreach (DerivationOutputs::iterator
, i
, drv
.outputs
) {
2326 Path path
= i
->second
.path
;
2327 if (missingPaths
.find(path
) == missingPaths
.end()) continue;
2329 Path actualPath
= path
;
2331 actualPath
= chrootRootDir
+ path
;
2332 if (pathExists(actualPath
)) {
2333 /* Move output paths from the chroot to the Nix store. */
2334 if (buildMode
== bmRepair
)
2335 replaceValidPath(path
, actualPath
);
2337 if (buildMode
!= bmCheck
&& rename(actualPath
.c_str(), path
.c_str()) == -1)
2338 throw SysError(format("moving build output `%1%' from the chroot to the Nix store") % path
);
2340 if (buildMode
!= bmCheck
) actualPath
= path
;
2342 Path redirected
= redirectedOutputs
[path
];
2343 if (buildMode
== bmRepair
2344 && redirectedBadOutputs
.find(path
) != redirectedBadOutputs
.end()
2345 && pathExists(redirected
))
2346 replaceValidPath(path
, redirected
);
2347 if (buildMode
== bmCheck
)
2348 actualPath
= redirected
;
2352 if (lstat(actualPath
.c_str(), &st
) == -1) {
2353 if (errno
== ENOENT
)
2355 format("builder for `%1%' failed to produce output path `%2%'")
2357 throw SysError(format("getting attributes of path `%1%'") % actualPath
);
2361 /* Check that the output is not group or world writable, as
2362 that means that someone else can have interfered with the
2363 build. Also, the output should be owned by the build
2365 if ((!S_ISLNK(st
.st_mode
) && (st
.st_mode
& (S_IWGRP
| S_IWOTH
))) ||
2366 (buildUser
.enabled() && st
.st_uid
!= buildUser
.getUID()))
2367 throw BuildError(format("suspicious ownership or permission on `%1%'; rejecting this build output") % path
);
2370 /* Apply hash rewriting if necessary. */
2371 bool rewritten
= false;
2372 if (!rewritesFromTmp
.empty()) {
2373 printMsg(lvlError
, format("warning: rewriting hashes in `%1%'; cross fingers") % path
);
2375 /* Canonicalise first. This ensures that the path we're
2376 rewriting doesn't contain a hard link to /etc/shadow or
2377 something like that. */
2378 canonicalisePathMetaData(actualPath
, buildUser
.enabled() ? buildUser
.getUID() : -1, inodesSeen
);
2380 /* FIXME: this is in-memory. */
2382 dumpPath(actualPath
, sink
);
2383 deletePath(actualPath
);
2384 sink
.s
= rewriteHashes(sink
.s
, rewritesFromTmp
);
2385 StringSource
source(sink
.s
);
2386 restorePath(actualPath
, source
);
2391 startNest(nest
, lvlTalkative
,
2392 format("scanning for references inside `%1%'") % path
);
2394 /* Check that fixed-output derivations produced the right
2395 outputs (i.e., the content hash should match the specified
2397 if (i
->second
.hash
!= "") {
2399 bool recursive
; HashType ht
; Hash h
;
2400 i
->second
.parseHashInfo(recursive
, ht
, h
);
2403 /* The output path should be a regular file without
2404 execute permission. */
2405 if (!S_ISREG(st
.st_mode
) || (st
.st_mode
& S_IXUSR
) != 0)
2407 format("output path `%1% should be a non-executable regular file") % path
);
2410 /* Check the hash. */
2411 Hash h2
= recursive
? hashPath(ht
, actualPath
).first
: hashFile(ht
, actualPath
);
2414 format("output path `%1%' should have %2% hash `%3%', instead has `%4%'")
2415 % path
% i
->second
.hashAlgo
% printHash16or32(h
) % printHash16or32(h2
));
2418 /* Get rid of all weird permissions. This also checks that
2419 all files are owned by the build user, if applicable. */
2420 canonicalisePathMetaData(actualPath
,
2421 buildUser
.enabled() && !rewritten
? buildUser
.getUID() : -1, inodesSeen
);
2423 /* For this output path, find the references to other paths
2424 contained in it. Compute the SHA-256 NAR hash at the same
2425 time. The hash is stored in the database so that we can
2426 verify later on whether nobody has messed with the store. */
2428 PathSet references
= scanForReferences(actualPath
, allPaths
, hash
);
2430 if (buildMode
== bmCheck
) {
2431 ValidPathInfo info
= worker
.store
.queryPathInfo(path
);
2432 if (hash
.first
!= info
.hash
)
2433 throw Error(format("derivation `%1%' may not be deterministic: hash mismatch in output `%2%'") % drvPath
% path
);
2437 /* For debugging, print out the referenced and unreferenced
2439 foreach (PathSet::iterator
, i
, inputPaths
) {
2440 PathSet::iterator j
= references
.find(*i
);
2441 if (j
== references
.end())
2442 debug(format("unreferenced input: `%1%'") % *i
);
2444 debug(format("referenced input: `%1%'") % *i
);
2447 /* Enforce `allowedReferences' and friends. */
2448 auto checkRefs
= [&](const string
& attrName
, bool allowed
, bool recursive
) {
2449 if (drv
.env
.find(attrName
) == drv
.env
.end()) return;
2451 PathSet spec
= parseReferenceSpecifiers(drv
, get(drv
.env
, attrName
));
2455 /* Our requisites are the union of the closures of our references. */
2456 for (auto & i
: references
)
2457 /* Don't call computeFSClosure on ourselves. */
2458 if (actualPath
!= i
)
2459 computeFSClosure(worker
.store
, i
, used
);
2463 for (auto & i
: used
)
2465 if (spec
.find(i
) == spec
.end())
2466 throw BuildError(format("output (`%1%') is not allowed to refer to path `%2%'") % actualPath
% i
);
2468 if (spec
.find(i
) != spec
.end())
2469 throw BuildError(format("output (`%1%') is not allowed to refer to path `%2%'") % actualPath
% i
);
2473 checkRefs("allowedReferences", true, false);
2474 checkRefs("allowedRequisites", true, true);
2475 checkRefs("disallowedReferences", false, false);
2476 checkRefs("disallowedRequisites", false, true);
2478 worker
.store
.optimisePath(path
); // FIXME: combine with scanForReferences()
2480 worker
.store
.markContentsGood(path
);
2484 info
.hash
= hash
.first
;
2485 info
.narSize
= hash
.second
;
2486 info
.references
= references
;
2487 info
.deriver
= drvPath
;
2488 infos
.push_back(info
);
2491 if (buildMode
== bmCheck
) return;
2493 if (curRound
> 1 && prevInfos
!= infos
)
2494 throw NotDeterministic(
2495 format("result of ‘%1%’ differs from previous round; rejecting as non-deterministic")
2498 if (curRound
< nrRounds
) {
2503 /* Register each output path as valid, and register the sets of
2504 paths referenced by each of them. If there are cycles in the
2505 outputs, this will fail. */
2506 worker
.store
.registerValidPaths(infos
);
2510 string drvsLogDir
= "drvs";
2513 Path
DerivationGoal::openLogFile()
2517 if (!settings
.keepLog
) return "";
2519 string baseName
= baseNameOf(drvPath
);
2521 /* Create a log file. */
2522 Path dir
= (format("%1%/%2%/%3%/") % settings
.nixLogDir
% drvsLogDir
% string(baseName
, 0, 2)).str();
2525 if (settings
.compressLog
) {
2527 Path logFileName
= (format("%1%/%2%.bz2") % dir
% string(baseName
, 2)).str();
2528 AutoCloseFD fd
= open(logFileName
.c_str(), O_CREAT
| O_WRONLY
| O_TRUNC
, 0666);
2529 if (fd
== -1) throw SysError(format("creating log file `%1%'") % logFileName
);
2532 if (!(fLogFile
= fdopen(fd
.borrow(), "w")))
2533 throw SysError(format("opening file `%1%'") % logFileName
);
2536 if (!(bzLogFile
= BZ2_bzWriteOpen(&err
, fLogFile
, 9, 0, 0)))
2537 throw Error(format("cannot open compressed log file `%1%'") % logFileName
);
2542 Path logFileName
= (format("%1%/%2%") % dir
% string(baseName
, 2)).str();
2543 fdLogFile
= open(logFileName
.c_str(), O_CREAT
| O_WRONLY
| O_TRUNC
, 0666);
2544 if (fdLogFile
== -1) throw SysError(format("creating log file `%1%'") % logFileName
);
2545 closeOnExec(fdLogFile
);
2551 void DerivationGoal::closeLogFile()
2555 BZ2_bzWriteClose(&err
, bzLogFile
, 0, 0, 0);
2557 if (err
!= BZ_OK
) throw Error(format("cannot close compressed log file (BZip2 error = %1%)") % err
);
2569 void DerivationGoal::deleteTmpDir(bool force
)
2572 if (settings
.keepFailed
&& !force
) {
2574 format("note: keeping build directory `%2%'")
2575 % drvPath
% tmpDir
);
2576 chmod(tmpDir
.c_str(), 0755);
2585 void DerivationGoal::handleChildOutput(int fd
, const string
& data
)
2587 if ((hook
&& fd
== hook
->builderOut
.readSide
) ||
2588 (!hook
&& fd
== builderOut
.readSide
))
2590 logSize
+= data
.size();
2591 if (settings
.maxLogSize
&& logSize
> settings
.maxLogSize
) {
2593 format("%1% killed after writing more than %2% bytes of log output")
2594 % getName() % settings
.maxLogSize
);
2595 timedOut(); // not really a timeout, but close enough
2598 if (verbosity
>= settings
.buildVerbosity
)
2599 writeToStderr(data
);
2602 BZ2_bzWrite(&err
, bzLogFile
, (unsigned char *) data
.data(), data
.size());
2603 if (err
!= BZ_OK
) throw Error(format("cannot write to compressed log file (BZip2 error = %1%)") % err
);
2604 } else if (fdLogFile
!= -1)
2605 writeFull(fdLogFile
, data
);
2608 if (hook
&& fd
== hook
->fromHook
.readSide
)
2609 writeToStderr(data
);
2613 void DerivationGoal::handleEOF(int fd
)
2615 worker
.wakeUp(shared_from_this());
2619 PathSet
DerivationGoal::checkPathValidity(bool returnValid
, bool checkHash
)
2622 foreach (DerivationOutputs::iterator
, i
, drv
.outputs
) {
2623 if (!wantOutput(i
->first
, wantedOutputs
)) continue;
2625 worker
.store
.isValidPath(i
->second
.path
) &&
2626 (!checkHash
|| worker
.store
.pathContentsGood(i
->second
.path
));
2627 if (good
== returnValid
) result
.insert(i
->second
.path
);
2633 bool DerivationGoal::pathFailed(const Path
& path
)
2635 if (!settings
.cacheFailure
) return false;
2637 if (!worker
.store
.hasPathFailed(path
)) return false;
2639 printMsg(lvlError
, format("builder for `%1%' failed previously (cached)") % path
);
2641 if (settings
.printBuildTrace
)
2642 printMsg(lvlError
, format("@ build-failed %1% - cached") % drvPath
);
2644 done(BuildResult::CachedFailure
);
2650 Path
DerivationGoal::addHashRewrite(const Path
& path
)
2652 string h1
= string(path
, settings
.nixStore
.size() + 1, 32);
2653 string h2
= string(printHash32(hashString(htSHA256
, "rewrite:" + drvPath
+ ":" + path
)), 0, 32);
2654 Path p
= settings
.nixStore
+ "/" + h2
+ string(path
, settings
.nixStore
.size() + 33);
2655 if (pathExists(p
)) deletePath(p
);
2656 assert(path
.size() == p
.size());
2657 rewritesToTmp
[h1
] = h2
;
2658 rewritesFromTmp
[h2
] = h1
;
2659 redirectedOutputs
[path
] = p
;
2664 void DerivationGoal::done(BuildResult::Status status
, const string
& msg
)
2666 result
.status
= status
;
2667 result
.errorMsg
= msg
;
2668 amDone(result
.success() ? ecSuccess
: ecFailed
);
2669 if (result
.status
== BuildResult::TimedOut
)
2670 worker
.timedOut
= true;
2671 if (result
.status
== BuildResult::PermanentFailure
|| result
.status
== BuildResult::CachedFailure
)
2672 worker
.permanentFailure
= true;
2676 //////////////////////////////////////////////////////////////////////
2679 class SubstitutionGoal
: public Goal
2681 friend class Worker
;
2684 /* The store path that should be realised through a substitute. */
2687 /* The remaining substituters. */
2690 /* The current substituter. */
2693 /* Whether any substituter can realise this path */
2696 /* Path info returned by the substituter's query info operation. */
2697 SubstitutablePathInfo info
;
2699 /* Pipe for the substituter's standard output. */
2702 /* Pipe for the substituter's standard error. */
2705 /* The process ID of the builder. */
2708 /* Lock on the store path. */
2709 std::shared_ptr
<PathLocks
> outputLock
;
2711 /* Whether to try to repair a valid path. */
2714 /* Location where we're downloading the substitute. Differs from
2715 storePath when doing a repair. */
2718 typedef void (SubstitutionGoal::*GoalState
)();
2722 SubstitutionGoal(const Path
& storePath
, Worker
& worker
, bool repair
= false);
2723 ~SubstitutionGoal();
2729 /* "a$" ensures substitution goals happen before derivation
2731 return "a$" + storePathToName(storePath
) + "$" + storePath
;
2740 void referencesValid();
2744 /* Callback used by the worker to write to the log. */
2745 void handleChildOutput(int fd
, const string
& data
);
2746 void handleEOF(int fd
);
2748 Path
getStorePath() { return storePath
; }
2752 SubstitutionGoal::SubstitutionGoal(const Path
& storePath
, Worker
& worker
, bool repair
)
2754 , hasSubstitute(false)
2757 this->storePath
= storePath
;
2758 state
= &SubstitutionGoal::init
;
2759 name
= (format("substitution of `%1%'") % storePath
).str();
2764 SubstitutionGoal::~SubstitutionGoal()
2766 if (pid
!= -1) worker
.childTerminated(pid
);
2770 void SubstitutionGoal::timedOut()
2772 if (settings
.printBuildTrace
)
2773 printMsg(lvlError
, format("@ substituter-failed %1% timeout") % storePath
);
2775 pid_t savedPid
= pid
;
2777 worker
.childTerminated(savedPid
);
2783 void SubstitutionGoal::work()
2789 void SubstitutionGoal::init()
2793 worker
.store
.addTempRoot(storePath
);
2795 /* If the path already exists we're done. */
2796 if (!repair
&& worker
.store
.isValidPath(storePath
)) {
2801 if (settings
.readOnlyMode
)
2802 throw Error(format("cannot substitute path `%1%' - no write access to the Nix store") % storePath
);
2804 subs
= settings
.substituters
;
2810 void SubstitutionGoal::tryNext()
2812 trace("trying next substituter");
2814 if (subs
.size() == 0) {
2815 /* None left. Terminate this goal and let someone else deal
2817 debug(format("path `%1%' is required, but there is no substituter that can build it") % storePath
);
2818 /* Hack: don't indicate failure if there were no substituters.
2819 In that case the calling derivation should just do a
2821 amDone(hasSubstitute
? ecFailed
: ecNoSubstituters
);
2828 SubstitutablePathInfos infos
;
2829 PathSet
dummy(singleton
<PathSet
>(storePath
));
2830 worker
.store
.querySubstitutablePathInfos(sub
, dummy
, infos
);
2831 SubstitutablePathInfos::iterator k
= infos
.find(storePath
);
2832 if (k
== infos
.end()) { tryNext(); return; }
2834 hasSubstitute
= true;
2836 /* To maintain the closure invariant, we first have to realise the
2837 paths referenced by this one. */
2838 foreach (PathSet::iterator
, i
, info
.references
)
2839 if (*i
!= storePath
) /* ignore self-references */
2840 addWaitee(worker
.makeSubstitutionGoal(*i
));
2842 if (waitees
.empty()) /* to prevent hang (no wake-up event) */
2845 state
= &SubstitutionGoal::referencesValid
;
2849 void SubstitutionGoal::referencesValid()
2851 trace("all references realised");
2854 debug(format("some references of path `%1%' could not be realised") % storePath
);
2855 amDone(nrNoSubstituters
> 0 || nrIncompleteClosure
> 0 ? ecIncompleteClosure
: ecFailed
);
2859 foreach (PathSet::iterator
, i
, info
.references
)
2860 if (*i
!= storePath
) /* ignore self-references */
2861 assert(worker
.store
.isValidPath(*i
));
2863 state
= &SubstitutionGoal::tryToRun
;
2864 worker
.wakeUp(shared_from_this());
2868 void SubstitutionGoal::tryToRun()
2870 trace("trying to run");
2872 /* Make sure that we are allowed to start a build. Note that even
2873 is maxBuildJobs == 0 (no local builds allowed), we still allow
2874 a substituter to run. This is because substitutions cannot be
2875 distributed to another machine via the build hook. */
2876 if (worker
.getNrLocalBuilds() >= (settings
.maxBuildJobs
== 0 ? 1 : settings
.maxBuildJobs
)) {
2877 worker
.waitForBuildSlot(shared_from_this());
2881 /* Maybe a derivation goal has already locked this path
2882 (exceedingly unlikely, since it should have used a substitute
2883 first, but let's be defensive). */
2884 outputLock
.reset(); // make sure this goal's lock is gone
2885 if (pathIsLockedByMe(storePath
)) {
2886 debug(format("restarting substitution of `%1%' because it's locked by another goal")
2888 worker
.waitForAnyGoal(shared_from_this());
2889 return; /* restart in the tryToRun() state when another goal finishes */
2892 /* Acquire a lock on the output path. */
2893 outputLock
= std::shared_ptr
<PathLocks
>(new PathLocks
);
2894 if (!outputLock
->lockPaths(singleton
<PathSet
>(storePath
), "", false)) {
2895 worker
.waitForAWhile(shared_from_this());
2899 /* Check again whether the path is invalid. */
2900 if (!repair
&& worker
.store
.isValidPath(storePath
)) {
2901 debug(format("store path `%1%' has become valid") % storePath
);
2902 outputLock
->setDeletion(true);
2907 printMsg(lvlInfo
, format("fetching path `%1%'...") % storePath
);
2912 destPath
= repair
? storePath
+ ".tmp" : storePath
;
2914 /* Remove the (stale) output path if it exists. */
2915 if (pathExists(destPath
))
2916 deletePath(destPath
);
2918 worker
.store
.setSubstituterEnv();
2920 /* Fill in the arguments. */
2922 args
.push_back(baseNameOf(sub
));
2923 args
.push_back("--substitute");
2924 args
.push_back(storePath
);
2925 args
.push_back(destPath
);
2927 /* Fork the substitute program. */
2928 pid
= startProcess([&]() {
2930 commonChildInit(logPipe
);
2932 if (dup2(outPipe
.writeSide
, STDOUT_FILENO
) == -1)
2933 throw SysError("cannot dup output pipe into stdout");
2935 execv(sub
.c_str(), stringsToCharPtrs(args
).data());
2937 throw SysError(format("executing `%1%'") % sub
);
2940 pid
.setSeparatePG(true);
2941 pid
.setKillSignal(SIGTERM
);
2942 outPipe
.writeSide
.close();
2943 logPipe
.writeSide
.close();
2944 worker
.childStarted(shared_from_this(),
2945 pid
, singleton
<set
<int> >(logPipe
.readSide
), true, true);
2947 state
= &SubstitutionGoal::finished
;
2949 if (settings
.printBuildTrace
)
2950 printMsg(lvlError
, format("@ substituter-started %1% %2%") % storePath
% sub
);
2954 void SubstitutionGoal::finished()
2956 trace("substitute finished");
2958 /* Since we got an EOF on the logger pipe, the substitute is
2959 presumed to have terminated. */
2960 pid_t savedPid
= pid
;
2961 int status
= pid
.wait(true);
2963 /* So the child is gone now. */
2964 worker
.childTerminated(savedPid
);
2966 /* Close the read side of the logger pipe. */
2967 logPipe
.readSide
.close();
2969 /* Get the hash info from stdout. */
2970 string dummy
= readLine(outPipe
.readSide
);
2971 string expectedHashStr
= statusOk(status
) ? readLine(outPipe
.readSide
) : "";
2972 outPipe
.readSide
.close();
2974 /* Check the exit status and the build result. */
2978 if (!statusOk(status
))
2979 throw SubstError(format("fetching path `%1%' %2%")
2980 % storePath
% statusToString(status
));
2982 if (!pathExists(destPath
))
2983 throw SubstError(format("substitute did not produce path `%1%'") % destPath
);
2985 hash
= hashPath(htSHA256
, destPath
);
2987 /* Verify the expected hash we got from the substituer. */
2988 if (expectedHashStr
!= "") {
2989 size_t n
= expectedHashStr
.find(':');
2990 if (n
== string::npos
)
2991 throw Error(format("bad hash from substituter: %1%") % expectedHashStr
);
2992 HashType hashType
= parseHashType(string(expectedHashStr
, 0, n
));
2993 if (hashType
== htUnknown
)
2994 throw Error(format("unknown hash algorithm in `%1%'") % expectedHashStr
);
2995 Hash expectedHash
= parseHash16or32(hashType
, string(expectedHashStr
, n
+ 1));
2996 Hash actualHash
= hashType
== htSHA256
? hash
.first
: hashPath(hashType
, destPath
).first
;
2997 if (expectedHash
!= actualHash
)
2998 throw SubstError(format("hash mismatch in downloaded path `%1%': expected %2%, got %3%")
2999 % storePath
% printHash(expectedHash
) % printHash(actualHash
));
3002 } catch (SubstError
& e
) {
3004 printMsg(lvlInfo
, e
.msg());
3006 if (settings
.printBuildTrace
) {
3007 printMsg(lvlError
, format("@ substituter-failed %1% %2% %3%")
3008 % storePath
% status
% e
.msg());
3011 /* Try the next substitute. */
3012 state
= &SubstitutionGoal::tryNext
;
3013 worker
.wakeUp(shared_from_this());
3017 if (repair
) replaceValidPath(storePath
, destPath
);
3019 canonicalisePathMetaData(storePath
, -1);
3021 worker
.store
.optimisePath(storePath
); // FIXME: combine with hashPath()
3023 ValidPathInfo info2
;
3024 info2
.path
= storePath
;
3025 info2
.hash
= hash
.first
;
3026 info2
.narSize
= hash
.second
;
3027 info2
.references
= info
.references
;
3028 info2
.deriver
= info
.deriver
;
3029 worker
.store
.registerValidPath(info2
);
3031 outputLock
->setDeletion(true);
3034 worker
.store
.markContentsGood(storePath
);
3037 format("substitution of path `%1%' succeeded") % storePath
);
3039 if (settings
.printBuildTrace
)
3040 printMsg(lvlError
, format("@ substituter-succeeded %1%") % storePath
);
3046 void SubstitutionGoal::handleChildOutput(int fd
, const string
& data
)
3048 assert(fd
== logPipe
.readSide
);
3049 if (verbosity
>= settings
.buildVerbosity
) writeToStderr(data
);
3050 /* Don't write substitution output to a log file for now. We
3051 probably should, though. */
3055 void SubstitutionGoal::handleEOF(int fd
)
3057 if (fd
== logPipe
.readSide
) worker
.wakeUp(shared_from_this());
3062 //////////////////////////////////////////////////////////////////////
3065 static bool working
= false;
3068 Worker::Worker(LocalStore
& store
)
3071 /* Debugging: prevent recursive workers. */
3072 if (working
) abort();
3076 permanentFailure
= false;
3085 /* Explicitly get rid of all strong pointers now. After this all
3086 goals that refer to this worker should be gone. (Otherwise we
3087 are in trouble, since goals may call childTerminated() etc. in
3088 their destructors). */
3093 GoalPtr
Worker::makeDerivationGoal(const Path
& path
,
3094 const StringSet
& wantedOutputs
, BuildMode buildMode
)
3096 GoalPtr goal
= derivationGoals
[path
].lock();
3098 goal
= GoalPtr(new DerivationGoal(path
, wantedOutputs
, *this, buildMode
));
3099 derivationGoals
[path
] = goal
;
3102 (dynamic_cast<DerivationGoal
*>(goal
.get()))->addWantedOutputs(wantedOutputs
);
3107 GoalPtr
Worker::makeSubstitutionGoal(const Path
& path
, bool repair
)
3109 GoalPtr goal
= substitutionGoals
[path
].lock();
3111 goal
= GoalPtr(new SubstitutionGoal(path
, *this, repair
));
3112 substitutionGoals
[path
] = goal
;
3119 static void removeGoal(GoalPtr goal
, WeakGoalMap
& goalMap
)
3121 /* !!! inefficient */
3122 for (WeakGoalMap::iterator i
= goalMap
.begin();
3123 i
!= goalMap
.end(); )
3124 if (i
->second
.lock() == goal
) {
3125 WeakGoalMap::iterator j
= i
; ++j
;
3133 void Worker::removeGoal(GoalPtr goal
)
3135 nix::removeGoal(goal
, derivationGoals
);
3136 nix::removeGoal(goal
, substitutionGoals
);
3137 if (topGoals
.find(goal
) != topGoals
.end()) {
3138 topGoals
.erase(goal
);
3139 /* If a top-level goal failed, then kill all other goals
3140 (unless keepGoing was set). */
3141 if (goal
->getExitCode() == Goal::ecFailed
&& !settings
.keepGoing
)
3145 /* Wake up goals waiting for any goal to finish. */
3146 foreach (WeakGoals::iterator
, i
, waitingForAnyGoal
) {
3147 GoalPtr goal
= i
->lock();
3148 if (goal
) wakeUp(goal
);
3151 waitingForAnyGoal
.clear();
3155 void Worker::wakeUp(GoalPtr goal
)
3157 goal
->trace("woken up");
3158 addToWeakGoals(awake
, goal
);
3162 unsigned Worker::getNrLocalBuilds()
3164 return nrLocalBuilds
;
3168 void Worker::childStarted(GoalPtr goal
,
3169 pid_t pid
, const set
<int> & fds
, bool inBuildSlot
,
3170 bool respectTimeouts
)
3175 child
.timeStarted
= child
.lastOutput
= time(0);
3176 child
.inBuildSlot
= inBuildSlot
;
3177 child
.respectTimeouts
= respectTimeouts
;
3178 children
[pid
] = child
;
3179 if (inBuildSlot
) nrLocalBuilds
++;
3183 void Worker::childTerminated(pid_t pid
, bool wakeSleepers
)
3185 assert(pid
!= -1); /* common mistake */
3187 Children::iterator i
= children
.find(pid
);
3188 assert(i
!= children
.end());
3190 if (i
->second
.inBuildSlot
) {
3191 assert(nrLocalBuilds
> 0);
3195 children
.erase(pid
);
3199 /* Wake up goals waiting for a build slot. */
3200 foreach (WeakGoals::iterator
, i
, wantingToBuild
) {
3201 GoalPtr goal
= i
->lock();
3202 if (goal
) wakeUp(goal
);
3205 wantingToBuild
.clear();
3210 void Worker::waitForBuildSlot(GoalPtr goal
)
3212 debug("wait for build slot");
3213 if (getNrLocalBuilds() < settings
.maxBuildJobs
)
3214 wakeUp(goal
); /* we can do it right away */
3216 addToWeakGoals(wantingToBuild
, goal
);
3220 void Worker::waitForAnyGoal(GoalPtr goal
)
3222 debug("wait for any goal");
3223 addToWeakGoals(waitingForAnyGoal
, goal
);
3227 void Worker::waitForAWhile(GoalPtr goal
)
3229 debug("wait for a while");
3230 addToWeakGoals(waitingForAWhile
, goal
);
3234 void Worker::run(const Goals
& _topGoals
)
3236 foreach (Goals::iterator
, i
, _topGoals
) topGoals
.insert(*i
);
3238 startNest(nest
, lvlDebug
, format("entered goal loop"));
3244 /* Call every wake goal (in the ordering established by
3245 CompareGoalPtrs). */
3246 while (!awake
.empty() && !topGoals
.empty()) {
3248 for (auto & i
: awake
) {
3249 GoalPtr goal
= i
.lock();
3250 if (goal
) awake2
.insert(goal
);
3253 for (auto & goal
: awake2
) {
3256 if (topGoals
.empty()) break; // stuff may have been cancelled
3260 if (topGoals
.empty()) break;
3262 /* Wait for input. */
3263 if (!children
.empty() || !waitingForAWhile
.empty())
3266 if (awake
.empty() && settings
.maxBuildJobs
== 0) throw Error(
3267 "unable to start any build; either increase `--max-jobs' "
3268 "or enable distributed builds");
3269 assert(!awake
.empty());
3273 /* If --keep-going is not set, it's possible that the main goal
3274 exited while some of its subgoals were still active. But if
3275 --keep-going *is* set, then they must all be finished now. */
3276 assert(!settings
.keepGoing
|| awake
.empty());
3277 assert(!settings
.keepGoing
|| wantingToBuild
.empty());
3278 assert(!settings
.keepGoing
|| children
.empty());
3282 void Worker::waitForInput()
3284 printMsg(lvlVomit
, "waiting for children");
3286 /* Process output from the file descriptors attached to the
3287 children, namely log output and output path creation commands.
3288 We also use this to detect child termination: if we get EOF on
3289 the logger pipe of a build, we assume that the builder has
3292 bool useTimeout
= false;
3293 struct timeval timeout
;
3294 timeout
.tv_usec
= 0;
3295 time_t before
= time(0);
3297 /* If we're monitoring for silence on stdout/stderr, or if there
3298 is a build timeout, then wait for input until the first
3299 deadline for any child. */
3300 assert(sizeof(time_t) >= sizeof(long));
3301 time_t nearest
= LONG_MAX
; // nearest deadline
3302 foreach (Children::iterator
, i
, children
) {
3303 if (!i
->second
.respectTimeouts
) continue;
3304 if (settings
.maxSilentTime
!= 0)
3305 nearest
= std::min(nearest
, i
->second
.lastOutput
+ settings
.maxSilentTime
);
3306 if (settings
.buildTimeout
!= 0)
3307 nearest
= std::min(nearest
, i
->second
.timeStarted
+ settings
.buildTimeout
);
3309 if (nearest
!= LONG_MAX
) {
3310 timeout
.tv_sec
= std::max((time_t) 1, nearest
- before
);
3312 printMsg(lvlVomit
, format("sleeping %1% seconds") % timeout
.tv_sec
);
3315 /* If we are polling goals that are waiting for a lock, then wake
3316 up after a few seconds at most. */
3317 if (!waitingForAWhile
.empty()) {
3319 if (lastWokenUp
== 0)
3320 printMsg(lvlError
, "waiting for locks or build slots...");
3321 if (lastWokenUp
== 0 || lastWokenUp
> before
) lastWokenUp
= before
;
3322 timeout
.tv_sec
= std::max((time_t) 1, (time_t) (lastWokenUp
+ settings
.pollInterval
- before
));
3323 } else lastWokenUp
= 0;
3325 using namespace std
;
3326 /* Use select() to wait for the input side of any logger pipe to
3327 become `available'. Note that `available' (i.e., non-blocking)
3332 foreach (Children::iterator
, i
, children
) {
3333 foreach (set
<int>::iterator
, j
, i
->second
.fds
) {
3335 if (*j
>= fdMax
) fdMax
= *j
+ 1;
3339 if (select(fdMax
, &fds
, 0, 0, useTimeout
? &timeout
: 0) == -1) {
3340 if (errno
== EINTR
) return;
3341 throw SysError("waiting for input");
3344 time_t after
= time(0);
3346 /* Process all available file descriptors. */
3348 /* Since goals may be canceled from inside the loop below (causing
3349 them go be erased from the `children' map), we have to be
3350 careful that we don't keep iterators alive across calls to
3353 foreach (Children::iterator
, i
, children
) pids
.insert(i
->first
);
3355 foreach (set
<pid_t
>::iterator
, i
, pids
) {
3357 Children::iterator j
= children
.find(*i
);
3358 if (j
== children
.end()) continue; // child destroyed
3359 GoalPtr goal
= j
->second
.goal
.lock();
3362 set
<int> fds2(j
->second
.fds
);
3363 foreach (set
<int>::iterator
, k
, fds2
) {
3364 if (FD_ISSET(*k
, &fds
)) {
3365 unsigned char buffer
[4096];
3366 ssize_t rd
= read(*k
, buffer
, sizeof(buffer
));
3369 throw SysError(format("reading from %1%")
3371 } else if (rd
== 0) {
3372 debug(format("%1%: got EOF") % goal
->getName());
3373 goal
->handleEOF(*k
);
3374 j
->second
.fds
.erase(*k
);
3376 printMsg(lvlVomit
, format("%1%: read %2% bytes")
3377 % goal
->getName() % rd
);
3378 string
data((char *) buffer
, rd
);
3379 j
->second
.lastOutput
= after
;
3380 goal
->handleChildOutput(*k
, data
);
3385 if (goal
->getExitCode() == Goal::ecBusy
&&
3386 settings
.maxSilentTime
!= 0 &&
3387 j
->second
.respectTimeouts
&&
3388 after
- j
->second
.lastOutput
>= (time_t) settings
.maxSilentTime
)
3391 format("%1% timed out after %2% seconds of silence")
3392 % goal
->getName() % settings
.maxSilentTime
);
3396 else if (goal
->getExitCode() == Goal::ecBusy
&&
3397 settings
.buildTimeout
!= 0 &&
3398 j
->second
.respectTimeouts
&&
3399 after
- j
->second
.timeStarted
>= (time_t) settings
.buildTimeout
)
3402 format("%1% timed out after %2% seconds")
3403 % goal
->getName() % settings
.buildTimeout
);
3408 if (!waitingForAWhile
.empty() && lastWokenUp
+ settings
.pollInterval
<= after
) {
3409 lastWokenUp
= after
;
3410 foreach (WeakGoals::iterator
, i
, waitingForAWhile
) {
3411 GoalPtr goal
= i
->lock();
3412 if (goal
) wakeUp(goal
);
3414 waitingForAWhile
.clear();
3419 unsigned int Worker::exitStatus()
3421 return timedOut
? 101 : (permanentFailure
? 100 : 1);
3425 //////////////////////////////////////////////////////////////////////
3428 void LocalStore::buildPaths(const PathSet
& drvPaths
, BuildMode buildMode
)
3430 startNest(nest
, lvlDebug
,
3431 format("building %1%") % showPaths(drvPaths
));
3433 Worker
worker(*this);
3436 foreach (PathSet::const_iterator
, i
, drvPaths
) {
3437 DrvPathWithOutputs i2
= parseDrvPathWithOutputs(*i
);
3438 if (isDerivation(i2
.first
))
3439 goals
.insert(worker
.makeDerivationGoal(i2
.first
, i2
.second
, buildMode
));
3441 goals
.insert(worker
.makeSubstitutionGoal(*i
, buildMode
));
3447 foreach (Goals::iterator
, i
, goals
)
3448 if ((*i
)->getExitCode() == Goal::ecFailed
) {
3449 DerivationGoal
* i2
= dynamic_cast<DerivationGoal
*>(i
->get());
3450 if (i2
) failed
.insert(i2
->getDrvPath());
3451 else failed
.insert(dynamic_cast<SubstitutionGoal
*>(i
->get())->getStorePath());
3454 if (!failed
.empty())
3455 throw Error(format("build of %1% failed") % showPaths(failed
), worker
.exitStatus());
3459 void LocalStore::ensurePath(const Path
& path
)
3461 /* If the path is already valid, we're done. */
3462 if (isValidPath(path
)) return;
3464 Worker
worker(*this);
3465 GoalPtr goal
= worker
.makeSubstitutionGoal(path
);
3466 Goals goals
= singleton
<Goals
>(goal
);
3470 if (goal
->getExitCode() != Goal::ecSuccess
)
3471 throw Error(format("path `%1%' does not exist and cannot be created") % path
, worker
.exitStatus());
3475 void LocalStore::repairPath(const Path
& path
)
3477 Worker
worker(*this);
3478 GoalPtr goal
= worker
.makeSubstitutionGoal(path
, true);
3479 Goals goals
= singleton
<Goals
>(goal
);
3483 if (goal
->getExitCode() != Goal::ecSuccess
)
3484 throw Error(format("cannot repair path `%1%'") % path
, worker
.exitStatus());