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 static string pathNullDevice
= "/dev/null";
86 /* Forward definition. */
91 /* A pointer to a goal. */
94 typedef std::shared_ptr
<Goal
> GoalPtr
;
95 typedef std::weak_ptr
<Goal
> WeakGoalPtr
;
97 struct CompareGoalPtrs
{
98 bool operator() (const GoalPtr
& a
, const GoalPtr
& b
);
102 typedef set
<GoalPtr
, CompareGoalPtrs
> Goals
;
103 typedef list
<WeakGoalPtr
> WeakGoals
;
105 /* A map of paths to goals (and the other way around). */
106 typedef map
<Path
, WeakGoalPtr
> WeakGoalMap
;
110 class Goal
: public std::enable_shared_from_this
<Goal
>
113 typedef enum {ecBusy
, ecSuccess
, ecFailed
, ecNoSubstituters
, ecIncompleteClosure
} ExitCode
;
117 /* Backlink to the worker. */
120 /* Goals that this goal is waiting for. */
123 /* Goals waiting for this one to finish. Must use weak pointers
124 here to prevent cycles. */
127 /* Number of goals we are/were waiting for that have failed. */
128 unsigned int nrFailed
;
130 /* Number of substitution goals we are/were waiting for that
131 failed because there are no substituters. */
132 unsigned int nrNoSubstituters
;
134 /* Number of substitution goals we are/were waiting for that
135 failed because othey had unsubstitutable references. */
136 unsigned int nrIncompleteClosure
;
138 /* Name of this goal for debugging purposes. */
141 /* Whether the goal is finished. */
144 Goal(Worker
& worker
) : worker(worker
)
146 nrFailed
= nrNoSubstituters
= nrIncompleteClosure
= 0;
152 trace("goal destroyed");
156 virtual void work() = 0;
158 void addWaitee(GoalPtr waitee
);
160 virtual void waiteeDone(GoalPtr waitee
, ExitCode result
);
162 virtual void handleChildOutput(int fd
, const string
& data
)
167 virtual void handleEOF(int fd
)
172 void trace(const format
& f
);
179 ExitCode
getExitCode()
184 /* Callback in case of a timeout. It should wake up its waiters,
185 get rid of any running child processes that are being monitored
186 by the worker (important!), etc. */
187 virtual void timedOut() = 0;
189 virtual string
key() = 0;
192 void amDone(ExitCode result
);
196 bool CompareGoalPtrs::operator() (const GoalPtr
& a
, const GoalPtr
& b
) {
197 string s1
= a
->key();
198 string s2
= b
->key();
203 /* A mapping used to remember for each child process to what goal it
204 belongs, and file descriptors for receiving log data and output
205 path creation commands. */
210 bool respectTimeouts
;
212 time_t lastOutput
; /* time we last got output on stdout/stderr */
216 typedef map
<pid_t
, Child
> Children
;
219 /* The worker class. */
224 /* Note: the worker should only have strong pointers to the
227 /* The top-level goals of the worker. */
230 /* Goals that are ready to do some work. */
233 /* Goals waiting for a build slot. */
234 WeakGoals wantingToBuild
;
236 /* Child processes currently running. */
239 /* Number of build slots occupied. This includes local builds and
240 substitutions but not remote builds via the build hook. */
241 unsigned int nrLocalBuilds
;
243 /* Maps used to prevent multiple instantiations of a goal for the
244 same derivation / path. */
245 WeakGoalMap derivationGoals
;
246 WeakGoalMap substitutionGoals
;
248 /* Goals waiting for busy paths to be unlocked. */
249 WeakGoals waitingForAnyGoal
;
251 /* Goals sleeping for a few seconds (polling a lock). */
252 WeakGoals waitingForAWhile
;
254 /* Last time the goals in `waitingForAWhile' where woken up. */
259 /* Set if at least one derivation had a BuildError (i.e. permanent
261 bool permanentFailure
;
263 /* Set if at least one derivation had a timeout. */
268 std::shared_ptr
<HookInstance
> hook
;
270 Worker(LocalStore
& store
);
273 /* Make a goal (with caching). */
274 GoalPtr
makeDerivationGoal(const Path
& drvPath
, const StringSet
& wantedOutputs
, BuildMode buildMode
= bmNormal
);
275 GoalPtr
makeSubstitutionGoal(const Path
& storePath
, bool repair
= false);
277 /* Remove a dead goal. */
278 void removeGoal(GoalPtr goal
);
280 /* Wake up a goal (i.e., there is something for it to do). */
281 void wakeUp(GoalPtr goal
);
283 /* Return the number of local build and substitution processes
284 currently running (but not remote builds via the build
286 unsigned int getNrLocalBuilds();
288 /* Registers a running child process. `inBuildSlot' means that
289 the process counts towards the jobs limit. */
290 void childStarted(GoalPtr goal
, pid_t pid
,
291 const set
<int> & fds
, bool inBuildSlot
, bool respectTimeouts
);
293 /* Unregisters a running child process. `wakeSleepers' should be
294 false if there is no sense in waking up goals that are sleeping
295 because they can't run yet (e.g., there is no free build slot,
296 or the hook would still say `postpone'). */
297 void childTerminated(pid_t pid
, bool wakeSleepers
= true);
299 /* Put `goal' to sleep until a build slot becomes available (which
300 might be right away). */
301 void waitForBuildSlot(GoalPtr goal
);
303 /* Wait for any goal to finish. Pretty indiscriminate way to
304 wait for some resource that some other goal is holding. */
305 void waitForAnyGoal(GoalPtr goal
);
307 /* Wait for a few seconds and then retry this goal. Used when
308 waiting for a lock held by another process. This kind of
309 polling is inefficient, but POSIX doesn't really provide a way
310 to wait for multiple locks in the main select() loop. */
311 void waitForAWhile(GoalPtr goal
);
313 /* Loop until the specified top-level goals have finished. */
314 void run(const Goals
& topGoals
);
316 /* Wait for input to become available. */
319 unsigned int exitStatus();
323 //////////////////////////////////////////////////////////////////////
326 void addToWeakGoals(WeakGoals
& goals
, GoalPtr p
)
330 foreach (WeakGoals::iterator
, i
, goals
)
331 if (i
->lock() == p
) return;
336 void Goal::addWaitee(GoalPtr waitee
)
338 waitees
.insert(waitee
);
339 addToWeakGoals(waitee
->waiters
, shared_from_this());
343 void Goal::waiteeDone(GoalPtr waitee
, ExitCode result
)
345 assert(waitees
.find(waitee
) != waitees
.end());
346 waitees
.erase(waitee
);
348 trace(format("waitee `%1%' done; %2% left") %
349 waitee
->name
% waitees
.size());
351 if (result
== ecFailed
|| result
== ecNoSubstituters
|| result
== ecIncompleteClosure
) ++nrFailed
;
353 if (result
== ecNoSubstituters
) ++nrNoSubstituters
;
355 if (result
== ecIncompleteClosure
) ++nrIncompleteClosure
;
357 if (waitees
.empty() || (result
== ecFailed
&& !settings
.keepGoing
)) {
359 /* If we failed and keepGoing is not set, we remove all
360 remaining waitees. */
361 foreach (Goals::iterator
, i
, waitees
) {
364 foreach (WeakGoals::iterator
, j
, goal
->waiters
)
365 if (j
->lock() != shared_from_this()) waiters2
.push_back(*j
);
366 goal
->waiters
= waiters2
;
370 worker
.wakeUp(shared_from_this());
375 void Goal::amDone(ExitCode result
)
378 assert(exitCode
== ecBusy
);
379 assert(result
== ecSuccess
|| result
== ecFailed
|| result
== ecNoSubstituters
|| result
== ecIncompleteClosure
);
381 foreach (WeakGoals::iterator
, i
, waiters
) {
382 GoalPtr goal
= i
->lock();
383 if (goal
) goal
->waiteeDone(shared_from_this(), result
);
386 worker
.removeGoal(shared_from_this());
390 void Goal::trace(const format
& f
)
392 debug(format("%1%: %2%") % name
% f
);
397 //////////////////////////////////////////////////////////////////////
400 /* Common initialisation performed in child processes. */
401 static void commonChildInit(Pipe
& logPipe
)
403 /* Put the child in a separate session (and thus a separate
404 process group) so that it has no controlling terminal (meaning
405 that e.g. ssh cannot open /dev/tty) and it doesn't receive
408 throw SysError(format("creating a new session"));
410 /* Dup the write side of the logger pipe into stderr. */
411 if (dup2(logPipe
.writeSide
, STDERR_FILENO
) == -1)
412 throw SysError("cannot pipe standard error into log file");
414 /* Dup stderr to stdout. */
415 if (dup2(STDERR_FILENO
, STDOUT_FILENO
) == -1)
416 throw SysError("cannot dup stderr into stdout");
418 /* Reroute stdin to /dev/null. */
419 int fdDevNull
= open(pathNullDevice
.c_str(), O_RDWR
);
421 throw SysError(format("cannot open `%1%'") % pathNullDevice
);
422 if (dup2(fdDevNull
, STDIN_FILENO
) == -1)
423 throw SysError("cannot dup null device into stdin");
427 /* Restore default handling of SIGPIPE, otherwise some programs will
428 randomly say "Broken pipe". */
429 static void restoreSIGPIPE()
431 struct sigaction act
, oact
;
432 act
.sa_handler
= SIG_DFL
;
434 sigemptyset(&act
.sa_mask
);
435 if (sigaction(SIGPIPE
, &act
, &oact
)) throw SysError("resetting SIGPIPE");
439 //////////////////////////////////////////////////////////////////////
445 /* POSIX locks suck. If we have a lock on a file, and we open and
446 close that file again (without closing the original file
447 descriptor), we lose the lock. So we have to be *very* careful
448 not to open a lock file on which we are holding a lock. */
449 static PathSet lockedPaths
; /* !!! not thread-safe */
452 AutoCloseFD fdUserLock
;
457 std::vector
<gid_t
> supplementaryGIDs
;
468 string
getUser() { return user
; }
469 uid_t
getUID() { return uid
; }
470 uid_t
getGID() { return gid
; }
471 std::vector
<gid_t
> getSupplementaryGIDs() { return supplementaryGIDs
; }
473 bool enabled() { return uid
!= 0; }
478 PathSet
UserLock::lockedPaths
;
487 UserLock::~UserLock()
493 void UserLock::acquire()
497 assert(settings
.buildUsersGroup
!= "");
499 /* Get the members of the build-users-group. */
500 struct group
* gr
= getgrnam(settings
.buildUsersGroup
.c_str());
502 throw Error(format("the group `%1%' specified in `build-users-group' does not exist")
503 % settings
.buildUsersGroup
);
506 /* Copy the result of getgrnam. */
508 for (char * * p
= gr
->gr_mem
; *p
; ++p
) {
509 debug(format("found build user `%1%'") % *p
);
514 throw Error(format("the build users group `%1%' has no members")
515 % settings
.buildUsersGroup
);
517 /* Find a user account that isn't currently in use for another
519 foreach (Strings::iterator
, i
, users
) {
520 debug(format("trying user `%1%'") % *i
);
522 struct passwd
* pw
= getpwnam(i
->c_str());
524 throw Error(format("the user `%1%' in the group `%2%' does not exist")
525 % *i
% settings
.buildUsersGroup
);
527 createDirs(settings
.nixStateDir
+ "/userpool");
529 fnUserLock
= (format("%1%/userpool/%2%") % settings
.nixStateDir
% pw
->pw_uid
).str();
531 if (lockedPaths
.find(fnUserLock
) != lockedPaths
.end())
532 /* We already have a lock on this one. */
535 AutoCloseFD fd
= open(fnUserLock
.c_str(), O_RDWR
| O_CREAT
, 0600);
537 throw SysError(format("opening user lock `%1%'") % fnUserLock
);
540 if (lockFile(fd
, ltWrite
, false)) {
541 fdUserLock
= fd
.borrow();
542 lockedPaths
.insert(fnUserLock
);
546 /* Sanity check... */
547 if (uid
== getuid() || uid
== geteuid())
548 throw Error(format("the build user should not be a member of `%1%'")
549 % settings
.buildUsersGroup
);
551 /* Get the list of supplementary groups of this build user. This
552 is usually either empty or contains a group such as "kvm". */
553 supplementaryGIDs
.resize(10);
554 int ngroups
= supplementaryGIDs
.size();
555 int err
= getgrouplist(pw
->pw_name
, pw
->pw_gid
,
556 supplementaryGIDs
.data(), &ngroups
);
558 throw Error(format("failed to get list of supplementary groups for ‘%1%’") % pw
->pw_name
);
560 supplementaryGIDs
.resize(ngroups
);
566 throw Error(format("all build users are currently in use; "
567 "consider creating additional users and adding them to the `%1%' group")
568 % settings
.buildUsersGroup
);
572 void UserLock::release()
574 if (uid
== 0) return;
575 fdUserLock
.close(); /* releases lock */
576 assert(lockedPaths
.find(fnUserLock
) != lockedPaths
.end());
577 lockedPaths
.erase(fnUserLock
);
583 void UserLock::kill()
590 //////////////////////////////////////////////////////////////////////
595 /* Pipes for talking to the build hook. */
598 /* Pipe for the hook's standard output/error. */
601 /* Pipe for the builder's standard output/error. */
604 /* The process ID of the hook. */
613 HookInstance::HookInstance()
615 debug("starting build hook");
617 const Path
&buildHook
= settings
.guixProgram
;
619 /* Create a pipe to get the output of the child. */
622 /* Create the communication pipes. */
625 /* Create a pipe to get the output of the builder. */
629 pid
= startProcess([&]() {
631 commonChildInit(fromHook
);
633 if (chdir("/") == -1) throw SysError("changing into `/");
635 /* Dup the communication pipes. */
636 if (dup2(toHook
.readSide
, STDIN_FILENO
) == -1)
637 throw SysError("dupping to-hook read side");
639 /* Use fd 4 for the builder's stdout/stderr. */
640 if (dup2(builderOut
.writeSide
, 4) == -1)
641 throw SysError("dupping builder's stdout/stderr");
643 execl(buildHook
.c_str(), buildHook
.c_str(), "offload",
644 settings
.thisSystem
.c_str(),
645 (format("%1%") % settings
.maxSilentTime
).str().c_str(),
646 (format("%1%") % settings
.printBuildTrace
).str().c_str(),
647 (format("%1%") % settings
.buildTimeout
).str().c_str(),
650 throw SysError(format("executing `%1% offload'") % buildHook
);
653 pid
.setSeparatePG(true);
654 fromHook
.writeSide
.close();
655 toHook
.readSide
.close();
659 HookInstance::~HookInstance()
662 toHook
.writeSide
.close();
670 //////////////////////////////////////////////////////////////////////
673 typedef map
<string
, string
> HashRewrites
;
676 string
rewriteHashes(string s
, const HashRewrites
& rewrites
)
678 foreach (HashRewrites::const_iterator
, i
, rewrites
) {
679 assert(i
->first
.size() == i
->second
.size());
681 while ((j
= s
.find(i
->first
, j
)) != string::npos
) {
682 debug(format("rewriting @ %1%") % j
);
683 s
.replace(j
, i
->second
.size(), i
->second
);
690 //////////////////////////////////////////////////////////////////////
693 typedef enum {rpAccept
, rpDecline
, rpPostpone
} HookReply
;
695 class SubstitutionGoal
;
697 class DerivationGoal
: public Goal
700 /* The path of the derivation. */
703 /* The specific outputs that we need to build. Empty means all of
705 StringSet wantedOutputs
;
707 /* Whether additional wanted outputs have been added. */
710 /* Whether to retry substituting the outputs after building the
712 bool retrySubstitution
;
714 /* The derivation stored at drvPath. */
717 /* The remainder is state held during the build. */
719 /* Locks on the output paths. */
720 PathLocks outputLocks
;
722 /* All input paths (that is, the union of FS closures of the
723 immediate input paths). */
726 /* Referenceable paths (i.e., input and output paths). */
729 /* Outputs that are already valid. If we're repairing, these are
730 the outputs that are valid *and* not corrupt. */
733 /* Outputs that are corrupt or not valid. */
734 PathSet missingPaths
;
736 /* User selected for running the builder. */
739 /* The process ID of the builder. */
742 /* The temporary directory. */
745 /* The path of the temporary directory in the sandbox. */
746 Path tmpDirInSandbox
;
748 /* File descriptor for the log file. */
754 AutoCloseFD fdLogFile
;
756 /* Number of bytes received from the builder's stdout/stderr. */
757 unsigned long logSize
;
759 /* Pipe for the builder's standard output/error. */
762 /* The build hook. */
763 std::shared_ptr
<HookInstance
> hook
;
765 /* Whether we're currently doing a chroot build. */
770 /* RAII object to delete the chroot directory. */
771 std::shared_ptr
<AutoDelete
> autoDelChroot
;
773 /* All inputs that are regular files. */
774 PathSet regularInputPaths
;
776 /* Whether this is a fixed-output derivation. */
779 typedef void (DerivationGoal::*GoalState
)();
782 /* Stuff we need to pass to runChild(). */
783 typedef map
<Path
, Path
> DirsInChroot
; // maps target path to source path
784 DirsInChroot dirsInChroot
;
785 typedef map
<string
, string
> Environment
;
788 /* Hash rewriting. */
789 HashRewrites rewritesToTmp
, rewritesFromTmp
;
790 typedef map
<Path
, Path
> RedirectedOutputs
;
791 RedirectedOutputs redirectedOutputs
;
795 /* If we're repairing without a chroot, there may be outputs that
796 are valid but corrupt. So we redirect these outputs to
798 PathSet redirectedBadOutputs
;
800 /* The current round, if we're building multiple times. */
801 unsigned int curRound
= 1;
803 unsigned int nrRounds
;
805 /* Path registration info from the previous round, if we're
806 building multiple times. Since this contains the hash, it
807 allows us to compare whether two rounds produced the same
809 ValidPathInfos prevInfos
;
814 DerivationGoal(const Path
& drvPath
, const StringSet
& wantedOutputs
, Worker
& worker
, BuildMode buildMode
= bmNormal
);
817 void timedOut() override
;
821 /* Ensure that derivations get built in order of their name,
822 i.e. a derivation named "aardvark" always comes before
823 "baboon". And substitution goals always happen before
824 derivation goals (due to "b$"). */
825 return "b$" + storePathToName(drvPath
) + "$" + drvPath
;
835 /* Add wanted outputs to an already existing derivation goal. */
836 void addWantedOutputs(const StringSet
& outputs
);
838 BuildResult
getResult() { return result
; }
843 void haveDerivation();
844 void outputsSubstituted();
845 void closureRepaired();
846 void inputsRealised();
850 /* Is the build hook willing to perform the build? */
851 HookReply
tryBuildHook();
853 /* Start building a derivation. */
856 /* Run the builder's process. */
859 friend int childEntry(void *);
861 /* Check that the derivation outputs all exist and register them
863 void registerOutputs();
865 /* Open a log file and a pipe to it. */
868 /* Close the log file. */
871 /* Delete the temporary directory, if we have one. */
872 void deleteTmpDir(bool force
);
874 /* Callback used by the worker to write to the log. */
875 void handleChildOutput(int fd
, const string
& data
);
876 void handleEOF(int fd
);
878 /* Return the set of (in)valid paths. */
879 PathSet
checkPathValidity(bool returnValid
, bool checkHash
);
881 /* Abort the goal if `path' failed to build. */
882 bool pathFailed(const Path
& path
);
884 /* Forcibly kill the child process, if any. */
887 Path
addHashRewrite(const Path
& path
);
889 void repairClosure();
891 void done(BuildResult::Status status
, const string
& msg
= "");
895 DerivationGoal::DerivationGoal(const Path
& drvPath
, const StringSet
& wantedOutputs
, Worker
& worker
, BuildMode buildMode
)
897 , wantedOutputs(wantedOutputs
)
899 , retrySubstitution(false)
906 , buildMode(buildMode
)
908 this->drvPath
= drvPath
;
909 state
= &DerivationGoal::init
;
910 name
= (format("building of `%1%'") % drvPath
).str();
913 /* Prevent the .chroot directory from being
914 garbage-collected. (See isActiveTempFile() in gc.cc.) */
915 worker
.store
.addTempRoot(drvPath
);
919 DerivationGoal::~DerivationGoal()
921 /* Careful: we should never ever throw an exception from a
923 try { killChild(); } catch (...) { ignoreException(); }
924 try { deleteTmpDir(false); } catch (...) { ignoreException(); }
925 try { closeLogFile(); } catch (...) { ignoreException(); }
929 void DerivationGoal::killChild()
932 worker
.childTerminated(pid
);
934 if (buildUser
.enabled()) {
935 /* If we're using a build user, then there is a tricky
936 race condition: if we kill the build user before the
937 child has done its setuid() to the build user uid, then
938 it won't be killed, and we'll potentially lock up in
939 pid.wait(). So also send a conventional kill to the
941 ::kill(-pid
, SIGKILL
); /* ignore the result */
950 /* If there was a build hook involved, remove it from the worker's
952 if (hook
&& hook
->pid
!= -1) {
953 worker
.childTerminated(hook
->pid
);
959 void DerivationGoal::timedOut()
961 if (settings
.printBuildTrace
)
962 printMsg(lvlError
, format("@ build-failed %1% - timeout") % drvPath
);
964 done(BuildResult::TimedOut
);
968 void DerivationGoal::work()
974 void DerivationGoal::addWantedOutputs(const StringSet
& outputs
)
976 /* If we already want all outputs, there is nothing to do. */
977 if (wantedOutputs
.empty()) return;
979 if (outputs
.empty()) {
980 wantedOutputs
.clear();
983 foreach (StringSet::const_iterator
, i
, outputs
)
984 if (wantedOutputs
.find(*i
) == wantedOutputs
.end()) {
985 wantedOutputs
.insert(*i
);
991 void DerivationGoal::init()
995 if (settings
.readOnlyMode
)
996 throw Error(format("cannot build derivation `%1%' - no write access to the store") % drvPath
);
998 /* The first thing to do is to make sure that the derivation
999 exists. If it doesn't, it may be created through a
1001 if (buildMode
== bmNormal
&& worker
.store
.isValidPath(drvPath
)) {
1006 addWaitee(worker
.makeSubstitutionGoal(drvPath
));
1008 state
= &DerivationGoal::haveDerivation
;
1012 void DerivationGoal::haveDerivation()
1014 trace("loading derivation");
1016 if (nrFailed
!= 0) {
1017 printMsg(lvlError
, format("cannot build missing derivation ‘%1%’") % drvPath
);
1018 done(BuildResult::MiscFailure
);
1022 /* `drvPath' should already be a root, but let's be on the safe
1023 side: if the user forgot to make it a root, we wouldn't want
1024 things being garbage collected while we're busy. */
1025 worker
.store
.addTempRoot(drvPath
);
1027 assert(worker
.store
.isValidPath(drvPath
));
1029 /* Get the derivation. */
1030 drv
= derivationFromPath(worker
.store
, drvPath
);
1032 foreach (DerivationOutputs::iterator
, i
, drv
.outputs
)
1033 worker
.store
.addTempRoot(i
->second
.path
);
1035 /* Check what outputs paths are not already valid. */
1036 PathSet invalidOutputs
= checkPathValidity(false, buildMode
== bmRepair
);
1038 /* If they are all valid, then we're done. */
1039 if (invalidOutputs
.size() == 0 && buildMode
== bmNormal
) {
1040 done(BuildResult::AlreadyValid
);
1044 /* Check whether any output previously failed to build. If so,
1046 foreach (PathSet::iterator
, i
, invalidOutputs
)
1047 if (pathFailed(*i
)) return;
1049 /* We are first going to try to create the invalid output paths
1050 through substitutes. If that doesn't work, we'll build
1052 if (settings
.useSubstitutes
&& substitutesAllowed(drv
))
1053 foreach (PathSet::iterator
, i
, invalidOutputs
)
1054 addWaitee(worker
.makeSubstitutionGoal(*i
, buildMode
== bmRepair
));
1056 if (waitees
.empty()) /* to prevent hang (no wake-up event) */
1057 outputsSubstituted();
1059 state
= &DerivationGoal::outputsSubstituted
;
1063 void DerivationGoal::outputsSubstituted()
1065 trace("all outputs substituted (maybe)");
1067 if (nrFailed
> 0 && nrFailed
> nrNoSubstituters
+ nrIncompleteClosure
&& !settings
.tryFallback
)
1068 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
);
1070 /* If the substitutes form an incomplete closure, then we should
1071 build the dependencies of this derivation, but after that, we
1072 can still use the substitutes for this derivation itself. */
1073 if (nrIncompleteClosure
> 0 && !retrySubstitution
) retrySubstitution
= true;
1075 nrFailed
= nrNoSubstituters
= nrIncompleteClosure
= 0;
1078 needRestart
= false;
1083 unsigned int nrInvalid
= checkPathValidity(false, buildMode
== bmRepair
).size();
1084 if (buildMode
== bmNormal
&& nrInvalid
== 0) {
1085 done(BuildResult::Substituted
);
1088 if (buildMode
== bmRepair
&& nrInvalid
== 0) {
1092 if (buildMode
== bmCheck
&& nrInvalid
> 0)
1093 throw Error(format("some outputs of `%1%' are not valid, so checking is not possible") % drvPath
);
1095 /* Otherwise, at least one of the output paths could not be
1096 produced using a substitute. So we have to build instead. */
1098 /* Make sure checkPathValidity() from now on checks all
1100 wantedOutputs
= PathSet();
1102 /* The inputs must be built before we can build this goal. */
1103 foreach (DerivationInputs::iterator
, i
, drv
.inputDrvs
)
1104 addWaitee(worker
.makeDerivationGoal(i
->first
, i
->second
, buildMode
== bmRepair
? bmRepair
: bmNormal
));
1106 foreach (PathSet::iterator
, i
, drv
.inputSrcs
)
1107 addWaitee(worker
.makeSubstitutionGoal(*i
));
1109 if (waitees
.empty()) /* to prevent hang (no wake-up event) */
1112 state
= &DerivationGoal::inputsRealised
;
1116 void DerivationGoal::repairClosure()
1118 /* If we're repairing, we now know that our own outputs are valid.
1119 Now check whether the other paths in the outputs closure are
1120 good. If not, then start derivation goals for the derivations
1121 that produced those outputs. */
1123 /* Get the output closure. */
1124 PathSet outputClosure
;
1125 foreach (DerivationOutputs::iterator
, i
, drv
.outputs
) {
1126 if (!wantOutput(i
->first
, wantedOutputs
)) continue;
1127 computeFSClosure(worker
.store
, i
->second
.path
, outputClosure
);
1130 /* Filter out our own outputs (which we have already checked). */
1131 foreach (DerivationOutputs::iterator
, i
, drv
.outputs
)
1132 outputClosure
.erase(i
->second
.path
);
1134 /* Get all dependencies of this derivation so that we know which
1135 derivation is responsible for which path in the output
1137 PathSet inputClosure
;
1138 computeFSClosure(worker
.store
, drvPath
, inputClosure
);
1139 std::map
<Path
, Path
> outputsToDrv
;
1140 foreach (PathSet::iterator
, i
, inputClosure
)
1141 if (isDerivation(*i
)) {
1142 Derivation drv
= derivationFromPath(worker
.store
, *i
);
1143 foreach (DerivationOutputs::iterator
, j
, drv
.outputs
)
1144 outputsToDrv
[j
->second
.path
] = *i
;
1147 /* Check each path (slow!). */
1149 foreach (PathSet::iterator
, i
, outputClosure
) {
1150 if (worker
.store
.pathContentsGood(*i
)) continue;
1151 printMsg(lvlError
, format("found corrupted or missing path `%1%' in the output closure of `%2%'") % *i
% drvPath
);
1152 Path drvPath2
= outputsToDrv
[*i
];
1154 addWaitee(worker
.makeSubstitutionGoal(*i
, true));
1156 addWaitee(worker
.makeDerivationGoal(drvPath2
, PathSet(), bmRepair
));
1159 if (waitees
.empty()) {
1160 done(BuildResult::AlreadyValid
);
1164 state
= &DerivationGoal::closureRepaired
;
1168 void DerivationGoal::closureRepaired()
1170 trace("closure repaired");
1172 throw Error(format("some paths in the output closure of derivation ‘%1%’ could not be repaired") % drvPath
);
1173 done(BuildResult::AlreadyValid
);
1177 void DerivationGoal::inputsRealised()
1179 trace("all inputs realised");
1181 if (nrFailed
!= 0) {
1183 format("cannot build derivation `%1%': %2% dependencies couldn't be built")
1184 % drvPath
% nrFailed
);
1185 done(BuildResult::DependencyFailed
);
1189 if (retrySubstitution
) {
1194 /* Gather information necessary for computing the closure and/or
1195 running the build hook. */
1197 /* The outputs are referenceable paths. */
1198 foreach (DerivationOutputs::iterator
, i
, drv
.outputs
) {
1199 debug(format("building path `%1%'") % i
->second
.path
);
1200 allPaths
.insert(i
->second
.path
);
1203 /* Determine the full set of input paths. */
1205 /* First, the input derivations. */
1206 foreach (DerivationInputs::iterator
, i
, drv
.inputDrvs
) {
1207 /* Add the relevant output closures of the input derivation
1208 `*i' as input paths. Only add the closures of output paths
1209 that are specified as inputs. */
1210 assert(worker
.store
.isValidPath(i
->first
));
1211 Derivation inDrv
= derivationFromPath(worker
.store
, i
->first
);
1212 foreach (StringSet::iterator
, j
, i
->second
)
1213 if (inDrv
.outputs
.find(*j
) != inDrv
.outputs
.end())
1214 computeFSClosure(worker
.store
, inDrv
.outputs
[*j
].path
, inputPaths
);
1217 format("derivation `%1%' requires non-existent output `%2%' from input derivation `%3%'")
1218 % drvPath
% *j
% i
->first
);
1221 /* Second, the input sources. */
1222 foreach (PathSet::iterator
, i
, drv
.inputSrcs
)
1223 computeFSClosure(worker
.store
, *i
, inputPaths
);
1225 debug(format("added input paths %1%") % showPaths(inputPaths
));
1227 allPaths
.insert(inputPaths
.begin(), inputPaths
.end());
1229 /* Is this a fixed-output derivation? */
1231 for (auto & i
: drv
.outputs
)
1232 if (i
.second
.hash
== "") fixedOutput
= false;
1234 /* Don't repeat fixed-output derivations since they're already
1235 verified by their output hash.*/
1236 nrRounds
= fixedOutput
? 1 : settings
.get("build-repeat", 0) + 1;
1238 /* Okay, try to build. Note that here we don't wait for a build
1239 slot to become available, since we don't need one if there is a
1241 state
= &DerivationGoal::tryToBuild
;
1242 worker
.wakeUp(shared_from_this());
1246 static bool canBuildLocally(const string
& platform
)
1248 return platform
== settings
.thisSystem
1250 || (platform
== "i686-linux" && settings
.thisSystem
== "x86_64-linux")
1251 || (platform
== "armhf-linux" && settings
.thisSystem
== "aarch64-linux")
1257 static string
get(const StringPairs
& map
, const string
& key
, const string
& def
= "")
1259 StringPairs::const_iterator i
= map
.find(key
);
1260 return i
== map
.end() ? def
: i
->second
;
1264 bool willBuildLocally(const Derivation
& drv
)
1266 return get(drv
.env
, "preferLocalBuild") == "1" && canBuildLocally(drv
.platform
);
1270 bool substitutesAllowed(const Derivation
& drv
)
1272 return get(drv
.env
, "allowSubstitutes", "1") == "1";
1276 void DerivationGoal::tryToBuild()
1278 trace("trying to build");
1280 /* Check for the possibility that some other goal in this process
1281 has locked the output since we checked in haveDerivation().
1282 (It can't happen between here and the lockPaths() call below
1283 because we're not allowing multi-threading.) If so, put this
1284 goal to sleep until another goal finishes, then try again. */
1285 foreach (DerivationOutputs::iterator
, i
, drv
.outputs
)
1286 if (pathIsLockedByMe(i
->second
.path
)) {
1287 debug(format("putting derivation `%1%' to sleep because `%2%' is locked by another goal")
1288 % drvPath
% i
->second
.path
);
1289 worker
.waitForAnyGoal(shared_from_this());
1293 /* Obtain locks on all output paths. The locks are automatically
1294 released when we exit this function or the client crashes. If we
1295 can't acquire the lock, then continue; hopefully some other
1296 goal can start a build, and if not, the main loop will sleep a
1297 few seconds and then retry this goal. */
1298 if (!outputLocks
.lockPaths(outputPaths(drv
), "", false)) {
1299 worker
.waitForAWhile(shared_from_this());
1303 /* Now check again whether the outputs are valid. This is because
1304 another process may have started building in parallel. After
1305 it has finished and released the locks, we can (and should)
1306 reuse its results. (Strictly speaking the first check can be
1307 omitted, but that would be less efficient.) Note that since we
1308 now hold the locks on the output paths, no other process can
1309 build this derivation, so no further checks are necessary. */
1310 validPaths
= checkPathValidity(true, buildMode
== bmRepair
);
1311 if (buildMode
!= bmCheck
&& validPaths
.size() == drv
.outputs
.size()) {
1312 debug(format("skipping build of derivation `%1%', someone beat us to it") % drvPath
);
1313 outputLocks
.setDeletion(true);
1314 done(BuildResult::AlreadyValid
);
1318 missingPaths
= outputPaths(drv
);
1319 if (buildMode
!= bmCheck
)
1320 foreach (PathSet::iterator
, i
, validPaths
) missingPaths
.erase(*i
);
1322 /* If any of the outputs already exist but are not valid, delete
1324 foreach (DerivationOutputs::iterator
, i
, drv
.outputs
) {
1325 Path path
= i
->second
.path
;
1326 if (worker
.store
.isValidPath(path
)) continue;
1327 if (!pathExists(path
)) continue;
1328 debug(format("removing invalid path `%1%'") % path
);
1332 /* Check again whether any output previously failed to build,
1333 because some other process may have tried and failed before we
1334 acquired the lock. */
1335 foreach (DerivationOutputs::iterator
, i
, drv
.outputs
)
1336 if (pathFailed(i
->second
.path
)) return;
1338 /* Don't do a remote build if the derivation has the attribute
1339 `preferLocalBuild' set. Also, check and repair modes are only
1340 supported for local builds. */
1341 bool buildLocally
= buildMode
!= bmNormal
|| willBuildLocally(drv
);
1343 /* Is the build hook willing to accept this job? */
1344 if (!buildLocally
) {
1345 switch (tryBuildHook()) {
1347 /* Yes, it has started doing so. Wait until we get
1348 EOF from the hook. */
1349 state
= &DerivationGoal::buildDone
;
1352 /* Not now; wait until at least one child finishes or
1353 the wake-up timeout expires. */
1354 worker
.waitForAWhile(shared_from_this());
1355 outputLocks
.unlock();
1358 /* We should do it ourselves. */
1363 /* Make sure that we are allowed to start a build. If this
1364 derivation prefers to be done locally, do it even if
1365 maxBuildJobs is 0. */
1366 unsigned int curBuilds
= worker
.getNrLocalBuilds();
1367 if (curBuilds
>= settings
.maxBuildJobs
&& !(buildLocally
&& curBuilds
== 0)) {
1368 worker
.waitForBuildSlot(shared_from_this());
1369 outputLocks
.unlock();
1375 /* Okay, we have to build. */
1378 } catch (BuildError
& e
) {
1379 printMsg(lvlError
, e
.msg());
1380 outputLocks
.unlock();
1381 buildUser
.release();
1382 if (settings
.printBuildTrace
)
1383 printMsg(lvlError
, format("@ build-failed %1% - %2% %3%")
1384 % drvPath
% 0 % e
.msg());
1385 worker
.permanentFailure
= true;
1386 done(BuildResult::InputRejected
, e
.msg());
1390 /* This state will be reached when we get EOF on the child's
1392 state
= &DerivationGoal::buildDone
;
1396 void replaceValidPath(const Path
& storePath
, const Path tmpPath
)
1398 /* We can't atomically replace storePath (the original) with
1399 tmpPath (the replacement), so we have to move it out of the
1400 way first. We'd better not be interrupted here, because if
1401 we're repairing (say) Glibc, we end up with a broken system. */
1402 Path oldPath
= (format("%1%.old-%2%-%3%") % storePath
% getpid() % rand()).str();
1403 if (pathExists(storePath
))
1404 rename(storePath
.c_str(), oldPath
.c_str());
1405 if (rename(tmpPath
.c_str(), storePath
.c_str()) == -1)
1406 throw SysError(format("moving `%1%' to `%2%'") % tmpPath
% storePath
);
1407 if (pathExists(oldPath
))
1408 deletePath(oldPath
);
1412 MakeError(NotDeterministic
, BuildError
)
1415 void DerivationGoal::buildDone()
1417 trace("build done");
1419 /* Since we got an EOF on the logger pipe, the builder is presumed
1420 to have terminated. In fact, the builder could also have
1421 simply have closed its end of the pipe --- just don't do that
1426 savedPid
= hook
->pid
;
1427 status
= hook
->pid
.wait(true);
1429 /* !!! this could block! security problem! solution: kill the
1432 status
= pid
.wait(true);
1435 debug(format("builder process for `%1%' finished") % drvPath
);
1437 /* So the child is gone now. */
1438 worker
.childTerminated(savedPid
);
1440 /* Close the read side of the logger pipe. */
1442 hook
->builderOut
.readSide
.close();
1443 hook
->fromHook
.readSide
.close();
1445 else builderOut
.readSide
.close();
1447 /* Close the log file. */
1450 /* When running under a build user, make sure that all processes
1451 running under that uid are gone. This is to prevent a
1452 malicious user from leaving behind a process that keeps files
1453 open and modifies them after they have been chown'ed to
1455 if (buildUser
.enabled()) buildUser
.kill();
1457 bool diskFull
= false;
1461 /* Check the exit status. */
1462 if (!statusOk(status
)) {
1464 /* Heuristically check whether the build failure may have
1465 been caused by a disk full condition. We have no way
1466 of knowing whether the build actually got an ENOSPC.
1467 So instead, check if the disk is (nearly) full now. If
1468 so, we don't mark this build as a permanent failure. */
1470 unsigned long long required
= 8ULL * 1024 * 1024; // FIXME: make configurable
1472 if (statvfs(settings
.nixStore
.c_str(), &st
) == 0 &&
1473 (unsigned long long) st
.f_bavail
* st
.f_bsize
< required
)
1475 if (statvfs(tmpDir
.c_str(), &st
) == 0 &&
1476 (unsigned long long) st
.f_bavail
* st
.f_bsize
< required
)
1480 deleteTmpDir(false);
1482 /* Move paths out of the chroot for easier debugging of
1484 if (useChroot
&& buildMode
== bmNormal
)
1485 foreach (PathSet::iterator
, i
, missingPaths
)
1486 if (pathExists(chrootRootDir
+ *i
))
1487 rename((chrootRootDir
+ *i
).c_str(), i
->c_str());
1490 printMsg(lvlError
, "note: build failure may have been caused by lack of free disk space");
1492 throw BuildError(format("builder for `%1%' %2%")
1493 % drvPath
% statusToString(status
));
1496 /* Compute the FS closure of the outputs and register them as
1500 if (buildMode
== bmCheck
) {
1501 done(BuildResult::Built
);
1505 /* Delete unused redirected outputs (when doing hash rewriting). */
1506 foreach (RedirectedOutputs::iterator
, i
, redirectedOutputs
)
1507 if (pathExists(i
->second
)) deletePath(i
->second
);
1509 /* Delete the chroot (if we were using one). */
1510 autoDelChroot
.reset(); /* this runs the destructor */
1514 /* Repeat the build if necessary. */
1515 if (curRound
++ < nrRounds
) {
1516 outputLocks
.unlock();
1517 buildUser
.release();
1518 state
= &DerivationGoal::tryToBuild
;
1519 worker
.wakeUp(shared_from_this());
1523 /* It is now safe to delete the lock files, since all future
1524 lockers will see that the output paths are valid; they will
1525 not create new lock files with the same names as the old
1526 (unlinked) lock files. */
1527 outputLocks
.setDeletion(true);
1528 outputLocks
.unlock();
1530 } catch (BuildError
& e
) {
1532 printMsg(lvlError
, e
.msg());
1533 outputLocks
.unlock();
1534 buildUser
.release();
1536 BuildResult::Status st
= BuildResult::MiscFailure
;
1538 if (hook
&& WIFEXITED(status
) && WEXITSTATUS(status
) == 101) {
1539 if (settings
.printBuildTrace
)
1540 printMsg(lvlError
, format("@ build-failed %1% - timeout") % drvPath
);
1541 st
= BuildResult::TimedOut
;
1544 else if (hook
&& (!WIFEXITED(status
) || WEXITSTATUS(status
) != 100)) {
1545 if (settings
.printBuildTrace
)
1546 printMsg(lvlError
, format("@ hook-failed %1% - %2% %3%")
1547 % drvPath
% status
% e
.msg());
1551 if (settings
.printBuildTrace
)
1552 printMsg(lvlError
, format("@ build-failed %1% - %2% %3%")
1553 % drvPath
% 1 % e
.msg());
1556 statusOk(status
) ? BuildResult::OutputRejected
:
1557 fixedOutput
|| diskFull
? BuildResult::TransientFailure
:
1558 BuildResult::PermanentFailure
;
1560 /* Register the outputs of this build as "failed" so we
1561 won't try to build them again (negative caching).
1562 However, don't do this for fixed-output derivations,
1563 since they're likely to fail for transient reasons
1564 (e.g., fetchurl not being able to access the network).
1565 Hook errors (like communication problems with the
1566 remote machine) shouldn't be cached either. */
1567 if (settings
.cacheFailure
&& !fixedOutput
&& !diskFull
)
1568 foreach (DerivationOutputs::iterator
, i
, drv
.outputs
)
1569 worker
.store
.registerFailedPath(i
->second
.path
);
1576 /* Release the build user, if applicable. */
1577 buildUser
.release();
1579 if (settings
.printBuildTrace
)
1580 printMsg(lvlError
, format("@ build-succeeded %1% -") % drvPath
);
1582 done(BuildResult::Built
);
1586 HookReply
DerivationGoal::tryBuildHook()
1588 if (!settings
.useBuildHook
) return rpDecline
;
1591 worker
.hook
= std::shared_ptr
<HookInstance
>(new HookInstance
);
1593 /* Tell the hook about system features (beyond the system type)
1594 required from the build machine. (The hook could parse the
1595 drv file itself, but this is easier.) */
1596 Strings features
= tokenizeString
<Strings
>(get(drv
.env
, "requiredSystemFeatures"));
1597 foreach (Strings::iterator
, i
, features
) checkStoreName(*i
); /* !!! abuse */
1599 /* Send the request to the hook. */
1600 writeLine(worker
.hook
->toHook
.writeSide
, (format("%1% %2% %3% %4%")
1601 % (worker
.getNrLocalBuilds() < settings
.maxBuildJobs
? "1" : "0")
1602 % drv
.platform
% drvPath
% concatStringsSep(",", features
)).str());
1604 /* Read the first line of input, which should be a word indicating
1605 whether the hook wishes to perform the build. */
1608 string s
= readLine(worker
.hook
->fromHook
.readSide
);
1609 if (string(s
, 0, 2) == "# ") {
1610 reply
= string(s
, 2);
1617 debug(format("hook reply is `%1%'") % reply
);
1619 if (reply
== "decline" || reply
== "postpone")
1620 return reply
== "decline" ? rpDecline
: rpPostpone
;
1621 else if (reply
!= "accept")
1622 throw Error(format("bad hook reply `%1%'") % reply
);
1624 printMsg(lvlTalkative
, format("using hook to build path(s) %1%") % showPaths(missingPaths
));
1627 worker
.hook
.reset();
1629 /* Tell the hook all the inputs that have to be copied to the
1630 remote system. This unfortunately has to contain the entire
1631 derivation closure to ensure that the validity invariant holds
1632 on the remote system. (I.e., it's unfortunate that we have to
1633 list it since the remote system *probably* already has it.) */
1635 allInputs
.insert(inputPaths
.begin(), inputPaths
.end());
1636 computeFSClosure(worker
.store
, drvPath
, allInputs
);
1639 foreach (PathSet::iterator
, i
, allInputs
) { s
+= *i
; s
+= ' '; }
1640 writeLine(hook
->toHook
.writeSide
, s
);
1642 /* Tell the hooks the missing outputs that have to be copied back
1643 from the remote system. */
1645 foreach (PathSet::iterator
, i
, missingPaths
) { s
+= *i
; s
+= ' '; }
1646 writeLine(hook
->toHook
.writeSide
, s
);
1648 hook
->toHook
.writeSide
.close();
1650 /* Create the log file and pipe. */
1651 Path logFile
= openLogFile();
1654 fds
.insert(hook
->fromHook
.readSide
);
1655 fds
.insert(hook
->builderOut
.readSide
);
1656 worker
.childStarted(shared_from_this(), hook
->pid
, fds
, false, true);
1658 if (settings
.printBuildTrace
)
1659 printMsg(lvlError
, format("@ build-started %1% - %2% %3% %4%")
1660 % drvPath
% drv
.platform
% logFile
% hook
->pid
);
1666 void chmod_(const Path
& path
, mode_t mode
)
1668 if (chmod(path
.c_str(), mode
) == -1)
1669 throw SysError(format("setting permissions on `%1%'") % path
);
1673 int childEntry(void * arg
)
1675 ((DerivationGoal
*) arg
)->runChild();
1680 void DerivationGoal::startBuilder()
1683 buildMode
== bmRepair
? "repairing path(s) %1%" :
1684 buildMode
== bmCheck
? "checking path(s) %1%" :
1685 nrRounds
> 1 ? "building path(s) %1% (round %2%/%3%)" :
1686 "building path(s) %1%");
1687 f
.exceptions(boost::io::all_error_bits
^ boost::io::too_many_args_bit
);
1688 startNest(nest
, lvlInfo
, f
% showPaths(missingPaths
) % curRound
% nrRounds
);
1690 /* Note: built-in builders are *not* running in a chroot environment so
1691 that we can easily implement them in Guile without having it as a
1692 derivation input (they are running under a separate build user,
1694 useChroot
= settings
.useChroot
&& !isBuiltin(drv
);
1696 /* Construct the environment passed to the builder. */
1699 /* Most shells initialise PATH to some default (/bin:/usr/bin:...) when
1700 PATH is not set. We don't want this, so we fill it in with some dummy
1702 env
["PATH"] = "/path-not-set";
1704 /* Set HOME to a non-existing path to prevent certain programs from using
1705 /etc/passwd (or NIS, or whatever) to locate the home directory (for
1706 example, wget looks for ~/.wgetrc). I.e., these tools use /etc/passwd
1707 if HOME is not set, but they will just assume that the settings file
1708 they are looking for does not exist if HOME is set but points to some
1709 non-existing path. */
1710 Path homeDir
= "/homeless-shelter";
1711 env
["HOME"] = homeDir
;
1713 /* Tell the builder where the store is. Usually they
1714 shouldn't care, but this is useful for purity checking (e.g.,
1715 the compiler or linker might only want to accept paths to files
1716 in the store or in the build directory). */
1717 env
["NIX_STORE"] = settings
.nixStore
;
1719 /* The maximum number of cores to utilize for parallel building. */
1720 env
["NIX_BUILD_CORES"] = (format("%d") % settings
.buildCores
).str();
1722 /* Add all bindings specified in the derivation. */
1723 foreach (StringPairs::iterator
, i
, drv
.env
)
1724 env
[i
->first
] = i
->second
;
1726 /* Create a temporary directory where the build will take
1728 auto drvName
= storePathToName(drvPath
);
1729 tmpDir
= createTempDir("", "guix-build-" + drvName
, false, false, 0700);
1731 /* In a sandbox, for determinism, always use the same temporary
1733 tmpDirInSandbox
= useChroot
? canonPath("/tmp", true) + "/guix-build-" + drvName
+ "-0" : tmpDir
;
1735 /* For convenience, set an environment pointing to the top build
1737 env
["NIX_BUILD_TOP"] = tmpDirInSandbox
;
1739 /* Also set TMPDIR and variants to point to this directory. */
1740 env
["TMPDIR"] = env
["TEMPDIR"] = env
["TMP"] = env
["TEMP"] = tmpDirInSandbox
;
1742 /* Explicitly set PWD to prevent problems with chroot builds. In
1743 particular, dietlibc cannot figure out the cwd because the
1744 inode of the current directory doesn't appear in .. (because
1745 getdents returns the inode of the mount point). */
1746 env
["PWD"] = tmpDirInSandbox
;
1748 /* Compatibility hack with Nix <= 0.7: if this is a fixed-output
1749 derivation, tell the builder, so that for instance `fetchurl'
1750 can skip checking the output. On older Nixes, this environment
1751 variable won't be set, so `fetchurl' will do the check. */
1752 if (fixedOutput
) env
["NIX_OUTPUT_CHECKED"] = "1";
1754 /* *Only* if this is a fixed-output derivation, propagate the
1755 values of the environment variables specified in the
1756 `impureEnvVars' attribute to the builder. This allows for
1757 instance environment variables for proxy configuration such as
1758 `http_proxy' to be easily passed to downloaders like
1759 `fetchurl'. Passing such environment variables from the caller
1760 to the builder is generally impure, but the output of
1761 fixed-output derivations is by definition pure (since we
1762 already know the cryptographic hash of the output). */
1764 Strings varNames
= tokenizeString
<Strings
>(get(drv
.env
, "impureEnvVars"));
1765 foreach (Strings::iterator
, i
, varNames
) env
[*i
] = getEnv(*i
);
1768 /* The `exportReferencesGraph' feature allows the references graph
1769 to be passed to a builder. This attribute should be a list of
1770 pairs [name1 path1 name2 path2 ...]. The references graph of
1771 each `pathN' will be stored in a text file `nameN' in the
1772 temporary build directory. The text files have the format used
1773 by `nix-store --register-validity'. However, the deriver
1774 fields are left empty. */
1775 string s
= get(drv
.env
, "exportReferencesGraph");
1776 Strings ss
= tokenizeString
<Strings
>(s
);
1777 if (ss
.size() % 2 != 0)
1778 throw BuildError(format("odd number of tokens in `exportReferencesGraph': `%1%'") % s
);
1779 for (Strings::iterator i
= ss
.begin(); i
!= ss
.end(); ) {
1780 string fileName
= *i
++;
1781 checkStoreName(fileName
); /* !!! abuse of this function */
1783 /* Check that the store path is valid. */
1784 Path storePath
= *i
++;
1785 if (!isInStore(storePath
))
1786 throw BuildError(format("`exportReferencesGraph' contains a non-store path `%1%'")
1788 storePath
= toStorePath(storePath
);
1789 if (!worker
.store
.isValidPath(storePath
))
1790 throw BuildError(format("`exportReferencesGraph' contains an invalid path `%1%'")
1793 /* If there are derivations in the graph, then include their
1794 outputs as well. This is useful if you want to do things
1795 like passing all build-time dependencies of some path to a
1796 derivation that builds a NixOS DVD image. */
1797 PathSet paths
, paths2
;
1798 computeFSClosure(worker
.store
, storePath
, paths
);
1801 foreach (PathSet::iterator
, j
, paths2
) {
1802 if (isDerivation(*j
)) {
1803 Derivation drv
= derivationFromPath(worker
.store
, *j
);
1804 foreach (DerivationOutputs::iterator
, k
, drv
.outputs
)
1805 computeFSClosure(worker
.store
, k
->second
.path
, paths
);
1809 /* Write closure info to `fileName'. */
1810 writeFile(tmpDir
+ "/" + fileName
,
1811 worker
.store
.makeValidityRegistration(paths
, false, false));
1815 /* If `build-users-group' is not empty, then we have to build as
1816 one of the members of that group. */
1817 if (settings
.buildUsersGroup
!= "") {
1818 buildUser
.acquire();
1819 assert(buildUser
.getUID() != 0);
1820 assert(buildUser
.getGID() != 0);
1822 /* Make sure that no other processes are executing under this
1826 /* Change ownership of the temporary build directory. */
1827 if (chown(tmpDir
.c_str(), buildUser
.getUID(), buildUser
.getGID()) == -1)
1828 throw SysError(format("cannot change ownership of '%1%'") % tmpDir
);
1833 /* Create a temporary directory in which we set up the chroot
1834 environment using bind-mounts. We put it in the store
1835 to ensure that we can create hard-links to non-directory
1836 inputs in the fake store in the chroot (see below). */
1837 chrootRootDir
= drvPath
+ ".chroot";
1838 if (pathExists(chrootRootDir
)) deletePath(chrootRootDir
);
1840 /* Clean up the chroot directory automatically. */
1841 autoDelChroot
= std::shared_ptr
<AutoDelete
>(new AutoDelete(chrootRootDir
));
1843 printMsg(lvlChatty
, format("setting up chroot environment in `%1%'") % chrootRootDir
);
1845 if (mkdir(chrootRootDir
.c_str(), 0750) == -1)
1846 throw SysError(format("cannot create ‘%1%’") % chrootRootDir
);
1848 if (chown(chrootRootDir
.c_str(), 0, buildUser
.getGID()) == -1)
1849 throw SysError(format("cannot change ownership of ‘%1%’") % chrootRootDir
);
1851 /* Create a writable /tmp in the chroot. Many builders need
1852 this. (Of course they should really respect $TMPDIR
1854 Path chrootTmpDir
= chrootRootDir
+ "/tmp";
1855 createDirs(chrootTmpDir
);
1856 chmod_(chrootTmpDir
, 01777);
1858 /* Create a /etc/passwd with entries for the build user and the
1859 nobody account. The latter is kind of a hack to support
1861 createDirs(chrootRootDir
+ "/etc");
1863 writeFile(chrootRootDir
+ "/etc/passwd",
1865 "nixbld:x:%1%:%2%:Nix build user:/:/noshell\n"
1866 "nobody:x:65534:65534:Nobody:/:/noshell\n")
1867 % (buildUser
.enabled() ? buildUser
.getUID() : getuid())
1868 % (buildUser
.enabled() ? buildUser
.getGID() : getgid())).str());
1870 /* Declare the build user's group so that programs get a consistent
1871 view of the system (e.g., "id -gn"). */
1872 writeFile(chrootRootDir
+ "/etc/group",
1873 (format("nixbld:!:%1%:\n")
1874 % (buildUser
.enabled() ? buildUser
.getGID() : getgid())).str());
1876 /* Create /etc/hosts with localhost entry. */
1878 writeFile(chrootRootDir
+ "/etc/hosts", "127.0.0.1 localhost\n");
1880 /* Bind-mount a user-configurable set of directories from the
1881 host file system. */
1882 PathSet dirs
= tokenizeString
<StringSet
>(settings
.get("build-chroot-dirs", string(DEFAULT_CHROOT_DIRS
)));
1883 PathSet dirs2
= tokenizeString
<StringSet
>(settings
.get("build-extra-chroot-dirs", string("")));
1884 dirs
.insert(dirs2
.begin(), dirs2
.end());
1885 for (auto & i
: dirs
) {
1886 size_t p
= i
.find('=');
1887 if (p
== string::npos
)
1888 dirsInChroot
[i
] = i
;
1890 dirsInChroot
[string(i
, 0, p
)] = string(i
, p
+ 1);
1892 dirsInChroot
[tmpDirInSandbox
] = tmpDir
;
1894 /* Make the closure of the inputs available in the chroot,
1895 rather than the whole store. This prevents any access
1896 to undeclared dependencies. Directories are bind-mounted,
1897 while other inputs are hard-linked (since only directories
1898 can be bind-mounted). !!! As an extra security
1899 precaution, make the fake store only writable by the
1901 Path chrootStoreDir
= chrootRootDir
+ settings
.nixStore
;
1902 createDirs(chrootStoreDir
);
1903 chmod_(chrootStoreDir
, 01775);
1905 if (chown(chrootStoreDir
.c_str(), 0, buildUser
.getGID()) == -1)
1906 throw SysError(format("cannot change ownership of ‘%1%’") % chrootStoreDir
);
1908 foreach (PathSet::iterator
, i
, inputPaths
) {
1910 if (lstat(i
->c_str(), &st
))
1911 throw SysError(format("getting attributes of path `%1%'") % *i
);
1912 if (S_ISDIR(st
.st_mode
))
1913 dirsInChroot
[*i
] = *i
;
1915 Path p
= chrootRootDir
+ *i
;
1916 if (link(i
->c_str(), p
.c_str()) == -1) {
1917 /* Hard-linking fails if we exceed the maximum
1918 link count on a file (e.g. 32000 of ext3),
1919 which is quite possible after a `nix-store
1921 if (errno
!= EMLINK
)
1922 throw SysError(format("linking `%1%' to `%2%'") % p
% *i
);
1925 StringSource
source(sink
.s
);
1926 restorePath(p
, source
);
1929 regularInputPaths
.insert(*i
);
1933 /* If we're repairing, checking or rebuilding part of a
1934 multiple-outputs derivation, it's possible that we're
1935 rebuilding a path that is in settings.dirsInChroot
1936 (typically the dependencies of /bin/sh). Throw them
1938 for (auto & i
: drv
.outputs
)
1939 dirsInChroot
.erase(i
.second
.path
);
1942 throw Error("chroot builds are not supported on this platform");
1948 if (pathExists(homeDir
))
1949 throw Error(format("directory `%1%' exists; please remove it") % homeDir
);
1951 /* We're not doing a chroot build, but we have some valid
1952 output paths. Since we can't just overwrite or delete
1953 them, we have to do hash rewriting: i.e. in the
1954 environment/arguments passed to the build, we replace the
1955 hashes of the valid outputs with unique dummy strings;
1956 after the build, we discard the redirected outputs
1957 corresponding to the valid outputs, and rewrite the
1958 contents of the new outputs to replace the dummy strings
1959 with the actual hashes. */
1960 if (validPaths
.size() > 0)
1961 foreach (PathSet::iterator
, i
, validPaths
)
1964 /* If we're repairing, then we don't want to delete the
1965 corrupt outputs in advance. So rewrite them as well. */
1966 if (buildMode
== bmRepair
)
1967 foreach (PathSet::iterator
, i
, missingPaths
)
1968 if (worker
.store
.isValidPath(*i
) && pathExists(*i
)) {
1970 redirectedBadOutputs
.insert(*i
);
1975 /* Run the builder. */
1976 printMsg(lvlChatty
, format("executing builder `%1%'") % drv
.builder
);
1978 /* Create the log file. */
1979 Path logFile
= openLogFile();
1981 /* Create a pipe to get the output of the builder. */
1982 builderOut
.create();
1984 /* Fork a child to build the package. Note that while we
1985 currently use forks to run and wait for the children, it
1986 shouldn't be hard to use threads for this on systems where
1987 fork() is unavailable or inefficient.
1989 If we're building in a chroot, then also set up private
1990 namespaces for the build:
1992 - The PID namespace causes the build to start as PID 1.
1993 Processes outside of the chroot are not visible to those on
1994 the inside, but processes inside the chroot are visible from
1995 the outside (though with different PIDs).
1997 - The private mount namespace ensures that all the bind mounts
1998 we do will only show up in this process and its children, and
1999 will disappear automatically when we're done.
2001 - The private network namespace ensures that the builder cannot
2002 talk to the outside world (or vice versa). It only has a
2003 private loopback interface.
2005 - The IPC namespace prevents the builder from communicating
2006 with outside processes using SysV IPC mechanisms (shared
2007 memory, message queues, semaphores). It also ensures that
2008 all IPC objects are destroyed when the builder exits.
2010 - The UTS namespace ensures that builders see a hostname of
2011 localhost rather than the actual hostname.
2015 char stack
[32 * 1024];
2016 int flags
= CLONE_NEWPID
| CLONE_NEWNS
| CLONE_NEWIPC
| CLONE_NEWUTS
| SIGCHLD
;
2017 if (!fixedOutput
) flags
|= CLONE_NEWNET
;
2018 /* Ensure proper alignment on the stack. On aarch64, it has to be 16
2020 pid
= clone(childEntry
,
2021 (char *)(((uintptr_t)stack
+ sizeof(stack
) - 8) & ~(uintptr_t)0xf),
2024 throw SysError("cloning builder process");
2029 if (pid
== 0) runChild();
2032 if (pid
== -1) throw SysError("unable to fork");
2035 pid
.setSeparatePG(true);
2036 builderOut
.writeSide
.close();
2037 worker
.childStarted(shared_from_this(), pid
,
2038 singleton
<set
<int> >(builderOut
.readSide
), true, true);
2040 /* Check if setting up the build environment failed. */
2041 string msg
= readLine(builderOut
.readSide
);
2042 if (!msg
.empty()) throw Error(msg
);
2044 if (settings
.printBuildTrace
) {
2045 printMsg(lvlError
, format("@ build-started %1% - %2% %3% %4%")
2046 % drvPath
% drv
.platform
% logFile
% pid
);
2052 void DerivationGoal::runChild()
2054 /* Warning: in the child we should absolutely not make any SQLite
2063 commonChildInit(builderOut
);
2067 /* Initialise the loopback interface. */
2068 AutoCloseFD
fd(socket(PF_INET
, SOCK_DGRAM
, IPPROTO_IP
));
2069 if (fd
== -1) throw SysError("cannot open IP socket");
2072 strcpy(ifr
.ifr_name
, "lo");
2073 ifr
.ifr_flags
= IFF_UP
| IFF_LOOPBACK
| IFF_RUNNING
;
2074 if (ioctl(fd
, SIOCSIFFLAGS
, &ifr
) == -1)
2075 throw SysError("cannot set loopback interface flags");
2079 /* Set the hostname etc. to fixed values. */
2080 char hostname
[] = "localhost";
2081 if (sethostname(hostname
, sizeof(hostname
)) == -1)
2082 throw SysError("cannot set host name");
2083 char domainname
[] = "(none)"; // kernel default
2084 if (setdomainname(domainname
, sizeof(domainname
)) == -1)
2085 throw SysError("cannot set domain name");
2087 /* Make all filesystems private. This is necessary
2088 because subtrees may have been mounted as "shared"
2089 (MS_SHARED). (Systemd does this, for instance.) Even
2090 though we have a private mount namespace, mounting
2091 filesystems on top of a shared subtree still propagates
2092 outside of the namespace. Making a subtree private is
2093 local to the namespace, though, so setting MS_PRIVATE
2094 does not affect the outside world. */
2095 if (mount(0, "/", 0, MS_REC
|MS_PRIVATE
, 0) == -1) {
2096 throw SysError("unable to make ‘/’ private mount");
2099 /* Bind-mount chroot directory to itself, to treat it as a
2100 different filesystem from /, as needed for pivot_root. */
2101 if (mount(chrootRootDir
.c_str(), chrootRootDir
.c_str(), 0, MS_BIND
, 0) == -1)
2102 throw SysError(format("unable to bind mount ‘%1%’") % chrootRootDir
);
2104 /* Set up a nearly empty /dev, unless the user asked to
2105 bind-mount the host /dev. */
2107 if (dirsInChroot
.find("/dev") == dirsInChroot
.end()) {
2108 createDirs(chrootRootDir
+ "/dev/shm");
2109 createDirs(chrootRootDir
+ "/dev/pts");
2110 ss
.push_back("/dev/full");
2112 if (pathExists("/dev/kvm"))
2113 ss
.push_back("/dev/kvm");
2115 ss
.push_back("/dev/null");
2116 ss
.push_back("/dev/random");
2117 ss
.push_back("/dev/tty");
2118 ss
.push_back("/dev/urandom");
2119 ss
.push_back("/dev/zero");
2120 createSymlink("/proc/self/fd", chrootRootDir
+ "/dev/fd");
2121 createSymlink("/proc/self/fd/0", chrootRootDir
+ "/dev/stdin");
2122 createSymlink("/proc/self/fd/1", chrootRootDir
+ "/dev/stdout");
2123 createSymlink("/proc/self/fd/2", chrootRootDir
+ "/dev/stderr");
2126 /* Fixed-output derivations typically need to access the
2127 network, so give them access to /etc/resolv.conf and so
2130 ss
.push_back("/etc/resolv.conf");
2131 ss
.push_back("/etc/nsswitch.conf");
2132 ss
.push_back("/etc/services");
2133 ss
.push_back("/etc/hosts");
2136 for (auto & i
: ss
) dirsInChroot
[i
] = i
;
2138 /* Bind-mount all the directories from the "host"
2139 filesystem that we want in the chroot
2141 foreach (DirsInChroot::iterator
, i
, dirsInChroot
) {
2143 Path source
= i
->second
;
2144 Path target
= chrootRootDir
+ i
->first
;
2145 if (source
== "/proc") continue; // backwards compatibility
2146 debug(format("bind mounting `%1%' to `%2%'") % source
% target
);
2147 if (stat(source
.c_str(), &st
) == -1)
2148 throw SysError(format("getting attributes of path `%1%'") % source
);
2149 if (S_ISDIR(st
.st_mode
))
2152 createDirs(dirOf(target
));
2153 writeFile(target
, "");
2155 if (mount(source
.c_str(), target
.c_str(), "", MS_BIND
, 0) == -1)
2156 throw SysError(format("bind mount from `%1%' to `%2%' failed") % source
% target
);
2159 /* Bind a new instance of procfs on /proc to reflect our
2160 private PID namespace. */
2161 createDirs(chrootRootDir
+ "/proc");
2162 if (mount("none", (chrootRootDir
+ "/proc").c_str(), "proc", 0, 0) == -1)
2163 throw SysError("mounting /proc");
2165 /* Mount a new tmpfs on /dev/shm to ensure that whatever
2166 the builder puts in /dev/shm is cleaned up automatically. */
2167 if (pathExists("/dev/shm") && mount("none", (chrootRootDir
+ "/dev/shm").c_str(), "tmpfs", 0, 0) == -1)
2168 throw SysError("mounting /dev/shm");
2170 /* Mount a new devpts on /dev/pts. Note that this
2171 requires the kernel to be compiled with
2172 CONFIG_DEVPTS_MULTIPLE_INSTANCES=y (which is the case
2173 if /dev/ptx/ptmx exists). */
2174 if (pathExists("/dev/pts/ptmx") &&
2175 !pathExists(chrootRootDir
+ "/dev/ptmx")
2176 && dirsInChroot
.find("/dev/pts") == dirsInChroot
.end())
2178 if (mount("none", (chrootRootDir
+ "/dev/pts").c_str(), "devpts", 0, "newinstance,mode=0620") == -1)
2179 throw SysError("mounting /dev/pts");
2180 createSymlink("/dev/pts/ptmx", chrootRootDir
+ "/dev/ptmx");
2182 /* Make sure /dev/pts/ptmx is world-writable. With some
2183 Linux versions, it is created with permissions 0. */
2184 chmod_(chrootRootDir
+ "/dev/pts/ptmx", 0666);
2187 /* Do the chroot(). */
2188 if (chdir(chrootRootDir
.c_str()) == -1)
2189 throw SysError(format("cannot change directory to '%1%'") % chrootRootDir
);
2191 if (mkdir("real-root", 0) == -1)
2192 throw SysError("cannot create real-root directory");
2194 if (pivot_root(".", "real-root") == -1)
2195 throw SysError(format("cannot pivot old root directory onto '%1%'") % (chrootRootDir
+ "/real-root"));
2197 if (chroot(".") == -1)
2198 throw SysError(format("cannot change root directory to '%1%'") % chrootRootDir
);
2200 if (umount2("real-root", MNT_DETACH
) == -1)
2201 throw SysError("cannot unmount real root filesystem");
2203 if (rmdir("real-root") == -1)
2204 throw SysError("cannot remove real-root directory");
2208 if (chdir(tmpDirInSandbox
.c_str()) == -1)
2209 throw SysError(format("changing into `%1%'") % tmpDir
);
2211 /* Close all other file descriptors. */
2212 closeMostFDs(set
<int>());
2215 /* Change the personality to 32-bit if we're doing an
2216 i686-linux build on an x86_64-linux machine. */
2217 struct utsname utsbuf
;
2219 if (drv
.platform
== "i686-linux" &&
2220 (settings
.thisSystem
== "x86_64-linux" ||
2221 (!strcmp(utsbuf
.sysname
, "Linux") && !strcmp(utsbuf
.machine
, "x86_64")))) {
2222 if (personality(PER_LINUX32
) == -1)
2223 throw SysError("cannot set i686-linux personality");
2226 if (drv
.platform
== "armhf-linux" &&
2227 (settings
.thisSystem
== "aarch64-linux" ||
2228 (!strcmp(utsbuf
.sysname
, "Linux") && !strcmp(utsbuf
.machine
, "aarch64")))) {
2229 if (personality(PER_LINUX32
) == -1)
2230 throw SysError("cannot set armhf-linux personality");
2233 /* Impersonate a Linux 2.6 machine to get some determinism in
2234 builds that depend on the kernel version. */
2235 if ((drv
.platform
== "i686-linux" || drv
.platform
== "x86_64-linux") && settings
.impersonateLinux26
) {
2236 int cur
= personality(0xffffffff);
2237 if (cur
!= -1) personality(cur
| 0x0020000 /* == UNAME26 */);
2240 /* Disable address space randomization for improved
2242 int cur
= personality(0xffffffff);
2243 if (cur
!= -1) personality(cur
| ADDR_NO_RANDOMIZE
);
2246 /* Fill in the environment. */
2248 foreach (Environment::const_iterator
, i
, env
)
2249 envStrs
.push_back(rewriteHashes(i
->first
+ "=" + i
->second
, rewritesToTmp
));
2251 /* If we are running in `build-users' mode, then switch to the
2252 user we allocated above. Make sure that we drop all root
2253 privileges. Note that above we have closed all file
2254 descriptors except std*, so that's safe. Also note that
2255 setuid() when run as root sets the real, effective and
2257 if (buildUser
.enabled()) {
2258 /* Preserve supplementary groups of the build user, to allow
2259 admins to specify groups such as "kvm". */
2260 if (setgroups(buildUser
.getSupplementaryGIDs().size(),
2261 buildUser
.getSupplementaryGIDs().data()) == -1)
2262 throw SysError("cannot set supplementary groups of build user");
2264 if (setgid(buildUser
.getGID()) == -1 ||
2265 getgid() != buildUser
.getGID() ||
2266 getegid() != buildUser
.getGID())
2267 throw SysError("setgid failed");
2269 if (setuid(buildUser
.getUID()) == -1 ||
2270 getuid() != buildUser
.getUID() ||
2271 geteuid() != buildUser
.getUID())
2272 throw SysError("setuid failed");
2277 /* Indicate that we managed to set up the build environment. */
2278 writeFull(STDERR_FILENO
, "\n");
2280 /* Execute the program. This should not return. */
2281 if (isBuiltin(drv
)) {
2285 auto buildDrv
= lookupBuiltinBuilder(drv
.builder
);
2286 if (buildDrv
!= NULL
) {
2287 /* Check what the output file name is. When doing a
2288 'bmCheck' build, the output file name is different from
2289 that specified in DRV due to hash rewriting. */
2290 Path output
= drv
.outputs
["out"].path
;
2291 auto redirected
= redirectedOutputs
.find(output
);
2292 if (redirected
!= redirectedOutputs
.end())
2293 output
= redirected
->second
;
2295 buildDrv(drv
, drvPath
, output
);
2298 throw Error(format("unsupported builtin function '%1%'") % string(drv
.builder
, 8));
2300 } catch (std::exception
& e
) {
2301 writeFull(STDERR_FILENO
, "error: " + string(e
.what()) + "\n");
2306 /* Fill in the arguments. */
2308 string builderBasename
= baseNameOf(drv
.builder
);
2309 args
.push_back(builderBasename
);
2310 foreach (Strings::iterator
, i
, drv
.args
)
2311 args
.push_back(rewriteHashes(*i
, rewritesToTmp
));
2313 execve(drv
.builder
.c_str(), stringsToCharPtrs(args
).data(), stringsToCharPtrs(envStrs
).data());
2317 /* Right platform? Check this after we've tried 'execve' to allow for
2318 transparent emulation of different platforms with binfmt_misc
2319 handlers that invoke QEMU. */
2320 if (error
== ENOEXEC
&& !canBuildLocally(drv
.platform
)) {
2321 if (settings
.printBuildTrace
)
2322 printMsg(lvlError
, format("@ unsupported-platform %1% %2%") % drvPath
% drv
.platform
);
2324 format("a `%1%' is required to build `%3%', but I am a `%2%'")
2325 % drv
.platform
% settings
.thisSystem
% drvPath
);
2329 throw SysError(format("executing `%1%'") % drv
.builder
);
2331 } catch (std::exception
& e
) {
2332 writeFull(STDERR_FILENO
, "while setting up the build environment: " + string(e
.what()) + "\n");
2336 abort(); /* never reached */
2340 /* Parse a list of reference specifiers. Each element must either be
2341 a store path, or the symbolic name of the output of the derivation
2343 PathSet
parseReferenceSpecifiers(const Derivation
& drv
, string attr
)
2346 Paths paths
= tokenizeString
<Paths
>(attr
);
2347 foreach (Strings::iterator
, i
, paths
) {
2348 if (isStorePath(*i
))
2350 else if (drv
.outputs
.find(*i
) != drv
.outputs
.end())
2351 result
.insert(drv
.outputs
.find(*i
)->second
.path
);
2352 else throw BuildError(
2353 format("derivation contains an invalid reference specifier `%1%'")
2360 void DerivationGoal::registerOutputs()
2362 /* When using a build hook, the build hook can register the output
2363 as valid (by doing `nix-store --import'). If so we don't have
2364 to do anything here. */
2366 bool allValid
= true;
2367 foreach (DerivationOutputs::iterator
, i
, drv
.outputs
)
2368 if (!worker
.store
.isValidPath(i
->second
.path
)) allValid
= false;
2369 if (allValid
) return;
2372 ValidPathInfos infos
;
2374 /* Set of inodes seen during calls to canonicalisePathMetaData()
2375 for this build's outputs. This needs to be shared between
2376 outputs to allow hard links between outputs. */
2377 InodesSeen inodesSeen
;
2379 Path checkSuffix
= "-check";
2381 /* Check whether the output paths were created, and grep each
2382 output path to determine what other paths it references. Also make all
2383 output paths read-only. */
2384 foreach (DerivationOutputs::iterator
, i
, drv
.outputs
) {
2385 Path path
= i
->second
.path
;
2386 if (missingPaths
.find(path
) == missingPaths
.end()) continue;
2388 Path actualPath
= path
;
2390 actualPath
= chrootRootDir
+ path
;
2391 if (pathExists(actualPath
)) {
2392 /* Move output paths from the chroot to the store. */
2393 if (buildMode
== bmRepair
)
2394 replaceValidPath(path
, actualPath
);
2396 if (buildMode
!= bmCheck
&& rename(actualPath
.c_str(), path
.c_str()) == -1)
2397 throw SysError(format("moving build output `%1%' from the chroot to the store") % path
);
2399 if (buildMode
!= bmCheck
) actualPath
= path
;
2401 Path redirected
= redirectedOutputs
[path
];
2402 if (buildMode
== bmRepair
2403 && redirectedBadOutputs
.find(path
) != redirectedBadOutputs
.end()
2404 && pathExists(redirected
))
2405 replaceValidPath(path
, redirected
);
2406 if (buildMode
== bmCheck
&& redirected
!= "")
2407 actualPath
= redirected
;
2411 if (lstat(actualPath
.c_str(), &st
) == -1) {
2412 if (errno
== ENOENT
)
2414 format("builder for `%1%' failed to produce output path `%2%'")
2416 throw SysError(format("getting attributes of path `%1%'") % actualPath
);
2420 /* Check that the output is not group or world writable, as
2421 that means that someone else can have interfered with the
2422 build. Also, the output should be owned by the build
2424 if ((!S_ISLNK(st
.st_mode
) && (st
.st_mode
& (S_IWGRP
| S_IWOTH
))) ||
2425 (buildUser
.enabled() && st
.st_uid
!= buildUser
.getUID()))
2426 throw BuildError(format("suspicious ownership or permission on `%1%'; rejecting this build output") % path
);
2429 /* Apply hash rewriting if necessary. */
2430 bool rewritten
= false;
2431 if (!rewritesFromTmp
.empty()) {
2432 printMsg(lvlError
, format("warning: rewriting hashes in `%1%'; cross fingers") % path
);
2434 /* Canonicalise first. This ensures that the path we're
2435 rewriting doesn't contain a hard link to /etc/shadow or
2436 something like that. */
2437 canonicalisePathMetaData(actualPath
, buildUser
.enabled() ? buildUser
.getUID() : -1, inodesSeen
);
2439 /* FIXME: this is in-memory. */
2441 dumpPath(actualPath
, sink
);
2442 deletePath(actualPath
);
2443 sink
.s
= rewriteHashes(sink
.s
, rewritesFromTmp
);
2444 StringSource
source(sink
.s
);
2445 restorePath(actualPath
, source
);
2450 startNest(nest
, lvlTalkative
,
2451 format("scanning for references inside `%1%'") % path
);
2453 /* Check that fixed-output derivations produced the right
2454 outputs (i.e., the content hash should match the specified
2456 if (i
->second
.hash
!= "") {
2458 bool recursive
; HashType ht
; Hash h
;
2459 i
->second
.parseHashInfo(recursive
, ht
, h
);
2462 /* The output path should be a regular file without
2463 execute permission. */
2464 if (!S_ISREG(st
.st_mode
) || (st
.st_mode
& S_IXUSR
) != 0)
2466 format("output path `%1% should be a non-executable regular file") % path
);
2469 /* Check the hash. */
2470 Hash h2
= recursive
? hashPath(ht
, actualPath
).first
: hashFile(ht
, actualPath
);
2472 if (settings
.printBuildTrace
)
2473 printMsg(lvlError
, format("@ hash-mismatch %1% %2% %3% %4%")
2474 % path
% i
->second
.hashAlgo
2475 % printHash16or32(h
) % printHash16or32(h2
));
2476 throw BuildError(format("hash mismatch for store item '%1%'") % path
);
2480 /* Get rid of all weird permissions. This also checks that
2481 all files are owned by the build user, if applicable. */
2482 canonicalisePathMetaData(actualPath
,
2483 buildUser
.enabled() && !rewritten
? buildUser
.getUID() : -1, inodesSeen
);
2485 /* For this output path, find the references to other paths
2486 contained in it. Compute the SHA-256 NAR hash at the same
2487 time. The hash is stored in the database so that we can
2488 verify later on whether nobody has messed with the store. */
2490 PathSet references
= scanForReferences(actualPath
, allPaths
, hash
);
2492 if (buildMode
== bmCheck
) {
2493 if (!store
->isValidPath(path
)) continue;
2494 ValidPathInfo info
= worker
.store
.queryPathInfo(path
);
2495 if (hash
.first
!= info
.hash
) {
2496 if (settings
.keepFailed
) {
2497 Path dst
= path
+ checkSuffix
;
2498 if (pathExists(dst
)) deletePath(dst
);
2499 if (rename(actualPath
.c_str(), dst
.c_str()))
2500 throw SysError(format("renaming `%1%' to `%2%'") % actualPath
% dst
);
2501 throw Error(format("derivation `%1%' may not be deterministic: output `%2%' differs from ‘%3%’")
2502 % drvPath
% path
% dst
);
2504 throw Error(format("derivation `%1%' may not be deterministic: output `%2%' differs")
2508 if (settings
.printBuildTrace
)
2509 printMsg(lvlError
, format("@ build-succeeded %1% -") % drvPath
);
2514 /* For debugging, print out the referenced and unreferenced
2516 foreach (PathSet::iterator
, i
, inputPaths
) {
2517 PathSet::iterator j
= references
.find(*i
);
2518 if (j
== references
.end())
2519 debug(format("unreferenced input: `%1%'") % *i
);
2521 debug(format("referenced input: `%1%'") % *i
);
2524 /* Enforce `allowedReferences' and friends. */
2525 auto checkRefs
= [&](const string
& attrName
, bool allowed
, bool recursive
) {
2526 if (drv
.env
.find(attrName
) == drv
.env
.end()) return;
2528 PathSet spec
= parseReferenceSpecifiers(drv
, get(drv
.env
, attrName
));
2532 /* Our requisites are the union of the closures of our references. */
2533 for (auto & i
: references
)
2534 /* Don't call computeFSClosure on ourselves. */
2535 if (actualPath
!= i
)
2536 computeFSClosure(worker
.store
, i
, used
);
2540 for (auto & i
: used
)
2542 if (spec
.find(i
) == spec
.end())
2543 throw BuildError(format("output (`%1%') is not allowed to refer to path `%2%'") % actualPath
% i
);
2545 if (spec
.find(i
) != spec
.end())
2546 throw BuildError(format("output (`%1%') is not allowed to refer to path `%2%'") % actualPath
% i
);
2550 checkRefs("allowedReferences", true, false);
2551 checkRefs("allowedRequisites", true, true);
2552 checkRefs("disallowedReferences", false, false);
2553 checkRefs("disallowedRequisites", false, true);
2555 if (curRound
== nrRounds
) {
2556 worker
.store
.optimisePath(path
); // FIXME: combine with scanForReferences()
2558 worker
.store
.markContentsGood(path
);
2563 info
.hash
= hash
.first
;
2564 info
.narSize
= hash
.second
;
2565 info
.references
= references
;
2566 info
.deriver
= drvPath
;
2567 infos
.push_back(info
);
2570 if (buildMode
== bmCheck
) return;
2572 /* Compare the result with the previous round, and report which
2573 path is different, if any.*/
2574 if (curRound
> 1 && prevInfos
!= infos
) {
2575 assert(prevInfos
.size() == infos
.size());
2576 for (auto i
= prevInfos
.begin(), j
= infos
.begin(); i
!= prevInfos
.end(); ++i
, ++j
)
2578 Path prev
= i
->path
+ checkSuffix
;
2579 if (pathExists(prev
))
2580 throw NotDeterministic(
2581 format("output ‘%1%’ of ‘%2%’ differs from ‘%3%’ from previous round")
2582 % i
->path
% drvPath
% prev
);
2584 throw NotDeterministic(
2585 format("output ‘%1%’ of ‘%2%’ differs from previous round")
2586 % i
->path
% drvPath
);
2588 assert(false); // shouldn't happen
2591 if (settings
.keepFailed
) {
2592 for (auto & i
: drv
.outputs
) {
2593 Path prev
= i
.second
.path
+ checkSuffix
;
2594 if (pathExists(prev
)) deletePath(prev
);
2595 if (curRound
< nrRounds
) {
2596 Path dst
= i
.second
.path
+ checkSuffix
;
2597 if (rename(i
.second
.path
.c_str(), dst
.c_str()))
2598 throw SysError(format("renaming ‘%1%’ to ‘%2%’") % i
.second
.path
% dst
);
2604 if (curRound
< nrRounds
) {
2609 /* Register each output path as valid, and register the sets of
2610 paths referenced by each of them. If there are cycles in the
2611 outputs, this will fail. */
2612 worker
.store
.registerValidPaths(infos
);
2616 string drvsLogDir
= "drvs";
2619 Path
DerivationGoal::openLogFile()
2623 if (!settings
.keepLog
) return "";
2625 string baseName
= baseNameOf(drvPath
);
2627 /* Create a log file. */
2628 Path dir
= (format("%1%/%2%/%3%/") % settings
.nixLogDir
% drvsLogDir
% string(baseName
, 0, 2)).str();
2631 switch (settings
.logCompression
)
2633 case COMPRESSION_GZIP
: {
2634 Path logFileName
= (format("%1%/%2%.gz") % dir
% string(baseName
, 2)).str();
2635 AutoCloseFD fd
= open(logFileName
.c_str(), O_CREAT
| O_WRONLY
| O_TRUNC
, 0666);
2636 if (fd
== -1) throw SysError(format("creating log file `%1%'") % logFileName
);
2639 /* Note: FD will be closed by 'gzclose'. */
2640 if (!(gzLogFile
= gzdopen(fd
.borrow(), "w")))
2641 throw Error(format("cannot open compressed log file `%1%'") % logFileName
);
2643 gzbuffer(gzLogFile
, 32768);
2644 gzsetparams(gzLogFile
, Z_BEST_COMPRESSION
, Z_DEFAULT_STRATEGY
);
2650 case COMPRESSION_BZIP2
: {
2651 Path logFileName
= (format("%1%/%2%.bz2") % dir
% string(baseName
, 2)).str();
2652 AutoCloseFD fd
= open(logFileName
.c_str(), O_CREAT
| O_WRONLY
| O_TRUNC
, 0666);
2653 if (fd
== -1) throw SysError(format("creating log file `%1%'") % logFileName
);
2656 if (!(fLogFile
= fdopen(fd
.borrow(), "w")))
2657 throw SysError(format("opening file `%1%'") % logFileName
);
2660 if (!(bzLogFile
= BZ2_bzWriteOpen(&err
, fLogFile
, 9, 0, 0)))
2661 throw Error(format("cannot open compressed log file `%1%'") % logFileName
);
2667 case COMPRESSION_NONE
: {
2668 Path logFileName
= (format("%1%/%2%") % dir
% string(baseName
, 2)).str();
2669 fdLogFile
= open(logFileName
.c_str(), O_CREAT
| O_WRONLY
| O_TRUNC
, 0666);
2670 if (fdLogFile
== -1) throw SysError(format("creating log file `%1%'") % logFileName
);
2671 closeOnExec(fdLogFile
);
2680 void DerivationGoal::closeLogFile()
2684 err
= gzclose(gzLogFile
);
2686 if (err
!= Z_OK
) throw Error(format("cannot close compressed log file (gzip error = %1%)") % err
);
2689 else if (bzLogFile
) {
2691 BZ2_bzWriteClose(&err
, bzLogFile
, 0, 0, 0);
2693 if (err
!= BZ_OK
) throw Error(format("cannot close compressed log file (BZip2 error = %1%)") % err
);
2706 static void _chown(const Path
& path
, uid_t uid
, gid_t gid
)
2710 if (lchown(path
.c_str(), uid
, gid
) == -1) {
2711 throw SysError(format("change owner and group of `%1%'") % path
);
2713 struct stat st
= lstat(path
);
2714 if (S_ISDIR(st
.st_mode
)) {
2715 for (auto & i
: readDirectory(path
))
2716 _chown(path
+ "/" + i
.name
, uid
, gid
);
2721 void DerivationGoal::deleteTmpDir(bool force
)
2724 if (settings
.keepFailed
&& !force
) {
2726 format("note: keeping build directory `%2%'")
2727 % drvPath
% tmpDir
);
2728 chmod(tmpDir
.c_str(), 0755);
2729 // Change the ownership if clientUid is set. Never change the
2730 // ownership or the group to "root" for security reasons.
2731 if (settings
.clientUid
!= (uid_t
) -1 && settings
.clientUid
!= 0) {
2732 _chown(tmpDir
, settings
.clientUid
,
2733 settings
.clientGid
!= 0 ? settings
.clientGid
: -1);
2743 void DerivationGoal::handleChildOutput(int fd
, const string
& data
)
2747 if (settings
.multiplexedBuildOutput
) {
2748 /* Print a prefix that allows clients to determine whether a message
2749 comes from the daemon or from a build process, and in the latter
2750 case, which build process it comes from. The PID here matches the
2751 one given in "@ build-started" traces; it's shorter that the
2752 derivation file name, hence this choice. */
2753 prefix
= "@ build-log "
2754 + std::to_string(pid
< 0 ? hook
->pid
: pid
)
2755 + " " + std::to_string(data
.size()) + "\n";
2758 if ((hook
&& fd
== hook
->builderOut
.readSide
) ||
2759 (!hook
&& fd
== builderOut
.readSide
))
2761 logSize
+= data
.size();
2762 if (settings
.maxLogSize
&& logSize
> settings
.maxLogSize
) {
2764 format("%1% killed after writing more than %2% bytes of log output")
2765 % getName() % settings
.maxLogSize
);
2766 timedOut(); // not really a timeout, but close enough
2769 if (verbosity
>= settings
.buildVerbosity
)
2770 writeToStderr(prefix
+ data
);
2773 if (data
.size() > 0) {
2775 count
= gzwrite(gzLogFile
, data
.data(), data
.size());
2776 if (count
== 0) throw Error(format("cannot write to compressed log file (gzip error = %1%)") % gzerror(gzLogFile
, &err
));
2779 } else if (bzLogFile
) {
2781 BZ2_bzWrite(&err
, bzLogFile
, (unsigned char *) data
.data(), data
.size());
2782 if (err
!= BZ_OK
) throw Error(format("cannot write to compressed log file (BZip2 error = %1%)") % err
);
2784 } else if (fdLogFile
!= -1)
2785 writeFull(fdLogFile
, data
);
2788 if (hook
&& fd
== hook
->fromHook
.readSide
)
2789 writeToStderr(prefix
+ data
);
2793 void DerivationGoal::handleEOF(int fd
)
2795 worker
.wakeUp(shared_from_this());
2799 PathSet
DerivationGoal::checkPathValidity(bool returnValid
, bool checkHash
)
2802 foreach (DerivationOutputs::iterator
, i
, drv
.outputs
) {
2803 if (!wantOutput(i
->first
, wantedOutputs
)) continue;
2805 worker
.store
.isValidPath(i
->second
.path
) &&
2806 (!checkHash
|| worker
.store
.pathContentsGood(i
->second
.path
));
2807 if (good
== returnValid
) result
.insert(i
->second
.path
);
2813 bool DerivationGoal::pathFailed(const Path
& path
)
2815 if (!settings
.cacheFailure
) return false;
2817 if (!worker
.store
.hasPathFailed(path
)) return false;
2819 printMsg(lvlError
, format("builder for `%1%' failed previously (cached)") % path
);
2821 if (settings
.printBuildTrace
)
2822 printMsg(lvlError
, format("@ build-failed %1% - cached") % drvPath
);
2824 done(BuildResult::CachedFailure
);
2830 Path
DerivationGoal::addHashRewrite(const Path
& path
)
2832 string h1
= string(path
, settings
.nixStore
.size() + 1, 32);
2833 string h2
= string(printHash32(hashString(htSHA256
, "rewrite:" + drvPath
+ ":" + path
)), 0, 32);
2834 Path p
= settings
.nixStore
+ "/" + h2
+ string(path
, settings
.nixStore
.size() + 33);
2835 if (pathExists(p
)) deletePath(p
);
2836 assert(path
.size() == p
.size());
2837 rewritesToTmp
[h1
] = h2
;
2838 rewritesFromTmp
[h2
] = h1
;
2839 redirectedOutputs
[path
] = p
;
2840 printMsg(lvlChatty
, format("output '%1%' redirected to '%2%'")
2846 void DerivationGoal::done(BuildResult::Status status
, const string
& msg
)
2848 result
.status
= status
;
2849 result
.errorMsg
= msg
;
2850 amDone(result
.success() ? ecSuccess
: ecFailed
);
2851 if (result
.status
== BuildResult::TimedOut
)
2852 worker
.timedOut
= true;
2853 if (result
.status
== BuildResult::PermanentFailure
|| result
.status
== BuildResult::CachedFailure
)
2854 worker
.permanentFailure
= true;
2858 //////////////////////////////////////////////////////////////////////
2861 class SubstitutionGoal
: public Goal
2863 friend class Worker
;
2866 /* The store path that should be realised through a substitute. */
2869 /* Path info returned by the substituter's query info operation. */
2870 SubstitutablePathInfo info
;
2872 /* Pipe for the substituter's standard output. */
2875 /* Pipe for the substituter's standard error. */
2878 /* The process ID of the builder. */
2881 /* Lock on the store path. */
2882 std::shared_ptr
<PathLocks
> outputLock
;
2884 /* Whether to try to repair a valid path. */
2887 /* Location where we're downloading the substitute. Differs from
2888 storePath when doing a repair. */
2891 typedef void (SubstitutionGoal::*GoalState
)();
2897 SubstitutionGoal(const Path
& storePath
, Worker
& worker
, bool repair
= false);
2898 ~SubstitutionGoal();
2904 /* "a$" ensures substitution goals happen before derivation
2906 return "a$" + storePathToName(storePath
) + "$" + storePath
;
2914 void referencesValid();
2918 /* Callback used by the worker to write to the log. */
2919 void handleChildOutput(int fd
, const string
& data
);
2920 void handleEOF(int fd
);
2922 Path
getStorePath() { return storePath
; }
2926 SubstitutionGoal::SubstitutionGoal(const Path
& storePath
, Worker
& worker
, bool repair
)
2930 this->storePath
= storePath
;
2931 state
= &SubstitutionGoal::init
;
2932 name
= (format("substitution of `%1%'") % storePath
).str();
2937 SubstitutionGoal::~SubstitutionGoal()
2939 if (pid
!= -1) worker
.childTerminated(pid
);
2943 void SubstitutionGoal::timedOut()
2945 if (settings
.printBuildTrace
)
2946 printMsg(lvlError
, format("@ substituter-failed %1% timeout") % storePath
);
2948 pid_t savedPid
= pid
;
2950 worker
.childTerminated(savedPid
);
2956 void SubstitutionGoal::work()
2962 void SubstitutionGoal::init()
2966 worker
.store
.addTempRoot(storePath
);
2968 /* If the path already exists we're done. */
2969 if (!repair
&& worker
.store
.isValidPath(storePath
)) {
2974 if (settings
.readOnlyMode
)
2975 throw Error(format("cannot substitute path `%1%' - no write access to the store") % storePath
);
2981 void SubstitutionGoal::tryNext()
2983 trace("trying substituter");
2985 SubstitutablePathInfos infos
;
2986 PathSet
dummy(singleton
<PathSet
>(storePath
));
2987 worker
.store
.querySubstitutablePathInfos(dummy
, infos
);
2988 SubstitutablePathInfos::iterator k
= infos
.find(storePath
);
2989 if (k
== infos
.end()) {
2990 /* None left. Terminate this goal and let someone else deal
2992 debug(format("path `%1%' is required, but there is no substituter that can build it") % storePath
);
2993 /* Hack: don't indicate failure if there were no substituters.
2994 In that case the calling derivation should just do a
2996 amDone(ecNoSubstituters
);
3000 /* Found a substitute. */
3003 /* To maintain the closure invariant, we first have to realise the
3004 paths referenced by this one. */
3005 foreach (PathSet::iterator
, i
, info
.references
)
3006 if (*i
!= storePath
) /* ignore self-references */
3007 addWaitee(worker
.makeSubstitutionGoal(*i
));
3009 if (waitees
.empty()) /* to prevent hang (no wake-up event) */
3012 state
= &SubstitutionGoal::referencesValid
;
3016 void SubstitutionGoal::referencesValid()
3018 trace("all references realised");
3021 debug(format("some references of path `%1%' could not be realised") % storePath
);
3022 amDone(nrNoSubstituters
> 0 || nrIncompleteClosure
> 0 ? ecIncompleteClosure
: ecFailed
);
3026 foreach (PathSet::iterator
, i
, info
.references
)
3027 if (*i
!= storePath
) /* ignore self-references */
3028 assert(worker
.store
.isValidPath(*i
));
3030 state
= &SubstitutionGoal::tryToRun
;
3031 worker
.wakeUp(shared_from_this());
3035 void SubstitutionGoal::tryToRun()
3037 trace("trying to run");
3039 /* Make sure that we are allowed to start a build. Note that even
3040 is maxBuildJobs == 0 (no local builds allowed), we still allow
3041 a substituter to run. This is because substitutions cannot be
3042 distributed to another machine via the build hook. */
3043 if (worker
.getNrLocalBuilds() >= (settings
.maxBuildJobs
== 0 ? 1 : settings
.maxBuildJobs
)) {
3044 worker
.waitForBuildSlot(shared_from_this());
3048 /* Maybe a derivation goal has already locked this path
3049 (exceedingly unlikely, since it should have used a substitute
3050 first, but let's be defensive). */
3051 outputLock
.reset(); // make sure this goal's lock is gone
3052 if (pathIsLockedByMe(storePath
)) {
3053 debug(format("restarting substitution of `%1%' because it's locked by another goal")
3055 worker
.waitForAnyGoal(shared_from_this());
3056 return; /* restart in the tryToRun() state when another goal finishes */
3059 /* Acquire a lock on the output path. */
3060 outputLock
= std::shared_ptr
<PathLocks
>(new PathLocks
);
3061 if (!outputLock
->lockPaths(singleton
<PathSet
>(storePath
), "", false)) {
3062 worker
.waitForAWhile(shared_from_this());
3066 /* Check again whether the path is invalid. */
3067 if (!repair
&& worker
.store
.isValidPath(storePath
)) {
3068 debug(format("store path `%1%' has become valid") % storePath
);
3069 outputLock
->setDeletion(true);
3074 printMsg(lvlInfo
, format("fetching path `%1%'...") % storePath
);
3079 destPath
= repair
? storePath
+ ".tmp" : storePath
;
3081 /* Remove the (stale) output path if it exists. */
3082 if (pathExists(destPath
))
3083 deletePath(destPath
);
3085 worker
.store
.setSubstituterEnv();
3087 /* Fill in the arguments. */
3089 args
.push_back("guix");
3090 args
.push_back("substitute");
3091 args
.push_back("--substitute");
3092 args
.push_back(storePath
);
3093 args
.push_back(destPath
);
3095 /* Fork the substitute program. */
3096 pid
= startProcess([&]() {
3098 commonChildInit(logPipe
);
3100 if (dup2(outPipe
.writeSide
, STDOUT_FILENO
) == -1)
3101 throw SysError("cannot dup output pipe into stdout");
3103 execv(settings
.guixProgram
.c_str(), stringsToCharPtrs(args
).data());
3105 throw SysError(format("executing `%1% substitute'") % settings
.guixProgram
);
3108 pid
.setSeparatePG(true);
3109 pid
.setKillSignal(SIGTERM
);
3110 outPipe
.writeSide
.close();
3111 logPipe
.writeSide
.close();
3112 worker
.childStarted(shared_from_this(),
3113 pid
, singleton
<set
<int> >(logPipe
.readSide
), true, true);
3115 state
= &SubstitutionGoal::finished
;
3117 if (settings
.printBuildTrace
)
3118 /* The second element in the message used to be the name of the
3119 substituter but we're left with only one. */
3120 printMsg(lvlError
, format("@ substituter-started %1% substitute") % storePath
);
3124 void SubstitutionGoal::finished()
3126 trace("substitute finished");
3128 /* Since we got an EOF on the logger pipe, the substitute is
3129 presumed to have terminated. */
3130 pid_t savedPid
= pid
;
3131 int status
= pid
.wait(true);
3133 /* So the child is gone now. */
3134 worker
.childTerminated(savedPid
);
3136 /* Close the read side of the logger pipe. */
3137 logPipe
.readSide
.close();
3139 /* Get the hash info from stdout. */
3140 string dummy
= readLine(outPipe
.readSide
);
3141 string expectedHashStr
= statusOk(status
) ? readLine(outPipe
.readSide
) : "";
3142 outPipe
.readSide
.close();
3144 /* Check the exit status and the build result. */
3148 if (!statusOk(status
))
3149 throw SubstError(format("fetching path `%1%' %2%")
3150 % storePath
% statusToString(status
));
3152 if (!pathExists(destPath
))
3153 throw SubstError(format("substitute did not produce path `%1%'") % destPath
);
3155 hash
= hashPath(htSHA256
, destPath
);
3157 /* Verify the expected hash we got from the substituer. */
3158 if (expectedHashStr
!= "") {
3159 size_t n
= expectedHashStr
.find(':');
3160 if (n
== string::npos
)
3161 throw Error(format("bad hash from substituter: %1%") % expectedHashStr
);
3162 HashType hashType
= parseHashType(string(expectedHashStr
, 0, n
));
3163 if (hashType
== htUnknown
)
3164 throw Error(format("unknown hash algorithm in `%1%'") % expectedHashStr
);
3165 Hash expectedHash
= parseHash16or32(hashType
, string(expectedHashStr
, n
+ 1));
3166 Hash actualHash
= hashType
== htSHA256
? hash
.first
: hashPath(hashType
, destPath
).first
;
3167 if (expectedHash
!= actualHash
) {
3168 if (settings
.printBuildTrace
)
3169 printMsg(lvlError
, format("@ hash-mismatch %1% %2% %3% %4%")
3170 % storePath
% "sha256"
3171 % printHash16or32(expectedHash
)
3172 % printHash16or32(actualHash
));
3173 throw SubstError(format("hash mismatch for substituted item `%1%'") % storePath
);
3177 } catch (SubstError
& e
) {
3179 printMsg(lvlInfo
, e
.msg());
3181 if (settings
.printBuildTrace
) {
3182 printMsg(lvlError
, format("@ substituter-failed %1% %2% %3%")
3183 % storePath
% status
% e
.msg());
3190 if (repair
) replaceValidPath(storePath
, destPath
);
3192 canonicalisePathMetaData(storePath
, -1);
3194 worker
.store
.optimisePath(storePath
); // FIXME: combine with hashPath()
3196 ValidPathInfo info2
;
3197 info2
.path
= storePath
;
3198 info2
.hash
= hash
.first
;
3199 info2
.narSize
= hash
.second
;
3200 info2
.references
= info
.references
;
3201 info2
.deriver
= info
.deriver
;
3202 worker
.store
.registerValidPath(info2
);
3204 outputLock
->setDeletion(true);
3207 worker
.store
.markContentsGood(storePath
);
3210 format("substitution of path `%1%' succeeded") % storePath
);
3212 if (settings
.printBuildTrace
)
3213 printMsg(lvlError
, format("@ substituter-succeeded %1%") % storePath
);
3219 void SubstitutionGoal::handleChildOutput(int fd
, const string
& data
)
3221 assert(fd
== logPipe
.readSide
);
3222 if (verbosity
>= settings
.buildVerbosity
) writeToStderr(data
);
3223 /* Don't write substitution output to a log file for now. We
3224 probably should, though. */
3228 void SubstitutionGoal::handleEOF(int fd
)
3230 if (fd
== logPipe
.readSide
) worker
.wakeUp(shared_from_this());
3235 //////////////////////////////////////////////////////////////////////
3238 static bool working
= false;
3241 Worker::Worker(LocalStore
& store
)
3244 /* Debugging: prevent recursive workers. */
3245 if (working
) abort();
3249 permanentFailure
= false;
3258 /* Explicitly get rid of all strong pointers now. After this all
3259 goals that refer to this worker should be gone. (Otherwise we
3260 are in trouble, since goals may call childTerminated() etc. in
3261 their destructors). */
3266 GoalPtr
Worker::makeDerivationGoal(const Path
& path
,
3267 const StringSet
& wantedOutputs
, BuildMode buildMode
)
3269 GoalPtr goal
= derivationGoals
[path
].lock();
3271 goal
= GoalPtr(new DerivationGoal(path
, wantedOutputs
, *this, buildMode
));
3272 derivationGoals
[path
] = goal
;
3275 (dynamic_cast<DerivationGoal
*>(goal
.get()))->addWantedOutputs(wantedOutputs
);
3280 GoalPtr
Worker::makeSubstitutionGoal(const Path
& path
, bool repair
)
3282 GoalPtr goal
= substitutionGoals
[path
].lock();
3284 goal
= GoalPtr(new SubstitutionGoal(path
, *this, repair
));
3285 substitutionGoals
[path
] = goal
;
3292 static void removeGoal(GoalPtr goal
, WeakGoalMap
& goalMap
)
3294 /* !!! inefficient */
3295 for (WeakGoalMap::iterator i
= goalMap
.begin();
3296 i
!= goalMap
.end(); )
3297 if (i
->second
.lock() == goal
) {
3298 WeakGoalMap::iterator j
= i
; ++j
;
3306 void Worker::removeGoal(GoalPtr goal
)
3308 nix::removeGoal(goal
, derivationGoals
);
3309 nix::removeGoal(goal
, substitutionGoals
);
3310 if (topGoals
.find(goal
) != topGoals
.end()) {
3311 topGoals
.erase(goal
);
3312 /* If a top-level goal failed, then kill all other goals
3313 (unless keepGoing was set). */
3314 if (goal
->getExitCode() == Goal::ecFailed
&& !settings
.keepGoing
)
3318 /* Wake up goals waiting for any goal to finish. */
3319 foreach (WeakGoals::iterator
, i
, waitingForAnyGoal
) {
3320 GoalPtr goal
= i
->lock();
3321 if (goal
) wakeUp(goal
);
3324 waitingForAnyGoal
.clear();
3328 void Worker::wakeUp(GoalPtr goal
)
3330 goal
->trace("woken up");
3331 addToWeakGoals(awake
, goal
);
3335 unsigned Worker::getNrLocalBuilds()
3337 return nrLocalBuilds
;
3341 void Worker::childStarted(GoalPtr goal
,
3342 pid_t pid
, const set
<int> & fds
, bool inBuildSlot
,
3343 bool respectTimeouts
)
3348 child
.timeStarted
= child
.lastOutput
= time(0);
3349 child
.inBuildSlot
= inBuildSlot
;
3350 child
.respectTimeouts
= respectTimeouts
;
3351 children
[pid
] = child
;
3352 if (inBuildSlot
) nrLocalBuilds
++;
3356 void Worker::childTerminated(pid_t pid
, bool wakeSleepers
)
3358 assert(pid
!= -1); /* common mistake */
3360 Children::iterator i
= children
.find(pid
);
3361 assert(i
!= children
.end());
3363 if (i
->second
.inBuildSlot
) {
3364 assert(nrLocalBuilds
> 0);
3368 children
.erase(pid
);
3372 /* Wake up goals waiting for a build slot. */
3373 foreach (WeakGoals::iterator
, i
, wantingToBuild
) {
3374 GoalPtr goal
= i
->lock();
3375 if (goal
) wakeUp(goal
);
3378 wantingToBuild
.clear();
3383 void Worker::waitForBuildSlot(GoalPtr goal
)
3385 debug("wait for build slot");
3386 if (getNrLocalBuilds() < settings
.maxBuildJobs
)
3387 wakeUp(goal
); /* we can do it right away */
3389 addToWeakGoals(wantingToBuild
, goal
);
3393 void Worker::waitForAnyGoal(GoalPtr goal
)
3395 debug("wait for any goal");
3396 addToWeakGoals(waitingForAnyGoal
, goal
);
3400 void Worker::waitForAWhile(GoalPtr goal
)
3402 debug("wait for a while");
3403 addToWeakGoals(waitingForAWhile
, goal
);
3407 void Worker::run(const Goals
& _topGoals
)
3409 foreach (Goals::iterator
, i
, _topGoals
) topGoals
.insert(*i
);
3411 startNest(nest
, lvlDebug
, format("entered goal loop"));
3417 /* Call every wake goal (in the ordering established by
3418 CompareGoalPtrs). */
3419 while (!awake
.empty() && !topGoals
.empty()) {
3421 for (auto & i
: awake
) {
3422 GoalPtr goal
= i
.lock();
3423 if (goal
) awake2
.insert(goal
);
3426 for (auto & goal
: awake2
) {
3429 if (topGoals
.empty()) break; // stuff may have been cancelled
3433 if (topGoals
.empty()) break;
3435 /* Wait for input. */
3436 if (!children
.empty() || !waitingForAWhile
.empty())
3439 if (awake
.empty() && settings
.maxBuildJobs
== 0) throw Error(
3440 "unable to start any build; either increase `--max-jobs' "
3441 "or enable distributed builds");
3442 assert(!awake
.empty());
3446 /* If --keep-going is not set, it's possible that the main goal
3447 exited while some of its subgoals were still active. But if
3448 --keep-going *is* set, then they must all be finished now. */
3449 assert(!settings
.keepGoing
|| awake
.empty());
3450 assert(!settings
.keepGoing
|| wantingToBuild
.empty());
3451 assert(!settings
.keepGoing
|| children
.empty());
3455 void Worker::waitForInput()
3457 printMsg(lvlVomit
, "waiting for children");
3459 /* Process output from the file descriptors attached to the
3460 children, namely log output and output path creation commands.
3461 We also use this to detect child termination: if we get EOF on
3462 the logger pipe of a build, we assume that the builder has
3465 bool useTimeout
= false;
3466 struct timeval timeout
;
3467 timeout
.tv_usec
= 0;
3468 time_t before
= time(0);
3470 /* If we're monitoring for silence on stdout/stderr, or if there
3471 is a build timeout, then wait for input until the first
3472 deadline for any child. */
3473 assert(sizeof(time_t) >= sizeof(long));
3474 time_t nearest
= LONG_MAX
; // nearest deadline
3475 foreach (Children::iterator
, i
, children
) {
3476 if (!i
->second
.respectTimeouts
) continue;
3477 if (settings
.maxSilentTime
!= 0)
3478 nearest
= std::min(nearest
, i
->second
.lastOutput
+ settings
.maxSilentTime
);
3479 if (settings
.buildTimeout
!= 0)
3480 nearest
= std::min(nearest
, i
->second
.timeStarted
+ settings
.buildTimeout
);
3482 if (nearest
!= LONG_MAX
) {
3483 timeout
.tv_sec
= std::max((time_t) 1, nearest
- before
);
3485 printMsg(lvlVomit
, format("sleeping %1% seconds") % timeout
.tv_sec
);
3488 /* If we are polling goals that are waiting for a lock, then wake
3489 up after a few seconds at most. */
3490 if (!waitingForAWhile
.empty()) {
3492 if (lastWokenUp
== 0)
3493 printMsg(lvlError
, "waiting for locks or build slots...");
3494 if (lastWokenUp
== 0 || lastWokenUp
> before
) lastWokenUp
= before
;
3495 timeout
.tv_sec
= std::max((time_t) 1, (time_t) (lastWokenUp
+ settings
.pollInterval
- before
));
3496 } else lastWokenUp
= 0;
3498 using namespace std
;
3499 /* Use select() to wait for the input side of any logger pipe to
3500 become `available'. Note that `available' (i.e., non-blocking)
3505 foreach (Children::iterator
, i
, children
) {
3506 foreach (set
<int>::iterator
, j
, i
->second
.fds
) {
3508 if (*j
>= fdMax
) fdMax
= *j
+ 1;
3512 if (select(fdMax
, &fds
, 0, 0, useTimeout
? &timeout
: 0) == -1) {
3513 if (errno
== EINTR
) return;
3514 throw SysError("waiting for input");
3517 time_t after
= time(0);
3519 /* Process all available file descriptors. */
3521 /* Since goals may be canceled from inside the loop below (causing
3522 them go be erased from the `children' map), we have to be
3523 careful that we don't keep iterators alive across calls to
3526 foreach (Children::iterator
, i
, children
) pids
.insert(i
->first
);
3528 foreach (set
<pid_t
>::iterator
, i
, pids
) {
3530 Children::iterator j
= children
.find(*i
);
3531 if (j
== children
.end()) continue; // child destroyed
3532 GoalPtr goal
= j
->second
.goal
.lock();
3535 set
<int> fds2(j
->second
.fds
);
3536 foreach (set
<int>::iterator
, k
, fds2
) {
3537 if (FD_ISSET(*k
, &fds
)) {
3538 unsigned char buffer
[4096];
3539 ssize_t rd
= read(*k
, buffer
, sizeof(buffer
));
3542 throw SysError(format("reading from %1%")
3544 } else if (rd
== 0) {
3545 debug(format("%1%: got EOF") % goal
->getName());
3546 goal
->handleEOF(*k
);
3547 j
->second
.fds
.erase(*k
);
3549 printMsg(lvlVomit
, format("%1%: read %2% bytes")
3550 % goal
->getName() % rd
);
3551 string
data((char *) buffer
, rd
);
3552 j
->second
.lastOutput
= after
;
3553 goal
->handleChildOutput(*k
, data
);
3558 if (goal
->getExitCode() == Goal::ecBusy
&&
3559 settings
.maxSilentTime
!= 0 &&
3560 j
->second
.respectTimeouts
&&
3561 after
- j
->second
.lastOutput
>= (time_t) settings
.maxSilentTime
)
3564 format("%1% timed out after %2% seconds of silence")
3565 % goal
->getName() % settings
.maxSilentTime
);
3569 else if (goal
->getExitCode() == Goal::ecBusy
&&
3570 settings
.buildTimeout
!= 0 &&
3571 j
->second
.respectTimeouts
&&
3572 after
- j
->second
.timeStarted
>= (time_t) settings
.buildTimeout
)
3575 format("%1% timed out after %2% seconds")
3576 % goal
->getName() % settings
.buildTimeout
);
3581 if (!waitingForAWhile
.empty() && lastWokenUp
+ settings
.pollInterval
<= after
) {
3582 lastWokenUp
= after
;
3583 foreach (WeakGoals::iterator
, i
, waitingForAWhile
) {
3584 GoalPtr goal
= i
->lock();
3585 if (goal
) wakeUp(goal
);
3587 waitingForAWhile
.clear();
3592 unsigned int Worker::exitStatus()
3594 return timedOut
? 101 : (permanentFailure
? 100 : 1);
3598 //////////////////////////////////////////////////////////////////////
3601 void LocalStore::buildPaths(const PathSet
& drvPaths
, BuildMode buildMode
)
3603 startNest(nest
, lvlDebug
,
3604 format("building %1%") % showPaths(drvPaths
));
3606 Worker
worker(*this);
3609 foreach (PathSet::const_iterator
, i
, drvPaths
) {
3610 DrvPathWithOutputs i2
= parseDrvPathWithOutputs(*i
);
3611 if (isDerivation(i2
.first
))
3612 goals
.insert(worker
.makeDerivationGoal(i2
.first
, i2
.second
, buildMode
));
3614 goals
.insert(worker
.makeSubstitutionGoal(*i
, buildMode
));
3620 foreach (Goals::iterator
, i
, goals
)
3621 if ((*i
)->getExitCode() == Goal::ecFailed
) {
3622 DerivationGoal
* i2
= dynamic_cast<DerivationGoal
*>(i
->get());
3623 if (i2
) failed
.insert(i2
->getDrvPath());
3624 else failed
.insert(dynamic_cast<SubstitutionGoal
*>(i
->get())->getStorePath());
3627 if (!failed
.empty())
3628 throw Error(format("build of %1% failed") % showPaths(failed
), worker
.exitStatus());
3632 void LocalStore::ensurePath(const Path
& path
)
3634 /* If the path is already valid, we're done. */
3635 if (isValidPath(path
)) return;
3637 Worker
worker(*this);
3638 GoalPtr goal
= worker
.makeSubstitutionGoal(path
);
3639 Goals goals
= singleton
<Goals
>(goal
);
3643 if (goal
->getExitCode() != Goal::ecSuccess
)
3644 throw Error(format("path `%1%' does not exist and cannot be created") % path
, worker
.exitStatus());
3648 void LocalStore::repairPath(const Path
& path
)
3650 Worker
worker(*this);
3651 GoalPtr goal
= worker
.makeSubstitutionGoal(path
, true);
3652 Goals goals
= singleton
<Goals
>(goal
);
3656 if (goal
->getExitCode() != Goal::ecSuccess
) {
3657 /* Since substituting the path didn't work, if we have a valid
3658 deriver, then rebuild the deriver. */
3659 Path deriver
= queryDeriver(path
);
3660 if (deriver
!= "" && isValidPath(deriver
)) {
3662 goals
.insert(worker
.makeDerivationGoal(deriver
, StringSet(), bmRepair
));
3665 throw Error(format("cannot repair path `%1%'") % path
, worker
.exitStatus());