3 #include "references.hh"
4 #include "pathlocks.hh"
7 #include "local-store.hh"
10 #include "affinity.hh"
11 #include "builtins.hh"
21 #include <sys/types.h>
23 #include <sys/utsname.h>
40 /* Includes required for chroot support. */
42 #include <sys/param.h>
45 #include <sys/mount.h>
47 #if HAVE_SYS_SYSCALL_H
48 #include <sys/syscall.h>
55 #define CHROOT_ENABLED HAVE_CHROOT && HAVE_SYS_MOUNT_H && defined(MS_BIND) && defined(MS_PRIVATE)
56 #define CLONE_ENABLED defined(CLONE_NEWNS)
58 #if defined(SYS_pivot_root)
59 #define pivot_root(new_root, put_old) (syscall(SYS_pivot_root, new_root,put_old))
63 #include <sys/socket.h>
64 #include <sys/ioctl.h>
66 #include <netinet/ip.h>
70 #include <sys/personality.h>
74 #include <sys/statvfs.h>
83 /* Forward definition. */
87 /* A pointer to a goal. */
90 typedef std::shared_ptr
<Goal
> GoalPtr
;
91 typedef std::weak_ptr
<Goal
> WeakGoalPtr
;
93 struct CompareGoalPtrs
{
94 bool operator() (const GoalPtr
& a
, const GoalPtr
& b
);
98 typedef set
<GoalPtr
, CompareGoalPtrs
> Goals
;
99 typedef list
<WeakGoalPtr
> WeakGoals
;
101 /* A map of paths to goals (and the other way around). */
102 typedef map
<Path
, WeakGoalPtr
> WeakGoalMap
;
106 class Goal
: public std::enable_shared_from_this
<Goal
>
109 typedef enum {ecBusy
, ecSuccess
, ecFailed
, ecNoSubstituters
, ecIncompleteClosure
} ExitCode
;
113 /* Backlink to the worker. */
116 /* Goals that this goal is waiting for. */
119 /* Goals waiting for this one to finish. Must use weak pointers
120 here to prevent cycles. */
123 /* Number of goals we are/were waiting for that have failed. */
124 unsigned int nrFailed
;
126 /* Number of substitution goals we are/were waiting for that
127 failed because there are no substituters. */
128 unsigned int nrNoSubstituters
;
130 /* Number of substitution goals we are/were waiting for that
131 failed because othey had unsubstitutable references. */
132 unsigned int nrIncompleteClosure
;
134 /* Name of this goal for debugging purposes. */
137 /* Whether the goal is finished. */
140 Goal(Worker
& worker
) : worker(worker
)
142 nrFailed
= nrNoSubstituters
= nrIncompleteClosure
= 0;
148 trace("goal destroyed");
152 virtual void work() = 0;
154 void addWaitee(GoalPtr waitee
);
156 virtual void waiteeDone(GoalPtr waitee
, ExitCode result
);
158 virtual void handleChildOutput(int fd
, const string
& data
)
163 virtual void handleEOF(int fd
)
168 void trace(const format
& f
);
175 ExitCode
getExitCode()
180 /* Callback in case of a timeout. It should wake up its waiters,
181 get rid of any running child processes that are being monitored
182 by the worker (important!), etc. */
183 virtual void timedOut() = 0;
185 virtual string
key() = 0;
188 void amDone(ExitCode result
);
192 bool CompareGoalPtrs::operator() (const GoalPtr
& a
, const GoalPtr
& b
) {
193 string s1
= a
->key();
194 string s2
= b
->key();
199 /* A mapping used to remember for each child process to what goal it
200 belongs, and file descriptors for receiving log data and output
201 path creation commands. */
206 bool respectTimeouts
;
208 time_t lastOutput
; /* time we last got output on stdout/stderr */
212 typedef map
<pid_t
, Child
> Children
;
215 /* The worker class. */
220 /* Note: the worker should only have strong pointers to the
223 /* The top-level goals of the worker. */
226 /* Goals that are ready to do some work. */
229 /* Goals waiting for a build slot. */
230 WeakGoals wantingToBuild
;
232 /* Child processes currently running. */
235 /* Number of build slots occupied. This includes local builds and
236 substitutions but not remote builds via the build hook. */
237 unsigned int nrLocalBuilds
;
239 /* Maps used to prevent multiple instantiations of a goal for the
240 same derivation / path. */
241 WeakGoalMap derivationGoals
;
242 WeakGoalMap substitutionGoals
;
244 /* Goals waiting for busy paths to be unlocked. */
245 WeakGoals waitingForAnyGoal
;
247 /* Goals sleeping for a few seconds (polling a lock). */
248 WeakGoals waitingForAWhile
;
250 /* Last time the goals in `waitingForAWhile' where woken up. */
255 /* Set if at least one derivation had a BuildError (i.e. permanent
257 bool permanentFailure
;
259 /* Set if at least one derivation had a timeout. */
264 std::shared_ptr
<Agent
> hook
;
265 std::shared_ptr
<Agent
> substituter
;
267 Worker(LocalStore
& store
);
270 /* Make a goal (with caching). */
271 GoalPtr
makeDerivationGoal(const Path
& drvPath
, const StringSet
& wantedOutputs
, BuildMode buildMode
= bmNormal
);
272 GoalPtr
makeSubstitutionGoal(const Path
& storePath
, bool repair
= false);
274 /* Remove a dead goal. */
275 void removeGoal(GoalPtr goal
);
277 /* Wake up a goal (i.e., there is something for it to do). */
278 void wakeUp(GoalPtr goal
);
280 /* Return the number of local build and substitution processes
281 currently running (but not remote builds via the build
283 unsigned int getNrLocalBuilds();
285 /* Registers a running child process. `inBuildSlot' means that
286 the process counts towards the jobs limit. */
287 void childStarted(GoalPtr goal
, pid_t pid
,
288 const set
<int> & fds
, bool inBuildSlot
, bool respectTimeouts
);
290 /* Unregisters a running child process. `wakeSleepers' should be
291 false if there is no sense in waking up goals that are sleeping
292 because they can't run yet (e.g., there is no free build slot,
293 or the hook would still say `postpone'). */
294 void childTerminated(pid_t pid
, bool wakeSleepers
= true);
296 /* Put `goal' to sleep until a build slot becomes available (which
297 might be right away). */
298 void waitForBuildSlot(GoalPtr goal
);
300 /* Wait for any goal to finish. Pretty indiscriminate way to
301 wait for some resource that some other goal is holding. */
302 void waitForAnyGoal(GoalPtr goal
);
304 /* Wait for a few seconds and then retry this goal. Used when
305 waiting for a lock held by another process. This kind of
306 polling is inefficient, but POSIX doesn't really provide a way
307 to wait for multiple locks in the main select() loop. */
308 void waitForAWhile(GoalPtr goal
);
310 /* Loop until the specified top-level goals have finished. */
311 void run(const Goals
& topGoals
);
313 /* Wait for input to become available. */
316 unsigned int exitStatus();
320 //////////////////////////////////////////////////////////////////////
323 void addToWeakGoals(WeakGoals
& goals
, GoalPtr p
)
327 foreach (WeakGoals::iterator
, i
, goals
)
328 if (i
->lock() == p
) return;
333 void Goal::addWaitee(GoalPtr waitee
)
335 waitees
.insert(waitee
);
336 addToWeakGoals(waitee
->waiters
, shared_from_this());
340 void Goal::waiteeDone(GoalPtr waitee
, ExitCode result
)
342 assert(waitees
.find(waitee
) != waitees
.end());
343 waitees
.erase(waitee
);
345 trace(format("waitee `%1%' done; %2% left") %
346 waitee
->name
% waitees
.size());
348 if (result
== ecFailed
|| result
== ecNoSubstituters
|| result
== ecIncompleteClosure
) ++nrFailed
;
350 if (result
== ecNoSubstituters
) ++nrNoSubstituters
;
352 if (result
== ecIncompleteClosure
) ++nrIncompleteClosure
;
354 if (waitees
.empty() || (result
== ecFailed
&& !settings
.keepGoing
)) {
356 /* If we failed and keepGoing is not set, we remove all
357 remaining waitees. */
358 foreach (Goals::iterator
, i
, waitees
) {
361 foreach (WeakGoals::iterator
, j
, goal
->waiters
)
362 if (j
->lock() != shared_from_this()) waiters2
.push_back(*j
);
363 goal
->waiters
= waiters2
;
367 worker
.wakeUp(shared_from_this());
372 void Goal::amDone(ExitCode result
)
375 assert(exitCode
== ecBusy
);
376 assert(result
== ecSuccess
|| result
== ecFailed
|| result
== ecNoSubstituters
|| result
== ecIncompleteClosure
);
378 foreach (WeakGoals::iterator
, i
, waiters
) {
379 GoalPtr goal
= i
->lock();
380 if (goal
) goal
->waiteeDone(shared_from_this(), result
);
383 worker
.removeGoal(shared_from_this());
387 void Goal::trace(const format
& f
)
389 debug(format("%1%: %2%") % name
% f
);
394 //////////////////////////////////////////////////////////////////////
397 /* Restore default handling of SIGPIPE, otherwise some programs will
398 randomly say "Broken pipe". */
399 static void restoreSIGPIPE()
401 struct sigaction act
, oact
;
402 act
.sa_handler
= SIG_DFL
;
404 sigemptyset(&act
.sa_mask
);
405 if (sigaction(SIGPIPE
, &act
, &oact
)) throw SysError("resetting SIGPIPE");
409 //////////////////////////////////////////////////////////////////////
415 /* POSIX locks suck. If we have a lock on a file, and we open and
416 close that file again (without closing the original file
417 descriptor), we lose the lock. So we have to be *very* careful
418 not to open a lock file on which we are holding a lock. */
419 static PathSet lockedPaths
; /* !!! not thread-safe */
422 AutoCloseFD fdUserLock
;
427 std::vector
<gid_t
> supplementaryGIDs
;
438 string
getUser() { return user
; }
439 uid_t
getUID() { return uid
; }
440 uid_t
getGID() { return gid
; }
441 std::vector
<gid_t
> getSupplementaryGIDs() { return supplementaryGIDs
; }
443 bool enabled() { return uid
!= 0; }
448 PathSet
UserLock::lockedPaths
;
457 UserLock::~UserLock()
463 void UserLock::acquire()
467 assert(settings
.buildUsersGroup
!= "");
469 /* Get the members of the build-users-group. */
470 struct group
* gr
= getgrnam(settings
.buildUsersGroup
.c_str());
472 throw Error(format("the group `%1%' specified in `build-users-group' does not exist")
473 % settings
.buildUsersGroup
);
476 /* Copy the result of getgrnam. */
478 for (char * * p
= gr
->gr_mem
; *p
; ++p
) {
479 debug(format("found build user `%1%'") % *p
);
484 throw Error(format("the build users group `%1%' has no members")
485 % settings
.buildUsersGroup
);
487 /* Find a user account that isn't currently in use for another
489 foreach (Strings::iterator
, i
, users
) {
490 debug(format("trying user `%1%'") % *i
);
492 struct passwd
* pw
= getpwnam(i
->c_str());
494 throw Error(format("the user `%1%' in the group `%2%' does not exist")
495 % *i
% settings
.buildUsersGroup
);
497 createDirs(settings
.nixStateDir
+ "/userpool");
499 fnUserLock
= (format("%1%/userpool/%2%") % settings
.nixStateDir
% pw
->pw_uid
).str();
501 if (lockedPaths
.find(fnUserLock
) != lockedPaths
.end())
502 /* We already have a lock on this one. */
505 AutoCloseFD fd
= open(fnUserLock
.c_str(), O_RDWR
| O_CREAT
, 0600);
507 throw SysError(format("opening user lock `%1%'") % fnUserLock
);
510 if (lockFile(fd
, ltWrite
, false)) {
511 fdUserLock
= fd
.borrow();
512 lockedPaths
.insert(fnUserLock
);
516 /* Sanity check... */
517 if (uid
== getuid() || uid
== geteuid())
518 throw Error(format("the build user should not be a member of `%1%'")
519 % settings
.buildUsersGroup
);
521 /* Get the list of supplementary groups of this build user. This
522 is usually either empty or contains a group such as "kvm". */
523 supplementaryGIDs
.resize(10);
524 int ngroups
= supplementaryGIDs
.size();
525 int err
= getgrouplist(pw
->pw_name
, pw
->pw_gid
,
526 supplementaryGIDs
.data(), &ngroups
);
528 throw Error(format("failed to get list of supplementary groups for ‘%1%’") % pw
->pw_name
);
530 supplementaryGIDs
.resize(ngroups
);
536 throw Error(format("all build users are currently in use; "
537 "consider creating additional users and adding them to the `%1%' group")
538 % settings
.buildUsersGroup
);
542 void UserLock::release()
544 if (uid
== 0) return;
545 fdUserLock
.close(); /* releases lock */
546 assert(lockedPaths
.find(fnUserLock
) != lockedPaths
.end());
547 lockedPaths
.erase(fnUserLock
);
553 void UserLock::kill()
559 //////////////////////////////////////////////////////////////////////
562 typedef map
<string
, string
> HashRewrites
;
565 string
rewriteHashes(string s
, const HashRewrites
& rewrites
)
567 foreach (HashRewrites::const_iterator
, i
, rewrites
) {
568 assert(i
->first
.size() == i
->second
.size());
570 while ((j
= s
.find(i
->first
, j
)) != string::npos
) {
571 debug(format("rewriting @ %1%") % j
);
572 s
.replace(j
, i
->second
.size(), i
->second
);
579 //////////////////////////////////////////////////////////////////////
582 typedef enum {rpAccept
, rpDecline
, rpPostpone
} HookReply
;
584 class SubstitutionGoal
;
586 class DerivationGoal
: public Goal
589 /* The path of the derivation. */
592 /* The specific outputs that we need to build. Empty means all of
594 StringSet wantedOutputs
;
596 /* Whether additional wanted outputs have been added. */
599 /* Whether to retry substituting the outputs after building the
601 bool retrySubstitution
;
603 /* The derivation stored at drvPath. */
606 /* The remainder is state held during the build. */
608 /* Locks on the output paths. */
609 PathLocks outputLocks
;
611 /* All input paths (that is, the union of FS closures of the
612 immediate input paths). */
615 /* Referenceable paths (i.e., input and output paths). */
618 /* Outputs that are already valid. If we're repairing, these are
619 the outputs that are valid *and* not corrupt. */
622 /* Outputs that are corrupt or not valid. */
623 PathSet missingPaths
;
625 /* User selected for running the builder. */
628 /* The process ID of the builder. */
631 /* The temporary directory. */
634 /* The path of the temporary directory in the sandbox. */
635 Path tmpDirInSandbox
;
637 /* File descriptor for the log file. */
643 AutoCloseFD fdLogFile
;
645 /* Number of bytes received from the builder's stdout/stderr. */
646 unsigned long logSize
;
648 /* Pipe for the builder's standard output/error. */
651 /* The build hook. */
652 std::shared_ptr
<Agent
> hook
;
654 /* Whether we're currently doing a chroot build. */
659 /* RAII object to delete the chroot directory. */
660 std::shared_ptr
<AutoDelete
> autoDelChroot
;
662 /* All inputs that are regular files. */
663 PathSet regularInputPaths
;
665 /* Whether this is a fixed-output derivation. */
668 typedef void (DerivationGoal::*GoalState
)();
671 /* Stuff we need to pass to runChild(). */
672 typedef map
<Path
, Path
> DirsInChroot
; // maps target path to source path
673 DirsInChroot dirsInChroot
;
674 typedef map
<string
, string
> Environment
;
677 /* Hash rewriting. */
678 HashRewrites rewritesToTmp
, rewritesFromTmp
;
679 typedef map
<Path
, Path
> RedirectedOutputs
;
680 RedirectedOutputs redirectedOutputs
;
684 /* If we're repairing without a chroot, there may be outputs that
685 are valid but corrupt. So we redirect these outputs to
687 PathSet redirectedBadOutputs
;
689 /* The current round, if we're building multiple times. */
690 unsigned int curRound
= 1;
692 unsigned int nrRounds
;
694 /* Path registration info from the previous round, if we're
695 building multiple times. Since this contains the hash, it
696 allows us to compare whether two rounds produced the same
698 ValidPathInfos prevInfos
;
703 DerivationGoal(const Path
& drvPath
, const StringSet
& wantedOutputs
, Worker
& worker
, BuildMode buildMode
= bmNormal
);
706 void timedOut() override
;
710 /* Ensure that derivations get built in order of their name,
711 i.e. a derivation named "aardvark" always comes before
712 "baboon". And substitution goals always happen before
713 derivation goals (due to "b$"). */
714 return "b$" + storePathToName(drvPath
) + "$" + drvPath
;
724 /* Add wanted outputs to an already existing derivation goal. */
725 void addWantedOutputs(const StringSet
& outputs
);
727 BuildResult
getResult() { return result
; }
732 void haveDerivation();
733 void outputsSubstituted();
734 void closureRepaired();
735 void inputsRealised();
739 /* Is the build hook willing to perform the build? */
740 HookReply
tryBuildHook();
742 /* Start building a derivation. */
745 /* Run the builder's process. */
748 friend int childEntry(void *);
750 /* Check that the derivation outputs all exist and register them
752 void registerOutputs();
754 /* Open a log file and a pipe to it. */
757 /* Close the log file. */
760 /* Delete the temporary directory, if we have one. */
761 void deleteTmpDir(bool force
);
763 /* Callback used by the worker to write to the log. */
764 void handleChildOutput(int fd
, const string
& data
);
765 void handleEOF(int fd
);
767 /* Return the set of (in)valid paths. */
768 PathSet
checkPathValidity(bool returnValid
, bool checkHash
);
770 /* Abort the goal if `path' failed to build. */
771 bool pathFailed(const Path
& path
);
773 /* Forcibly kill the child process, if any. */
776 Path
addHashRewrite(const Path
& path
);
778 void repairClosure();
780 void done(BuildResult::Status status
, const string
& msg
= "");
784 DerivationGoal::DerivationGoal(const Path
& drvPath
, const StringSet
& wantedOutputs
, Worker
& worker
, BuildMode buildMode
)
786 , wantedOutputs(wantedOutputs
)
788 , retrySubstitution(false)
795 , buildMode(buildMode
)
797 this->drvPath
= drvPath
;
798 state
= &DerivationGoal::init
;
799 name
= (format("building of `%1%'") % drvPath
).str();
802 /* Prevent the .chroot directory from being
803 garbage-collected. (See isActiveTempFile() in gc.cc.) */
804 worker
.store
.addTempRoot(drvPath
);
808 DerivationGoal::~DerivationGoal()
810 /* Careful: we should never ever throw an exception from a
812 try { killChild(); } catch (...) { ignoreException(); }
813 try { deleteTmpDir(false); } catch (...) { ignoreException(); }
814 try { closeLogFile(); } catch (...) { ignoreException(); }
818 void DerivationGoal::killChild()
821 worker
.childTerminated(pid
);
823 if (buildUser
.enabled()) {
824 /* If we're using a build user, then there is a tricky
825 race condition: if we kill the build user before the
826 child has done its setuid() to the build user uid, then
827 it won't be killed, and we'll potentially lock up in
828 pid.wait(). So also send a conventional kill to the
830 ::kill(-pid
, SIGKILL
); /* ignore the result */
839 /* If there was a build hook involved, remove it from the worker's
841 if (hook
&& hook
->pid
!= -1) {
842 worker
.childTerminated(hook
->pid
);
848 void DerivationGoal::timedOut()
850 if (settings
.printBuildTrace
)
851 printMsg(lvlError
, format("@ build-failed %1% - timeout") % drvPath
);
853 done(BuildResult::TimedOut
);
857 void DerivationGoal::work()
863 void DerivationGoal::addWantedOutputs(const StringSet
& outputs
)
865 /* If we already want all outputs, there is nothing to do. */
866 if (wantedOutputs
.empty()) return;
868 if (outputs
.empty()) {
869 wantedOutputs
.clear();
872 foreach (StringSet::const_iterator
, i
, outputs
)
873 if (wantedOutputs
.find(*i
) == wantedOutputs
.end()) {
874 wantedOutputs
.insert(*i
);
880 void DerivationGoal::init()
884 if (settings
.readOnlyMode
)
885 throw Error(format("cannot build derivation `%1%' - no write access to the store") % drvPath
);
887 /* The first thing to do is to make sure that the derivation
888 exists. If it doesn't, it may be created through a
890 if (buildMode
== bmNormal
&& worker
.store
.isValidPath(drvPath
)) {
895 addWaitee(worker
.makeSubstitutionGoal(drvPath
));
897 state
= &DerivationGoal::haveDerivation
;
901 void DerivationGoal::haveDerivation()
903 trace("loading derivation");
906 printMsg(lvlError
, format("cannot build missing derivation ‘%1%’") % drvPath
);
907 done(BuildResult::MiscFailure
);
911 /* `drvPath' should already be a root, but let's be on the safe
912 side: if the user forgot to make it a root, we wouldn't want
913 things being garbage collected while we're busy. */
914 worker
.store
.addTempRoot(drvPath
);
916 assert(worker
.store
.isValidPath(drvPath
));
918 /* Get the derivation. */
919 drv
= derivationFromPath(worker
.store
, drvPath
);
921 foreach (DerivationOutputs::iterator
, i
, drv
.outputs
)
922 worker
.store
.addTempRoot(i
->second
.path
);
924 /* Check what outputs paths are not already valid. */
925 PathSet invalidOutputs
= checkPathValidity(false, buildMode
== bmRepair
);
927 /* If they are all valid, then we're done. */
928 if (invalidOutputs
.size() == 0 && buildMode
== bmNormal
) {
929 done(BuildResult::AlreadyValid
);
933 /* Check whether any output previously failed to build. If so,
935 foreach (PathSet::iterator
, i
, invalidOutputs
)
936 if (pathFailed(*i
)) return;
938 /* We are first going to try to create the invalid output paths
939 through substitutes. If that doesn't work, we'll build
941 if (settings
.useSubstitutes
&& substitutesAllowed(drv
))
942 foreach (PathSet::iterator
, i
, invalidOutputs
)
943 addWaitee(worker
.makeSubstitutionGoal(*i
, buildMode
== bmRepair
));
945 if (waitees
.empty()) /* to prevent hang (no wake-up event) */
946 outputsSubstituted();
948 state
= &DerivationGoal::outputsSubstituted
;
952 void DerivationGoal::outputsSubstituted()
954 trace("all outputs substituted (maybe)");
956 if (nrFailed
> 0 && nrFailed
> nrNoSubstituters
+ nrIncompleteClosure
&& !settings
.tryFallback
)
957 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
);
959 /* If the substitutes form an incomplete closure, then we should
960 build the dependencies of this derivation, but after that, we
961 can still use the substitutes for this derivation itself. */
962 if (nrIncompleteClosure
> 0 && !retrySubstitution
) retrySubstitution
= true;
964 nrFailed
= nrNoSubstituters
= nrIncompleteClosure
= 0;
972 unsigned int nrInvalid
= checkPathValidity(false, buildMode
== bmRepair
).size();
973 if (buildMode
== bmNormal
&& nrInvalid
== 0) {
974 done(BuildResult::Substituted
);
977 if (buildMode
== bmRepair
&& nrInvalid
== 0) {
981 if (buildMode
== bmCheck
&& nrInvalid
> 0)
982 throw Error(format("some outputs of `%1%' are not valid, so checking is not possible") % drvPath
);
984 /* Otherwise, at least one of the output paths could not be
985 produced using a substitute. So we have to build instead. */
987 /* Make sure checkPathValidity() from now on checks all
989 wantedOutputs
= PathSet();
991 /* The inputs must be built before we can build this goal. */
992 foreach (DerivationInputs::iterator
, i
, drv
.inputDrvs
)
993 addWaitee(worker
.makeDerivationGoal(i
->first
, i
->second
, buildMode
== bmRepair
? bmRepair
: bmNormal
));
995 foreach (PathSet::iterator
, i
, drv
.inputSrcs
)
996 addWaitee(worker
.makeSubstitutionGoal(*i
));
998 if (waitees
.empty()) /* to prevent hang (no wake-up event) */
1001 state
= &DerivationGoal::inputsRealised
;
1005 void DerivationGoal::repairClosure()
1007 /* If we're repairing, we now know that our own outputs are valid.
1008 Now check whether the other paths in the outputs closure are
1009 good. If not, then start derivation goals for the derivations
1010 that produced those outputs. */
1012 /* Get the output closure. */
1013 PathSet outputClosure
;
1014 foreach (DerivationOutputs::iterator
, i
, drv
.outputs
) {
1015 if (!wantOutput(i
->first
, wantedOutputs
)) continue;
1016 computeFSClosure(worker
.store
, i
->second
.path
, outputClosure
);
1019 /* Filter out our own outputs (which we have already checked). */
1020 foreach (DerivationOutputs::iterator
, i
, drv
.outputs
)
1021 outputClosure
.erase(i
->second
.path
);
1023 /* Get all dependencies of this derivation so that we know which
1024 derivation is responsible for which path in the output
1026 PathSet inputClosure
;
1027 computeFSClosure(worker
.store
, drvPath
, inputClosure
);
1028 std::map
<Path
, Path
> outputsToDrv
;
1029 foreach (PathSet::iterator
, i
, inputClosure
)
1030 if (isDerivation(*i
)) {
1031 Derivation drv
= derivationFromPath(worker
.store
, *i
);
1032 foreach (DerivationOutputs::iterator
, j
, drv
.outputs
)
1033 outputsToDrv
[j
->second
.path
] = *i
;
1036 /* Check each path (slow!). */
1038 foreach (PathSet::iterator
, i
, outputClosure
) {
1039 if (worker
.store
.pathContentsGood(*i
)) continue;
1040 printMsg(lvlError
, format("found corrupted or missing path `%1%' in the output closure of `%2%'") % *i
% drvPath
);
1041 Path drvPath2
= outputsToDrv
[*i
];
1043 addWaitee(worker
.makeSubstitutionGoal(*i
, true));
1045 addWaitee(worker
.makeDerivationGoal(drvPath2
, PathSet(), bmRepair
));
1048 if (waitees
.empty()) {
1049 done(BuildResult::AlreadyValid
);
1053 state
= &DerivationGoal::closureRepaired
;
1057 void DerivationGoal::closureRepaired()
1059 trace("closure repaired");
1061 throw Error(format("some paths in the output closure of derivation ‘%1%’ could not be repaired") % drvPath
);
1062 done(BuildResult::AlreadyValid
);
1066 void DerivationGoal::inputsRealised()
1068 trace("all inputs realised");
1070 if (nrFailed
!= 0) {
1072 format("cannot build derivation `%1%': %2% dependencies couldn't be built")
1073 % drvPath
% nrFailed
);
1074 done(BuildResult::DependencyFailed
);
1078 if (retrySubstitution
) {
1083 /* Gather information necessary for computing the closure and/or
1084 running the build hook. */
1086 /* The outputs are referenceable paths. */
1087 foreach (DerivationOutputs::iterator
, i
, drv
.outputs
) {
1088 debug(format("building path `%1%'") % i
->second
.path
);
1089 allPaths
.insert(i
->second
.path
);
1092 /* Determine the full set of input paths. */
1094 /* First, the input derivations. */
1095 foreach (DerivationInputs::iterator
, i
, drv
.inputDrvs
) {
1096 /* Add the relevant output closures of the input derivation
1097 `*i' as input paths. Only add the closures of output paths
1098 that are specified as inputs. */
1099 assert(worker
.store
.isValidPath(i
->first
));
1100 Derivation inDrv
= derivationFromPath(worker
.store
, i
->first
);
1101 foreach (StringSet::iterator
, j
, i
->second
)
1102 if (inDrv
.outputs
.find(*j
) != inDrv
.outputs
.end())
1103 computeFSClosure(worker
.store
, inDrv
.outputs
[*j
].path
, inputPaths
);
1106 format("derivation `%1%' requires non-existent output `%2%' from input derivation `%3%'")
1107 % drvPath
% *j
% i
->first
);
1110 /* Second, the input sources. */
1111 foreach (PathSet::iterator
, i
, drv
.inputSrcs
)
1112 computeFSClosure(worker
.store
, *i
, inputPaths
);
1114 debug(format("added input paths %1%") % showPaths(inputPaths
));
1116 allPaths
.insert(inputPaths
.begin(), inputPaths
.end());
1118 /* Is this a fixed-output derivation? */
1120 for (auto & i
: drv
.outputs
)
1121 if (i
.second
.hash
== "") fixedOutput
= false;
1123 /* Don't repeat fixed-output derivations since they're already
1124 verified by their output hash.*/
1125 nrRounds
= fixedOutput
? 1 : settings
.get("build-repeat", 0) + 1;
1127 /* Okay, try to build. Note that here we don't wait for a build
1128 slot to become available, since we don't need one if there is a
1130 state
= &DerivationGoal::tryToBuild
;
1131 worker
.wakeUp(shared_from_this());
1135 static bool canBuildLocally(const string
& platform
)
1137 return platform
== settings
.thisSystem
1139 || (platform
== "i686-linux" && settings
.thisSystem
== "x86_64-linux")
1140 || (platform
== "armhf-linux" && settings
.thisSystem
== "aarch64-linux")
1146 static string
get(const StringPairs
& map
, const string
& key
, const string
& def
= "")
1148 StringPairs::const_iterator i
= map
.find(key
);
1149 return i
== map
.end() ? def
: i
->second
;
1153 bool willBuildLocally(const Derivation
& drv
)
1155 return get(drv
.env
, "preferLocalBuild") == "1" && canBuildLocally(drv
.platform
);
1159 bool substitutesAllowed(const Derivation
& drv
)
1161 return get(drv
.env
, "allowSubstitutes", "1") == "1";
1165 void DerivationGoal::tryToBuild()
1167 trace("trying to build");
1169 /* Check for the possibility that some other goal in this process
1170 has locked the output since we checked in haveDerivation().
1171 (It can't happen between here and the lockPaths() call below
1172 because we're not allowing multi-threading.) If so, put this
1173 goal to sleep until another goal finishes, then try again. */
1174 foreach (DerivationOutputs::iterator
, i
, drv
.outputs
)
1175 if (pathIsLockedByMe(i
->second
.path
)) {
1176 debug(format("putting derivation `%1%' to sleep because `%2%' is locked by another goal")
1177 % drvPath
% i
->second
.path
);
1178 worker
.waitForAnyGoal(shared_from_this());
1182 /* Obtain locks on all output paths. The locks are automatically
1183 released when we exit this function or the client crashes. If we
1184 can't acquire the lock, then continue; hopefully some other
1185 goal can start a build, and if not, the main loop will sleep a
1186 few seconds and then retry this goal. */
1187 if (!outputLocks
.lockPaths(outputPaths(drv
), "", false)) {
1188 worker
.waitForAWhile(shared_from_this());
1192 /* Now check again whether the outputs are valid. This is because
1193 another process may have started building in parallel. After
1194 it has finished and released the locks, we can (and should)
1195 reuse its results. (Strictly speaking the first check can be
1196 omitted, but that would be less efficient.) Note that since we
1197 now hold the locks on the output paths, no other process can
1198 build this derivation, so no further checks are necessary. */
1199 validPaths
= checkPathValidity(true, buildMode
== bmRepair
);
1200 if (buildMode
!= bmCheck
&& validPaths
.size() == drv
.outputs
.size()) {
1201 debug(format("skipping build of derivation `%1%', someone beat us to it") % drvPath
);
1202 outputLocks
.setDeletion(true);
1203 done(BuildResult::AlreadyValid
);
1207 missingPaths
= outputPaths(drv
);
1208 if (buildMode
!= bmCheck
)
1209 foreach (PathSet::iterator
, i
, validPaths
) missingPaths
.erase(*i
);
1211 /* If any of the outputs already exist but are not valid, delete
1213 foreach (DerivationOutputs::iterator
, i
, drv
.outputs
) {
1214 Path path
= i
->second
.path
;
1215 if (worker
.store
.isValidPath(path
)) continue;
1216 if (!pathExists(path
)) continue;
1217 debug(format("removing invalid path `%1%'") % path
);
1221 /* Check again whether any output previously failed to build,
1222 because some other process may have tried and failed before we
1223 acquired the lock. */
1224 foreach (DerivationOutputs::iterator
, i
, drv
.outputs
)
1225 if (pathFailed(i
->second
.path
)) return;
1227 /* Don't do a remote build if the derivation has the attribute
1228 `preferLocalBuild' set. Also, check and repair modes are only
1229 supported for local builds. */
1230 bool buildLocally
= buildMode
!= bmNormal
|| willBuildLocally(drv
);
1232 /* Is the build hook willing to accept this job? */
1233 if (!buildLocally
) {
1234 switch (tryBuildHook()) {
1236 /* Yes, it has started doing so. Wait until we get
1237 EOF from the hook. */
1238 state
= &DerivationGoal::buildDone
;
1241 /* Not now; wait until at least one child finishes or
1242 the wake-up timeout expires. */
1243 worker
.waitForAWhile(shared_from_this());
1244 outputLocks
.unlock();
1247 /* We should do it ourselves. */
1252 /* Make sure that we are allowed to start a build. If this
1253 derivation prefers to be done locally, do it even if
1254 maxBuildJobs is 0. */
1255 unsigned int curBuilds
= worker
.getNrLocalBuilds();
1256 if (curBuilds
>= settings
.maxBuildJobs
&& !(buildLocally
&& curBuilds
== 0)) {
1257 worker
.waitForBuildSlot(shared_from_this());
1258 outputLocks
.unlock();
1264 /* Okay, we have to build. */
1267 } catch (BuildError
& e
) {
1268 printMsg(lvlError
, e
.msg());
1269 outputLocks
.unlock();
1270 buildUser
.release();
1271 if (settings
.printBuildTrace
)
1272 printMsg(lvlError
, format("@ build-failed %1% - %2% %3%")
1273 % drvPath
% 0 % e
.msg());
1274 worker
.permanentFailure
= true;
1275 done(BuildResult::InputRejected
, e
.msg());
1279 /* This state will be reached when we get EOF on the child's
1281 state
= &DerivationGoal::buildDone
;
1285 void replaceValidPath(const Path
& storePath
, const Path tmpPath
)
1287 /* We can't atomically replace storePath (the original) with
1288 tmpPath (the replacement), so we have to move it out of the
1289 way first. We'd better not be interrupted here, because if
1290 we're repairing (say) Glibc, we end up with a broken system. */
1291 Path oldPath
= (format("%1%.old-%2%-%3%") % storePath
% getpid() % rand()).str();
1292 if (pathExists(storePath
))
1293 rename(storePath
.c_str(), oldPath
.c_str());
1294 if (rename(tmpPath
.c_str(), storePath
.c_str()) == -1)
1295 throw SysError(format("moving `%1%' to `%2%'") % tmpPath
% storePath
);
1296 if (pathExists(oldPath
))
1297 deletePath(oldPath
);
1301 MakeError(NotDeterministic
, BuildError
)
1304 void DerivationGoal::buildDone()
1306 trace("build done");
1308 /* Since we got an EOF on the logger pipe, the builder is presumed
1309 to have terminated. In fact, the builder could also have
1310 simply have closed its end of the pipe --- just don't do that
1315 savedPid
= hook
->pid
;
1316 status
= hook
->pid
.wait(true);
1318 /* !!! this could block! security problem! solution: kill the
1321 status
= pid
.wait(true);
1324 debug(format("builder process for `%1%' finished") % drvPath
);
1326 /* So the child is gone now. */
1327 worker
.childTerminated(savedPid
);
1329 /* Close the read side of the logger pipe. */
1331 hook
->builderOut
.readSide
.close();
1332 hook
->fromAgent
.readSide
.close();
1334 else builderOut
.readSide
.close();
1336 /* Close the log file. */
1339 /* When running under a build user, make sure that all processes
1340 running under that uid are gone. This is to prevent a
1341 malicious user from leaving behind a process that keeps files
1342 open and modifies them after they have been chown'ed to
1344 if (buildUser
.enabled()) buildUser
.kill();
1346 bool diskFull
= false;
1350 /* Check the exit status. */
1351 if (!statusOk(status
)) {
1353 /* Heuristically check whether the build failure may have
1354 been caused by a disk full condition. We have no way
1355 of knowing whether the build actually got an ENOSPC.
1356 So instead, check if the disk is (nearly) full now. If
1357 so, we don't mark this build as a permanent failure. */
1359 unsigned long long required
= 8ULL * 1024 * 1024; // FIXME: make configurable
1361 if (statvfs(settings
.nixStore
.c_str(), &st
) == 0 &&
1362 (unsigned long long) st
.f_bavail
* st
.f_bsize
< required
)
1364 if (statvfs(tmpDir
.c_str(), &st
) == 0 &&
1365 (unsigned long long) st
.f_bavail
* st
.f_bsize
< required
)
1369 deleteTmpDir(false);
1371 /* Move paths out of the chroot for easier debugging of
1373 if (useChroot
&& buildMode
== bmNormal
)
1374 foreach (PathSet::iterator
, i
, missingPaths
)
1375 if (pathExists(chrootRootDir
+ *i
))
1376 rename((chrootRootDir
+ *i
).c_str(), i
->c_str());
1379 printMsg(lvlError
, "note: build failure may have been caused by lack of free disk space");
1381 throw BuildError(format("builder for `%1%' %2%")
1382 % drvPath
% statusToString(status
));
1385 /* Compute the FS closure of the outputs and register them as
1389 /* Delete unused redirected outputs (when doing hash rewriting). */
1390 foreach (RedirectedOutputs::iterator
, i
, redirectedOutputs
)
1391 if (pathExists(i
->second
)) deletePath(i
->second
);
1393 /* Delete the chroot (if we were using one). */
1394 autoDelChroot
.reset(); /* this runs the destructor */
1398 /* Repeat the build if necessary. */
1399 if (curRound
++ < nrRounds
) {
1400 outputLocks
.unlock();
1401 buildUser
.release();
1402 state
= &DerivationGoal::tryToBuild
;
1403 worker
.wakeUp(shared_from_this());
1407 /* It is now safe to delete the lock files, since all future
1408 lockers will see that the output paths are valid; they will
1409 not create new lock files with the same names as the old
1410 (unlinked) lock files. */
1411 outputLocks
.setDeletion(true);
1412 outputLocks
.unlock();
1414 } catch (BuildError
& e
) {
1416 printMsg(lvlError
, e
.msg());
1417 outputLocks
.unlock();
1418 buildUser
.release();
1420 BuildResult::Status st
= BuildResult::MiscFailure
;
1422 if (hook
&& WIFEXITED(status
) && WEXITSTATUS(status
) == 101) {
1423 if (settings
.printBuildTrace
)
1424 printMsg(lvlError
, format("@ build-failed %1% - timeout") % drvPath
);
1425 st
= BuildResult::TimedOut
;
1428 else if (hook
&& (!WIFEXITED(status
) || WEXITSTATUS(status
) != 100)) {
1429 if (settings
.printBuildTrace
)
1430 printMsg(lvlError
, format("@ hook-failed %1% - %2% %3%")
1431 % drvPath
% status
% e
.msg());
1435 if (settings
.printBuildTrace
)
1436 printMsg(lvlError
, format("@ build-failed %1% - %2% %3%")
1437 % drvPath
% 1 % e
.msg());
1440 statusOk(status
) ? BuildResult::OutputRejected
:
1441 fixedOutput
|| diskFull
? BuildResult::TransientFailure
:
1442 BuildResult::PermanentFailure
;
1444 /* Register the outputs of this build as "failed" so we
1445 won't try to build them again (negative caching).
1446 However, don't do this for fixed-output derivations,
1447 since they're likely to fail for transient reasons
1448 (e.g., fetchurl not being able to access the network).
1449 Hook errors (like communication problems with the
1450 remote machine) shouldn't be cached either. */
1451 if (settings
.cacheFailure
&& !fixedOutput
&& !diskFull
)
1452 foreach (DerivationOutputs::iterator
, i
, drv
.outputs
)
1453 worker
.store
.registerFailedPath(i
->second
.path
);
1460 /* Release the build user, if applicable. */
1461 buildUser
.release();
1463 if (settings
.printBuildTrace
)
1464 printMsg(lvlError
, format("@ build-succeeded %1% -") % drvPath
);
1466 done(BuildResult::Built
);
1470 HookReply
DerivationGoal::tryBuildHook()
1472 if (!settings
.useBuildHook
) return rpDecline
;
1477 settings
.thisSystem
.c_str(),
1478 (format("%1%") % settings
.maxSilentTime
).str().c_str(),
1479 (format("%1%") % settings
.printBuildTrace
).str().c_str(),
1480 (format("%1%") % settings
.buildTimeout
).str().c_str()
1483 worker
.hook
= std::make_shared
<Agent
>(settings
.guixProgram
, args
);
1486 /* Tell the hook about system features (beyond the system type)
1487 required from the build machine. (The hook could parse the
1488 drv file itself, but this is easier.) */
1489 Strings features
= tokenizeString
<Strings
>(get(drv
.env
, "requiredSystemFeatures"));
1490 foreach (Strings::iterator
, i
, features
) checkStoreName(*i
); /* !!! abuse */
1492 /* Send the request to the hook. */
1493 writeLine(worker
.hook
->toAgent
.writeSide
, (format("%1% %2% %3% %4%")
1494 % (worker
.getNrLocalBuilds() < settings
.maxBuildJobs
? "1" : "0")
1495 % drv
.platform
% drvPath
% concatStringsSep(",", features
)).str());
1497 /* Read the first line of input, which should be a word indicating
1498 whether the hook wishes to perform the build. */
1501 string s
= readLine(worker
.hook
->fromAgent
.readSide
);
1502 if (string(s
, 0, 2) == "# ") {
1503 reply
= string(s
, 2);
1510 debug(format("hook reply is `%1%'") % reply
);
1512 if (reply
== "decline" || reply
== "postpone")
1513 return reply
== "decline" ? rpDecline
: rpPostpone
;
1514 else if (reply
!= "accept")
1515 throw Error(format("bad hook reply `%1%'") % reply
);
1517 printMsg(lvlTalkative
, format("using hook to build path(s) %1%") % showPaths(missingPaths
));
1520 worker
.hook
.reset();
1522 /* Tell the hook all the inputs that have to be copied to the
1523 remote system. This unfortunately has to contain the entire
1524 derivation closure to ensure that the validity invariant holds
1525 on the remote system. (I.e., it's unfortunate that we have to
1526 list it since the remote system *probably* already has it.) */
1528 allInputs
.insert(inputPaths
.begin(), inputPaths
.end());
1529 computeFSClosure(worker
.store
, drvPath
, allInputs
);
1532 foreach (PathSet::iterator
, i
, allInputs
) { s
+= *i
; s
+= ' '; }
1533 writeLine(hook
->toAgent
.writeSide
, s
);
1535 /* Tell the hooks the missing outputs that have to be copied back
1536 from the remote system. */
1538 foreach (PathSet::iterator
, i
, missingPaths
) { s
+= *i
; s
+= ' '; }
1539 writeLine(hook
->toAgent
.writeSide
, s
);
1541 hook
->toAgent
.writeSide
.close();
1543 /* Create the log file and pipe. */
1544 Path logFile
= openLogFile();
1547 fds
.insert(hook
->fromAgent
.readSide
);
1548 fds
.insert(hook
->builderOut
.readSide
);
1549 worker
.childStarted(shared_from_this(), hook
->pid
, fds
, false, true);
1551 if (settings
.printBuildTrace
)
1552 printMsg(lvlError
, format("@ build-started %1% - %2% %3% %4%")
1553 % drvPath
% drv
.platform
% logFile
% hook
->pid
);
1559 void chmod_(const Path
& path
, mode_t mode
)
1561 if (chmod(path
.c_str(), mode
) == -1)
1562 throw SysError(format("setting permissions on `%1%'") % path
);
1566 int childEntry(void * arg
)
1568 ((DerivationGoal
*) arg
)->runChild();
1573 void DerivationGoal::startBuilder()
1576 buildMode
== bmRepair
? "repairing path(s) %1%" :
1577 buildMode
== bmCheck
? "checking path(s) %1%" :
1578 nrRounds
> 1 ? "building path(s) %1% (round %2%/%3%)" :
1579 "building path(s) %1%");
1580 f
.exceptions(boost::io::all_error_bits
^ boost::io::too_many_args_bit
);
1581 startNest(nest
, lvlInfo
, f
% showPaths(missingPaths
) % curRound
% nrRounds
);
1583 /* Note: built-in builders are *not* running in a chroot environment so
1584 that we can easily implement them in Guile without having it as a
1585 derivation input (they are running under a separate build user,
1587 useChroot
= settings
.useChroot
&& !isBuiltin(drv
);
1589 /* Construct the environment passed to the builder. */
1592 /* Most shells initialise PATH to some default (/bin:/usr/bin:...) when
1593 PATH is not set. We don't want this, so we fill it in with some dummy
1595 env
["PATH"] = "/path-not-set";
1597 /* Set HOME to a non-existing path to prevent certain programs from using
1598 /etc/passwd (or NIS, or whatever) to locate the home directory (for
1599 example, wget looks for ~/.wgetrc). I.e., these tools use /etc/passwd
1600 if HOME is not set, but they will just assume that the settings file
1601 they are looking for does not exist if HOME is set but points to some
1602 non-existing path. */
1603 Path homeDir
= "/homeless-shelter";
1604 env
["HOME"] = homeDir
;
1606 /* Tell the builder where the store is. Usually they
1607 shouldn't care, but this is useful for purity checking (e.g.,
1608 the compiler or linker might only want to accept paths to files
1609 in the store or in the build directory). */
1610 env
["NIX_STORE"] = settings
.nixStore
;
1612 /* The maximum number of cores to utilize for parallel building. */
1613 env
["NIX_BUILD_CORES"] = (format("%d") % settings
.buildCores
).str();
1615 /* Add all bindings specified in the derivation. */
1616 foreach (StringPairs::iterator
, i
, drv
.env
)
1617 env
[i
->first
] = i
->second
;
1619 /* Create a temporary directory where the build will take
1621 auto drvName
= storePathToName(drvPath
);
1622 tmpDir
= createTempDir("", "guix-build-" + drvName
, false, false, 0700);
1624 /* In a sandbox, for determinism, always use the same temporary
1626 tmpDirInSandbox
= useChroot
? canonPath("/tmp", true) + "/guix-build-" + drvName
+ "-0" : tmpDir
;
1628 /* For convenience, set an environment pointing to the top build
1630 env
["NIX_BUILD_TOP"] = tmpDirInSandbox
;
1632 /* Also set TMPDIR and variants to point to this directory. */
1633 env
["TMPDIR"] = env
["TEMPDIR"] = env
["TMP"] = env
["TEMP"] = tmpDirInSandbox
;
1635 /* Explicitly set PWD to prevent problems with chroot builds. In
1636 particular, dietlibc cannot figure out the cwd because the
1637 inode of the current directory doesn't appear in .. (because
1638 getdents returns the inode of the mount point). */
1639 env
["PWD"] = tmpDirInSandbox
;
1641 /* *Only* if this is a fixed-output derivation, propagate the
1642 values of the environment variables specified in the
1643 `impureEnvVars' attribute to the builder. This allows for
1644 instance environment variables for proxy configuration such as
1645 `http_proxy' to be easily passed to downloaders like
1646 `fetchurl'. Passing such environment variables from the caller
1647 to the builder is generally impure, but the output of
1648 fixed-output derivations is by definition pure (since we
1649 already know the cryptographic hash of the output). */
1651 Strings varNames
= tokenizeString
<Strings
>(get(drv
.env
, "impureEnvVars"));
1652 foreach (Strings::iterator
, i
, varNames
) env
[*i
] = getEnv(*i
);
1655 /* The `exportReferencesGraph' feature allows the references graph
1656 to be passed to a builder. This attribute should be a list of
1657 pairs [name1 path1 name2 path2 ...]. The references graph of
1658 each `pathN' will be stored in a text file `nameN' in the
1659 temporary build directory. The text files have the format used
1660 by `nix-store --register-validity'. However, the deriver
1661 fields are left empty. */
1662 string s
= get(drv
.env
, "exportReferencesGraph");
1663 Strings ss
= tokenizeString
<Strings
>(s
);
1664 if (ss
.size() % 2 != 0)
1665 throw BuildError(format("odd number of tokens in `exportReferencesGraph': `%1%'") % s
);
1666 for (Strings::iterator i
= ss
.begin(); i
!= ss
.end(); ) {
1667 string fileName
= *i
++;
1668 checkStoreName(fileName
); /* !!! abuse of this function */
1670 /* Check that the store path is valid. */
1671 Path storePath
= *i
++;
1672 if (!isInStore(storePath
))
1673 throw BuildError(format("`exportReferencesGraph' contains a non-store path `%1%'")
1675 storePath
= toStorePath(storePath
);
1676 if (!worker
.store
.isValidPath(storePath
))
1677 throw BuildError(format("`exportReferencesGraph' contains an invalid path `%1%'")
1680 /* If there are derivations in the graph, then include their
1681 outputs as well. This is useful if you want to do things
1682 like passing all build-time dependencies of some path to a
1683 derivation that builds a NixOS DVD image. */
1684 PathSet paths
, paths2
;
1685 computeFSClosure(worker
.store
, storePath
, paths
);
1688 foreach (PathSet::iterator
, j
, paths2
) {
1689 if (isDerivation(*j
)) {
1690 Derivation drv
= derivationFromPath(worker
.store
, *j
);
1691 foreach (DerivationOutputs::iterator
, k
, drv
.outputs
)
1692 computeFSClosure(worker
.store
, k
->second
.path
, paths
);
1696 /* Write closure info to `fileName'. */
1697 writeFile(tmpDir
+ "/" + fileName
,
1698 worker
.store
.makeValidityRegistration(paths
, false, false));
1702 /* If `build-users-group' is not empty, then we have to build as
1703 one of the members of that group. */
1704 if (settings
.buildUsersGroup
!= "") {
1705 buildUser
.acquire();
1706 assert(buildUser
.getUID() != 0);
1707 assert(buildUser
.getGID() != 0);
1709 /* Make sure that no other processes are executing under this
1713 /* Change ownership of the temporary build directory. */
1714 if (chown(tmpDir
.c_str(), buildUser
.getUID(), buildUser
.getGID()) == -1)
1715 throw SysError(format("cannot change ownership of '%1%'") % tmpDir
);
1720 /* Create a temporary directory in which we set up the chroot
1721 environment using bind-mounts. We put it in the store
1722 to ensure that we can create hard-links to non-directory
1723 inputs in the fake store in the chroot (see below). */
1724 chrootRootDir
= drvPath
+ ".chroot";
1725 if (pathExists(chrootRootDir
)) deletePath(chrootRootDir
);
1727 /* Clean up the chroot directory automatically. */
1728 autoDelChroot
= std::shared_ptr
<AutoDelete
>(new AutoDelete(chrootRootDir
));
1730 printMsg(lvlChatty
, format("setting up chroot environment in `%1%'") % chrootRootDir
);
1732 if (mkdir(chrootRootDir
.c_str(), 0750) == -1)
1733 throw SysError(format("cannot create ‘%1%’") % chrootRootDir
);
1735 if (chown(chrootRootDir
.c_str(), 0, buildUser
.getGID()) == -1)
1736 throw SysError(format("cannot change ownership of ‘%1%’") % chrootRootDir
);
1738 /* Create a writable /tmp in the chroot. Many builders need
1739 this. (Of course they should really respect $TMPDIR
1741 Path chrootTmpDir
= chrootRootDir
+ "/tmp";
1742 createDirs(chrootTmpDir
);
1743 chmod_(chrootTmpDir
, 01777);
1745 /* Create a /etc/passwd with entries for the build user and the
1746 nobody account. The latter is kind of a hack to support
1748 createDirs(chrootRootDir
+ "/etc");
1750 writeFile(chrootRootDir
+ "/etc/passwd",
1752 "nixbld:x:%1%:%2%:Nix build user:/:/noshell\n"
1753 "nobody:x:65534:65534:Nobody:/:/noshell\n")
1754 % (buildUser
.enabled() ? buildUser
.getUID() : getuid())
1755 % (buildUser
.enabled() ? buildUser
.getGID() : getgid())).str());
1757 /* Declare the build user's group so that programs get a consistent
1758 view of the system (e.g., "id -gn"). */
1759 writeFile(chrootRootDir
+ "/etc/group",
1760 (format("nixbld:!:%1%:\n")
1761 % (buildUser
.enabled() ? buildUser
.getGID() : getgid())).str());
1763 /* Create /etc/hosts with localhost entry. */
1765 writeFile(chrootRootDir
+ "/etc/hosts", "127.0.0.1 localhost\n");
1767 /* Bind-mount a user-configurable set of directories from the
1768 host file system. */
1769 PathSet dirs
= tokenizeString
<StringSet
>(settings
.get("build-chroot-dirs", string(DEFAULT_CHROOT_DIRS
)));
1770 PathSet dirs2
= tokenizeString
<StringSet
>(settings
.get("build-extra-chroot-dirs", string("")));
1771 dirs
.insert(dirs2
.begin(), dirs2
.end());
1772 for (auto & i
: dirs
) {
1773 size_t p
= i
.find('=');
1774 if (p
== string::npos
)
1775 dirsInChroot
[i
] = i
;
1777 dirsInChroot
[string(i
, 0, p
)] = string(i
, p
+ 1);
1779 dirsInChroot
[tmpDirInSandbox
] = tmpDir
;
1781 /* Make the closure of the inputs available in the chroot,
1782 rather than the whole store. This prevents any access
1783 to undeclared dependencies. Directories are bind-mounted,
1784 while other inputs are hard-linked (since only directories
1785 can be bind-mounted). !!! As an extra security
1786 precaution, make the fake store only writable by the
1788 Path chrootStoreDir
= chrootRootDir
+ settings
.nixStore
;
1789 createDirs(chrootStoreDir
);
1790 chmod_(chrootStoreDir
, 01775);
1792 if (chown(chrootStoreDir
.c_str(), 0, buildUser
.getGID()) == -1)
1793 throw SysError(format("cannot change ownership of ‘%1%’") % chrootStoreDir
);
1795 foreach (PathSet::iterator
, i
, inputPaths
) {
1797 if (lstat(i
->c_str(), &st
))
1798 throw SysError(format("getting attributes of path `%1%'") % *i
);
1799 if (S_ISDIR(st
.st_mode
))
1800 dirsInChroot
[*i
] = *i
;
1802 Path p
= chrootRootDir
+ *i
;
1803 if (link(i
->c_str(), p
.c_str()) == -1) {
1804 /* Hard-linking fails if we exceed the maximum
1805 link count on a file (e.g. 32000 of ext3),
1806 which is quite possible after a `nix-store
1808 if (errno
!= EMLINK
)
1809 throw SysError(format("linking `%1%' to `%2%'") % p
% *i
);
1812 StringSource
source(sink
.s
);
1813 restorePath(p
, source
);
1816 regularInputPaths
.insert(*i
);
1820 /* If we're repairing, checking or rebuilding part of a
1821 multiple-outputs derivation, it's possible that we're
1822 rebuilding a path that is in settings.dirsInChroot
1823 (typically the dependencies of /bin/sh). Throw them
1825 for (auto & i
: drv
.outputs
)
1826 dirsInChroot
.erase(i
.second
.path
);
1829 throw Error("chroot builds are not supported on this platform");
1835 if (pathExists(homeDir
))
1836 throw Error(format("directory `%1%' exists; please remove it") % homeDir
);
1838 /* We're not doing a chroot build, but we have some valid
1839 output paths. Since we can't just overwrite or delete
1840 them, we have to do hash rewriting: i.e. in the
1841 environment/arguments passed to the build, we replace the
1842 hashes of the valid outputs with unique dummy strings;
1843 after the build, we discard the redirected outputs
1844 corresponding to the valid outputs, and rewrite the
1845 contents of the new outputs to replace the dummy strings
1846 with the actual hashes. */
1847 if (validPaths
.size() > 0)
1848 foreach (PathSet::iterator
, i
, validPaths
)
1851 /* If we're repairing, then we don't want to delete the
1852 corrupt outputs in advance. So rewrite them as well. */
1853 if (buildMode
== bmRepair
)
1854 foreach (PathSet::iterator
, i
, missingPaths
)
1855 if (worker
.store
.isValidPath(*i
) && pathExists(*i
)) {
1857 redirectedBadOutputs
.insert(*i
);
1862 /* Run the builder. */
1863 printMsg(lvlChatty
, format("executing builder `%1%'") % drv
.builder
);
1865 /* Create the log file. */
1866 Path logFile
= openLogFile();
1868 /* Create a pipe to get the output of the builder. */
1869 builderOut
.create();
1871 /* Fork a child to build the package. Note that while we
1872 currently use forks to run and wait for the children, it
1873 shouldn't be hard to use threads for this on systems where
1874 fork() is unavailable or inefficient.
1876 If we're building in a chroot, then also set up private
1877 namespaces for the build:
1879 - The PID namespace causes the build to start as PID 1.
1880 Processes outside of the chroot are not visible to those on
1881 the inside, but processes inside the chroot are visible from
1882 the outside (though with different PIDs).
1884 - The private mount namespace ensures that all the bind mounts
1885 we do will only show up in this process and its children, and
1886 will disappear automatically when we're done.
1888 - The private network namespace ensures that the builder cannot
1889 talk to the outside world (or vice versa). It only has a
1890 private loopback interface.
1892 - The IPC namespace prevents the builder from communicating
1893 with outside processes using SysV IPC mechanisms (shared
1894 memory, message queues, semaphores). It also ensures that
1895 all IPC objects are destroyed when the builder exits.
1897 - The UTS namespace ensures that builders see a hostname of
1898 localhost rather than the actual hostname.
1902 char stack
[32 * 1024];
1903 int flags
= CLONE_NEWPID
| CLONE_NEWNS
| CLONE_NEWIPC
| CLONE_NEWUTS
| SIGCHLD
;
1904 if (!fixedOutput
) flags
|= CLONE_NEWNET
;
1905 /* Ensure proper alignment on the stack. On aarch64, it has to be 16
1907 pid
= clone(childEntry
,
1908 (char *)(((uintptr_t)stack
+ sizeof(stack
) - 8) & ~(uintptr_t)0xf),
1911 throw SysError("cloning builder process");
1916 if (pid
== 0) runChild();
1919 if (pid
== -1) throw SysError("unable to fork");
1922 pid
.setSeparatePG(true);
1923 builderOut
.writeSide
.close();
1924 worker
.childStarted(shared_from_this(), pid
,
1925 singleton
<set
<int> >(builderOut
.readSide
), true, true);
1927 /* Check if setting up the build environment failed. */
1928 string msg
= readLine(builderOut
.readSide
);
1929 if (!msg
.empty()) throw Error(msg
);
1931 if (settings
.printBuildTrace
) {
1932 printMsg(lvlError
, format("@ build-started %1% - %2% %3% %4%")
1933 % drvPath
% drv
.platform
% logFile
% pid
);
1938 /* Return true if the operating system kernel part of SYSTEM1 and SYSTEM2 (the
1939 bit that comes after the hyphen in system types such as "i686-linux") is
1941 static bool sameOperatingSystemKernel(const std::string
& system1
, const std::string
& system2
)
1943 auto os1
= system1
.substr(system1
.find("-"));
1944 auto os2
= system2
.substr(system2
.find("-"));
1948 void DerivationGoal::runChild()
1950 /* Warning: in the child we should absolutely not make any SQLite
1959 commonChildInit(builderOut
);
1963 /* Initialise the loopback interface. */
1964 AutoCloseFD
fd(socket(PF_INET
, SOCK_DGRAM
, IPPROTO_IP
));
1965 if (fd
== -1) throw SysError("cannot open IP socket");
1968 strcpy(ifr
.ifr_name
, "lo");
1969 ifr
.ifr_flags
= IFF_UP
| IFF_LOOPBACK
| IFF_RUNNING
;
1970 if (ioctl(fd
, SIOCSIFFLAGS
, &ifr
) == -1)
1971 throw SysError("cannot set loopback interface flags");
1975 /* Set the hostname etc. to fixed values. */
1976 char hostname
[] = "localhost";
1977 if (sethostname(hostname
, sizeof(hostname
)) == -1)
1978 throw SysError("cannot set host name");
1979 char domainname
[] = "(none)"; // kernel default
1980 if (setdomainname(domainname
, sizeof(domainname
)) == -1)
1981 throw SysError("cannot set domain name");
1983 /* Make all filesystems private. This is necessary
1984 because subtrees may have been mounted as "shared"
1985 (MS_SHARED). (Systemd does this, for instance.) Even
1986 though we have a private mount namespace, mounting
1987 filesystems on top of a shared subtree still propagates
1988 outside of the namespace. Making a subtree private is
1989 local to the namespace, though, so setting MS_PRIVATE
1990 does not affect the outside world. */
1991 if (mount(0, "/", 0, MS_REC
|MS_PRIVATE
, 0) == -1) {
1992 throw SysError("unable to make ‘/’ private mount");
1995 /* Bind-mount chroot directory to itself, to treat it as a
1996 different filesystem from /, as needed for pivot_root. */
1997 if (mount(chrootRootDir
.c_str(), chrootRootDir
.c_str(), 0, MS_BIND
, 0) == -1)
1998 throw SysError(format("unable to bind mount ‘%1%’") % chrootRootDir
);
2000 /* Set up a nearly empty /dev, unless the user asked to
2001 bind-mount the host /dev. */
2003 if (dirsInChroot
.find("/dev") == dirsInChroot
.end()) {
2004 createDirs(chrootRootDir
+ "/dev/shm");
2005 createDirs(chrootRootDir
+ "/dev/pts");
2006 ss
.push_back("/dev/full");
2008 if (pathExists("/dev/kvm"))
2009 ss
.push_back("/dev/kvm");
2011 ss
.push_back("/dev/null");
2012 ss
.push_back("/dev/random");
2013 ss
.push_back("/dev/tty");
2014 ss
.push_back("/dev/urandom");
2015 ss
.push_back("/dev/zero");
2016 createSymlink("/proc/self/fd", chrootRootDir
+ "/dev/fd");
2017 createSymlink("/proc/self/fd/0", chrootRootDir
+ "/dev/stdin");
2018 createSymlink("/proc/self/fd/1", chrootRootDir
+ "/dev/stdout");
2019 createSymlink("/proc/self/fd/2", chrootRootDir
+ "/dev/stderr");
2022 /* Fixed-output derivations typically need to access the
2023 network, so give them access to /etc/resolv.conf and so
2026 ss
.push_back("/etc/resolv.conf");
2027 ss
.push_back("/etc/nsswitch.conf");
2028 ss
.push_back("/etc/services");
2029 ss
.push_back("/etc/hosts");
2032 for (auto & i
: ss
) dirsInChroot
[i
] = i
;
2034 /* Bind-mount all the directories from the "host"
2035 filesystem that we want in the chroot
2037 foreach (DirsInChroot::iterator
, i
, dirsInChroot
) {
2039 Path source
= i
->second
;
2040 Path target
= chrootRootDir
+ i
->first
;
2041 if (source
== "/proc") continue; // backwards compatibility
2042 debug(format("bind mounting `%1%' to `%2%'") % source
% target
);
2043 if (stat(source
.c_str(), &st
) == -1)
2044 throw SysError(format("getting attributes of path `%1%'") % source
);
2045 if (S_ISDIR(st
.st_mode
))
2048 createDirs(dirOf(target
));
2049 writeFile(target
, "");
2051 if (mount(source
.c_str(), target
.c_str(), "", MS_BIND
, 0) == -1)
2052 throw SysError(format("bind mount from `%1%' to `%2%' failed") % source
% target
);
2055 /* Bind a new instance of procfs on /proc to reflect our
2056 private PID namespace. */
2057 createDirs(chrootRootDir
+ "/proc");
2058 if (mount("none", (chrootRootDir
+ "/proc").c_str(), "proc", 0, 0) == -1)
2059 throw SysError("mounting /proc");
2061 /* Mount a new tmpfs on /dev/shm to ensure that whatever
2062 the builder puts in /dev/shm is cleaned up automatically. */
2063 if (pathExists("/dev/shm") && mount("none", (chrootRootDir
+ "/dev/shm").c_str(), "tmpfs", 0, 0) == -1)
2064 throw SysError("mounting /dev/shm");
2066 /* Mount a new devpts on /dev/pts. Note that this
2067 requires the kernel to be compiled with
2068 CONFIG_DEVPTS_MULTIPLE_INSTANCES=y (which is the case
2069 if /dev/ptx/ptmx exists). */
2070 if (pathExists("/dev/pts/ptmx") &&
2071 !pathExists(chrootRootDir
+ "/dev/ptmx")
2072 && dirsInChroot
.find("/dev/pts") == dirsInChroot
.end())
2074 if (mount("none", (chrootRootDir
+ "/dev/pts").c_str(), "devpts", 0, "newinstance,mode=0620") == -1)
2075 throw SysError("mounting /dev/pts");
2076 createSymlink("/dev/pts/ptmx", chrootRootDir
+ "/dev/ptmx");
2078 /* Make sure /dev/pts/ptmx is world-writable. With some
2079 Linux versions, it is created with permissions 0. */
2080 chmod_(chrootRootDir
+ "/dev/pts/ptmx", 0666);
2083 /* Do the chroot(). */
2084 if (chdir(chrootRootDir
.c_str()) == -1)
2085 throw SysError(format("cannot change directory to '%1%'") % chrootRootDir
);
2087 if (mkdir("real-root", 0) == -1)
2088 throw SysError("cannot create real-root directory");
2090 if (pivot_root(".", "real-root") == -1)
2091 throw SysError(format("cannot pivot old root directory onto '%1%'") % (chrootRootDir
+ "/real-root"));
2093 if (chroot(".") == -1)
2094 throw SysError(format("cannot change root directory to '%1%'") % chrootRootDir
);
2096 if (umount2("real-root", MNT_DETACH
) == -1)
2097 throw SysError("cannot unmount real root filesystem");
2099 if (rmdir("real-root") == -1)
2100 throw SysError("cannot remove real-root directory");
2104 if (chdir(tmpDirInSandbox
.c_str()) == -1)
2105 throw SysError(format("changing into `%1%'") % tmpDir
);
2107 /* Close all other file descriptors. */
2108 closeMostFDs(set
<int>());
2111 /* Change the personality to 32-bit if we're doing an
2112 i686-linux build on an x86_64-linux machine. */
2113 struct utsname utsbuf
;
2115 if (drv
.platform
== "i686-linux" &&
2116 (settings
.thisSystem
== "x86_64-linux" ||
2117 (!strcmp(utsbuf
.sysname
, "Linux") && !strcmp(utsbuf
.machine
, "x86_64")))) {
2118 if (personality(PER_LINUX32
) == -1)
2119 throw SysError("cannot set i686-linux personality");
2122 if (drv
.platform
== "armhf-linux" &&
2123 (settings
.thisSystem
== "aarch64-linux" ||
2124 (!strcmp(utsbuf
.sysname
, "Linux") && !strcmp(utsbuf
.machine
, "aarch64")))) {
2125 if (personality(PER_LINUX32
) == -1)
2126 throw SysError("cannot set armhf-linux personality");
2129 /* Impersonate a Linux 2.6 machine to get some determinism in
2130 builds that depend on the kernel version. */
2131 if ((drv
.platform
== "i686-linux" || drv
.platform
== "x86_64-linux") && settings
.impersonateLinux26
) {
2132 int cur
= personality(0xffffffff);
2133 if (cur
!= -1) personality(cur
| 0x0020000 /* == UNAME26 */);
2136 /* Disable address space randomization for improved
2138 int cur
= personality(0xffffffff);
2139 if (cur
!= -1) personality(cur
| ADDR_NO_RANDOMIZE
);
2142 /* Fill in the environment. */
2144 foreach (Environment::const_iterator
, i
, env
)
2145 envStrs
.push_back(rewriteHashes(i
->first
+ "=" + i
->second
, rewritesToTmp
));
2147 /* If we are running in `build-users' mode, then switch to the
2148 user we allocated above. Make sure that we drop all root
2149 privileges. Note that above we have closed all file
2150 descriptors except std*, so that's safe. Also note that
2151 setuid() when run as root sets the real, effective and
2153 if (buildUser
.enabled()) {
2154 /* Preserve supplementary groups of the build user, to allow
2155 admins to specify groups such as "kvm". */
2156 if (setgroups(buildUser
.getSupplementaryGIDs().size(),
2157 buildUser
.getSupplementaryGIDs().data()) == -1)
2158 throw SysError("cannot set supplementary groups of build user");
2160 if (setgid(buildUser
.getGID()) == -1 ||
2161 getgid() != buildUser
.getGID() ||
2162 getegid() != buildUser
.getGID())
2163 throw SysError("setgid failed");
2165 if (setuid(buildUser
.getUID()) == -1 ||
2166 getuid() != buildUser
.getUID() ||
2167 geteuid() != buildUser
.getUID())
2168 throw SysError("setuid failed");
2173 /* Indicate that we managed to set up the build environment. */
2174 writeFull(STDERR_FILENO
, "\n");
2176 /* Execute the program. This should not return. */
2177 if (isBuiltin(drv
)) {
2181 auto buildDrv
= lookupBuiltinBuilder(drv
.builder
);
2182 if (buildDrv
!= NULL
) {
2183 /* Check what the output file name is. When doing a
2184 'bmCheck' build, the output file name is different from
2185 that specified in DRV due to hash rewriting. */
2186 Path output
= drv
.outputs
["out"].path
;
2187 auto redirected
= redirectedOutputs
.find(output
);
2188 if (redirected
!= redirectedOutputs
.end())
2189 output
= redirected
->second
;
2191 buildDrv(drv
, drvPath
, output
);
2194 throw Error(format("unsupported builtin function '%1%'") % string(drv
.builder
, 8));
2196 } catch (std::exception
& e
) {
2197 writeFull(STDERR_FILENO
, "error: " + string(e
.what()) + "\n");
2202 /* Fill in the arguments. */
2204 string builderBasename
= baseNameOf(drv
.builder
);
2205 args
.push_back(builderBasename
);
2206 foreach (Strings::iterator
, i
, drv
.args
)
2207 args
.push_back(rewriteHashes(*i
, rewritesToTmp
));
2209 /* If DRV targets the same operating system kernel, try to execute it:
2210 there might be binfmt_misc set up for user-land emulation of other
2211 architectures. However, if it targets a different operating
2212 system--e.g., "i586-gnu" vs. "x86_64-linux"--do not try executing
2213 it: the ELF file for that OS is likely indistinguishable from a
2214 native ELF binary and it would just crash at run time. */
2216 if (sameOperatingSystemKernel(drv
.platform
, settings
.thisSystem
)) {
2217 execve(drv
.builder
.c_str(), stringsToCharPtrs(args
).data(),
2218 stringsToCharPtrs(envStrs
).data());
2224 /* Right platform? Check this after we've tried 'execve' to allow for
2225 transparent emulation of different platforms with binfmt_misc
2226 handlers that invoke QEMU. */
2227 if (error
== ENOEXEC
&& !canBuildLocally(drv
.platform
)) {
2228 if (settings
.printBuildTrace
)
2229 printMsg(lvlError
, format("@ unsupported-platform %1% %2%") % drvPath
% drv
.platform
);
2231 format("a `%1%' is required to build `%3%', but I am a `%2%'")
2232 % drv
.platform
% settings
.thisSystem
% drvPath
);
2236 throw SysError(format("executing `%1%'") % drv
.builder
);
2238 } catch (std::exception
& e
) {
2239 writeFull(STDERR_FILENO
, "while setting up the build environment: " + string(e
.what()) + "\n");
2243 abort(); /* never reached */
2247 /* Parse a list of reference specifiers. Each element must either be
2248 a store path, or the symbolic name of the output of the derivation
2250 PathSet
parseReferenceSpecifiers(const Derivation
& drv
, string attr
)
2253 Paths paths
= tokenizeString
<Paths
>(attr
);
2254 foreach (Strings::iterator
, i
, paths
) {
2255 if (isStorePath(*i
))
2257 else if (drv
.outputs
.find(*i
) != drv
.outputs
.end())
2258 result
.insert(drv
.outputs
.find(*i
)->second
.path
);
2259 else throw BuildError(
2260 format("derivation contains an invalid reference specifier `%1%'")
2267 void DerivationGoal::registerOutputs()
2269 /* When using a build hook, the build hook can register the output
2270 as valid (by doing `nix-store --import'). If so we don't have
2271 to do anything here. */
2273 bool allValid
= true;
2274 foreach (DerivationOutputs::iterator
, i
, drv
.outputs
)
2275 if (!worker
.store
.isValidPath(i
->second
.path
)) allValid
= false;
2276 if (allValid
) return;
2279 ValidPathInfos infos
;
2281 /* Set of inodes seen during calls to canonicalisePathMetaData()
2282 for this build's outputs. This needs to be shared between
2283 outputs to allow hard links between outputs. */
2284 InodesSeen inodesSeen
;
2286 Path checkSuffix
= "-check";
2288 /* Check whether the output paths were created, and grep each
2289 output path to determine what other paths it references. Also make all
2290 output paths read-only. */
2291 foreach (DerivationOutputs::iterator
, i
, drv
.outputs
) {
2292 Path path
= i
->second
.path
;
2293 if (missingPaths
.find(path
) == missingPaths
.end()) continue;
2295 Path actualPath
= path
;
2297 actualPath
= chrootRootDir
+ path
;
2298 if (pathExists(actualPath
)) {
2299 /* Move output paths from the chroot to the store. */
2300 if (buildMode
== bmRepair
)
2301 replaceValidPath(path
, actualPath
);
2303 if (buildMode
!= bmCheck
&& rename(actualPath
.c_str(), path
.c_str()) == -1)
2304 throw SysError(format("moving build output `%1%' from the chroot to the store") % path
);
2306 if (buildMode
!= bmCheck
) actualPath
= path
;
2308 Path redirected
= redirectedOutputs
[path
];
2309 if (buildMode
== bmRepair
2310 && redirectedBadOutputs
.find(path
) != redirectedBadOutputs
.end()
2311 && pathExists(redirected
))
2312 replaceValidPath(path
, redirected
);
2313 if (buildMode
== bmCheck
&& redirected
!= "")
2314 actualPath
= redirected
;
2318 if (lstat(actualPath
.c_str(), &st
) == -1) {
2319 if (errno
== ENOENT
)
2321 format("builder for `%1%' failed to produce output path `%2%'")
2323 throw SysError(format("getting attributes of path `%1%'") % actualPath
);
2327 /* Check that the output is not group or world writable, as
2328 that means that someone else can have interfered with the
2329 build. Also, the output should be owned by the build
2331 if ((!S_ISLNK(st
.st_mode
) && (st
.st_mode
& (S_IWGRP
| S_IWOTH
))) ||
2332 (buildUser
.enabled() && st
.st_uid
!= buildUser
.getUID()))
2333 throw BuildError(format("suspicious ownership or permission on `%1%'; rejecting this build output") % path
);
2336 /* Apply hash rewriting if necessary. */
2337 bool rewritten
= false;
2338 if (!rewritesFromTmp
.empty()) {
2339 printMsg(lvlError
, format("warning: rewriting hashes in `%1%'; cross fingers") % path
);
2341 /* Canonicalise first. This ensures that the path we're
2342 rewriting doesn't contain a hard link to /etc/shadow or
2343 something like that. */
2344 canonicalisePathMetaData(actualPath
, buildUser
.enabled() ? buildUser
.getUID() : -1, inodesSeen
);
2346 /* FIXME: this is in-memory. */
2348 dumpPath(actualPath
, sink
);
2349 deletePath(actualPath
);
2350 sink
.s
= rewriteHashes(sink
.s
, rewritesFromTmp
);
2351 StringSource
source(sink
.s
);
2352 restorePath(actualPath
, source
);
2357 startNest(nest
, lvlTalkative
,
2358 format("scanning for references inside `%1%'") % path
);
2360 /* Check that fixed-output derivations produced the right
2361 outputs (i.e., the content hash should match the specified
2363 if (i
->second
.hash
!= "") {
2365 bool recursive
; HashType ht
; Hash h
;
2366 i
->second
.parseHashInfo(recursive
, ht
, h
);
2369 /* The output path should be a regular file without
2370 execute permission. */
2371 if (!S_ISREG(st
.st_mode
) || (st
.st_mode
& S_IXUSR
) != 0)
2373 format("output path `%1% should be a non-executable regular file") % path
);
2376 /* Check the hash. */
2377 Hash h2
= recursive
? hashPath(ht
, actualPath
).first
: hashFile(ht
, actualPath
);
2379 if (settings
.printBuildTrace
)
2380 printMsg(lvlError
, format("@ hash-mismatch %1% %2% %3% %4%")
2381 % path
% i
->second
.hashAlgo
2382 % printHash16or32(h
) % printHash16or32(h2
));
2383 throw BuildError(format("hash mismatch for store item '%1%'") % path
);
2387 /* Get rid of all weird permissions. This also checks that
2388 all files are owned by the build user, if applicable. */
2389 canonicalisePathMetaData(actualPath
,
2390 buildUser
.enabled() && !rewritten
? buildUser
.getUID() : -1, inodesSeen
);
2392 /* For this output path, find the references to other paths
2393 contained in it. Compute the SHA-256 NAR hash at the same
2394 time. The hash is stored in the database so that we can
2395 verify later on whether nobody has messed with the store. */
2397 PathSet references
= scanForReferences(actualPath
, allPaths
, hash
);
2399 if (buildMode
== bmCheck
) {
2400 if (!store
->isValidPath(path
)) continue;
2401 ValidPathInfo info
= worker
.store
.queryPathInfo(path
);
2402 if (hash
.first
!= info
.hash
) {
2403 if (settings
.keepFailed
) {
2404 Path dst
= path
+ checkSuffix
;
2405 if (pathExists(dst
)) deletePath(dst
);
2406 if (rename(actualPath
.c_str(), dst
.c_str()))
2407 throw SysError(format("renaming `%1%' to `%2%'") % actualPath
% dst
);
2408 throw Error(format("derivation `%1%' may not be deterministic: output `%2%' differs from ‘%3%’")
2409 % drvPath
% path
% dst
);
2411 throw Error(format("derivation `%1%' may not be deterministic: output `%2%' differs")
2415 if (settings
.printBuildTrace
)
2416 printMsg(lvlError
, format("@ build-succeeded %1% -") % drvPath
);
2421 /* For debugging, print out the referenced and unreferenced
2423 foreach (PathSet::iterator
, i
, inputPaths
) {
2424 PathSet::iterator j
= references
.find(*i
);
2425 if (j
== references
.end())
2426 debug(format("unreferenced input: `%1%'") % *i
);
2428 debug(format("referenced input: `%1%'") % *i
);
2431 /* Enforce `allowedReferences' and friends. */
2432 auto checkRefs
= [&](const string
& attrName
, bool allowed
, bool recursive
) {
2433 if (drv
.env
.find(attrName
) == drv
.env
.end()) return;
2435 PathSet spec
= parseReferenceSpecifiers(drv
, get(drv
.env
, attrName
));
2439 /* Our requisites are the union of the closures of our references. */
2440 for (auto & i
: references
)
2441 /* Don't call computeFSClosure on ourselves. */
2442 if (actualPath
!= i
)
2443 computeFSClosure(worker
.store
, i
, used
);
2447 for (auto & i
: used
)
2449 if (spec
.find(i
) == spec
.end())
2450 throw BuildError(format("output (`%1%') is not allowed to refer to path `%2%'") % actualPath
% i
);
2452 if (spec
.find(i
) != spec
.end())
2453 throw BuildError(format("output (`%1%') is not allowed to refer to path `%2%'") % actualPath
% i
);
2457 checkRefs("allowedReferences", true, false);
2458 checkRefs("allowedRequisites", true, true);
2459 checkRefs("disallowedReferences", false, false);
2460 checkRefs("disallowedRequisites", false, true);
2462 if (curRound
== nrRounds
) {
2463 worker
.store
.optimisePath(path
); // FIXME: combine with scanForReferences()
2465 worker
.store
.markContentsGood(path
);
2470 info
.hash
= hash
.first
;
2471 info
.narSize
= hash
.second
;
2472 info
.references
= references
;
2473 info
.deriver
= drvPath
;
2474 infos
.push_back(info
);
2477 /* Compare the result with the previous round, and report which
2478 path is different, if any.*/
2479 if (curRound
> 1 && prevInfos
!= infos
) {
2480 assert(prevInfos
.size() == infos
.size());
2481 for (auto i
= prevInfos
.begin(), j
= infos
.begin(); i
!= prevInfos
.end(); ++i
, ++j
)
2483 Path prev
= i
->path
+ checkSuffix
;
2484 if (pathExists(prev
))
2485 throw NotDeterministic(
2486 format("output ‘%1%’ of ‘%2%’ differs from ‘%3%’ from previous round")
2487 % i
->path
% drvPath
% prev
);
2489 throw NotDeterministic(
2490 format("output ‘%1%’ of ‘%2%’ differs from previous round")
2491 % i
->path
% drvPath
);
2493 assert(false); // shouldn't happen
2496 if (settings
.keepFailed
) {
2497 for (auto & i
: drv
.outputs
) {
2498 Path prev
= i
.second
.path
+ checkSuffix
;
2499 if (pathExists(prev
)) deletePath(prev
);
2500 if (curRound
< nrRounds
) {
2501 Path dst
= i
.second
.path
+ checkSuffix
;
2502 if (rename(i
.second
.path
.c_str(), dst
.c_str()))
2503 throw SysError(format("renaming ‘%1%’ to ‘%2%’") % i
.second
.path
% dst
);
2509 if (curRound
< nrRounds
) {
2514 /* Register each output path as valid, and register the sets of
2515 paths referenced by each of them. If there are cycles in the
2516 outputs, this will fail. */
2517 worker
.store
.registerValidPaths(infos
);
2521 string drvsLogDir
= "drvs";
2524 Path
DerivationGoal::openLogFile()
2528 if (!settings
.keepLog
) return "";
2530 string baseName
= baseNameOf(drvPath
);
2532 /* Create a log file. */
2533 Path dir
= (format("%1%/%2%/%3%/") % settings
.nixLogDir
% drvsLogDir
% string(baseName
, 0, 2)).str();
2536 switch (settings
.logCompression
)
2538 case COMPRESSION_GZIP
: {
2539 Path logFileName
= (format("%1%/%2%.gz") % dir
% string(baseName
, 2)).str();
2540 AutoCloseFD fd
= open(logFileName
.c_str(), O_CREAT
| O_WRONLY
| O_TRUNC
, 0666);
2541 if (fd
== -1) throw SysError(format("creating log file `%1%'") % logFileName
);
2544 /* Note: FD will be closed by 'gzclose'. */
2545 if (!(gzLogFile
= gzdopen(fd
.borrow(), "w")))
2546 throw Error(format("cannot open compressed log file `%1%'") % logFileName
);
2548 gzbuffer(gzLogFile
, 32768);
2549 gzsetparams(gzLogFile
, Z_BEST_COMPRESSION
, Z_DEFAULT_STRATEGY
);
2555 case COMPRESSION_BZIP2
: {
2556 Path logFileName
= (format("%1%/%2%.bz2") % dir
% string(baseName
, 2)).str();
2557 AutoCloseFD fd
= open(logFileName
.c_str(), O_CREAT
| O_WRONLY
| O_TRUNC
, 0666);
2558 if (fd
== -1) throw SysError(format("creating log file `%1%'") % logFileName
);
2561 if (!(fLogFile
= fdopen(fd
.borrow(), "w")))
2562 throw SysError(format("opening file `%1%'") % logFileName
);
2565 if (!(bzLogFile
= BZ2_bzWriteOpen(&err
, fLogFile
, 9, 0, 0)))
2566 throw Error(format("cannot open compressed log file `%1%'") % logFileName
);
2572 case COMPRESSION_NONE
: {
2573 Path logFileName
= (format("%1%/%2%") % dir
% string(baseName
, 2)).str();
2574 fdLogFile
= open(logFileName
.c_str(), O_CREAT
| O_WRONLY
| O_TRUNC
, 0666);
2575 if (fdLogFile
== -1) throw SysError(format("creating log file `%1%'") % logFileName
);
2576 closeOnExec(fdLogFile
);
2585 void DerivationGoal::closeLogFile()
2589 err
= gzclose(gzLogFile
);
2591 if (err
!= Z_OK
) throw Error(format("cannot close compressed log file (gzip error = %1%)") % err
);
2594 else if (bzLogFile
) {
2596 BZ2_bzWriteClose(&err
, bzLogFile
, 0, 0, 0);
2598 if (err
!= BZ_OK
) throw Error(format("cannot close compressed log file (BZip2 error = %1%)") % err
);
2611 static void _chown(const Path
& path
, uid_t uid
, gid_t gid
)
2615 if (lchown(path
.c_str(), uid
, gid
) == -1) {
2616 throw SysError(format("change owner and group of `%1%'") % path
);
2618 struct stat st
= lstat(path
);
2619 if (S_ISDIR(st
.st_mode
)) {
2620 for (auto & i
: readDirectory(path
))
2621 _chown(path
+ "/" + i
.name
, uid
, gid
);
2626 void DerivationGoal::deleteTmpDir(bool force
)
2629 if (settings
.keepFailed
&& !force
) {
2631 format("note: keeping build directory `%2%'")
2632 % drvPath
% tmpDir
);
2633 chmod(tmpDir
.c_str(), 0755);
2634 // Change the ownership if clientUid is set. Never change the
2635 // ownership or the group to "root" for security reasons.
2636 if (settings
.clientUid
!= (uid_t
) -1 && settings
.clientUid
!= 0) {
2637 _chown(tmpDir
, settings
.clientUid
,
2638 settings
.clientGid
!= 0 ? settings
.clientGid
: -1);
2648 void DerivationGoal::handleChildOutput(int fd
, const string
& data
)
2652 if (settings
.multiplexedBuildOutput
) {
2653 /* Print a prefix that allows clients to determine whether a message
2654 comes from the daemon or from a build process, and in the latter
2655 case, which build process it comes from. The PID here matches the
2656 one given in "@ build-started" traces; it's shorter that the
2657 derivation file name, hence this choice. */
2658 prefix
= "@ build-log "
2659 + std::to_string(pid
< 0 ? hook
->pid
: pid
)
2660 + " " + std::to_string(data
.size()) + "\n";
2663 if ((hook
&& fd
== hook
->builderOut
.readSide
) ||
2664 (!hook
&& fd
== builderOut
.readSide
))
2666 logSize
+= data
.size();
2667 if (settings
.maxLogSize
&& logSize
> settings
.maxLogSize
) {
2669 format("%1% killed after writing more than %2% bytes of log output")
2670 % getName() % settings
.maxLogSize
);
2671 timedOut(); // not really a timeout, but close enough
2674 if (verbosity
>= settings
.buildVerbosity
)
2675 writeToStderr(prefix
+ data
);
2678 if (data
.size() > 0) {
2680 count
= gzwrite(gzLogFile
, data
.data(), data
.size());
2681 if (count
== 0) throw Error(format("cannot write to compressed log file (gzip error = %1%)") % gzerror(gzLogFile
, &err
));
2684 } else if (bzLogFile
) {
2686 BZ2_bzWrite(&err
, bzLogFile
, (unsigned char *) data
.data(), data
.size());
2687 if (err
!= BZ_OK
) throw Error(format("cannot write to compressed log file (BZip2 error = %1%)") % err
);
2689 } else if (fdLogFile
!= -1)
2690 writeFull(fdLogFile
, data
);
2693 if (hook
&& fd
== hook
->fromAgent
.readSide
)
2694 writeToStderr(prefix
+ data
);
2698 void DerivationGoal::handleEOF(int fd
)
2700 worker
.wakeUp(shared_from_this());
2704 PathSet
DerivationGoal::checkPathValidity(bool returnValid
, bool checkHash
)
2707 foreach (DerivationOutputs::iterator
, i
, drv
.outputs
) {
2708 if (!wantOutput(i
->first
, wantedOutputs
)) continue;
2710 worker
.store
.isValidPath(i
->second
.path
) &&
2711 (!checkHash
|| worker
.store
.pathContentsGood(i
->second
.path
));
2712 if (good
== returnValid
) result
.insert(i
->second
.path
);
2718 bool DerivationGoal::pathFailed(const Path
& path
)
2720 if (!settings
.cacheFailure
) return false;
2722 if (!worker
.store
.hasPathFailed(path
)) return false;
2724 printMsg(lvlError
, format("builder for `%1%' failed previously (cached)") % path
);
2726 if (settings
.printBuildTrace
)
2727 printMsg(lvlError
, format("@ build-failed %1% - cached") % drvPath
);
2729 done(BuildResult::CachedFailure
);
2735 Path
DerivationGoal::addHashRewrite(const Path
& path
)
2737 string h1
= string(path
, settings
.nixStore
.size() + 1, 32);
2738 string h2
= string(printHash32(hashString(htSHA256
, "rewrite:" + drvPath
+ ":" + path
)), 0, 32);
2739 Path p
= settings
.nixStore
+ "/" + h2
+ string(path
, settings
.nixStore
.size() + 33);
2740 if (pathExists(p
)) deletePath(p
);
2741 assert(path
.size() == p
.size());
2742 rewritesToTmp
[h1
] = h2
;
2743 rewritesFromTmp
[h2
] = h1
;
2744 redirectedOutputs
[path
] = p
;
2745 printMsg(lvlChatty
, format("output '%1%' redirected to '%2%'")
2751 void DerivationGoal::done(BuildResult::Status status
, const string
& msg
)
2753 result
.status
= status
;
2754 result
.errorMsg
= msg
;
2755 amDone(result
.success() ? ecSuccess
: ecFailed
);
2756 if (result
.status
== BuildResult::TimedOut
)
2757 worker
.timedOut
= true;
2758 if (result
.status
== BuildResult::PermanentFailure
|| result
.status
== BuildResult::CachedFailure
)
2759 worker
.permanentFailure
= true;
2763 //////////////////////////////////////////////////////////////////////
2766 class SubstitutionGoal
: public Goal
2768 friend class Worker
;
2771 /* The store path that should be realised through a substitute. */
2774 /* Path info returned by the substituter's query info operation. */
2775 SubstitutablePathInfo info
;
2777 /* Lock on the store path. */
2778 std::shared_ptr
<PathLocks
> outputLock
;
2780 /* Whether to try to repair a valid path. */
2783 /* Location where we're downloading the substitute. Differs from
2784 storePath when doing a repair. */
2787 typedef void (SubstitutionGoal::*GoalState
)();
2790 /* The substituter. */
2791 std::shared_ptr
<Agent
> substituter
;
2793 /* Either the empty string, or the status phrase returned by the
2800 SubstitutionGoal(const Path
& storePath
, Worker
& worker
, bool repair
= false);
2801 ~SubstitutionGoal();
2807 /* "a$" ensures substitution goals happen before derivation
2809 return "a$" + storePathToName(storePath
) + "$" + storePath
;
2817 void referencesValid();
2821 /* Callback used by the worker to write to the log. */
2822 void handleChildOutput(int fd
, const string
& data
);
2823 void handleEOF(int fd
);
2825 Path
getStorePath() { return storePath
; }
2829 SubstitutionGoal::SubstitutionGoal(const Path
& storePath
, Worker
& worker
, bool repair
)
2833 this->storePath
= storePath
;
2834 state
= &SubstitutionGoal::init
;
2835 name
= (format("substitution of `%1%'") % storePath
).str();
2840 SubstitutionGoal::~SubstitutionGoal()
2842 if (substituter
) worker
.childTerminated(substituter
->pid
);
2846 void SubstitutionGoal::timedOut()
2848 if (settings
.printBuildTrace
)
2849 printMsg(lvlError
, format("@ substituter-failed %1% timeout") % storePath
);
2851 pid_t savedPid
= substituter
->pid
;
2852 substituter
.reset();
2853 worker
.childTerminated(savedPid
);
2859 void SubstitutionGoal::work()
2865 void SubstitutionGoal::init()
2869 worker
.store
.addTempRoot(storePath
);
2871 /* If the path already exists we're done. */
2872 if (!repair
&& worker
.store
.isValidPath(storePath
)) {
2877 if (settings
.readOnlyMode
)
2878 throw Error(format("cannot substitute path `%1%' - no write access to the store") % storePath
);
2884 void SubstitutionGoal::tryNext()
2886 trace("trying substituter");
2888 SubstitutablePathInfos infos
;
2889 PathSet
dummy(singleton
<PathSet
>(storePath
));
2890 worker
.store
.querySubstitutablePathInfos(dummy
, infos
);
2891 SubstitutablePathInfos::iterator k
= infos
.find(storePath
);
2892 if (k
== infos
.end()) {
2893 /* None left. Terminate this goal and let someone else deal
2895 debug(format("path `%1%' is required, but there is no substituter that can build it") % storePath
);
2896 /* Hack: don't indicate failure if there were no substituters.
2897 In that case the calling derivation should just do a
2899 amDone(ecNoSubstituters
);
2903 /* Found a substitute. */
2906 /* To maintain the closure invariant, we first have to realise the
2907 paths referenced by this one. */
2908 foreach (PathSet::iterator
, i
, info
.references
)
2909 if (*i
!= storePath
) /* ignore self-references */
2910 addWaitee(worker
.makeSubstitutionGoal(*i
));
2912 if (waitees
.empty()) /* to prevent hang (no wake-up event) */
2915 state
= &SubstitutionGoal::referencesValid
;
2919 void SubstitutionGoal::referencesValid()
2921 trace("all references realised");
2924 debug(format("some references of path `%1%' could not be realised") % storePath
);
2925 amDone(nrNoSubstituters
> 0 || nrIncompleteClosure
> 0 ? ecIncompleteClosure
: ecFailed
);
2929 foreach (PathSet::iterator
, i
, info
.references
)
2930 if (*i
!= storePath
) /* ignore self-references */
2931 assert(worker
.store
.isValidPath(*i
));
2933 state
= &SubstitutionGoal::tryToRun
;
2934 worker
.wakeUp(shared_from_this());
2938 void SubstitutionGoal::tryToRun()
2940 trace("trying to run");
2942 /* Make sure that we are allowed to start a build. Note that even
2943 is maxBuildJobs == 0 (no local builds allowed), we still allow
2944 a substituter to run. This is because substitutions cannot be
2945 distributed to another machine via the build hook. */
2946 if (worker
.getNrLocalBuilds() >= (settings
.maxBuildJobs
== 0 ? 1 : settings
.maxBuildJobs
)) {
2947 worker
.waitForBuildSlot(shared_from_this());
2951 /* Maybe a derivation goal has already locked this path
2952 (exceedingly unlikely, since it should have used a substitute
2953 first, but let's be defensive). */
2954 outputLock
.reset(); // make sure this goal's lock is gone
2955 if (pathIsLockedByMe(storePath
)) {
2956 debug(format("restarting substitution of `%1%' because it's locked by another goal")
2958 worker
.waitForAnyGoal(shared_from_this());
2959 return; /* restart in the tryToRun() state when another goal finishes */
2962 /* Acquire a lock on the output path. */
2963 outputLock
= std::shared_ptr
<PathLocks
>(new PathLocks
);
2964 if (!outputLock
->lockPaths(singleton
<PathSet
>(storePath
), "", false)) {
2965 worker
.waitForAWhile(shared_from_this());
2969 /* Check again whether the path is invalid. */
2970 if (!repair
&& worker
.store
.isValidPath(storePath
)) {
2971 debug(format("store path `%1%' has become valid") % storePath
);
2972 outputLock
->setDeletion(true);
2977 printMsg(lvlInfo
, format("fetching path `%1%'...") % storePath
);
2979 destPath
= repair
? storePath
+ ".tmp" : storePath
;
2981 /* Remove the (stale) output path if it exists. */
2982 if (pathExists(destPath
))
2983 deletePath(destPath
);
2985 if (!worker
.substituter
) {
2986 const Strings args
= { "substitute", "--substitute" };
2987 const std::map
<string
, string
> env
= { { "_NIX_OPTIONS", settings
.pack() } };
2988 worker
.substituter
= std::make_shared
<Agent
>(settings
.guixProgram
, args
, env
);
2991 /* Borrow the worker's substituter. */
2992 if (!substituter
) substituter
.swap(worker
.substituter
);
2994 /* Send the request to the substituter. */
2995 writeLine(substituter
->toAgent
.writeSide
,
2996 (format("substitute %1% %2%") % storePath
% destPath
).str());
2999 fds
.insert(substituter
->fromAgent
.readSide
);
3000 fds
.insert(substituter
->builderOut
.readSide
);
3001 worker
.childStarted(shared_from_this(), substituter
->pid
, fds
, true, true);
3003 state
= &SubstitutionGoal::finished
;
3005 if (settings
.printBuildTrace
)
3006 /* The second element in the message used to be the name of the
3007 substituter but we're left with only one. */
3008 printMsg(lvlError
, format("@ substituter-started %1% substitute") % storePath
);
3012 void SubstitutionGoal::finished()
3014 trace("substitute finished");
3016 /* Remove the 'guix substitute' process from the list of children. */
3017 worker
.childTerminated(substituter
->pid
);
3019 /* If max-jobs > 1, the worker might have created a new 'substitute'
3020 process in the meantime. If that is the case, terminate ours;
3021 otherwise, give it back to the worker. */
3022 if (worker
.substituter
) {
3023 substituter
.reset ();
3025 worker
.substituter
.swap(substituter
);
3028 /* Check the exit status and the build result. */
3031 auto statusList
= tokenizeString
<vector
<string
> >(status
);
3033 if (statusList
.empty()) {
3034 throw SubstError(format("fetching path `%1%' (empty status: '%2%')")
3035 % storePath
% status
);
3036 } else if (statusList
[0] == "hash-mismatch") {
3037 if (settings
.printBuildTrace
) {
3038 auto hashType
= statusList
[1];
3039 auto expectedHash
= statusList
[2];
3040 auto actualHash
= statusList
[3];
3041 printMsg(lvlError
, format("@ hash-mismatch %1% %2% %3% %4%")
3043 % hashType
% expectedHash
% actualHash
);
3045 throw SubstError(format("hash mismatch for substituted item `%1%'") % storePath
);
3046 } else if (statusList
[0] == "success") {
3047 if (!pathExists(destPath
))
3048 throw SubstError(format("substitute did not produce path `%1%'") % destPath
);
3050 std::string hashStr
= statusList
[1];
3051 size_t n
= hashStr
.find(':');
3052 if (n
== string::npos
)
3053 throw Error(format("bad hash from substituter: %1%") % hashStr
);
3055 HashType hashType
= parseHashType(string(hashStr
, 0, n
));
3058 throw Error(format("unknown hash algorithm in `%1%'") % hashStr
);
3060 hash
.first
= parseHash16or32(hashType
, string(hashStr
, n
+ 1));
3061 hash
.second
= std::atoi(statusList
[2].c_str());
3064 /* The database only stores SHA256 hashes, so compute it. */
3065 hash
= hashPath(htSHA256
, destPath
);
3070 throw SubstError(format("fetching path `%1%' (status: '%2%')")
3071 % storePath
% status
);
3073 } catch (SubstError
& e
) {
3075 printMsg(lvlInfo
, e
.msg());
3077 if (settings
.printBuildTrace
) {
3078 printMsg(lvlError
, format("@ substituter-failed %1% %2% %3%")
3079 % storePath
% status
% e
.msg());
3086 if (repair
) replaceValidPath(storePath
, destPath
);
3088 canonicalisePathMetaData(storePath
, -1);
3090 worker
.store
.optimisePath(storePath
); // FIXME: combine with hashPath()
3092 ValidPathInfo info2
;
3093 info2
.path
= storePath
;
3094 info2
.hash
= hash
.first
;
3095 info2
.narSize
= hash
.second
;
3096 info2
.references
= info
.references
;
3097 info2
.deriver
= info
.deriver
;
3098 worker
.store
.registerValidPath(info2
);
3100 outputLock
->setDeletion(true);
3103 worker
.store
.markContentsGood(storePath
);
3106 format("substitution of path `%1%' succeeded") % storePath
);
3108 if (settings
.printBuildTrace
)
3109 printMsg(lvlError
, format("@ substituter-succeeded %1%") % storePath
);
3115 void SubstitutionGoal::handleChildOutput(int fd
, const string
& data
)
3117 if (verbosity
>= settings
.buildVerbosity
3118 && fd
== substituter
->builderOut
.readSide
) {
3119 writeToStderr(data
);
3120 /* Don't write substitution output to a log file for now. We
3121 probably should, though. */
3124 if (fd
== substituter
->fromAgent
.readSide
) {
3125 /* DATA may consist of several lines. Process them one by one. */
3126 string input
= data
;
3127 while (!input
.empty()) {
3128 /* Process up to the first newline. */
3129 size_t end
= input
.find_first_of("\n");
3130 string trimmed
= (end
!= string::npos
) ? input
.substr(0, end
) : input
;
3132 /* Update the goal's state accordingly. */
3135 worker
.wakeUp(shared_from_this());
3137 printMsg(lvlError
, format("unexpected substituter message '%1%'") % input
);
3140 input
= (end
!= string::npos
) ? input
.substr(end
+ 1) : "";
3146 void SubstitutionGoal::handleEOF(int fd
)
3148 worker
.wakeUp(shared_from_this());
3153 //////////////////////////////////////////////////////////////////////
3156 static bool working
= false;
3159 Worker::Worker(LocalStore
& store
)
3162 /* Debugging: prevent recursive workers. */
3163 if (working
) abort();
3167 permanentFailure
= false;
3176 /* Explicitly get rid of all strong pointers now. After this all
3177 goals that refer to this worker should be gone. (Otherwise we
3178 are in trouble, since goals may call childTerminated() etc. in
3179 their destructors). */
3184 GoalPtr
Worker::makeDerivationGoal(const Path
& path
,
3185 const StringSet
& wantedOutputs
, BuildMode buildMode
)
3187 GoalPtr goal
= derivationGoals
[path
].lock();
3189 goal
= GoalPtr(new DerivationGoal(path
, wantedOutputs
, *this, buildMode
));
3190 derivationGoals
[path
] = goal
;
3193 (dynamic_cast<DerivationGoal
*>(goal
.get()))->addWantedOutputs(wantedOutputs
);
3198 GoalPtr
Worker::makeSubstitutionGoal(const Path
& path
, bool repair
)
3200 GoalPtr goal
= substitutionGoals
[path
].lock();
3202 goal
= GoalPtr(new SubstitutionGoal(path
, *this, repair
));
3203 substitutionGoals
[path
] = goal
;
3210 static void removeGoal(GoalPtr goal
, WeakGoalMap
& goalMap
)
3212 /* !!! inefficient */
3213 for (WeakGoalMap::iterator i
= goalMap
.begin();
3214 i
!= goalMap
.end(); )
3215 if (i
->second
.lock() == goal
) {
3216 WeakGoalMap::iterator j
= i
; ++j
;
3224 void Worker::removeGoal(GoalPtr goal
)
3226 nix::removeGoal(goal
, derivationGoals
);
3227 nix::removeGoal(goal
, substitutionGoals
);
3228 if (topGoals
.find(goal
) != topGoals
.end()) {
3229 topGoals
.erase(goal
);
3230 /* If a top-level goal failed, then kill all other goals
3231 (unless keepGoing was set). */
3232 if (goal
->getExitCode() == Goal::ecFailed
&& !settings
.keepGoing
)
3236 /* Wake up goals waiting for any goal to finish. */
3237 foreach (WeakGoals::iterator
, i
, waitingForAnyGoal
) {
3238 GoalPtr goal
= i
->lock();
3239 if (goal
) wakeUp(goal
);
3242 waitingForAnyGoal
.clear();
3246 void Worker::wakeUp(GoalPtr goal
)
3248 goal
->trace("woken up");
3249 addToWeakGoals(awake
, goal
);
3253 unsigned Worker::getNrLocalBuilds()
3255 return nrLocalBuilds
;
3259 void Worker::childStarted(GoalPtr goal
,
3260 pid_t pid
, const set
<int> & fds
, bool inBuildSlot
,
3261 bool respectTimeouts
)
3266 child
.timeStarted
= child
.lastOutput
= time(0);
3267 child
.inBuildSlot
= inBuildSlot
;
3268 child
.respectTimeouts
= respectTimeouts
;
3269 children
[pid
] = child
;
3270 if (inBuildSlot
) nrLocalBuilds
++;
3274 void Worker::childTerminated(pid_t pid
, bool wakeSleepers
)
3276 assert(pid
!= -1); /* common mistake */
3278 Children::iterator i
= children
.find(pid
);
3279 assert(i
!= children
.end());
3281 if (i
->second
.inBuildSlot
) {
3282 assert(nrLocalBuilds
> 0);
3286 children
.erase(pid
);
3290 /* Wake up goals waiting for a build slot. */
3291 foreach (WeakGoals::iterator
, i
, wantingToBuild
) {
3292 GoalPtr goal
= i
->lock();
3293 if (goal
) wakeUp(goal
);
3296 wantingToBuild
.clear();
3301 void Worker::waitForBuildSlot(GoalPtr goal
)
3303 debug("wait for build slot");
3304 if (getNrLocalBuilds() < settings
.maxBuildJobs
)
3305 wakeUp(goal
); /* we can do it right away */
3307 addToWeakGoals(wantingToBuild
, goal
);
3311 void Worker::waitForAnyGoal(GoalPtr goal
)
3313 debug("wait for any goal");
3314 addToWeakGoals(waitingForAnyGoal
, goal
);
3318 void Worker::waitForAWhile(GoalPtr goal
)
3320 debug("wait for a while");
3321 addToWeakGoals(waitingForAWhile
, goal
);
3325 void Worker::run(const Goals
& _topGoals
)
3327 foreach (Goals::iterator
, i
, _topGoals
) topGoals
.insert(*i
);
3329 startNest(nest
, lvlDebug
, format("entered goal loop"));
3335 /* Call every wake goal (in the ordering established by
3336 CompareGoalPtrs). */
3337 while (!awake
.empty() && !topGoals
.empty()) {
3339 for (auto & i
: awake
) {
3340 GoalPtr goal
= i
.lock();
3341 if (goal
) awake2
.insert(goal
);
3344 for (auto & goal
: awake2
) {
3347 if (topGoals
.empty()) break; // stuff may have been cancelled
3351 if (topGoals
.empty()) break;
3353 /* Wait for input. */
3354 if (!children
.empty() || !waitingForAWhile
.empty())
3357 if (awake
.empty() && settings
.maxBuildJobs
== 0) throw Error(
3358 "unable to start any build; either increase `--max-jobs' "
3359 "or enable distributed builds");
3360 assert(!awake
.empty());
3364 /* If --keep-going is not set, it's possible that the main goal
3365 exited while some of its subgoals were still active. But if
3366 --keep-going *is* set, then they must all be finished now. */
3367 assert(!settings
.keepGoing
|| awake
.empty());
3368 assert(!settings
.keepGoing
|| wantingToBuild
.empty());
3369 assert(!settings
.keepGoing
|| children
.empty());
3373 void Worker::waitForInput()
3375 printMsg(lvlVomit
, "waiting for children");
3377 /* Process output from the file descriptors attached to the
3378 children, namely log output and output path creation commands.
3379 We also use this to detect child termination: if we get EOF on
3380 the logger pipe of a build, we assume that the builder has
3383 bool useTimeout
= false;
3384 struct timeval timeout
;
3385 timeout
.tv_usec
= 0;
3386 time_t before
= time(0);
3388 /* If we're monitoring for silence on stdout/stderr, or if there
3389 is a build timeout, then wait for input until the first
3390 deadline for any child. */
3391 assert(sizeof(time_t) >= sizeof(long));
3392 time_t nearest
= LONG_MAX
; // nearest deadline
3393 foreach (Children::iterator
, i
, children
) {
3394 if (!i
->second
.respectTimeouts
) continue;
3395 if (settings
.maxSilentTime
!= 0)
3396 nearest
= std::min(nearest
, i
->second
.lastOutput
+ settings
.maxSilentTime
);
3397 if (settings
.buildTimeout
!= 0)
3398 nearest
= std::min(nearest
, i
->second
.timeStarted
+ settings
.buildTimeout
);
3400 if (nearest
!= LONG_MAX
) {
3401 timeout
.tv_sec
= std::max((time_t) 1, nearest
- before
);
3403 printMsg(lvlVomit
, format("sleeping %1% seconds") % timeout
.tv_sec
);
3406 /* If we are polling goals that are waiting for a lock, then wake
3407 up after a few seconds at most. */
3408 if (!waitingForAWhile
.empty()) {
3410 if (lastWokenUp
== 0)
3411 printMsg(lvlError
, "waiting for locks or build slots...");
3412 if (lastWokenUp
== 0 || lastWokenUp
> before
) lastWokenUp
= before
;
3413 timeout
.tv_sec
= std::max((time_t) 1, (time_t) (lastWokenUp
+ settings
.pollInterval
- before
));
3414 } else lastWokenUp
= 0;
3416 using namespace std
;
3417 /* Use select() to wait for the input side of any logger pipe to
3418 become `available'. Note that `available' (i.e., non-blocking)
3423 foreach (Children::iterator
, i
, children
) {
3424 foreach (set
<int>::iterator
, j
, i
->second
.fds
) {
3426 if (*j
>= fdMax
) fdMax
= *j
+ 1;
3430 if (select(fdMax
, &fds
, 0, 0, useTimeout
? &timeout
: 0) == -1) {
3431 if (errno
== EINTR
) return;
3432 throw SysError("waiting for input");
3435 time_t after
= time(0);
3437 /* Process all available file descriptors. */
3439 /* Since goals may be canceled from inside the loop below (causing
3440 them go be erased from the `children' map), we have to be
3441 careful that we don't keep iterators alive across calls to
3444 foreach (Children::iterator
, i
, children
) pids
.insert(i
->first
);
3446 foreach (set
<pid_t
>::iterator
, i
, pids
) {
3448 Children::iterator j
= children
.find(*i
);
3449 if (j
== children
.end()) continue; // child destroyed
3450 GoalPtr goal
= j
->second
.goal
.lock();
3453 set
<int> fds2(j
->second
.fds
);
3454 foreach (set
<int>::iterator
, k
, fds2
) {
3455 if (FD_ISSET(*k
, &fds
)) {
3456 unsigned char buffer
[4096];
3457 ssize_t rd
= read(*k
, buffer
, sizeof(buffer
));
3460 throw SysError(format("reading from %1%")
3462 } else if (rd
== 0) {
3463 debug(format("%1%: got EOF") % goal
->getName());
3464 goal
->handleEOF(*k
);
3465 j
->second
.fds
.erase(*k
);
3467 printMsg(lvlVomit
, format("%1%: read %2% bytes")
3468 % goal
->getName() % rd
);
3469 string
data((char *) buffer
, rd
);
3470 j
->second
.lastOutput
= after
;
3471 goal
->handleChildOutput(*k
, data
);
3476 if (goal
->getExitCode() == Goal::ecBusy
&&
3477 settings
.maxSilentTime
!= 0 &&
3478 j
->second
.respectTimeouts
&&
3479 after
- j
->second
.lastOutput
>= (time_t) settings
.maxSilentTime
)
3482 format("%1% timed out after %2% seconds of silence")
3483 % goal
->getName() % settings
.maxSilentTime
);
3487 else if (goal
->getExitCode() == Goal::ecBusy
&&
3488 settings
.buildTimeout
!= 0 &&
3489 j
->second
.respectTimeouts
&&
3490 after
- j
->second
.timeStarted
>= (time_t) settings
.buildTimeout
)
3493 format("%1% timed out after %2% seconds")
3494 % goal
->getName() % settings
.buildTimeout
);
3499 if (!waitingForAWhile
.empty() && lastWokenUp
+ settings
.pollInterval
<= after
) {
3500 lastWokenUp
= after
;
3501 foreach (WeakGoals::iterator
, i
, waitingForAWhile
) {
3502 GoalPtr goal
= i
->lock();
3503 if (goal
) wakeUp(goal
);
3505 waitingForAWhile
.clear();
3510 unsigned int Worker::exitStatus()
3512 return timedOut
? 101 : (permanentFailure
? 100 : 1);
3516 //////////////////////////////////////////////////////////////////////
3519 void LocalStore::buildPaths(const PathSet
& drvPaths
, BuildMode buildMode
)
3521 startNest(nest
, lvlDebug
,
3522 format("building %1%") % showPaths(drvPaths
));
3524 Worker
worker(*this);
3527 foreach (PathSet::const_iterator
, i
, drvPaths
) {
3528 DrvPathWithOutputs i2
= parseDrvPathWithOutputs(*i
);
3529 if (isDerivation(i2
.first
))
3530 goals
.insert(worker
.makeDerivationGoal(i2
.first
, i2
.second
, buildMode
));
3532 goals
.insert(worker
.makeSubstitutionGoal(*i
, buildMode
));
3538 foreach (Goals::iterator
, i
, goals
)
3539 if ((*i
)->getExitCode() == Goal::ecFailed
) {
3540 DerivationGoal
* i2
= dynamic_cast<DerivationGoal
*>(i
->get());
3541 if (i2
) failed
.insert(i2
->getDrvPath());
3542 else failed
.insert(dynamic_cast<SubstitutionGoal
*>(i
->get())->getStorePath());
3545 if (!failed
.empty())
3546 throw Error(format("build of %1% failed") % showPaths(failed
), worker
.exitStatus());
3550 void LocalStore::ensurePath(const Path
& path
)
3552 /* If the path is already valid, we're done. */
3553 if (isValidPath(path
)) return;
3555 Worker
worker(*this);
3556 GoalPtr goal
= worker
.makeSubstitutionGoal(path
);
3557 Goals goals
= singleton
<Goals
>(goal
);
3561 if (goal
->getExitCode() != Goal::ecSuccess
)
3562 throw Error(format("path `%1%' does not exist and cannot be created") % path
, worker
.exitStatus());
3566 void LocalStore::repairPath(const Path
& path
)
3568 Worker
worker(*this);
3569 GoalPtr goal
= worker
.makeSubstitutionGoal(path
, true);
3570 Goals goals
= singleton
<Goals
>(goal
);
3574 if (goal
->getExitCode() != Goal::ecSuccess
) {
3575 /* Since substituting the path didn't work, if we have a valid
3576 deriver, then rebuild the deriver. */
3577 Path deriver
= queryDeriver(path
);
3578 if (deriver
!= "" && isValidPath(deriver
)) {
3580 goals
.insert(worker
.makeDerivationGoal(deriver
, StringSet(), bmRepair
));
3583 throw Error(format("cannot repair path `%1%'") % path
, worker
.exitStatus());