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>
54 /* In GNU libc 2.11, <sys/mount.h> does not define `MS_PRIVATE', but
56 #if !defined MS_PRIVATE && defined HAVE_LINUX_FS_H
60 #define CHROOT_ENABLED HAVE_CHROOT && HAVE_SYS_MOUNT_H && defined(MS_BIND) && defined(MS_PRIVATE) && defined(CLONE_NEWNS) && defined(SYS_pivot_root)
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 Path buildHook
= getEnv("NIX_BUILD_HOOK");
618 if (string(buildHook
, 0, 1) != "/") buildHook
= settings
.nixLibexecDir
+ "/nix/" + buildHook
;
619 buildHook
= canonPath(buildHook
);
621 /* Create a pipe to get the output of the child. */
624 /* Create the communication pipes. */
627 /* Create a pipe to get the output of the builder. */
631 pid
= startProcess([&]() {
633 commonChildInit(fromHook
);
635 if (chdir("/") == -1) throw SysError("changing into `/");
637 /* Dup the communication pipes. */
638 if (dup2(toHook
.readSide
, STDIN_FILENO
) == -1)
639 throw SysError("dupping to-hook read side");
641 /* Use fd 4 for the builder's stdout/stderr. */
642 if (dup2(builderOut
.writeSide
, 4) == -1)
643 throw SysError("dupping builder's stdout/stderr");
645 execl(buildHook
.c_str(), buildHook
.c_str(), settings
.thisSystem
.c_str(),
646 (format("%1%") % settings
.maxSilentTime
).str().c_str(),
647 (format("%1%") % settings
.printBuildTrace
).str().c_str(),
648 (format("%1%") % settings
.buildTimeout
).str().c_str(),
651 throw SysError(format("executing `%1%'") % buildHook
);
654 pid
.setSeparatePG(true);
655 fromHook
.writeSide
.close();
656 toHook
.readSide
.close();
660 HookInstance::~HookInstance()
663 toHook
.writeSide
.close();
671 //////////////////////////////////////////////////////////////////////
674 typedef map
<string
, string
> HashRewrites
;
677 string
rewriteHashes(string s
, const HashRewrites
& rewrites
)
679 foreach (HashRewrites::const_iterator
, i
, rewrites
) {
680 assert(i
->first
.size() == i
->second
.size());
682 while ((j
= s
.find(i
->first
, j
)) != string::npos
) {
683 debug(format("rewriting @ %1%") % j
);
684 s
.replace(j
, i
->second
.size(), i
->second
);
691 //////////////////////////////////////////////////////////////////////
694 typedef enum {rpAccept
, rpDecline
, rpPostpone
} HookReply
;
696 class SubstitutionGoal
;
698 class DerivationGoal
: public Goal
701 /* The path of the derivation. */
704 /* The specific outputs that we need to build. Empty means all of
706 StringSet wantedOutputs
;
708 /* Whether additional wanted outputs have been added. */
711 /* Whether to retry substituting the outputs after building the
713 bool retrySubstitution
;
715 /* The derivation stored at drvPath. */
718 /* The remainder is state held during the build. */
720 /* Locks on the output paths. */
721 PathLocks outputLocks
;
723 /* All input paths (that is, the union of FS closures of the
724 immediate input paths). */
727 /* Referenceable paths (i.e., input and output paths). */
730 /* Outputs that are already valid. If we're repairing, these are
731 the outputs that are valid *and* not corrupt. */
734 /* Outputs that are corrupt or not valid. */
735 PathSet missingPaths
;
737 /* User selected for running the builder. */
740 /* The process ID of the builder. */
743 /* The temporary directory. */
746 /* The path of the temporary directory in the sandbox. */
747 Path tmpDirInSandbox
;
749 /* File descriptor for the log file. */
755 AutoCloseFD fdLogFile
;
757 /* Number of bytes received from the builder's stdout/stderr. */
758 unsigned long logSize
;
760 /* Pipe for the builder's standard output/error. */
763 /* The build hook. */
764 std::shared_ptr
<HookInstance
> hook
;
766 /* Whether we're currently doing a chroot build. */
771 /* RAII object to delete the chroot directory. */
772 std::shared_ptr
<AutoDelete
> autoDelChroot
;
774 /* All inputs that are regular files. */
775 PathSet regularInputPaths
;
777 /* Whether this is a fixed-output derivation. */
780 typedef void (DerivationGoal::*GoalState
)();
783 /* Stuff we need to pass to runChild(). */
784 typedef map
<Path
, Path
> DirsInChroot
; // maps target path to source path
785 DirsInChroot dirsInChroot
;
786 typedef map
<string
, string
> Environment
;
789 /* Hash rewriting. */
790 HashRewrites rewritesToTmp
, rewritesFromTmp
;
791 typedef map
<Path
, Path
> RedirectedOutputs
;
792 RedirectedOutputs redirectedOutputs
;
796 /* If we're repairing without a chroot, there may be outputs that
797 are valid but corrupt. So we redirect these outputs to
799 PathSet redirectedBadOutputs
;
801 /* The current round, if we're building multiple times. */
802 unsigned int curRound
= 1;
804 unsigned int nrRounds
;
806 /* Path registration info from the previous round, if we're
807 building multiple times. Since this contains the hash, it
808 allows us to compare whether two rounds produced the same
810 ValidPathInfos prevInfos
;
815 DerivationGoal(const Path
& drvPath
, const StringSet
& wantedOutputs
, Worker
& worker
, BuildMode buildMode
= bmNormal
);
818 void timedOut() override
;
822 /* Ensure that derivations get built in order of their name,
823 i.e. a derivation named "aardvark" always comes before
824 "baboon". And substitution goals always happen before
825 derivation goals (due to "b$"). */
826 return "b$" + storePathToName(drvPath
) + "$" + drvPath
;
836 /* Add wanted outputs to an already existing derivation goal. */
837 void addWantedOutputs(const StringSet
& outputs
);
839 BuildResult
getResult() { return result
; }
844 void haveDerivation();
845 void outputsSubstituted();
846 void closureRepaired();
847 void inputsRealised();
851 /* Is the build hook willing to perform the build? */
852 HookReply
tryBuildHook();
854 /* Start building a derivation. */
857 /* Run the builder's process. */
860 friend int childEntry(void *);
862 /* Check that the derivation outputs all exist and register them
864 void registerOutputs();
866 /* Open a log file and a pipe to it. */
869 /* Close the log file. */
872 /* Delete the temporary directory, if we have one. */
873 void deleteTmpDir(bool force
);
875 /* Callback used by the worker to write to the log. */
876 void handleChildOutput(int fd
, const string
& data
);
877 void handleEOF(int fd
);
879 /* Return the set of (in)valid paths. */
880 PathSet
checkPathValidity(bool returnValid
, bool checkHash
);
882 /* Abort the goal if `path' failed to build. */
883 bool pathFailed(const Path
& path
);
885 /* Forcibly kill the child process, if any. */
888 Path
addHashRewrite(const Path
& path
);
890 void repairClosure();
892 void done(BuildResult::Status status
, const string
& msg
= "");
896 DerivationGoal::DerivationGoal(const Path
& drvPath
, const StringSet
& wantedOutputs
, Worker
& worker
, BuildMode buildMode
)
898 , wantedOutputs(wantedOutputs
)
900 , retrySubstitution(false)
907 , buildMode(buildMode
)
909 this->drvPath
= drvPath
;
910 state
= &DerivationGoal::init
;
911 name
= (format("building of `%1%'") % drvPath
).str();
914 /* Prevent the .chroot directory from being
915 garbage-collected. (See isActiveTempFile() in gc.cc.) */
916 worker
.store
.addTempRoot(drvPath
);
920 DerivationGoal::~DerivationGoal()
922 /* Careful: we should never ever throw an exception from a
924 try { killChild(); } catch (...) { ignoreException(); }
925 try { deleteTmpDir(false); } catch (...) { ignoreException(); }
926 try { closeLogFile(); } catch (...) { ignoreException(); }
930 void DerivationGoal::killChild()
933 worker
.childTerminated(pid
);
935 if (buildUser
.enabled()) {
936 /* If we're using a build user, then there is a tricky
937 race condition: if we kill the build user before the
938 child has done its setuid() to the build user uid, then
939 it won't be killed, and we'll potentially lock up in
940 pid.wait(). So also send a conventional kill to the
942 ::kill(-pid
, SIGKILL
); /* ignore the result */
955 void DerivationGoal::timedOut()
957 if (settings
.printBuildTrace
)
958 printMsg(lvlError
, format("@ build-failed %1% - timeout") % drvPath
);
960 done(BuildResult::TimedOut
);
964 void DerivationGoal::work()
970 void DerivationGoal::addWantedOutputs(const StringSet
& outputs
)
972 /* If we already want all outputs, there is nothing to do. */
973 if (wantedOutputs
.empty()) return;
975 if (outputs
.empty()) {
976 wantedOutputs
.clear();
979 foreach (StringSet::const_iterator
, i
, outputs
)
980 if (wantedOutputs
.find(*i
) == wantedOutputs
.end()) {
981 wantedOutputs
.insert(*i
);
987 void DerivationGoal::init()
991 if (settings
.readOnlyMode
)
992 throw Error(format("cannot build derivation `%1%' - no write access to the store") % drvPath
);
994 /* The first thing to do is to make sure that the derivation
995 exists. If it doesn't, it may be created through a
997 if (buildMode
== bmNormal
&& worker
.store
.isValidPath(drvPath
)) {
1002 addWaitee(worker
.makeSubstitutionGoal(drvPath
));
1004 state
= &DerivationGoal::haveDerivation
;
1008 void DerivationGoal::haveDerivation()
1010 trace("loading derivation");
1012 if (nrFailed
!= 0) {
1013 printMsg(lvlError
, format("cannot build missing derivation ‘%1%’") % drvPath
);
1014 done(BuildResult::MiscFailure
);
1018 /* `drvPath' should already be a root, but let's be on the safe
1019 side: if the user forgot to make it a root, we wouldn't want
1020 things being garbage collected while we're busy. */
1021 worker
.store
.addTempRoot(drvPath
);
1023 assert(worker
.store
.isValidPath(drvPath
));
1025 /* Get the derivation. */
1026 drv
= derivationFromPath(worker
.store
, drvPath
);
1028 foreach (DerivationOutputs::iterator
, i
, drv
.outputs
)
1029 worker
.store
.addTempRoot(i
->second
.path
);
1031 /* Check what outputs paths are not already valid. */
1032 PathSet invalidOutputs
= checkPathValidity(false, buildMode
== bmRepair
);
1034 /* If they are all valid, then we're done. */
1035 if (invalidOutputs
.size() == 0 && buildMode
== bmNormal
) {
1036 done(BuildResult::AlreadyValid
);
1040 /* Check whether any output previously failed to build. If so,
1042 foreach (PathSet::iterator
, i
, invalidOutputs
)
1043 if (pathFailed(*i
)) return;
1045 /* We are first going to try to create the invalid output paths
1046 through substitutes. If that doesn't work, we'll build
1048 if (settings
.useSubstitutes
&& substitutesAllowed(drv
))
1049 foreach (PathSet::iterator
, i
, invalidOutputs
)
1050 addWaitee(worker
.makeSubstitutionGoal(*i
, buildMode
== bmRepair
));
1052 if (waitees
.empty()) /* to prevent hang (no wake-up event) */
1053 outputsSubstituted();
1055 state
= &DerivationGoal::outputsSubstituted
;
1059 void DerivationGoal::outputsSubstituted()
1061 trace("all outputs substituted (maybe)");
1063 if (nrFailed
> 0 && nrFailed
> nrNoSubstituters
+ nrIncompleteClosure
&& !settings
.tryFallback
)
1064 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
);
1066 /* If the substitutes form an incomplete closure, then we should
1067 build the dependencies of this derivation, but after that, we
1068 can still use the substitutes for this derivation itself. */
1069 if (nrIncompleteClosure
> 0 && !retrySubstitution
) retrySubstitution
= true;
1071 nrFailed
= nrNoSubstituters
= nrIncompleteClosure
= 0;
1074 needRestart
= false;
1079 unsigned int nrInvalid
= checkPathValidity(false, buildMode
== bmRepair
).size();
1080 if (buildMode
== bmNormal
&& nrInvalid
== 0) {
1081 done(BuildResult::Substituted
);
1084 if (buildMode
== bmRepair
&& nrInvalid
== 0) {
1088 if (buildMode
== bmCheck
&& nrInvalid
> 0)
1089 throw Error(format("some outputs of `%1%' are not valid, so checking is not possible") % drvPath
);
1091 /* Otherwise, at least one of the output paths could not be
1092 produced using a substitute. So we have to build instead. */
1094 /* Make sure checkPathValidity() from now on checks all
1096 wantedOutputs
= PathSet();
1098 /* The inputs must be built before we can build this goal. */
1099 foreach (DerivationInputs::iterator
, i
, drv
.inputDrvs
)
1100 addWaitee(worker
.makeDerivationGoal(i
->first
, i
->second
, buildMode
== bmRepair
? bmRepair
: bmNormal
));
1102 foreach (PathSet::iterator
, i
, drv
.inputSrcs
)
1103 addWaitee(worker
.makeSubstitutionGoal(*i
));
1105 if (waitees
.empty()) /* to prevent hang (no wake-up event) */
1108 state
= &DerivationGoal::inputsRealised
;
1112 void DerivationGoal::repairClosure()
1114 /* If we're repairing, we now know that our own outputs are valid.
1115 Now check whether the other paths in the outputs closure are
1116 good. If not, then start derivation goals for the derivations
1117 that produced those outputs. */
1119 /* Get the output closure. */
1120 PathSet outputClosure
;
1121 foreach (DerivationOutputs::iterator
, i
, drv
.outputs
) {
1122 if (!wantOutput(i
->first
, wantedOutputs
)) continue;
1123 computeFSClosure(worker
.store
, i
->second
.path
, outputClosure
);
1126 /* Filter out our own outputs (which we have already checked). */
1127 foreach (DerivationOutputs::iterator
, i
, drv
.outputs
)
1128 outputClosure
.erase(i
->second
.path
);
1130 /* Get all dependencies of this derivation so that we know which
1131 derivation is responsible for which path in the output
1133 PathSet inputClosure
;
1134 computeFSClosure(worker
.store
, drvPath
, inputClosure
);
1135 std::map
<Path
, Path
> outputsToDrv
;
1136 foreach (PathSet::iterator
, i
, inputClosure
)
1137 if (isDerivation(*i
)) {
1138 Derivation drv
= derivationFromPath(worker
.store
, *i
);
1139 foreach (DerivationOutputs::iterator
, j
, drv
.outputs
)
1140 outputsToDrv
[j
->second
.path
] = *i
;
1143 /* Check each path (slow!). */
1145 foreach (PathSet::iterator
, i
, outputClosure
) {
1146 if (worker
.store
.pathContentsGood(*i
)) continue;
1147 printMsg(lvlError
, format("found corrupted or missing path `%1%' in the output closure of `%2%'") % *i
% drvPath
);
1148 Path drvPath2
= outputsToDrv
[*i
];
1150 addWaitee(worker
.makeSubstitutionGoal(*i
, true));
1152 addWaitee(worker
.makeDerivationGoal(drvPath2
, PathSet(), bmRepair
));
1155 if (waitees
.empty()) {
1156 done(BuildResult::AlreadyValid
);
1160 state
= &DerivationGoal::closureRepaired
;
1164 void DerivationGoal::closureRepaired()
1166 trace("closure repaired");
1168 throw Error(format("some paths in the output closure of derivation ‘%1%’ could not be repaired") % drvPath
);
1169 done(BuildResult::AlreadyValid
);
1173 void DerivationGoal::inputsRealised()
1175 trace("all inputs realised");
1177 if (nrFailed
!= 0) {
1179 format("cannot build derivation `%1%': %2% dependencies couldn't be built")
1180 % drvPath
% nrFailed
);
1181 done(BuildResult::DependencyFailed
);
1185 if (retrySubstitution
) {
1190 /* Gather information necessary for computing the closure and/or
1191 running the build hook. */
1193 /* The outputs are referenceable paths. */
1194 foreach (DerivationOutputs::iterator
, i
, drv
.outputs
) {
1195 debug(format("building path `%1%'") % i
->second
.path
);
1196 allPaths
.insert(i
->second
.path
);
1199 /* Determine the full set of input paths. */
1201 /* First, the input derivations. */
1202 foreach (DerivationInputs::iterator
, i
, drv
.inputDrvs
) {
1203 /* Add the relevant output closures of the input derivation
1204 `*i' as input paths. Only add the closures of output paths
1205 that are specified as inputs. */
1206 assert(worker
.store
.isValidPath(i
->first
));
1207 Derivation inDrv
= derivationFromPath(worker
.store
, i
->first
);
1208 foreach (StringSet::iterator
, j
, i
->second
)
1209 if (inDrv
.outputs
.find(*j
) != inDrv
.outputs
.end())
1210 computeFSClosure(worker
.store
, inDrv
.outputs
[*j
].path
, inputPaths
);
1213 format("derivation `%1%' requires non-existent output `%2%' from input derivation `%3%'")
1214 % drvPath
% *j
% i
->first
);
1217 /* Second, the input sources. */
1218 foreach (PathSet::iterator
, i
, drv
.inputSrcs
)
1219 computeFSClosure(worker
.store
, *i
, inputPaths
);
1221 debug(format("added input paths %1%") % showPaths(inputPaths
));
1223 allPaths
.insert(inputPaths
.begin(), inputPaths
.end());
1225 /* Is this a fixed-output derivation? */
1227 for (auto & i
: drv
.outputs
)
1228 if (i
.second
.hash
== "") fixedOutput
= false;
1230 /* Don't repeat fixed-output derivations since they're already
1231 verified by their output hash.*/
1232 nrRounds
= fixedOutput
? 1 : settings
.get("build-repeat", 0) + 1;
1234 /* Okay, try to build. Note that here we don't wait for a build
1235 slot to become available, since we don't need one if there is a
1237 state
= &DerivationGoal::tryToBuild
;
1238 worker
.wakeUp(shared_from_this());
1242 static bool canBuildLocally(const string
& platform
)
1244 return platform
== settings
.thisSystem
1246 || (platform
== "i686-linux" && settings
.thisSystem
== "x86_64-linux")
1247 || (platform
== "armhf-linux" && settings
.thisSystem
== "aarch64-linux")
1253 static string
get(const StringPairs
& map
, const string
& key
, const string
& def
= "")
1255 StringPairs::const_iterator i
= map
.find(key
);
1256 return i
== map
.end() ? def
: i
->second
;
1260 bool willBuildLocally(const Derivation
& drv
)
1262 return get(drv
.env
, "preferLocalBuild") == "1" && canBuildLocally(drv
.platform
);
1266 bool substitutesAllowed(const Derivation
& drv
)
1268 return get(drv
.env
, "allowSubstitutes", "1") == "1";
1272 void DerivationGoal::tryToBuild()
1274 trace("trying to build");
1276 /* Check for the possibility that some other goal in this process
1277 has locked the output since we checked in haveDerivation().
1278 (It can't happen between here and the lockPaths() call below
1279 because we're not allowing multi-threading.) If so, put this
1280 goal to sleep until another goal finishes, then try again. */
1281 foreach (DerivationOutputs::iterator
, i
, drv
.outputs
)
1282 if (pathIsLockedByMe(i
->second
.path
)) {
1283 debug(format("putting derivation `%1%' to sleep because `%2%' is locked by another goal")
1284 % drvPath
% i
->second
.path
);
1285 worker
.waitForAnyGoal(shared_from_this());
1289 /* Obtain locks on all output paths. The locks are automatically
1290 released when we exit this function or the client crashes. If we
1291 can't acquire the lock, then continue; hopefully some other
1292 goal can start a build, and if not, the main loop will sleep a
1293 few seconds and then retry this goal. */
1294 if (!outputLocks
.lockPaths(outputPaths(drv
), "", false)) {
1295 worker
.waitForAWhile(shared_from_this());
1299 /* Now check again whether the outputs are valid. This is because
1300 another process may have started building in parallel. After
1301 it has finished and released the locks, we can (and should)
1302 reuse its results. (Strictly speaking the first check can be
1303 omitted, but that would be less efficient.) Note that since we
1304 now hold the locks on the output paths, no other process can
1305 build this derivation, so no further checks are necessary. */
1306 validPaths
= checkPathValidity(true, buildMode
== bmRepair
);
1307 if (buildMode
!= bmCheck
&& validPaths
.size() == drv
.outputs
.size()) {
1308 debug(format("skipping build of derivation `%1%', someone beat us to it") % drvPath
);
1309 outputLocks
.setDeletion(true);
1310 done(BuildResult::AlreadyValid
);
1314 missingPaths
= outputPaths(drv
);
1315 if (buildMode
!= bmCheck
)
1316 foreach (PathSet::iterator
, i
, validPaths
) missingPaths
.erase(*i
);
1318 /* If any of the outputs already exist but are not valid, delete
1320 foreach (DerivationOutputs::iterator
, i
, drv
.outputs
) {
1321 Path path
= i
->second
.path
;
1322 if (worker
.store
.isValidPath(path
)) continue;
1323 if (!pathExists(path
)) continue;
1324 debug(format("removing invalid path `%1%'") % path
);
1328 /* Check again whether any output previously failed to build,
1329 because some other process may have tried and failed before we
1330 acquired the lock. */
1331 foreach (DerivationOutputs::iterator
, i
, drv
.outputs
)
1332 if (pathFailed(i
->second
.path
)) return;
1334 /* Don't do a remote build if the derivation has the attribute
1335 `preferLocalBuild' set. Also, check and repair modes are only
1336 supported for local builds. */
1337 bool buildLocally
= buildMode
!= bmNormal
|| willBuildLocally(drv
);
1339 /* Is the build hook willing to accept this job? */
1340 if (!buildLocally
) {
1341 switch (tryBuildHook()) {
1343 /* Yes, it has started doing so. Wait until we get
1344 EOF from the hook. */
1345 state
= &DerivationGoal::buildDone
;
1348 /* Not now; wait until at least one child finishes or
1349 the wake-up timeout expires. */
1350 worker
.waitForAWhile(shared_from_this());
1351 outputLocks
.unlock();
1354 /* We should do it ourselves. */
1359 /* Make sure that we are allowed to start a build. If this
1360 derivation prefers to be done locally, do it even if
1361 maxBuildJobs is 0. */
1362 unsigned int curBuilds
= worker
.getNrLocalBuilds();
1363 if (curBuilds
>= settings
.maxBuildJobs
&& !(buildLocally
&& curBuilds
== 0)) {
1364 worker
.waitForBuildSlot(shared_from_this());
1365 outputLocks
.unlock();
1371 /* Okay, we have to build. */
1374 } catch (BuildError
& e
) {
1375 printMsg(lvlError
, e
.msg());
1376 outputLocks
.unlock();
1377 buildUser
.release();
1378 if (settings
.printBuildTrace
)
1379 printMsg(lvlError
, format("@ build-failed %1% - %2% %3%")
1380 % drvPath
% 0 % e
.msg());
1381 worker
.permanentFailure
= true;
1382 done(BuildResult::InputRejected
, e
.msg());
1386 /* This state will be reached when we get EOF on the child's
1388 state
= &DerivationGoal::buildDone
;
1392 void replaceValidPath(const Path
& storePath
, const Path tmpPath
)
1394 /* We can't atomically replace storePath (the original) with
1395 tmpPath (the replacement), so we have to move it out of the
1396 way first. We'd better not be interrupted here, because if
1397 we're repairing (say) Glibc, we end up with a broken system. */
1398 Path oldPath
= (format("%1%.old-%2%-%3%") % storePath
% getpid() % rand()).str();
1399 if (pathExists(storePath
))
1400 rename(storePath
.c_str(), oldPath
.c_str());
1401 if (rename(tmpPath
.c_str(), storePath
.c_str()) == -1)
1402 throw SysError(format("moving `%1%' to `%2%'") % tmpPath
% storePath
);
1403 if (pathExists(oldPath
))
1404 deletePath(oldPath
);
1408 MakeError(NotDeterministic
, BuildError
)
1411 void DerivationGoal::buildDone()
1413 trace("build done");
1415 /* Since we got an EOF on the logger pipe, the builder is presumed
1416 to have terminated. In fact, the builder could also have
1417 simply have closed its end of the pipe --- just don't do that
1422 savedPid
= hook
->pid
;
1423 status
= hook
->pid
.wait(true);
1425 /* !!! this could block! security problem! solution: kill the
1428 status
= pid
.wait(true);
1431 debug(format("builder process for `%1%' finished") % drvPath
);
1433 /* So the child is gone now. */
1434 worker
.childTerminated(savedPid
);
1436 /* Close the read side of the logger pipe. */
1438 hook
->builderOut
.readSide
.close();
1439 hook
->fromHook
.readSide
.close();
1441 else builderOut
.readSide
.close();
1443 /* Close the log file. */
1446 /* When running under a build user, make sure that all processes
1447 running under that uid are gone. This is to prevent a
1448 malicious user from leaving behind a process that keeps files
1449 open and modifies them after they have been chown'ed to
1451 if (buildUser
.enabled()) buildUser
.kill();
1453 bool diskFull
= false;
1457 /* Check the exit status. */
1458 if (!statusOk(status
)) {
1460 /* Heuristically check whether the build failure may have
1461 been caused by a disk full condition. We have no way
1462 of knowing whether the build actually got an ENOSPC.
1463 So instead, check if the disk is (nearly) full now. If
1464 so, we don't mark this build as a permanent failure. */
1466 unsigned long long required
= 8ULL * 1024 * 1024; // FIXME: make configurable
1468 if (statvfs(settings
.nixStore
.c_str(), &st
) == 0 &&
1469 (unsigned long long) st
.f_bavail
* st
.f_bsize
< required
)
1471 if (statvfs(tmpDir
.c_str(), &st
) == 0 &&
1472 (unsigned long long) st
.f_bavail
* st
.f_bsize
< required
)
1476 deleteTmpDir(false);
1478 /* Move paths out of the chroot for easier debugging of
1480 if (useChroot
&& buildMode
== bmNormal
)
1481 foreach (PathSet::iterator
, i
, missingPaths
)
1482 if (pathExists(chrootRootDir
+ *i
))
1483 rename((chrootRootDir
+ *i
).c_str(), i
->c_str());
1486 printMsg(lvlError
, "note: build failure may have been caused by lack of free disk space");
1488 throw BuildError(format("builder for `%1%' %2%")
1489 % drvPath
% statusToString(status
));
1492 /* Compute the FS closure of the outputs and register them as
1496 if (buildMode
== bmCheck
) {
1497 done(BuildResult::Built
);
1501 /* Delete unused redirected outputs (when doing hash rewriting). */
1502 foreach (RedirectedOutputs::iterator
, i
, redirectedOutputs
)
1503 if (pathExists(i
->second
)) deletePath(i
->second
);
1505 /* Delete the chroot (if we were using one). */
1506 autoDelChroot
.reset(); /* this runs the destructor */
1510 /* Repeat the build if necessary. */
1511 if (curRound
++ < nrRounds
) {
1512 outputLocks
.unlock();
1513 buildUser
.release();
1514 state
= &DerivationGoal::tryToBuild
;
1515 worker
.wakeUp(shared_from_this());
1519 /* It is now safe to delete the lock files, since all future
1520 lockers will see that the output paths are valid; they will
1521 not create new lock files with the same names as the old
1522 (unlinked) lock files. */
1523 outputLocks
.setDeletion(true);
1524 outputLocks
.unlock();
1526 } catch (BuildError
& e
) {
1528 printMsg(lvlError
, e
.msg());
1529 outputLocks
.unlock();
1530 buildUser
.release();
1532 BuildResult::Status st
= BuildResult::MiscFailure
;
1534 if (hook
&& WIFEXITED(status
) && WEXITSTATUS(status
) == 101) {
1535 if (settings
.printBuildTrace
)
1536 printMsg(lvlError
, format("@ build-failed %1% - timeout") % drvPath
);
1537 st
= BuildResult::TimedOut
;
1540 else if (hook
&& (!WIFEXITED(status
) || WEXITSTATUS(status
) != 100)) {
1541 if (settings
.printBuildTrace
)
1542 printMsg(lvlError
, format("@ hook-failed %1% - %2% %3%")
1543 % drvPath
% status
% e
.msg());
1547 if (settings
.printBuildTrace
)
1548 printMsg(lvlError
, format("@ build-failed %1% - %2% %3%")
1549 % drvPath
% 1 % e
.msg());
1552 statusOk(status
) ? BuildResult::OutputRejected
:
1553 fixedOutput
|| diskFull
? BuildResult::TransientFailure
:
1554 BuildResult::PermanentFailure
;
1556 /* Register the outputs of this build as "failed" so we
1557 won't try to build them again (negative caching).
1558 However, don't do this for fixed-output derivations,
1559 since they're likely to fail for transient reasons
1560 (e.g., fetchurl not being able to access the network).
1561 Hook errors (like communication problems with the
1562 remote machine) shouldn't be cached either. */
1563 if (settings
.cacheFailure
&& !fixedOutput
&& !diskFull
)
1564 foreach (DerivationOutputs::iterator
, i
, drv
.outputs
)
1565 worker
.store
.registerFailedPath(i
->second
.path
);
1572 /* Release the build user, if applicable. */
1573 buildUser
.release();
1575 if (settings
.printBuildTrace
)
1576 printMsg(lvlError
, format("@ build-succeeded %1% -") % drvPath
);
1578 done(BuildResult::Built
);
1582 HookReply
DerivationGoal::tryBuildHook()
1584 if (!settings
.useBuildHook
|| getEnv("NIX_BUILD_HOOK") == "") return rpDecline
;
1587 worker
.hook
= std::shared_ptr
<HookInstance
>(new HookInstance
);
1589 /* Tell the hook about system features (beyond the system type)
1590 required from the build machine. (The hook could parse the
1591 drv file itself, but this is easier.) */
1592 Strings features
= tokenizeString
<Strings
>(get(drv
.env
, "requiredSystemFeatures"));
1593 foreach (Strings::iterator
, i
, features
) checkStoreName(*i
); /* !!! abuse */
1595 /* Send the request to the hook. */
1596 writeLine(worker
.hook
->toHook
.writeSide
, (format("%1% %2% %3% %4%")
1597 % (worker
.getNrLocalBuilds() < settings
.maxBuildJobs
? "1" : "0")
1598 % drv
.platform
% drvPath
% concatStringsSep(",", features
)).str());
1600 /* Read the first line of input, which should be a word indicating
1601 whether the hook wishes to perform the build. */
1604 string s
= readLine(worker
.hook
->fromHook
.readSide
);
1605 if (string(s
, 0, 2) == "# ") {
1606 reply
= string(s
, 2);
1613 debug(format("hook reply is `%1%'") % reply
);
1615 if (reply
== "decline" || reply
== "postpone")
1616 return reply
== "decline" ? rpDecline
: rpPostpone
;
1617 else if (reply
!= "accept")
1618 throw Error(format("bad hook reply `%1%'") % reply
);
1620 printMsg(lvlTalkative
, format("using hook to build path(s) %1%") % showPaths(missingPaths
));
1623 worker
.hook
.reset();
1625 /* Tell the hook all the inputs that have to be copied to the
1626 remote system. This unfortunately has to contain the entire
1627 derivation closure to ensure that the validity invariant holds
1628 on the remote system. (I.e., it's unfortunate that we have to
1629 list it since the remote system *probably* already has it.) */
1631 allInputs
.insert(inputPaths
.begin(), inputPaths
.end());
1632 computeFSClosure(worker
.store
, drvPath
, allInputs
);
1635 foreach (PathSet::iterator
, i
, allInputs
) { s
+= *i
; s
+= ' '; }
1636 writeLine(hook
->toHook
.writeSide
, s
);
1638 /* Tell the hooks the missing outputs that have to be copied back
1639 from the remote system. */
1641 foreach (PathSet::iterator
, i
, missingPaths
) { s
+= *i
; s
+= ' '; }
1642 writeLine(hook
->toHook
.writeSide
, s
);
1644 hook
->toHook
.writeSide
.close();
1646 /* Create the log file and pipe. */
1647 Path logFile
= openLogFile();
1650 fds
.insert(hook
->fromHook
.readSide
);
1651 fds
.insert(hook
->builderOut
.readSide
);
1652 worker
.childStarted(shared_from_this(), hook
->pid
, fds
, false, false);
1654 if (settings
.printBuildTrace
)
1655 printMsg(lvlError
, format("@ build-started %1% - %2% %3% %4%")
1656 % drvPath
% drv
.platform
% logFile
% hook
->pid
);
1662 void chmod_(const Path
& path
, mode_t mode
)
1664 if (chmod(path
.c_str(), mode
) == -1)
1665 throw SysError(format("setting permissions on `%1%'") % path
);
1669 int childEntry(void * arg
)
1671 ((DerivationGoal
*) arg
)->runChild();
1676 void DerivationGoal::startBuilder()
1679 buildMode
== bmRepair
? "repairing path(s) %1%" :
1680 buildMode
== bmCheck
? "checking path(s) %1%" :
1681 nrRounds
> 1 ? "building path(s) %1% (round %2%/%3%)" :
1682 "building path(s) %1%");
1683 f
.exceptions(boost::io::all_error_bits
^ boost::io::too_many_args_bit
);
1684 startNest(nest
, lvlInfo
, f
% showPaths(missingPaths
) % curRound
% nrRounds
);
1686 /* Note: built-in builders are *not* running in a chroot environment so
1687 that we can easily implement them in Guile without having it as a
1688 derivation input (they are running under a separate build user,
1690 useChroot
= settings
.useChroot
&& !isBuiltin(drv
);
1692 /* Construct the environment passed to the builder. */
1695 /* Most shells initialise PATH to some default (/bin:/usr/bin:...) when
1696 PATH is not set. We don't want this, so we fill it in with some dummy
1698 env
["PATH"] = "/path-not-set";
1700 /* Set HOME to a non-existing path to prevent certain programs from using
1701 /etc/passwd (or NIS, or whatever) to locate the home directory (for
1702 example, wget looks for ~/.wgetrc). I.e., these tools use /etc/passwd
1703 if HOME is not set, but they will just assume that the settings file
1704 they are looking for does not exist if HOME is set but points to some
1705 non-existing path. */
1706 Path homeDir
= "/homeless-shelter";
1707 env
["HOME"] = homeDir
;
1709 /* Tell the builder where the store is. Usually they
1710 shouldn't care, but this is useful for purity checking (e.g.,
1711 the compiler or linker might only want to accept paths to files
1712 in the store or in the build directory). */
1713 env
["NIX_STORE"] = settings
.nixStore
;
1715 /* The maximum number of cores to utilize for parallel building. */
1716 env
["NIX_BUILD_CORES"] = (format("%d") % settings
.buildCores
).str();
1718 /* Add all bindings specified in the derivation. */
1719 foreach (StringPairs::iterator
, i
, drv
.env
)
1720 env
[i
->first
] = i
->second
;
1722 /* Create a temporary directory where the build will take
1724 auto drvName
= storePathToName(drvPath
);
1725 tmpDir
= createTempDir("", "guix-build-" + drvName
, false, false, 0700);
1727 /* In a sandbox, for determinism, always use the same temporary
1729 tmpDirInSandbox
= useChroot
? canonPath("/tmp", true) + "/guix-build-" + drvName
+ "-0" : tmpDir
;
1731 /* For convenience, set an environment pointing to the top build
1733 env
["NIX_BUILD_TOP"] = tmpDirInSandbox
;
1735 /* Also set TMPDIR and variants to point to this directory. */
1736 env
["TMPDIR"] = env
["TEMPDIR"] = env
["TMP"] = env
["TEMP"] = tmpDirInSandbox
;
1738 /* Explicitly set PWD to prevent problems with chroot builds. In
1739 particular, dietlibc cannot figure out the cwd because the
1740 inode of the current directory doesn't appear in .. (because
1741 getdents returns the inode of the mount point). */
1742 env
["PWD"] = tmpDirInSandbox
;
1744 /* Compatibility hack with Nix <= 0.7: if this is a fixed-output
1745 derivation, tell the builder, so that for instance `fetchurl'
1746 can skip checking the output. On older Nixes, this environment
1747 variable won't be set, so `fetchurl' will do the check. */
1748 if (fixedOutput
) env
["NIX_OUTPUT_CHECKED"] = "1";
1750 /* *Only* if this is a fixed-output derivation, propagate the
1751 values of the environment variables specified in the
1752 `impureEnvVars' attribute to the builder. This allows for
1753 instance environment variables for proxy configuration such as
1754 `http_proxy' to be easily passed to downloaders like
1755 `fetchurl'. Passing such environment variables from the caller
1756 to the builder is generally impure, but the output of
1757 fixed-output derivations is by definition pure (since we
1758 already know the cryptographic hash of the output). */
1760 Strings varNames
= tokenizeString
<Strings
>(get(drv
.env
, "impureEnvVars"));
1761 foreach (Strings::iterator
, i
, varNames
) env
[*i
] = getEnv(*i
);
1764 /* The `exportReferencesGraph' feature allows the references graph
1765 to be passed to a builder. This attribute should be a list of
1766 pairs [name1 path1 name2 path2 ...]. The references graph of
1767 each `pathN' will be stored in a text file `nameN' in the
1768 temporary build directory. The text files have the format used
1769 by `nix-store --register-validity'. However, the deriver
1770 fields are left empty. */
1771 string s
= get(drv
.env
, "exportReferencesGraph");
1772 Strings ss
= tokenizeString
<Strings
>(s
);
1773 if (ss
.size() % 2 != 0)
1774 throw BuildError(format("odd number of tokens in `exportReferencesGraph': `%1%'") % s
);
1775 for (Strings::iterator i
= ss
.begin(); i
!= ss
.end(); ) {
1776 string fileName
= *i
++;
1777 checkStoreName(fileName
); /* !!! abuse of this function */
1779 /* Check that the store path is valid. */
1780 Path storePath
= *i
++;
1781 if (!isInStore(storePath
))
1782 throw BuildError(format("`exportReferencesGraph' contains a non-store path `%1%'")
1784 storePath
= toStorePath(storePath
);
1785 if (!worker
.store
.isValidPath(storePath
))
1786 throw BuildError(format("`exportReferencesGraph' contains an invalid path `%1%'")
1789 /* If there are derivations in the graph, then include their
1790 outputs as well. This is useful if you want to do things
1791 like passing all build-time dependencies of some path to a
1792 derivation that builds a NixOS DVD image. */
1793 PathSet paths
, paths2
;
1794 computeFSClosure(worker
.store
, storePath
, paths
);
1797 foreach (PathSet::iterator
, j
, paths2
) {
1798 if (isDerivation(*j
)) {
1799 Derivation drv
= derivationFromPath(worker
.store
, *j
);
1800 foreach (DerivationOutputs::iterator
, k
, drv
.outputs
)
1801 computeFSClosure(worker
.store
, k
->second
.path
, paths
);
1805 /* Write closure info to `fileName'. */
1806 writeFile(tmpDir
+ "/" + fileName
,
1807 worker
.store
.makeValidityRegistration(paths
, false, false));
1811 /* If `build-users-group' is not empty, then we have to build as
1812 one of the members of that group. */
1813 if (settings
.buildUsersGroup
!= "") {
1814 buildUser
.acquire();
1815 assert(buildUser
.getUID() != 0);
1816 assert(buildUser
.getGID() != 0);
1818 /* Make sure that no other processes are executing under this
1822 /* Change ownership of the temporary build directory. */
1823 if (chown(tmpDir
.c_str(), buildUser
.getUID(), buildUser
.getGID()) == -1)
1824 throw SysError(format("cannot change ownership of '%1%'") % tmpDir
);
1829 /* Create a temporary directory in which we set up the chroot
1830 environment using bind-mounts. We put it in the store
1831 to ensure that we can create hard-links to non-directory
1832 inputs in the fake store in the chroot (see below). */
1833 chrootRootDir
= drvPath
+ ".chroot";
1834 if (pathExists(chrootRootDir
)) deletePath(chrootRootDir
);
1836 /* Clean up the chroot directory automatically. */
1837 autoDelChroot
= std::shared_ptr
<AutoDelete
>(new AutoDelete(chrootRootDir
));
1839 printMsg(lvlChatty
, format("setting up chroot environment in `%1%'") % chrootRootDir
);
1841 if (mkdir(chrootRootDir
.c_str(), 0750) == -1)
1842 throw SysError(format("cannot create ‘%1%’") % chrootRootDir
);
1844 if (chown(chrootRootDir
.c_str(), 0, buildUser
.getGID()) == -1)
1845 throw SysError(format("cannot change ownership of ‘%1%’") % chrootRootDir
);
1847 /* Create a writable /tmp in the chroot. Many builders need
1848 this. (Of course they should really respect $TMPDIR
1850 Path chrootTmpDir
= chrootRootDir
+ "/tmp";
1851 createDirs(chrootTmpDir
);
1852 chmod_(chrootTmpDir
, 01777);
1854 /* Create a /etc/passwd with entries for the build user and the
1855 nobody account. The latter is kind of a hack to support
1857 createDirs(chrootRootDir
+ "/etc");
1859 writeFile(chrootRootDir
+ "/etc/passwd",
1861 "nixbld:x:%1%:%2%:Nix build user:/:/noshell\n"
1862 "nobody:x:65534:65534:Nobody:/:/noshell\n")
1863 % (buildUser
.enabled() ? buildUser
.getUID() : getuid())
1864 % (buildUser
.enabled() ? buildUser
.getGID() : getgid())).str());
1866 /* Declare the build user's group so that programs get a consistent
1867 view of the system (e.g., "id -gn"). */
1868 writeFile(chrootRootDir
+ "/etc/group",
1869 (format("nixbld:!:%1%:\n")
1870 % (buildUser
.enabled() ? buildUser
.getGID() : getgid())).str());
1872 /* Create /etc/hosts with localhost entry. */
1874 writeFile(chrootRootDir
+ "/etc/hosts", "127.0.0.1 localhost\n");
1876 /* Bind-mount a user-configurable set of directories from the
1877 host file system. */
1878 PathSet dirs
= tokenizeString
<StringSet
>(settings
.get("build-chroot-dirs", string(DEFAULT_CHROOT_DIRS
)));
1879 PathSet dirs2
= tokenizeString
<StringSet
>(settings
.get("build-extra-chroot-dirs", string("")));
1880 dirs
.insert(dirs2
.begin(), dirs2
.end());
1881 for (auto & i
: dirs
) {
1882 size_t p
= i
.find('=');
1883 if (p
== string::npos
)
1884 dirsInChroot
[i
] = i
;
1886 dirsInChroot
[string(i
, 0, p
)] = string(i
, p
+ 1);
1888 dirsInChroot
[tmpDirInSandbox
] = tmpDir
;
1890 /* Make the closure of the inputs available in the chroot,
1891 rather than the whole store. This prevents any access
1892 to undeclared dependencies. Directories are bind-mounted,
1893 while other inputs are hard-linked (since only directories
1894 can be bind-mounted). !!! As an extra security
1895 precaution, make the fake store only writable by the
1897 Path chrootStoreDir
= chrootRootDir
+ settings
.nixStore
;
1898 createDirs(chrootStoreDir
);
1899 chmod_(chrootStoreDir
, 01775);
1901 if (chown(chrootStoreDir
.c_str(), 0, buildUser
.getGID()) == -1)
1902 throw SysError(format("cannot change ownership of ‘%1%’") % chrootStoreDir
);
1904 foreach (PathSet::iterator
, i
, inputPaths
) {
1906 if (lstat(i
->c_str(), &st
))
1907 throw SysError(format("getting attributes of path `%1%'") % *i
);
1908 if (S_ISDIR(st
.st_mode
))
1909 dirsInChroot
[*i
] = *i
;
1911 Path p
= chrootRootDir
+ *i
;
1912 if (link(i
->c_str(), p
.c_str()) == -1) {
1913 /* Hard-linking fails if we exceed the maximum
1914 link count on a file (e.g. 32000 of ext3),
1915 which is quite possible after a `nix-store
1917 if (errno
!= EMLINK
)
1918 throw SysError(format("linking `%1%' to `%2%'") % p
% *i
);
1921 StringSource
source(sink
.s
);
1922 restorePath(p
, source
);
1925 regularInputPaths
.insert(*i
);
1929 /* If we're repairing, checking or rebuilding part of a
1930 multiple-outputs derivation, it's possible that we're
1931 rebuilding a path that is in settings.dirsInChroot
1932 (typically the dependencies of /bin/sh). Throw them
1934 for (auto & i
: drv
.outputs
)
1935 dirsInChroot
.erase(i
.second
.path
);
1938 throw Error("chroot builds are not supported on this platform");
1944 if (pathExists(homeDir
))
1945 throw Error(format("directory `%1%' exists; please remove it") % homeDir
);
1947 /* We're not doing a chroot build, but we have some valid
1948 output paths. Since we can't just overwrite or delete
1949 them, we have to do hash rewriting: i.e. in the
1950 environment/arguments passed to the build, we replace the
1951 hashes of the valid outputs with unique dummy strings;
1952 after the build, we discard the redirected outputs
1953 corresponding to the valid outputs, and rewrite the
1954 contents of the new outputs to replace the dummy strings
1955 with the actual hashes. */
1956 if (validPaths
.size() > 0)
1957 foreach (PathSet::iterator
, i
, validPaths
)
1960 /* If we're repairing, then we don't want to delete the
1961 corrupt outputs in advance. So rewrite them as well. */
1962 if (buildMode
== bmRepair
)
1963 foreach (PathSet::iterator
, i
, missingPaths
)
1964 if (worker
.store
.isValidPath(*i
) && pathExists(*i
)) {
1966 redirectedBadOutputs
.insert(*i
);
1971 /* Run the builder. */
1972 printMsg(lvlChatty
, format("executing builder `%1%'") % drv
.builder
);
1974 /* Create the log file. */
1975 Path logFile
= openLogFile();
1977 /* Create a pipe to get the output of the builder. */
1978 builderOut
.create();
1980 /* Fork a child to build the package. Note that while we
1981 currently use forks to run and wait for the children, it
1982 shouldn't be hard to use threads for this on systems where
1983 fork() is unavailable or inefficient.
1985 If we're building in a chroot, then also set up private
1986 namespaces for the build:
1988 - The PID namespace causes the build to start as PID 1.
1989 Processes outside of the chroot are not visible to those on
1990 the inside, but processes inside the chroot are visible from
1991 the outside (though with different PIDs).
1993 - The private mount namespace ensures that all the bind mounts
1994 we do will only show up in this process and its children, and
1995 will disappear automatically when we're done.
1997 - The private network namespace ensures that the builder cannot
1998 talk to the outside world (or vice versa). It only has a
1999 private loopback interface.
2001 - The IPC namespace prevents the builder from communicating
2002 with outside processes using SysV IPC mechanisms (shared
2003 memory, message queues, semaphores). It also ensures that
2004 all IPC objects are destroyed when the builder exits.
2006 - The UTS namespace ensures that builders see a hostname of
2007 localhost rather than the actual hostname.
2011 char stack
[32 * 1024];
2012 int flags
= CLONE_NEWPID
| CLONE_NEWNS
| CLONE_NEWIPC
| CLONE_NEWUTS
| SIGCHLD
;
2013 if (!fixedOutput
) flags
|= CLONE_NEWNET
;
2014 /* Ensure proper alignment on the stack. On aarch64, it has to be 16
2016 pid
= clone(childEntry
,
2017 (char *)(((uintptr_t)stack
+ sizeof(stack
) - 8) & ~(uintptr_t)0xf),
2020 throw SysError("cloning builder process");
2025 if (pid
== 0) runChild();
2028 if (pid
== -1) throw SysError("unable to fork");
2031 pid
.setSeparatePG(true);
2032 builderOut
.writeSide
.close();
2033 worker
.childStarted(shared_from_this(), pid
,
2034 singleton
<set
<int> >(builderOut
.readSide
), true, true);
2036 /* Check if setting up the build environment failed. */
2037 string msg
= readLine(builderOut
.readSide
);
2038 if (!msg
.empty()) throw Error(msg
);
2040 if (settings
.printBuildTrace
) {
2041 printMsg(lvlError
, format("@ build-started %1% - %2% %3% %4%")
2042 % drvPath
% drv
.platform
% logFile
% pid
);
2048 void DerivationGoal::runChild()
2050 /* Warning: in the child we should absolutely not make any SQLite
2059 commonChildInit(builderOut
);
2063 /* Initialise the loopback interface. */
2064 AutoCloseFD
fd(socket(PF_INET
, SOCK_DGRAM
, IPPROTO_IP
));
2065 if (fd
== -1) throw SysError("cannot open IP socket");
2068 strcpy(ifr
.ifr_name
, "lo");
2069 ifr
.ifr_flags
= IFF_UP
| IFF_LOOPBACK
| IFF_RUNNING
;
2070 if (ioctl(fd
, SIOCSIFFLAGS
, &ifr
) == -1)
2071 throw SysError("cannot set loopback interface flags");
2075 /* Set the hostname etc. to fixed values. */
2076 char hostname
[] = "localhost";
2077 if (sethostname(hostname
, sizeof(hostname
)) == -1)
2078 throw SysError("cannot set host name");
2079 char domainname
[] = "(none)"; // kernel default
2080 if (setdomainname(domainname
, sizeof(domainname
)) == -1)
2081 throw SysError("cannot set domain name");
2083 /* Make all filesystems private. This is necessary
2084 because subtrees may have been mounted as "shared"
2085 (MS_SHARED). (Systemd does this, for instance.) Even
2086 though we have a private mount namespace, mounting
2087 filesystems on top of a shared subtree still propagates
2088 outside of the namespace. Making a subtree private is
2089 local to the namespace, though, so setting MS_PRIVATE
2090 does not affect the outside world. */
2091 if (mount(0, "/", 0, MS_REC
|MS_PRIVATE
, 0) == -1) {
2092 throw SysError("unable to make ‘/’ private mount");
2095 /* Bind-mount chroot directory to itself, to treat it as a
2096 different filesystem from /, as needed for pivot_root. */
2097 if (mount(chrootRootDir
.c_str(), chrootRootDir
.c_str(), 0, MS_BIND
, 0) == -1)
2098 throw SysError(format("unable to bind mount ‘%1%’") % chrootRootDir
);
2100 /* Set up a nearly empty /dev, unless the user asked to
2101 bind-mount the host /dev. */
2103 if (dirsInChroot
.find("/dev") == dirsInChroot
.end()) {
2104 createDirs(chrootRootDir
+ "/dev/shm");
2105 createDirs(chrootRootDir
+ "/dev/pts");
2106 ss
.push_back("/dev/full");
2108 if (pathExists("/dev/kvm"))
2109 ss
.push_back("/dev/kvm");
2111 ss
.push_back("/dev/null");
2112 ss
.push_back("/dev/random");
2113 ss
.push_back("/dev/tty");
2114 ss
.push_back("/dev/urandom");
2115 ss
.push_back("/dev/zero");
2116 createSymlink("/proc/self/fd", chrootRootDir
+ "/dev/fd");
2117 createSymlink("/proc/self/fd/0", chrootRootDir
+ "/dev/stdin");
2118 createSymlink("/proc/self/fd/1", chrootRootDir
+ "/dev/stdout");
2119 createSymlink("/proc/self/fd/2", chrootRootDir
+ "/dev/stderr");
2122 /* Fixed-output derivations typically need to access the
2123 network, so give them access to /etc/resolv.conf and so
2126 ss
.push_back("/etc/resolv.conf");
2127 ss
.push_back("/etc/nsswitch.conf");
2128 ss
.push_back("/etc/services");
2129 ss
.push_back("/etc/hosts");
2132 for (auto & i
: ss
) dirsInChroot
[i
] = i
;
2134 /* Bind-mount all the directories from the "host"
2135 filesystem that we want in the chroot
2137 foreach (DirsInChroot::iterator
, i
, dirsInChroot
) {
2139 Path source
= i
->second
;
2140 Path target
= chrootRootDir
+ i
->first
;
2141 if (source
== "/proc") continue; // backwards compatibility
2142 debug(format("bind mounting `%1%' to `%2%'") % source
% target
);
2143 if (stat(source
.c_str(), &st
) == -1)
2144 throw SysError(format("getting attributes of path `%1%'") % source
);
2145 if (S_ISDIR(st
.st_mode
))
2148 createDirs(dirOf(target
));
2149 writeFile(target
, "");
2151 if (mount(source
.c_str(), target
.c_str(), "", MS_BIND
, 0) == -1)
2152 throw SysError(format("bind mount from `%1%' to `%2%' failed") % source
% target
);
2155 /* Bind a new instance of procfs on /proc to reflect our
2156 private PID namespace. */
2157 createDirs(chrootRootDir
+ "/proc");
2158 if (mount("none", (chrootRootDir
+ "/proc").c_str(), "proc", 0, 0) == -1)
2159 throw SysError("mounting /proc");
2161 /* Mount a new tmpfs on /dev/shm to ensure that whatever
2162 the builder puts in /dev/shm is cleaned up automatically. */
2163 if (pathExists("/dev/shm") && mount("none", (chrootRootDir
+ "/dev/shm").c_str(), "tmpfs", 0, 0) == -1)
2164 throw SysError("mounting /dev/shm");
2166 /* Mount a new devpts on /dev/pts. Note that this
2167 requires the kernel to be compiled with
2168 CONFIG_DEVPTS_MULTIPLE_INSTANCES=y (which is the case
2169 if /dev/ptx/ptmx exists). */
2170 if (pathExists("/dev/pts/ptmx") &&
2171 !pathExists(chrootRootDir
+ "/dev/ptmx")
2172 && dirsInChroot
.find("/dev/pts") == dirsInChroot
.end())
2174 if (mount("none", (chrootRootDir
+ "/dev/pts").c_str(), "devpts", 0, "newinstance,mode=0620") == -1)
2175 throw SysError("mounting /dev/pts");
2176 createSymlink("/dev/pts/ptmx", chrootRootDir
+ "/dev/ptmx");
2178 /* Make sure /dev/pts/ptmx is world-writable. With some
2179 Linux versions, it is created with permissions 0. */
2180 chmod_(chrootRootDir
+ "/dev/pts/ptmx", 0666);
2183 /* Do the chroot(). */
2184 if (chdir(chrootRootDir
.c_str()) == -1)
2185 throw SysError(format("cannot change directory to '%1%'") % chrootRootDir
);
2187 if (mkdir("real-root", 0) == -1)
2188 throw SysError("cannot create real-root directory");
2190 #define pivot_root(new_root, put_old) (syscall(SYS_pivot_root, new_root, put_old))
2191 if (pivot_root(".", "real-root") == -1)
2192 throw SysError(format("cannot pivot old root directory onto '%1%'") % (chrootRootDir
+ "/real-root"));
2195 if (chroot(".") == -1)
2196 throw SysError(format("cannot change root directory to '%1%'") % chrootRootDir
);
2198 if (umount2("real-root", MNT_DETACH
) == -1)
2199 throw SysError("cannot unmount real root filesystem");
2201 if (rmdir("real-root") == -1)
2202 throw SysError("cannot remove real-root directory");
2206 if (chdir(tmpDirInSandbox
.c_str()) == -1)
2207 throw SysError(format("changing into `%1%'") % tmpDir
);
2209 /* Close all other file descriptors. */
2210 closeMostFDs(set
<int>());
2213 /* Change the personality to 32-bit if we're doing an
2214 i686-linux build on an x86_64-linux machine. */
2215 struct utsname utsbuf
;
2217 if (drv
.platform
== "i686-linux" &&
2218 (settings
.thisSystem
== "x86_64-linux" ||
2219 (!strcmp(utsbuf
.sysname
, "Linux") && !strcmp(utsbuf
.machine
, "x86_64")))) {
2220 if (personality(PER_LINUX32
) == -1)
2221 throw SysError("cannot set i686-linux personality");
2224 if (drv
.platform
== "armhf-linux" &&
2225 (settings
.thisSystem
== "aarch64-linux" ||
2226 (!strcmp(utsbuf
.sysname
, "Linux") && !strcmp(utsbuf
.machine
, "aarch64")))) {
2227 if (personality(PER_LINUX32
) == -1)
2228 throw SysError("cannot set armhf-linux personality");
2231 /* Impersonate a Linux 2.6 machine to get some determinism in
2232 builds that depend on the kernel version. */
2233 if ((drv
.platform
== "i686-linux" || drv
.platform
== "x86_64-linux") && settings
.impersonateLinux26
) {
2234 int cur
= personality(0xffffffff);
2235 if (cur
!= -1) personality(cur
| 0x0020000 /* == UNAME26 */);
2238 /* Disable address space randomization for improved
2240 int cur
= personality(0xffffffff);
2241 if (cur
!= -1) personality(cur
| ADDR_NO_RANDOMIZE
);
2244 /* Fill in the environment. */
2246 foreach (Environment::const_iterator
, i
, env
)
2247 envStrs
.push_back(rewriteHashes(i
->first
+ "=" + i
->second
, rewritesToTmp
));
2249 /* If we are running in `build-users' mode, then switch to the
2250 user we allocated above. Make sure that we drop all root
2251 privileges. Note that above we have closed all file
2252 descriptors except std*, so that's safe. Also note that
2253 setuid() when run as root sets the real, effective and
2255 if (buildUser
.enabled()) {
2256 /* Preserve supplementary groups of the build user, to allow
2257 admins to specify groups such as "kvm". */
2258 if (setgroups(buildUser
.getSupplementaryGIDs().size(),
2259 buildUser
.getSupplementaryGIDs().data()) == -1)
2260 throw SysError("cannot set supplementary groups of build user");
2262 if (setgid(buildUser
.getGID()) == -1 ||
2263 getgid() != buildUser
.getGID() ||
2264 getegid() != buildUser
.getGID())
2265 throw SysError("setgid failed");
2267 if (setuid(buildUser
.getUID()) == -1 ||
2268 getuid() != buildUser
.getUID() ||
2269 geteuid() != buildUser
.getUID())
2270 throw SysError("setuid failed");
2275 /* Indicate that we managed to set up the build environment. */
2276 writeFull(STDERR_FILENO
, "\n");
2278 /* Execute the program. This should not return. */
2279 if (isBuiltin(drv
)) {
2283 auto buildDrv
= lookupBuiltinBuilder(drv
.builder
);
2284 if (buildDrv
!= NULL
) {
2285 /* Check what the output file name is. When doing a
2286 'bmCheck' build, the output file name is different from
2287 that specified in DRV due to hash rewriting. */
2288 Path output
= drv
.outputs
["out"].path
;
2289 auto redirected
= redirectedOutputs
.find(output
);
2290 if (redirected
!= redirectedOutputs
.end())
2291 output
= redirected
->second
;
2293 buildDrv(drv
, drvPath
, output
);
2296 throw Error(format("unsupported builtin function '%1%'") % string(drv
.builder
, 8));
2298 } catch (std::exception
& e
) {
2299 writeFull(STDERR_FILENO
, "error: " + string(e
.what()) + "\n");
2304 /* Fill in the arguments. */
2306 string builderBasename
= baseNameOf(drv
.builder
);
2307 args
.push_back(builderBasename
);
2308 foreach (Strings::iterator
, i
, drv
.args
)
2309 args
.push_back(rewriteHashes(*i
, rewritesToTmp
));
2311 execve(drv
.builder
.c_str(), stringsToCharPtrs(args
).data(), stringsToCharPtrs(envStrs
).data());
2315 /* Right platform? Check this after we've tried 'execve' to allow for
2316 transparent emulation of different platforms with binfmt_misc
2317 handlers that invoke QEMU. */
2318 if (error
== ENOEXEC
&& !canBuildLocally(drv
.platform
)) {
2319 if (settings
.printBuildTrace
)
2320 printMsg(lvlError
, format("@ unsupported-platform %1% %2%") % drvPath
% drv
.platform
);
2322 format("a `%1%' is required to build `%3%', but I am a `%2%'")
2323 % drv
.platform
% settings
.thisSystem
% drvPath
);
2327 throw SysError(format("executing `%1%'") % drv
.builder
);
2329 } catch (std::exception
& e
) {
2330 writeFull(STDERR_FILENO
, "while setting up the build environment: " + string(e
.what()) + "\n");
2334 abort(); /* never reached */
2338 /* Parse a list of reference specifiers. Each element must either be
2339 a store path, or the symbolic name of the output of the derivation
2341 PathSet
parseReferenceSpecifiers(const Derivation
& drv
, string attr
)
2344 Paths paths
= tokenizeString
<Paths
>(attr
);
2345 foreach (Strings::iterator
, i
, paths
) {
2346 if (isStorePath(*i
))
2348 else if (drv
.outputs
.find(*i
) != drv
.outputs
.end())
2349 result
.insert(drv
.outputs
.find(*i
)->second
.path
);
2350 else throw BuildError(
2351 format("derivation contains an illegal reference specifier `%1%'")
2358 void DerivationGoal::registerOutputs()
2360 /* When using a build hook, the build hook can register the output
2361 as valid (by doing `nix-store --import'). If so we don't have
2362 to do anything here. */
2364 bool allValid
= true;
2365 foreach (DerivationOutputs::iterator
, i
, drv
.outputs
)
2366 if (!worker
.store
.isValidPath(i
->second
.path
)) allValid
= false;
2367 if (allValid
) return;
2370 ValidPathInfos infos
;
2372 /* Set of inodes seen during calls to canonicalisePathMetaData()
2373 for this build's outputs. This needs to be shared between
2374 outputs to allow hard links between outputs. */
2375 InodesSeen inodesSeen
;
2377 Path checkSuffix
= "-check";
2379 /* Check whether the output paths were created, and grep each
2380 output path to determine what other paths it references. Also make all
2381 output paths read-only. */
2382 foreach (DerivationOutputs::iterator
, i
, drv
.outputs
) {
2383 Path path
= i
->second
.path
;
2384 if (missingPaths
.find(path
) == missingPaths
.end()) continue;
2386 Path actualPath
= path
;
2388 actualPath
= chrootRootDir
+ path
;
2389 if (pathExists(actualPath
)) {
2390 /* Move output paths from the chroot to the store. */
2391 if (buildMode
== bmRepair
)
2392 replaceValidPath(path
, actualPath
);
2394 if (buildMode
!= bmCheck
&& rename(actualPath
.c_str(), path
.c_str()) == -1)
2395 throw SysError(format("moving build output `%1%' from the chroot to the store") % path
);
2397 if (buildMode
!= bmCheck
) actualPath
= path
;
2399 Path redirected
= redirectedOutputs
[path
];
2400 if (buildMode
== bmRepair
2401 && redirectedBadOutputs
.find(path
) != redirectedBadOutputs
.end()
2402 && pathExists(redirected
))
2403 replaceValidPath(path
, redirected
);
2404 if (buildMode
== bmCheck
&& redirected
!= "")
2405 actualPath
= redirected
;
2409 if (lstat(actualPath
.c_str(), &st
) == -1) {
2410 if (errno
== ENOENT
)
2412 format("builder for `%1%' failed to produce output path `%2%'")
2414 throw SysError(format("getting attributes of path `%1%'") % actualPath
);
2418 /* Check that the output is not group or world writable, as
2419 that means that someone else can have interfered with the
2420 build. Also, the output should be owned by the build
2422 if ((!S_ISLNK(st
.st_mode
) && (st
.st_mode
& (S_IWGRP
| S_IWOTH
))) ||
2423 (buildUser
.enabled() && st
.st_uid
!= buildUser
.getUID()))
2424 throw BuildError(format("suspicious ownership or permission on `%1%'; rejecting this build output") % path
);
2427 /* Apply hash rewriting if necessary. */
2428 bool rewritten
= false;
2429 if (!rewritesFromTmp
.empty()) {
2430 printMsg(lvlError
, format("warning: rewriting hashes in `%1%'; cross fingers") % path
);
2432 /* Canonicalise first. This ensures that the path we're
2433 rewriting doesn't contain a hard link to /etc/shadow or
2434 something like that. */
2435 canonicalisePathMetaData(actualPath
, buildUser
.enabled() ? buildUser
.getUID() : -1, inodesSeen
);
2437 /* FIXME: this is in-memory. */
2439 dumpPath(actualPath
, sink
);
2440 deletePath(actualPath
);
2441 sink
.s
= rewriteHashes(sink
.s
, rewritesFromTmp
);
2442 StringSource
source(sink
.s
);
2443 restorePath(actualPath
, source
);
2448 startNest(nest
, lvlTalkative
,
2449 format("scanning for references inside `%1%'") % path
);
2451 /* Check that fixed-output derivations produced the right
2452 outputs (i.e., the content hash should match the specified
2454 if (i
->second
.hash
!= "") {
2456 bool recursive
; HashType ht
; Hash h
;
2457 i
->second
.parseHashInfo(recursive
, ht
, h
);
2460 /* The output path should be a regular file without
2461 execute permission. */
2462 if (!S_ISREG(st
.st_mode
) || (st
.st_mode
& S_IXUSR
) != 0)
2464 format("output path `%1% should be a non-executable regular file") % path
);
2467 /* Check the hash. */
2468 Hash h2
= recursive
? hashPath(ht
, actualPath
).first
: hashFile(ht
, actualPath
);
2470 if (settings
.printBuildTrace
)
2471 printMsg(lvlError
, format("@ hash-mismatch %1% %2% %3% %4%")
2472 % path
% i
->second
.hashAlgo
2473 % printHash16or32(h
) % printHash16or32(h2
));
2474 throw BuildError(format("hash mismatch for store item '%1%'") % path
);
2478 /* Get rid of all weird permissions. This also checks that
2479 all files are owned by the build user, if applicable. */
2480 canonicalisePathMetaData(actualPath
,
2481 buildUser
.enabled() && !rewritten
? buildUser
.getUID() : -1, inodesSeen
);
2483 /* For this output path, find the references to other paths
2484 contained in it. Compute the SHA-256 NAR hash at the same
2485 time. The hash is stored in the database so that we can
2486 verify later on whether nobody has messed with the store. */
2488 PathSet references
= scanForReferences(actualPath
, allPaths
, hash
);
2490 if (buildMode
== bmCheck
) {
2491 if (!store
->isValidPath(path
)) continue;
2492 ValidPathInfo info
= worker
.store
.queryPathInfo(path
);
2493 if (hash
.first
!= info
.hash
) {
2494 if (settings
.keepFailed
) {
2495 Path dst
= path
+ checkSuffix
;
2496 if (pathExists(dst
)) deletePath(dst
);
2497 if (rename(actualPath
.c_str(), dst
.c_str()))
2498 throw SysError(format("renaming `%1%' to `%2%'") % actualPath
% dst
);
2499 throw Error(format("derivation `%1%' may not be deterministic: output `%2%' differs from ‘%3%’")
2500 % drvPath
% path
% dst
);
2502 throw Error(format("derivation `%1%' may not be deterministic: output `%2%' differs")
2506 if (settings
.printBuildTrace
)
2507 printMsg(lvlError
, format("@ build-succeeded %1% -") % drvPath
);
2512 /* For debugging, print out the referenced and unreferenced
2514 foreach (PathSet::iterator
, i
, inputPaths
) {
2515 PathSet::iterator j
= references
.find(*i
);
2516 if (j
== references
.end())
2517 debug(format("unreferenced input: `%1%'") % *i
);
2519 debug(format("referenced input: `%1%'") % *i
);
2522 /* Enforce `allowedReferences' and friends. */
2523 auto checkRefs
= [&](const string
& attrName
, bool allowed
, bool recursive
) {
2524 if (drv
.env
.find(attrName
) == drv
.env
.end()) return;
2526 PathSet spec
= parseReferenceSpecifiers(drv
, get(drv
.env
, attrName
));
2530 /* Our requisites are the union of the closures of our references. */
2531 for (auto & i
: references
)
2532 /* Don't call computeFSClosure on ourselves. */
2533 if (actualPath
!= i
)
2534 computeFSClosure(worker
.store
, i
, used
);
2538 for (auto & i
: used
)
2540 if (spec
.find(i
) == spec
.end())
2541 throw BuildError(format("output (`%1%') is not allowed to refer to path `%2%'") % actualPath
% i
);
2543 if (spec
.find(i
) != spec
.end())
2544 throw BuildError(format("output (`%1%') is not allowed to refer to path `%2%'") % actualPath
% i
);
2548 checkRefs("allowedReferences", true, false);
2549 checkRefs("allowedRequisites", true, true);
2550 checkRefs("disallowedReferences", false, false);
2551 checkRefs("disallowedRequisites", false, true);
2553 if (curRound
== nrRounds
) {
2554 worker
.store
.optimisePath(path
); // FIXME: combine with scanForReferences()
2556 worker
.store
.markContentsGood(path
);
2561 info
.hash
= hash
.first
;
2562 info
.narSize
= hash
.second
;
2563 info
.references
= references
;
2564 info
.deriver
= drvPath
;
2565 infos
.push_back(info
);
2568 if (buildMode
== bmCheck
) return;
2570 /* Compare the result with the previous round, and report which
2571 path is different, if any.*/
2572 if (curRound
> 1 && prevInfos
!= infos
) {
2573 assert(prevInfos
.size() == infos
.size());
2574 for (auto i
= prevInfos
.begin(), j
= infos
.begin(); i
!= prevInfos
.end(); ++i
, ++j
)
2576 Path prev
= i
->path
+ checkSuffix
;
2577 if (pathExists(prev
))
2578 throw NotDeterministic(
2579 format("output ‘%1%’ of ‘%2%’ differs from ‘%3%’ from previous round")
2580 % i
->path
% drvPath
% prev
);
2582 throw NotDeterministic(
2583 format("output ‘%1%’ of ‘%2%’ differs from previous round")
2584 % i
->path
% drvPath
);
2586 assert(false); // shouldn't happen
2589 if (settings
.keepFailed
) {
2590 for (auto & i
: drv
.outputs
) {
2591 Path prev
= i
.second
.path
+ checkSuffix
;
2592 if (pathExists(prev
)) deletePath(prev
);
2593 if (curRound
< nrRounds
) {
2594 Path dst
= i
.second
.path
+ checkSuffix
;
2595 if (rename(i
.second
.path
.c_str(), dst
.c_str()))
2596 throw SysError(format("renaming ‘%1%’ to ‘%2%’") % i
.second
.path
% dst
);
2602 if (curRound
< nrRounds
) {
2607 /* Register each output path as valid, and register the sets of
2608 paths referenced by each of them. If there are cycles in the
2609 outputs, this will fail. */
2610 worker
.store
.registerValidPaths(infos
);
2614 string drvsLogDir
= "drvs";
2617 Path
DerivationGoal::openLogFile()
2621 if (!settings
.keepLog
) return "";
2623 string baseName
= baseNameOf(drvPath
);
2625 /* Create a log file. */
2626 Path dir
= (format("%1%/%2%/%3%/") % settings
.nixLogDir
% drvsLogDir
% string(baseName
, 0, 2)).str();
2629 switch (settings
.logCompression
)
2631 case COMPRESSION_GZIP
: {
2632 Path logFileName
= (format("%1%/%2%.gz") % dir
% string(baseName
, 2)).str();
2633 AutoCloseFD fd
= open(logFileName
.c_str(), O_CREAT
| O_WRONLY
| O_TRUNC
, 0666);
2634 if (fd
== -1) throw SysError(format("creating log file `%1%'") % logFileName
);
2637 /* Note: FD will be closed by 'gzclose'. */
2638 if (!(gzLogFile
= gzdopen(fd
.borrow(), "w")))
2639 throw Error(format("cannot open compressed log file `%1%'") % logFileName
);
2641 gzbuffer(gzLogFile
, 32768);
2642 gzsetparams(gzLogFile
, Z_BEST_COMPRESSION
, Z_DEFAULT_STRATEGY
);
2648 case COMPRESSION_BZIP2
: {
2649 Path logFileName
= (format("%1%/%2%.bz2") % dir
% string(baseName
, 2)).str();
2650 AutoCloseFD fd
= open(logFileName
.c_str(), O_CREAT
| O_WRONLY
| O_TRUNC
, 0666);
2651 if (fd
== -1) throw SysError(format("creating log file `%1%'") % logFileName
);
2654 if (!(fLogFile
= fdopen(fd
.borrow(), "w")))
2655 throw SysError(format("opening file `%1%'") % logFileName
);
2658 if (!(bzLogFile
= BZ2_bzWriteOpen(&err
, fLogFile
, 9, 0, 0)))
2659 throw Error(format("cannot open compressed log file `%1%'") % logFileName
);
2665 case COMPRESSION_NONE
: {
2666 Path logFileName
= (format("%1%/%2%") % dir
% string(baseName
, 2)).str();
2667 fdLogFile
= open(logFileName
.c_str(), O_CREAT
| O_WRONLY
| O_TRUNC
, 0666);
2668 if (fdLogFile
== -1) throw SysError(format("creating log file `%1%'") % logFileName
);
2669 closeOnExec(fdLogFile
);
2678 void DerivationGoal::closeLogFile()
2682 err
= gzclose(gzLogFile
);
2684 if (err
!= Z_OK
) throw Error(format("cannot close compressed log file (gzip error = %1%)") % err
);
2687 else if (bzLogFile
) {
2689 BZ2_bzWriteClose(&err
, bzLogFile
, 0, 0, 0);
2691 if (err
!= BZ_OK
) throw Error(format("cannot close compressed log file (BZip2 error = %1%)") % err
);
2704 static void _chown(const Path
& path
, uid_t uid
, gid_t gid
)
2708 if (lchown(path
.c_str(), uid
, gid
) == -1) {
2709 throw SysError(format("change owner and group of `%1%'") % path
);
2711 struct stat st
= lstat(path
);
2712 if (S_ISDIR(st
.st_mode
)) {
2713 for (auto & i
: readDirectory(path
))
2714 _chown(path
+ "/" + i
.name
, uid
, gid
);
2719 void DerivationGoal::deleteTmpDir(bool force
)
2722 if (settings
.keepFailed
&& !force
) {
2724 format("note: keeping build directory `%2%'")
2725 % drvPath
% tmpDir
);
2726 chmod(tmpDir
.c_str(), 0755);
2727 // Change the ownership if clientUid is set. Never change the
2728 // ownership or the group to "root" for security reasons.
2729 if (settings
.clientUid
!= (uid_t
) -1 && settings
.clientUid
!= 0) {
2730 _chown(tmpDir
, settings
.clientUid
,
2731 settings
.clientGid
!= 0 ? settings
.clientGid
: -1);
2741 void DerivationGoal::handleChildOutput(int fd
, const string
& data
)
2745 if (settings
.multiplexedBuildOutput
) {
2746 /* Print a prefix that allows clients to determine whether a message
2747 comes from the daemon or from a build process, and in the latter
2748 case, which build process it comes from. The PID here matches the
2749 one given in "@ build-started" traces; it's shorter that the
2750 derivation file name, hence this choice. */
2751 prefix
= "@ build-log "
2752 + std::to_string(pid
< 0 ? hook
->pid
: pid
)
2753 + " " + std::to_string(data
.size()) + "\n";
2756 if ((hook
&& fd
== hook
->builderOut
.readSide
) ||
2757 (!hook
&& fd
== builderOut
.readSide
))
2759 logSize
+= data
.size();
2760 if (settings
.maxLogSize
&& logSize
> settings
.maxLogSize
) {
2762 format("%1% killed after writing more than %2% bytes of log output")
2763 % getName() % settings
.maxLogSize
);
2764 timedOut(); // not really a timeout, but close enough
2767 if (verbosity
>= settings
.buildVerbosity
)
2768 writeToStderr(prefix
+ data
);
2771 if (data
.size() > 0) {
2773 count
= gzwrite(gzLogFile
, data
.data(), data
.size());
2774 if (count
== 0) throw Error(format("cannot write to compressed log file (gzip error = %1%)") % gzerror(gzLogFile
, &err
));
2777 } else if (bzLogFile
) {
2779 BZ2_bzWrite(&err
, bzLogFile
, (unsigned char *) data
.data(), data
.size());
2780 if (err
!= BZ_OK
) throw Error(format("cannot write to compressed log file (BZip2 error = %1%)") % err
);
2782 } else if (fdLogFile
!= -1)
2783 writeFull(fdLogFile
, data
);
2786 if (hook
&& fd
== hook
->fromHook
.readSide
)
2787 writeToStderr(prefix
+ data
);
2791 void DerivationGoal::handleEOF(int fd
)
2793 worker
.wakeUp(shared_from_this());
2797 PathSet
DerivationGoal::checkPathValidity(bool returnValid
, bool checkHash
)
2800 foreach (DerivationOutputs::iterator
, i
, drv
.outputs
) {
2801 if (!wantOutput(i
->first
, wantedOutputs
)) continue;
2803 worker
.store
.isValidPath(i
->second
.path
) &&
2804 (!checkHash
|| worker
.store
.pathContentsGood(i
->second
.path
));
2805 if (good
== returnValid
) result
.insert(i
->second
.path
);
2811 bool DerivationGoal::pathFailed(const Path
& path
)
2813 if (!settings
.cacheFailure
) return false;
2815 if (!worker
.store
.hasPathFailed(path
)) return false;
2817 printMsg(lvlError
, format("builder for `%1%' failed previously (cached)") % path
);
2819 if (settings
.printBuildTrace
)
2820 printMsg(lvlError
, format("@ build-failed %1% - cached") % drvPath
);
2822 done(BuildResult::CachedFailure
);
2828 Path
DerivationGoal::addHashRewrite(const Path
& path
)
2830 string h1
= string(path
, settings
.nixStore
.size() + 1, 32);
2831 string h2
= string(printHash32(hashString(htSHA256
, "rewrite:" + drvPath
+ ":" + path
)), 0, 32);
2832 Path p
= settings
.nixStore
+ "/" + h2
+ string(path
, settings
.nixStore
.size() + 33);
2833 if (pathExists(p
)) deletePath(p
);
2834 assert(path
.size() == p
.size());
2835 rewritesToTmp
[h1
] = h2
;
2836 rewritesFromTmp
[h2
] = h1
;
2837 redirectedOutputs
[path
] = p
;
2838 printMsg(lvlChatty
, format("output '%1%' redirected to '%2%'")
2844 void DerivationGoal::done(BuildResult::Status status
, const string
& msg
)
2846 result
.status
= status
;
2847 result
.errorMsg
= msg
;
2848 amDone(result
.success() ? ecSuccess
: ecFailed
);
2849 if (result
.status
== BuildResult::TimedOut
)
2850 worker
.timedOut
= true;
2851 if (result
.status
== BuildResult::PermanentFailure
|| result
.status
== BuildResult::CachedFailure
)
2852 worker
.permanentFailure
= true;
2856 //////////////////////////////////////////////////////////////////////
2859 class SubstitutionGoal
: public Goal
2861 friend class Worker
;
2864 /* The store path that should be realised through a substitute. */
2867 /* The remaining substituters. */
2870 /* The current substituter. */
2873 /* Whether any substituter can realise this path */
2876 /* Path info returned by the substituter's query info operation. */
2877 SubstitutablePathInfo info
;
2879 /* Pipe for the substituter's standard output. */
2882 /* Pipe for the substituter's standard error. */
2885 /* The process ID of the builder. */
2888 /* Lock on the store path. */
2889 std::shared_ptr
<PathLocks
> outputLock
;
2891 /* Whether to try to repair a valid path. */
2894 /* Location where we're downloading the substitute. Differs from
2895 storePath when doing a repair. */
2898 typedef void (SubstitutionGoal::*GoalState
)();
2902 SubstitutionGoal(const Path
& storePath
, Worker
& worker
, bool repair
= false);
2903 ~SubstitutionGoal();
2909 /* "a$" ensures substitution goals happen before derivation
2911 return "a$" + storePathToName(storePath
) + "$" + storePath
;
2920 void referencesValid();
2924 /* Callback used by the worker to write to the log. */
2925 void handleChildOutput(int fd
, const string
& data
);
2926 void handleEOF(int fd
);
2928 Path
getStorePath() { return storePath
; }
2932 SubstitutionGoal::SubstitutionGoal(const Path
& storePath
, Worker
& worker
, bool repair
)
2934 , hasSubstitute(false)
2937 this->storePath
= storePath
;
2938 state
= &SubstitutionGoal::init
;
2939 name
= (format("substitution of `%1%'") % storePath
).str();
2944 SubstitutionGoal::~SubstitutionGoal()
2946 if (pid
!= -1) worker
.childTerminated(pid
);
2950 void SubstitutionGoal::timedOut()
2952 if (settings
.printBuildTrace
)
2953 printMsg(lvlError
, format("@ substituter-failed %1% timeout") % storePath
);
2955 pid_t savedPid
= pid
;
2957 worker
.childTerminated(savedPid
);
2963 void SubstitutionGoal::work()
2969 void SubstitutionGoal::init()
2973 worker
.store
.addTempRoot(storePath
);
2975 /* If the path already exists we're done. */
2976 if (!repair
&& worker
.store
.isValidPath(storePath
)) {
2981 if (settings
.readOnlyMode
)
2982 throw Error(format("cannot substitute path `%1%' - no write access to the store") % storePath
);
2984 subs
= settings
.substituters
;
2990 void SubstitutionGoal::tryNext()
2992 trace("trying next substituter");
2994 if (subs
.size() == 0) {
2995 /* None left. Terminate this goal and let someone else deal
2997 debug(format("path `%1%' is required, but there is no substituter that can build it") % storePath
);
2998 /* Hack: don't indicate failure if there were no substituters.
2999 In that case the calling derivation should just do a
3001 amDone(hasSubstitute
? ecFailed
: ecNoSubstituters
);
3008 SubstitutablePathInfos infos
;
3009 PathSet
dummy(singleton
<PathSet
>(storePath
));
3010 worker
.store
.querySubstitutablePathInfos(sub
, dummy
, infos
);
3011 SubstitutablePathInfos::iterator k
= infos
.find(storePath
);
3012 if (k
== infos
.end()) { tryNext(); return; }
3014 hasSubstitute
= true;
3016 /* To maintain the closure invariant, we first have to realise the
3017 paths referenced by this one. */
3018 foreach (PathSet::iterator
, i
, info
.references
)
3019 if (*i
!= storePath
) /* ignore self-references */
3020 addWaitee(worker
.makeSubstitutionGoal(*i
));
3022 if (waitees
.empty()) /* to prevent hang (no wake-up event) */
3025 state
= &SubstitutionGoal::referencesValid
;
3029 void SubstitutionGoal::referencesValid()
3031 trace("all references realised");
3034 debug(format("some references of path `%1%' could not be realised") % storePath
);
3035 amDone(nrNoSubstituters
> 0 || nrIncompleteClosure
> 0 ? ecIncompleteClosure
: ecFailed
);
3039 foreach (PathSet::iterator
, i
, info
.references
)
3040 if (*i
!= storePath
) /* ignore self-references */
3041 assert(worker
.store
.isValidPath(*i
));
3043 state
= &SubstitutionGoal::tryToRun
;
3044 worker
.wakeUp(shared_from_this());
3048 void SubstitutionGoal::tryToRun()
3050 trace("trying to run");
3052 /* Make sure that we are allowed to start a build. Note that even
3053 is maxBuildJobs == 0 (no local builds allowed), we still allow
3054 a substituter to run. This is because substitutions cannot be
3055 distributed to another machine via the build hook. */
3056 if (worker
.getNrLocalBuilds() >= (settings
.maxBuildJobs
== 0 ? 1 : settings
.maxBuildJobs
)) {
3057 worker
.waitForBuildSlot(shared_from_this());
3061 /* Maybe a derivation goal has already locked this path
3062 (exceedingly unlikely, since it should have used a substitute
3063 first, but let's be defensive). */
3064 outputLock
.reset(); // make sure this goal's lock is gone
3065 if (pathIsLockedByMe(storePath
)) {
3066 debug(format("restarting substitution of `%1%' because it's locked by another goal")
3068 worker
.waitForAnyGoal(shared_from_this());
3069 return; /* restart in the tryToRun() state when another goal finishes */
3072 /* Acquire a lock on the output path. */
3073 outputLock
= std::shared_ptr
<PathLocks
>(new PathLocks
);
3074 if (!outputLock
->lockPaths(singleton
<PathSet
>(storePath
), "", false)) {
3075 worker
.waitForAWhile(shared_from_this());
3079 /* Check again whether the path is invalid. */
3080 if (!repair
&& worker
.store
.isValidPath(storePath
)) {
3081 debug(format("store path `%1%' has become valid") % storePath
);
3082 outputLock
->setDeletion(true);
3087 printMsg(lvlInfo
, format("fetching path `%1%'...") % storePath
);
3092 destPath
= repair
? storePath
+ ".tmp" : storePath
;
3094 /* Remove the (stale) output path if it exists. */
3095 if (pathExists(destPath
))
3096 deletePath(destPath
);
3098 worker
.store
.setSubstituterEnv();
3100 /* Fill in the arguments. */
3102 args
.push_back(baseNameOf(sub
));
3103 args
.push_back("--substitute");
3104 args
.push_back(storePath
);
3105 args
.push_back(destPath
);
3107 /* Fork the substitute program. */
3108 pid
= startProcess([&]() {
3110 commonChildInit(logPipe
);
3112 if (dup2(outPipe
.writeSide
, STDOUT_FILENO
) == -1)
3113 throw SysError("cannot dup output pipe into stdout");
3115 execv(sub
.c_str(), stringsToCharPtrs(args
).data());
3117 throw SysError(format("executing `%1%'") % sub
);
3120 pid
.setSeparatePG(true);
3121 pid
.setKillSignal(SIGTERM
);
3122 outPipe
.writeSide
.close();
3123 logPipe
.writeSide
.close();
3124 worker
.childStarted(shared_from_this(),
3125 pid
, singleton
<set
<int> >(logPipe
.readSide
), true, true);
3127 state
= &SubstitutionGoal::finished
;
3129 if (settings
.printBuildTrace
)
3130 printMsg(lvlError
, format("@ substituter-started %1% %2%") % storePath
% sub
);
3134 void SubstitutionGoal::finished()
3136 trace("substitute finished");
3138 /* Since we got an EOF on the logger pipe, the substitute is
3139 presumed to have terminated. */
3140 pid_t savedPid
= pid
;
3141 int status
= pid
.wait(true);
3143 /* So the child is gone now. */
3144 worker
.childTerminated(savedPid
);
3146 /* Close the read side of the logger pipe. */
3147 logPipe
.readSide
.close();
3149 /* Get the hash info from stdout. */
3150 string dummy
= readLine(outPipe
.readSide
);
3151 string expectedHashStr
= statusOk(status
) ? readLine(outPipe
.readSide
) : "";
3152 outPipe
.readSide
.close();
3154 /* Check the exit status and the build result. */
3158 if (!statusOk(status
))
3159 throw SubstError(format("fetching path `%1%' %2%")
3160 % storePath
% statusToString(status
));
3162 if (!pathExists(destPath
))
3163 throw SubstError(format("substitute did not produce path `%1%'") % destPath
);
3165 hash
= hashPath(htSHA256
, destPath
);
3167 /* Verify the expected hash we got from the substituer. */
3168 if (expectedHashStr
!= "") {
3169 size_t n
= expectedHashStr
.find(':');
3170 if (n
== string::npos
)
3171 throw Error(format("bad hash from substituter: %1%") % expectedHashStr
);
3172 HashType hashType
= parseHashType(string(expectedHashStr
, 0, n
));
3173 if (hashType
== htUnknown
)
3174 throw Error(format("unknown hash algorithm in `%1%'") % expectedHashStr
);
3175 Hash expectedHash
= parseHash16or32(hashType
, string(expectedHashStr
, n
+ 1));
3176 Hash actualHash
= hashType
== htSHA256
? hash
.first
: hashPath(hashType
, destPath
).first
;
3177 if (expectedHash
!= actualHash
) {
3178 if (settings
.printBuildTrace
)
3179 printMsg(lvlError
, format("@ hash-mismatch %1% %2% %3% %4%")
3180 % storePath
% "sha256"
3181 % printHash16or32(expectedHash
)
3182 % printHash16or32(actualHash
));
3183 throw SubstError(format("hash mismatch for substituted item `%1%'") % storePath
);
3187 } catch (SubstError
& e
) {
3189 printMsg(lvlInfo
, e
.msg());
3191 if (settings
.printBuildTrace
) {
3192 printMsg(lvlError
, format("@ substituter-failed %1% %2% %3%")
3193 % storePath
% status
% e
.msg());
3196 /* Try the next substitute. */
3197 state
= &SubstitutionGoal::tryNext
;
3198 worker
.wakeUp(shared_from_this());
3202 if (repair
) replaceValidPath(storePath
, destPath
);
3204 canonicalisePathMetaData(storePath
, -1);
3206 worker
.store
.optimisePath(storePath
); // FIXME: combine with hashPath()
3208 ValidPathInfo info2
;
3209 info2
.path
= storePath
;
3210 info2
.hash
= hash
.first
;
3211 info2
.narSize
= hash
.second
;
3212 info2
.references
= info
.references
;
3213 info2
.deriver
= info
.deriver
;
3214 worker
.store
.registerValidPath(info2
);
3216 outputLock
->setDeletion(true);
3219 worker
.store
.markContentsGood(storePath
);
3222 format("substitution of path `%1%' succeeded") % storePath
);
3224 if (settings
.printBuildTrace
)
3225 printMsg(lvlError
, format("@ substituter-succeeded %1%") % storePath
);
3231 void SubstitutionGoal::handleChildOutput(int fd
, const string
& data
)
3233 assert(fd
== logPipe
.readSide
);
3234 if (verbosity
>= settings
.buildVerbosity
) writeToStderr(data
);
3235 /* Don't write substitution output to a log file for now. We
3236 probably should, though. */
3240 void SubstitutionGoal::handleEOF(int fd
)
3242 if (fd
== logPipe
.readSide
) worker
.wakeUp(shared_from_this());
3247 //////////////////////////////////////////////////////////////////////
3250 static bool working
= false;
3253 Worker::Worker(LocalStore
& store
)
3256 /* Debugging: prevent recursive workers. */
3257 if (working
) abort();
3261 permanentFailure
= false;
3270 /* Explicitly get rid of all strong pointers now. After this all
3271 goals that refer to this worker should be gone. (Otherwise we
3272 are in trouble, since goals may call childTerminated() etc. in
3273 their destructors). */
3278 GoalPtr
Worker::makeDerivationGoal(const Path
& path
,
3279 const StringSet
& wantedOutputs
, BuildMode buildMode
)
3281 GoalPtr goal
= derivationGoals
[path
].lock();
3283 goal
= GoalPtr(new DerivationGoal(path
, wantedOutputs
, *this, buildMode
));
3284 derivationGoals
[path
] = goal
;
3287 (dynamic_cast<DerivationGoal
*>(goal
.get()))->addWantedOutputs(wantedOutputs
);
3292 GoalPtr
Worker::makeSubstitutionGoal(const Path
& path
, bool repair
)
3294 GoalPtr goal
= substitutionGoals
[path
].lock();
3296 goal
= GoalPtr(new SubstitutionGoal(path
, *this, repair
));
3297 substitutionGoals
[path
] = goal
;
3304 static void removeGoal(GoalPtr goal
, WeakGoalMap
& goalMap
)
3306 /* !!! inefficient */
3307 for (WeakGoalMap::iterator i
= goalMap
.begin();
3308 i
!= goalMap
.end(); )
3309 if (i
->second
.lock() == goal
) {
3310 WeakGoalMap::iterator j
= i
; ++j
;
3318 void Worker::removeGoal(GoalPtr goal
)
3320 nix::removeGoal(goal
, derivationGoals
);
3321 nix::removeGoal(goal
, substitutionGoals
);
3322 if (topGoals
.find(goal
) != topGoals
.end()) {
3323 topGoals
.erase(goal
);
3324 /* If a top-level goal failed, then kill all other goals
3325 (unless keepGoing was set). */
3326 if (goal
->getExitCode() == Goal::ecFailed
&& !settings
.keepGoing
)
3330 /* Wake up goals waiting for any goal to finish. */
3331 foreach (WeakGoals::iterator
, i
, waitingForAnyGoal
) {
3332 GoalPtr goal
= i
->lock();
3333 if (goal
) wakeUp(goal
);
3336 waitingForAnyGoal
.clear();
3340 void Worker::wakeUp(GoalPtr goal
)
3342 goal
->trace("woken up");
3343 addToWeakGoals(awake
, goal
);
3347 unsigned Worker::getNrLocalBuilds()
3349 return nrLocalBuilds
;
3353 void Worker::childStarted(GoalPtr goal
,
3354 pid_t pid
, const set
<int> & fds
, bool inBuildSlot
,
3355 bool respectTimeouts
)
3360 child
.timeStarted
= child
.lastOutput
= time(0);
3361 child
.inBuildSlot
= inBuildSlot
;
3362 child
.respectTimeouts
= respectTimeouts
;
3363 children
[pid
] = child
;
3364 if (inBuildSlot
) nrLocalBuilds
++;
3368 void Worker::childTerminated(pid_t pid
, bool wakeSleepers
)
3370 assert(pid
!= -1); /* common mistake */
3372 Children::iterator i
= children
.find(pid
);
3373 assert(i
!= children
.end());
3375 if (i
->second
.inBuildSlot
) {
3376 assert(nrLocalBuilds
> 0);
3380 children
.erase(pid
);
3384 /* Wake up goals waiting for a build slot. */
3385 foreach (WeakGoals::iterator
, i
, wantingToBuild
) {
3386 GoalPtr goal
= i
->lock();
3387 if (goal
) wakeUp(goal
);
3390 wantingToBuild
.clear();
3395 void Worker::waitForBuildSlot(GoalPtr goal
)
3397 debug("wait for build slot");
3398 if (getNrLocalBuilds() < settings
.maxBuildJobs
)
3399 wakeUp(goal
); /* we can do it right away */
3401 addToWeakGoals(wantingToBuild
, goal
);
3405 void Worker::waitForAnyGoal(GoalPtr goal
)
3407 debug("wait for any goal");
3408 addToWeakGoals(waitingForAnyGoal
, goal
);
3412 void Worker::waitForAWhile(GoalPtr goal
)
3414 debug("wait for a while");
3415 addToWeakGoals(waitingForAWhile
, goal
);
3419 void Worker::run(const Goals
& _topGoals
)
3421 foreach (Goals::iterator
, i
, _topGoals
) topGoals
.insert(*i
);
3423 startNest(nest
, lvlDebug
, format("entered goal loop"));
3429 /* Call every wake goal (in the ordering established by
3430 CompareGoalPtrs). */
3431 while (!awake
.empty() && !topGoals
.empty()) {
3433 for (auto & i
: awake
) {
3434 GoalPtr goal
= i
.lock();
3435 if (goal
) awake2
.insert(goal
);
3438 for (auto & goal
: awake2
) {
3441 if (topGoals
.empty()) break; // stuff may have been cancelled
3445 if (topGoals
.empty()) break;
3447 /* Wait for input. */
3448 if (!children
.empty() || !waitingForAWhile
.empty())
3451 if (awake
.empty() && settings
.maxBuildJobs
== 0) throw Error(
3452 "unable to start any build; either increase `--max-jobs' "
3453 "or enable distributed builds");
3454 assert(!awake
.empty());
3458 /* If --keep-going is not set, it's possible that the main goal
3459 exited while some of its subgoals were still active. But if
3460 --keep-going *is* set, then they must all be finished now. */
3461 assert(!settings
.keepGoing
|| awake
.empty());
3462 assert(!settings
.keepGoing
|| wantingToBuild
.empty());
3463 assert(!settings
.keepGoing
|| children
.empty());
3467 void Worker::waitForInput()
3469 printMsg(lvlVomit
, "waiting for children");
3471 /* Process output from the file descriptors attached to the
3472 children, namely log output and output path creation commands.
3473 We also use this to detect child termination: if we get EOF on
3474 the logger pipe of a build, we assume that the builder has
3477 bool useTimeout
= false;
3478 struct timeval timeout
;
3479 timeout
.tv_usec
= 0;
3480 time_t before
= time(0);
3482 /* If we're monitoring for silence on stdout/stderr, or if there
3483 is a build timeout, then wait for input until the first
3484 deadline for any child. */
3485 assert(sizeof(time_t) >= sizeof(long));
3486 time_t nearest
= LONG_MAX
; // nearest deadline
3487 foreach (Children::iterator
, i
, children
) {
3488 if (!i
->second
.respectTimeouts
) continue;
3489 if (settings
.maxSilentTime
!= 0)
3490 nearest
= std::min(nearest
, i
->second
.lastOutput
+ settings
.maxSilentTime
);
3491 if (settings
.buildTimeout
!= 0)
3492 nearest
= std::min(nearest
, i
->second
.timeStarted
+ settings
.buildTimeout
);
3494 if (nearest
!= LONG_MAX
) {
3495 timeout
.tv_sec
= std::max((time_t) 1, nearest
- before
);
3497 printMsg(lvlVomit
, format("sleeping %1% seconds") % timeout
.tv_sec
);
3500 /* If we are polling goals that are waiting for a lock, then wake
3501 up after a few seconds at most. */
3502 if (!waitingForAWhile
.empty()) {
3504 if (lastWokenUp
== 0)
3505 printMsg(lvlError
, "waiting for locks or build slots...");
3506 if (lastWokenUp
== 0 || lastWokenUp
> before
) lastWokenUp
= before
;
3507 timeout
.tv_sec
= std::max((time_t) 1, (time_t) (lastWokenUp
+ settings
.pollInterval
- before
));
3508 } else lastWokenUp
= 0;
3510 using namespace std
;
3511 /* Use select() to wait for the input side of any logger pipe to
3512 become `available'. Note that `available' (i.e., non-blocking)
3517 foreach (Children::iterator
, i
, children
) {
3518 foreach (set
<int>::iterator
, j
, i
->second
.fds
) {
3520 if (*j
>= fdMax
) fdMax
= *j
+ 1;
3524 if (select(fdMax
, &fds
, 0, 0, useTimeout
? &timeout
: 0) == -1) {
3525 if (errno
== EINTR
) return;
3526 throw SysError("waiting for input");
3529 time_t after
= time(0);
3531 /* Process all available file descriptors. */
3533 /* Since goals may be canceled from inside the loop below (causing
3534 them go be erased from the `children' map), we have to be
3535 careful that we don't keep iterators alive across calls to
3538 foreach (Children::iterator
, i
, children
) pids
.insert(i
->first
);
3540 foreach (set
<pid_t
>::iterator
, i
, pids
) {
3542 Children::iterator j
= children
.find(*i
);
3543 if (j
== children
.end()) continue; // child destroyed
3544 GoalPtr goal
= j
->second
.goal
.lock();
3547 set
<int> fds2(j
->second
.fds
);
3548 foreach (set
<int>::iterator
, k
, fds2
) {
3549 if (FD_ISSET(*k
, &fds
)) {
3550 unsigned char buffer
[4096];
3551 ssize_t rd
= read(*k
, buffer
, sizeof(buffer
));
3554 throw SysError(format("reading from %1%")
3556 } else if (rd
== 0) {
3557 debug(format("%1%: got EOF") % goal
->getName());
3558 goal
->handleEOF(*k
);
3559 j
->second
.fds
.erase(*k
);
3561 printMsg(lvlVomit
, format("%1%: read %2% bytes")
3562 % goal
->getName() % rd
);
3563 string
data((char *) buffer
, rd
);
3564 j
->second
.lastOutput
= after
;
3565 goal
->handleChildOutput(*k
, data
);
3570 if (goal
->getExitCode() == Goal::ecBusy
&&
3571 settings
.maxSilentTime
!= 0 &&
3572 j
->second
.respectTimeouts
&&
3573 after
- j
->second
.lastOutput
>= (time_t) settings
.maxSilentTime
)
3576 format("%1% timed out after %2% seconds of silence")
3577 % goal
->getName() % settings
.maxSilentTime
);
3581 else if (goal
->getExitCode() == Goal::ecBusy
&&
3582 settings
.buildTimeout
!= 0 &&
3583 j
->second
.respectTimeouts
&&
3584 after
- j
->second
.timeStarted
>= (time_t) settings
.buildTimeout
)
3587 format("%1% timed out after %2% seconds")
3588 % goal
->getName() % settings
.buildTimeout
);
3593 if (!waitingForAWhile
.empty() && lastWokenUp
+ settings
.pollInterval
<= after
) {
3594 lastWokenUp
= after
;
3595 foreach (WeakGoals::iterator
, i
, waitingForAWhile
) {
3596 GoalPtr goal
= i
->lock();
3597 if (goal
) wakeUp(goal
);
3599 waitingForAWhile
.clear();
3604 unsigned int Worker::exitStatus()
3606 return timedOut
? 101 : (permanentFailure
? 100 : 1);
3610 //////////////////////////////////////////////////////////////////////
3613 void LocalStore::buildPaths(const PathSet
& drvPaths
, BuildMode buildMode
)
3615 startNest(nest
, lvlDebug
,
3616 format("building %1%") % showPaths(drvPaths
));
3618 Worker
worker(*this);
3621 foreach (PathSet::const_iterator
, i
, drvPaths
) {
3622 DrvPathWithOutputs i2
= parseDrvPathWithOutputs(*i
);
3623 if (isDerivation(i2
.first
))
3624 goals
.insert(worker
.makeDerivationGoal(i2
.first
, i2
.second
, buildMode
));
3626 goals
.insert(worker
.makeSubstitutionGoal(*i
, buildMode
));
3632 foreach (Goals::iterator
, i
, goals
)
3633 if ((*i
)->getExitCode() == Goal::ecFailed
) {
3634 DerivationGoal
* i2
= dynamic_cast<DerivationGoal
*>(i
->get());
3635 if (i2
) failed
.insert(i2
->getDrvPath());
3636 else failed
.insert(dynamic_cast<SubstitutionGoal
*>(i
->get())->getStorePath());
3639 if (!failed
.empty())
3640 throw Error(format("build of %1% failed") % showPaths(failed
), worker
.exitStatus());
3644 void LocalStore::ensurePath(const Path
& path
)
3646 /* If the path is already valid, we're done. */
3647 if (isValidPath(path
)) return;
3649 Worker
worker(*this);
3650 GoalPtr goal
= worker
.makeSubstitutionGoal(path
);
3651 Goals goals
= singleton
<Goals
>(goal
);
3655 if (goal
->getExitCode() != Goal::ecSuccess
)
3656 throw Error(format("path `%1%' does not exist and cannot be created") % path
, worker
.exitStatus());
3660 void LocalStore::repairPath(const Path
& path
)
3662 Worker
worker(*this);
3663 GoalPtr goal
= worker
.makeSubstitutionGoal(path
, true);
3664 Goals goals
= singleton
<Goals
>(goal
);
3668 if (goal
->getExitCode() != Goal::ecSuccess
) {
3669 /* Since substituting the path didn't work, if we have a valid
3670 deriver, then rebuild the deriver. */
3671 Path deriver
= queryDeriver(path
);
3672 if (deriver
!= "" && isValidPath(deriver
)) {
3674 goals
.insert(worker
.makeDerivationGoal(deriver
, StringSet(), bmRepair
));
3677 throw Error(format("cannot repair path `%1%'") % path
, worker
.exitStatus());