gnu: linux-libre: Update to 5.1.4.
[guix.git] / nix / libstore / build.cc
blob06bc7601b9672e182717750163951e565d3621ad
1 #include "config.h"
3 #include "references.hh"
4 #include "pathlocks.hh"
5 #include "misc.hh"
6 #include "globals.hh"
7 #include "local-store.hh"
8 #include "util.hh"
9 #include "archive.hh"
10 #include "affinity.hh"
11 #include "builtins.hh"
13 #include <map>
14 #include <sstream>
15 #include <algorithm>
17 #include <limits.h>
18 #include <time.h>
19 #include <sys/time.h>
20 #include <sys/wait.h>
21 #include <sys/types.h>
22 #include <sys/stat.h>
23 #include <sys/utsname.h>
24 #include <fcntl.h>
25 #include <unistd.h>
26 #include <errno.h>
27 #include <stdio.h>
28 #include <cstring>
29 #include <stdint.h>
31 #include <pwd.h>
32 #include <grp.h>
34 #include <zlib.h>
36 #if HAVE_BZLIB_H
37 # include <bzlib.h>
38 #endif
40 /* Includes required for chroot support. */
41 #if HAVE_SYS_PARAM_H
42 #include <sys/param.h>
43 #endif
44 #if HAVE_SYS_MOUNT_H
45 #include <sys/mount.h>
46 #endif
47 #if HAVE_SYS_SYSCALL_H
48 #include <sys/syscall.h>
49 #endif
50 #if HAVE_SCHED_H
51 #include <sched.h>
52 #endif
54 /* In GNU libc 2.11, <sys/mount.h> does not define `MS_PRIVATE', but
55 <linux/fs.h> does. */
56 #if !defined MS_PRIVATE && defined HAVE_LINUX_FS_H
57 #include <linux/fs.h>
58 #endif
60 #define CHROOT_ENABLED HAVE_CHROOT && HAVE_SYS_MOUNT_H && defined(MS_BIND) && defined(MS_PRIVATE) && defined(CLONE_NEWNS) && defined(SYS_pivot_root)
62 #if CHROOT_ENABLED
63 #include <sys/socket.h>
64 #include <sys/ioctl.h>
65 #include <net/if.h>
66 #include <netinet/ip.h>
67 #endif
69 #if __linux__
70 #include <sys/personality.h>
71 #endif
73 #if HAVE_STATVFS
74 #include <sys/statvfs.h>
75 #endif
78 namespace nix {
80 using std::map;
83 static string pathNullDevice = "/dev/null";
86 /* Forward definition. */
87 class Worker;
88 struct HookInstance;
91 /* A pointer to a goal. */
92 class Goal;
93 class DerivationGoal;
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);
101 /* Set of goals. */
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>
112 public:
113 typedef enum {ecBusy, ecSuccess, ecFailed, ecNoSubstituters, ecIncompleteClosure} ExitCode;
115 protected:
117 /* Backlink to the worker. */
118 Worker & worker;
120 /* Goals that this goal is waiting for. */
121 Goals waitees;
123 /* Goals waiting for this one to finish. Must use weak pointers
124 here to prevent cycles. */
125 WeakGoals waiters;
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. */
139 string name;
141 /* Whether the goal is finished. */
142 ExitCode exitCode;
144 Goal(Worker & worker) : worker(worker)
146 nrFailed = nrNoSubstituters = nrIncompleteClosure = 0;
147 exitCode = ecBusy;
150 virtual ~Goal()
152 trace("goal destroyed");
155 public:
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)
164 abort();
167 virtual void handleEOF(int fd)
169 abort();
172 void trace(const format & f);
174 string getName()
176 return name;
179 ExitCode getExitCode()
181 return exitCode;
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;
191 protected:
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();
199 return s1 < s2;
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. */
206 struct Child
208 WeakGoalPtr goal;
209 set<int> fds;
210 bool respectTimeouts;
211 bool inBuildSlot;
212 time_t lastOutput; /* time we last got output on stdout/stderr */
213 time_t timeStarted;
216 typedef map<pid_t, Child> Children;
219 /* The worker class. */
220 class Worker
222 private:
224 /* Note: the worker should only have strong pointers to the
225 top-level goals. */
227 /* The top-level goals of the worker. */
228 Goals topGoals;
230 /* Goals that are ready to do some work. */
231 WeakGoals awake;
233 /* Goals waiting for a build slot. */
234 WeakGoals wantingToBuild;
236 /* Child processes currently running. */
237 Children children;
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. */
255 time_t lastWokenUp;
257 public:
259 /* Set if at least one derivation had a BuildError (i.e. permanent
260 failure). */
261 bool permanentFailure;
263 /* Set if at least one derivation had a timeout. */
264 bool timedOut;
266 LocalStore & store;
268 std::shared_ptr<HookInstance> hook;
270 Worker(LocalStore & store);
271 ~Worker();
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
285 hook). */
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. */
317 void waitForInput();
319 unsigned int exitStatus();
323 //////////////////////////////////////////////////////////////////////
326 void addToWeakGoals(WeakGoals & goals, GoalPtr p)
328 // FIXME: necessary?
329 // FIXME: O(n)
330 foreach (WeakGoals::iterator, i, goals)
331 if (i->lock() == p) return;
332 goals.push_back(p);
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) {
362 GoalPtr goal = *i;
363 WeakGoals waiters2;
364 foreach (WeakGoals::iterator, j, goal->waiters)
365 if (j->lock() != shared_from_this()) waiters2.push_back(*j);
366 goal->waiters = waiters2;
368 waitees.clear();
370 worker.wakeUp(shared_from_this());
375 void Goal::amDone(ExitCode result)
377 trace("done");
378 assert(exitCode == ecBusy);
379 assert(result == ecSuccess || result == ecFailed || result == ecNoSubstituters || result == ecIncompleteClosure);
380 exitCode = result;
381 foreach (WeakGoals::iterator, i, waiters) {
382 GoalPtr goal = i->lock();
383 if (goal) goal->waiteeDone(shared_from_this(), result);
385 waiters.clear();
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
406 terminal signals. */
407 if (setsid() == -1)
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);
420 if (fdDevNull == -1)
421 throw SysError(format("cannot open `%1%'") % pathNullDevice);
422 if (dup2(fdDevNull, STDIN_FILENO) == -1)
423 throw SysError("cannot dup null device into stdin");
424 close(fdDevNull);
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;
433 act.sa_flags = 0;
434 sigemptyset(&act.sa_mask);
435 if (sigaction(SIGPIPE, &act, &oact)) throw SysError("resetting SIGPIPE");
439 //////////////////////////////////////////////////////////////////////
442 class UserLock
444 private:
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 */
451 Path fnUserLock;
452 AutoCloseFD fdUserLock;
454 string user;
455 uid_t uid;
456 gid_t gid;
457 std::vector<gid_t> supplementaryGIDs;
459 public:
460 UserLock();
461 ~UserLock();
463 void acquire();
464 void release();
466 void kill();
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;
481 UserLock::UserLock()
483 uid = gid = 0;
487 UserLock::~UserLock()
489 release();
493 void UserLock::acquire()
495 assert(uid == 0);
497 assert(settings.buildUsersGroup != "");
499 /* Get the members of the build-users-group. */
500 struct group * gr = getgrnam(settings.buildUsersGroup.c_str());
501 if (!gr)
502 throw Error(format("the group `%1%' specified in `build-users-group' does not exist")
503 % settings.buildUsersGroup);
504 gid = gr->gr_gid;
506 /* Copy the result of getgrnam. */
507 Strings users;
508 for (char * * p = gr->gr_mem; *p; ++p) {
509 debug(format("found build user `%1%'") % *p);
510 users.push_back(*p);
513 if (users.empty())
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
518 build. */
519 foreach (Strings::iterator, i, users) {
520 debug(format("trying user `%1%'") % *i);
522 struct passwd * pw = getpwnam(i->c_str());
523 if (!pw)
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. */
533 continue;
535 AutoCloseFD fd = open(fnUserLock.c_str(), O_RDWR | O_CREAT, 0600);
536 if (fd == -1)
537 throw SysError(format("opening user lock `%1%'") % fnUserLock);
538 closeOnExec(fd);
540 if (lockFile(fd, ltWrite, false)) {
541 fdUserLock = fd.borrow();
542 lockedPaths.insert(fnUserLock);
543 user = *i;
544 uid = pw->pw_uid;
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);
557 if (err == -1)
558 throw Error(format("failed to get list of supplementary groups for ‘%1%’") % pw->pw_name);
560 supplementaryGIDs.resize(ngroups);
562 return;
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);
578 fnUserLock = "";
579 uid = 0;
583 void UserLock::kill()
585 assert(enabled());
586 killUser(uid);
590 //////////////////////////////////////////////////////////////////////
593 struct HookInstance
595 /* Pipes for talking to the build hook. */
596 Pipe toHook;
598 /* Pipe for the hook's standard output/error. */
599 Pipe fromHook;
601 /* Pipe for the builder's standard output/error. */
602 Pipe builderOut;
604 /* The process ID of the hook. */
605 Pid pid;
607 HookInstance();
609 ~HookInstance();
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. */
622 fromHook.create();
624 /* Create the communication pipes. */
625 toHook.create();
627 /* Create a pipe to get the output of the builder. */
628 builderOut.create();
630 /* Fork the hook. */
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(),
649 NULL);
651 throw SysError(format("executing `%1%'") % buildHook);
654 pid.setSeparatePG(true);
655 fromHook.writeSide.close();
656 toHook.readSide.close();
660 HookInstance::~HookInstance()
662 try {
663 toHook.writeSide.close();
664 pid.kill(true);
665 } catch (...) {
666 ignoreException();
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());
681 size_t j = 0;
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);
687 return s;
691 //////////////////////////////////////////////////////////////////////
694 typedef enum {rpAccept, rpDecline, rpPostpone} HookReply;
696 class SubstitutionGoal;
698 class DerivationGoal : public Goal
700 private:
701 /* The path of the derivation. */
702 Path drvPath;
704 /* The specific outputs that we need to build. Empty means all of
705 them. */
706 StringSet wantedOutputs;
708 /* Whether additional wanted outputs have been added. */
709 bool needRestart;
711 /* Whether to retry substituting the outputs after building the
712 inputs. */
713 bool retrySubstitution;
715 /* The derivation stored at drvPath. */
716 Derivation drv;
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). */
725 PathSet inputPaths;
727 /* Referenceable paths (i.e., input and output paths). */
728 PathSet allPaths;
730 /* Outputs that are already valid. If we're repairing, these are
731 the outputs that are valid *and* not corrupt. */
732 PathSet validPaths;
734 /* Outputs that are corrupt or not valid. */
735 PathSet missingPaths;
737 /* User selected for running the builder. */
738 UserLock buildUser;
740 /* The process ID of the builder. */
741 Pid pid;
743 /* The temporary directory. */
744 Path tmpDir;
746 /* The path of the temporary directory in the sandbox. */
747 Path tmpDirInSandbox;
749 /* File descriptor for the log file. */
750 FILE * fLogFile;
751 gzFile gzLogFile;
752 #if HAVE_BZLIB_H
753 BZFILE * bzLogFile;
754 #endif
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. */
761 Pipe builderOut;
763 /* The build hook. */
764 std::shared_ptr<HookInstance> hook;
766 /* Whether we're currently doing a chroot build. */
767 bool useChroot;
769 Path chrootRootDir;
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. */
778 bool fixedOutput;
780 typedef void (DerivationGoal::*GoalState)();
781 GoalState state;
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;
787 Environment env;
789 /* Hash rewriting. */
790 HashRewrites rewritesToTmp, rewritesFromTmp;
791 typedef map<Path, Path> RedirectedOutputs;
792 RedirectedOutputs redirectedOutputs;
794 BuildMode buildMode;
796 /* If we're repairing without a chroot, there may be outputs that
797 are valid but corrupt. So we redirect these outputs to
798 temporary paths. */
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
809 result. */
810 ValidPathInfos prevInfos;
812 BuildResult result;
814 public:
815 DerivationGoal(const Path & drvPath, const StringSet & wantedOutputs, Worker & worker, BuildMode buildMode = bmNormal);
816 ~DerivationGoal();
818 void timedOut() override;
820 string key()
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;
829 void work();
831 Path getDrvPath()
833 return drvPath;
836 /* Add wanted outputs to an already existing derivation goal. */
837 void addWantedOutputs(const StringSet & outputs);
839 BuildResult getResult() { return result; }
841 private:
842 /* The states. */
843 void init();
844 void haveDerivation();
845 void outputsSubstituted();
846 void closureRepaired();
847 void inputsRealised();
848 void tryToBuild();
849 void buildDone();
851 /* Is the build hook willing to perform the build? */
852 HookReply tryBuildHook();
854 /* Start building a derivation. */
855 void startBuilder();
857 /* Run the builder's process. */
858 void runChild();
860 friend int childEntry(void *);
862 /* Check that the derivation outputs all exist and register them
863 as valid. */
864 void registerOutputs();
866 /* Open a log file and a pipe to it. */
867 Path openLogFile();
869 /* Close the log file. */
870 void closeLogFile();
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. */
886 void killChild();
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)
897 : Goal(worker)
898 , wantedOutputs(wantedOutputs)
899 , needRestart(false)
900 , retrySubstitution(false)
901 , fLogFile(0)
902 , gzLogFile(0)
903 #if HAVE_BZLIB_H
904 , bzLogFile(0)
905 #endif
906 , useChroot(false)
907 , buildMode(buildMode)
909 this->drvPath = drvPath;
910 state = &DerivationGoal::init;
911 name = (format("building of `%1%'") % drvPath).str();
912 trace("created");
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
923 destructor. */
924 try { killChild(); } catch (...) { ignoreException(); }
925 try { deleteTmpDir(false); } catch (...) { ignoreException(); }
926 try { closeLogFile(); } catch (...) { ignoreException(); }
930 void DerivationGoal::killChild()
932 if (pid != -1) {
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
941 child. */
942 ::kill(-pid, SIGKILL); /* ignore the result */
943 buildUser.kill();
944 pid.wait(true);
945 } else
946 pid.kill();
948 assert(pid == -1);
951 hook.reset();
955 void DerivationGoal::timedOut()
957 if (settings.printBuildTrace)
958 printMsg(lvlError, format("@ build-failed %1% - timeout") % drvPath);
959 killChild();
960 done(BuildResult::TimedOut);
964 void DerivationGoal::work()
966 (this->*state)();
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();
977 needRestart = true;
978 } else
979 foreach (StringSet::const_iterator, i, outputs)
980 if (wantedOutputs.find(*i) == wantedOutputs.end()) {
981 wantedOutputs.insert(*i);
982 needRestart = true;
987 void DerivationGoal::init()
989 trace("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
996 substitute. */
997 if (buildMode == bmNormal && worker.store.isValidPath(drvPath)) {
998 haveDerivation();
999 return;
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);
1015 return;
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);
1037 return;
1040 /* Check whether any output previously failed to build. If so,
1041 don't bother. */
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
1047 them. */
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();
1054 else
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;
1073 if (needRestart) {
1074 needRestart = false;
1075 haveDerivation();
1076 return;
1079 unsigned int nrInvalid = checkPathValidity(false, buildMode == bmRepair).size();
1080 if (buildMode == bmNormal && nrInvalid == 0) {
1081 done(BuildResult::Substituted);
1082 return;
1084 if (buildMode == bmRepair && nrInvalid == 0) {
1085 repairClosure();
1086 return;
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
1095 outputs. */
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) */
1106 inputsRealised();
1107 else
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
1132 closure. */
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!). */
1144 PathSet broken;
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];
1149 if (drvPath2 == "")
1150 addWaitee(worker.makeSubstitutionGoal(*i, true));
1151 else
1152 addWaitee(worker.makeDerivationGoal(drvPath2, PathSet(), bmRepair));
1155 if (waitees.empty()) {
1156 done(BuildResult::AlreadyValid);
1157 return;
1160 state = &DerivationGoal::closureRepaired;
1164 void DerivationGoal::closureRepaired()
1166 trace("closure repaired");
1167 if (nrFailed > 0)
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) {
1178 printMsg(lvlError,
1179 format("cannot build derivation `%1%': %2% dependencies couldn't be built")
1180 % drvPath % nrFailed);
1181 done(BuildResult::DependencyFailed);
1182 return;
1185 if (retrySubstitution) {
1186 haveDerivation();
1187 return;
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);
1211 else
1212 throw Error(
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? */
1226 fixedOutput = true;
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
1236 build hook. */
1237 state = &DerivationGoal::tryToBuild;
1238 worker.wakeUp(shared_from_this());
1242 static bool canBuildLocally(const string & platform)
1244 return platform == settings.thisSystem
1245 #if __linux__
1246 || (platform == "i686-linux" && settings.thisSystem == "x86_64-linux")
1247 || (platform == "armhf-linux" && settings.thisSystem == "aarch64-linux")
1248 #endif
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());
1286 return;
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());
1296 return;
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);
1311 return;
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
1319 them. */
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);
1325 deletePath(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()) {
1342 case rpAccept:
1343 /* Yes, it has started doing so. Wait until we get
1344 EOF from the hook. */
1345 state = &DerivationGoal::buildDone;
1346 return;
1347 case rpPostpone:
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();
1352 return;
1353 case rpDecline:
1354 /* We should do it ourselves. */
1355 break;
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();
1366 return;
1369 try {
1371 /* Okay, we have to build. */
1372 startBuilder();
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());
1383 return;
1386 /* This state will be reached when we get EOF on the child's
1387 log pipe. */
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
1418 :-) */
1419 int status;
1420 pid_t savedPid;
1421 if (hook) {
1422 savedPid = hook->pid;
1423 status = hook->pid.wait(true);
1424 } else {
1425 /* !!! this could block! security problem! solution: kill the
1426 child */
1427 savedPid = pid;
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. */
1437 if (hook) {
1438 hook->builderOut.readSide.close();
1439 hook->fromHook.readSide.close();
1441 else builderOut.readSide.close();
1443 /* Close the log file. */
1444 closeLogFile();
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
1450 root. */
1451 if (buildUser.enabled()) buildUser.kill();
1453 bool diskFull = false;
1455 try {
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. */
1465 #if HAVE_STATVFS
1466 unsigned long long required = 8ULL * 1024 * 1024; // FIXME: make configurable
1467 struct statvfs st;
1468 if (statvfs(settings.nixStore.c_str(), &st) == 0 &&
1469 (unsigned long long) st.f_bavail * st.f_bsize < required)
1470 diskFull = true;
1471 if (statvfs(tmpDir.c_str(), &st) == 0 &&
1472 (unsigned long long) st.f_bavail * st.f_bsize < required)
1473 diskFull = true;
1474 #endif
1476 deleteTmpDir(false);
1478 /* Move paths out of the chroot for easier debugging of
1479 build failures. */
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());
1485 if (diskFull)
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
1493 being valid. */
1494 registerOutputs();
1496 if (buildMode == bmCheck) {
1497 done(BuildResult::Built);
1498 return;
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 */
1508 deleteTmpDir(true);
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());
1516 return;
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) {
1527 if (!hook)
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());
1546 else {
1547 if (settings.printBuildTrace)
1548 printMsg(lvlError, format("@ build-failed %1% - %2% %3%")
1549 % drvPath % 1 % e.msg());
1551 st =
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);
1568 done(st, e.msg());
1569 return;
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;
1586 if (!worker.hook)
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. */
1602 string reply;
1603 while (true) {
1604 string s = readLine(worker.hook->fromHook.readSide);
1605 if (string(s, 0, 2) == "# ") {
1606 reply = string(s, 2);
1607 break;
1609 s += "\n";
1610 writeToStderr(s);
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));
1622 hook = worker.hook;
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.) */
1630 PathSet allInputs;
1631 allInputs.insert(inputPaths.begin(), inputPaths.end());
1632 computeFSClosure(worker.store, drvPath, allInputs);
1634 string s;
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. */
1640 s = "";
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();
1649 set<int> fds;
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);
1658 return rpAccept;
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();
1672 return 1;
1676 void DerivationGoal::startBuilder()
1678 auto f = format(
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,
1689 though). */
1690 useChroot = settings.useChroot && !isBuiltin(drv);
1692 /* Construct the environment passed to the builder. */
1693 env.clear();
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
1697 value. */
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
1723 place. */
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
1728 directory. */
1729 tmpDirInSandbox = useChroot ? canonPath("/tmp", true) + "/guix-build-" + drvName + "-0" : tmpDir;
1731 /* For convenience, set an environment pointing to the top build
1732 directory. */
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). */
1759 if (fixedOutput) {
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%'")
1783 % storePath);
1784 storePath = toStorePath(storePath);
1785 if (!worker.store.isValidPath(storePath))
1786 throw BuildError(format("`exportReferencesGraph' contains an invalid path `%1%'")
1787 % storePath);
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);
1795 paths2 = 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
1819 uid. */
1820 buildUser.kill();
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);
1827 if (useChroot) {
1828 #if CHROOT_ENABLED
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
1849 instead.) */
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
1856 Samba-in-QEMU. */
1857 createDirs(chrootRootDir + "/etc");
1859 writeFile(chrootRootDir + "/etc/passwd",
1860 (format(
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. */
1873 if (!fixedOutput)
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;
1885 else
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
1896 build user. */
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) {
1905 struct stat st;
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;
1910 else {
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
1916 --optimise'. */
1917 if (errno != EMLINK)
1918 throw SysError(format("linking `%1%' to `%2%'") % p % *i);
1919 StringSink sink;
1920 dumpPath(*i, sink);
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
1933 out. */
1934 for (auto & i : drv.outputs)
1935 dirsInChroot.erase(i.second.path);
1937 #else
1938 throw Error("chroot builds are not supported on this platform");
1939 #endif
1942 else {
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)
1958 addHashRewrite(*i);
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)) {
1965 addHashRewrite(*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.
2009 #if CHROOT_ENABLED
2010 if (useChroot) {
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
2015 bytes. */
2016 pid = clone(childEntry,
2017 (char *)(((uintptr_t)stack + sizeof(stack) - 8) & ~(uintptr_t)0xf),
2018 flags, this);
2019 if (pid == -1)
2020 throw SysError("cloning builder process");
2021 } else
2022 #endif
2024 pid = fork();
2025 if (pid == 0) runChild();
2028 if (pid == -1) throw SysError("unable to fork");
2030 /* parent */
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
2051 calls! */
2053 try { /* child */
2055 _writeToStderr = 0;
2057 restoreAffinity();
2059 commonChildInit(builderOut);
2061 #if CHROOT_ENABLED
2062 if (useChroot) {
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");
2067 struct ifreq ifr;
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");
2073 fd.close();
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. */
2102 Strings ss;
2103 if (dirsInChroot.find("/dev") == dirsInChroot.end()) {
2104 createDirs(chrootRootDir + "/dev/shm");
2105 createDirs(chrootRootDir + "/dev/pts");
2106 ss.push_back("/dev/full");
2107 #ifdef __linux__
2108 if (pathExists("/dev/kvm"))
2109 ss.push_back("/dev/kvm");
2110 #endif
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
2124 on. */
2125 if (fixedOutput) {
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
2136 environment. */
2137 foreach (DirsInChroot::iterator, i, dirsInChroot) {
2138 struct stat st;
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))
2146 createDirs(target);
2147 else {
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"));
2193 #undef pivot_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");
2204 #endif
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>());
2212 #if __linux__
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;
2216 uname(&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
2239 determinism. */
2240 int cur = personality(0xffffffff);
2241 if (cur != -1) personality(cur | ADDR_NO_RANDOMIZE);
2242 #endif
2244 /* Fill in the environment. */
2245 Strings envStrs;
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
2254 saved UIDs. */
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");
2273 restoreSIGPIPE();
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)) {
2280 try {
2281 logType = ltFlat;
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);
2295 else
2296 throw Error(format("unsupported builtin function '%1%'") % string(drv.builder, 8));
2297 _exit(0);
2298 } catch (std::exception & e) {
2299 writeFull(STDERR_FILENO, "error: " + string(e.what()) + "\n");
2300 _exit(1);
2304 /* Fill in the arguments. */
2305 Strings args;
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());
2313 int error = errno;
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);
2321 throw Error(
2322 format("a `%1%' is required to build `%3%', but I am a `%2%'")
2323 % drv.platform % settings.thisSystem % drvPath);
2326 errno = error;
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");
2331 _exit(1);
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
2340 (such as `out'). */
2341 PathSet parseReferenceSpecifiers(const Derivation & drv, string attr)
2343 PathSet result;
2344 Paths paths = tokenizeString<Paths>(attr);
2345 foreach (Strings::iterator, i, paths) {
2346 if (isStorePath(*i))
2347 result.insert(*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%'")
2352 % *i);
2354 return result;
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. */
2363 if (hook) {
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;
2387 if (useChroot) {
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);
2393 else
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;
2398 } else {
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;
2408 struct stat st;
2409 if (lstat(actualPath.c_str(), &st) == -1) {
2410 if (errno == ENOENT)
2411 throw BuildError(
2412 format("builder for `%1%' failed to produce output path `%2%'")
2413 % drvPath % path);
2414 throw SysError(format("getting attributes of path `%1%'") % actualPath);
2417 #ifndef __CYGWIN__
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
2421 user. */
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);
2425 #endif
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. */
2438 StringSink sink;
2439 dumpPath(actualPath, sink);
2440 deletePath(actualPath);
2441 sink.s = rewriteHashes(sink.s, rewritesFromTmp);
2442 StringSource source(sink.s);
2443 restorePath(actualPath, source);
2445 rewritten = true;
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
2453 hash). */
2454 if (i->second.hash != "") {
2456 bool recursive; HashType ht; Hash h;
2457 i->second.parseHashInfo(recursive, ht, h);
2459 if (!recursive) {
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)
2463 throw BuildError(
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);
2469 if (h != h2) {
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. */
2487 HashResult hash;
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);
2501 } else
2502 throw Error(format("derivation `%1%' may not be deterministic: output `%2%' differs")
2503 % drvPath % path);
2506 if (settings.printBuildTrace)
2507 printMsg(lvlError, format("@ build-succeeded %1% -") % drvPath);
2509 continue;
2512 /* For debugging, print out the referenced and unreferenced
2513 paths. */
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);
2518 else
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));
2528 PathSet used;
2529 if (recursive) {
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);
2535 } else
2536 used = references;
2538 for (auto & i : used)
2539 if (allowed) {
2540 if (spec.find(i) == spec.end())
2541 throw BuildError(format("output (`%1%') is not allowed to refer to path `%2%'") % actualPath % i);
2542 } else {
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);
2559 ValidPathInfo info;
2560 info.path = 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)
2575 if (!(*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);
2581 else
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) {
2603 prevInfos = infos;
2604 return;
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()
2619 logSize = 0;
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();
2627 createDirs(dir);
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);
2635 closeOnExec(fd);
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);
2644 return logFileName;
2647 #if HAVE_BZLIB_H
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);
2652 closeOnExec(fd);
2654 if (!(fLogFile = fdopen(fd.borrow(), "w")))
2655 throw SysError(format("opening file `%1%'") % logFileName);
2657 int err;
2658 if (!(bzLogFile = BZ2_bzWriteOpen(&err, fLogFile, 9, 0, 0)))
2659 throw Error(format("cannot open compressed log file `%1%'") % logFileName);
2661 return logFileName;
2663 #endif
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);
2670 return logFileName;
2674 abort();
2678 void DerivationGoal::closeLogFile()
2680 if (gzLogFile) {
2681 int err;
2682 err = gzclose(gzLogFile);
2683 gzLogFile = NULL;
2684 if (err != Z_OK) throw Error(format("cannot close compressed log file (gzip error = %1%)") % err);
2686 #if HAVE_BZLIB_H
2687 else if (bzLogFile) {
2688 int err;
2689 BZ2_bzWriteClose(&err, bzLogFile, 0, 0, 0);
2690 bzLogFile = 0;
2691 if (err != BZ_OK) throw Error(format("cannot close compressed log file (BZip2 error = %1%)") % err);
2693 #endif
2695 if (fLogFile) {
2696 fclose(fLogFile);
2697 fLogFile = 0;
2700 fdLogFile.close();
2704 static void _chown(const Path & path, uid_t uid, gid_t gid)
2706 checkInterrupt();
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)
2721 if (tmpDir != "") {
2722 if (settings.keepFailed && !force) {
2723 printMsg(lvlError,
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);
2734 else
2735 deletePath(tmpDir);
2736 tmpDir = "";
2741 void DerivationGoal::handleChildOutput(int fd, const string & data)
2743 string prefix;
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) {
2761 printMsg(lvlError,
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
2765 return;
2767 if (verbosity >= settings.buildVerbosity)
2768 writeToStderr(prefix + data);
2770 if (gzLogFile) {
2771 if (data.size() > 0) {
2772 int count, err;
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));
2776 #if HAVE_BZLIB_H
2777 } else if (bzLogFile) {
2778 int err;
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);
2781 #endif
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)
2799 PathSet result;
2800 foreach (DerivationOutputs::iterator, i, drv.outputs) {
2801 if (!wantOutput(i->first, wantedOutputs)) continue;
2802 bool good =
2803 worker.store.isValidPath(i->second.path) &&
2804 (!checkHash || worker.store.pathContentsGood(i->second.path));
2805 if (good == returnValid) result.insert(i->second.path);
2807 return result;
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);
2824 return true;
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%'")
2839 % path % p);
2840 return p;
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;
2863 private:
2864 /* The store path that should be realised through a substitute. */
2865 Path storePath;
2867 /* The remaining substituters. */
2868 Paths subs;
2870 /* The current substituter. */
2871 Path sub;
2873 /* Whether any substituter can realise this path */
2874 bool hasSubstitute;
2876 /* Path info returned by the substituter's query info operation. */
2877 SubstitutablePathInfo info;
2879 /* Pipe for the substituter's standard output. */
2880 Pipe outPipe;
2882 /* Pipe for the substituter's standard error. */
2883 Pipe logPipe;
2885 /* The process ID of the builder. */
2886 Pid pid;
2888 /* Lock on the store path. */
2889 std::shared_ptr<PathLocks> outputLock;
2891 /* Whether to try to repair a valid path. */
2892 bool repair;
2894 /* Location where we're downloading the substitute. Differs from
2895 storePath when doing a repair. */
2896 Path destPath;
2898 typedef void (SubstitutionGoal::*GoalState)();
2899 GoalState state;
2901 public:
2902 SubstitutionGoal(const Path & storePath, Worker & worker, bool repair = false);
2903 ~SubstitutionGoal();
2905 void timedOut();
2907 string key()
2909 /* "a$" ensures substitution goals happen before derivation
2910 goals. */
2911 return "a$" + storePathToName(storePath) + "$" + storePath;
2914 void work();
2916 /* The states. */
2917 void init();
2918 void tryNext();
2919 void gotInfo();
2920 void referencesValid();
2921 void tryToRun();
2922 void finished();
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)
2933 : Goal(worker)
2934 , hasSubstitute(false)
2935 , repair(repair)
2937 this->storePath = storePath;
2938 state = &SubstitutionGoal::init;
2939 name = (format("substitution of `%1%'") % storePath).str();
2940 trace("created");
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);
2954 if (pid != -1) {
2955 pid_t savedPid = pid;
2956 pid.kill();
2957 worker.childTerminated(savedPid);
2959 amDone(ecFailed);
2963 void SubstitutionGoal::work()
2965 (this->*state)();
2969 void SubstitutionGoal::init()
2971 trace("init");
2973 worker.store.addTempRoot(storePath);
2975 /* If the path already exists we're done. */
2976 if (!repair && worker.store.isValidPath(storePath)) {
2977 amDone(ecSuccess);
2978 return;
2981 if (settings.readOnlyMode)
2982 throw Error(format("cannot substitute path `%1%' - no write access to the store") % storePath);
2984 subs = settings.substituters;
2986 tryNext();
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
2996 with it. */
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
3000 build. */
3001 amDone(hasSubstitute ? ecFailed : ecNoSubstituters);
3002 return;
3005 sub = subs.front();
3006 subs.pop_front();
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; }
3013 info = k->second;
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) */
3023 referencesValid();
3024 else
3025 state = &SubstitutionGoal::referencesValid;
3029 void SubstitutionGoal::referencesValid()
3031 trace("all references realised");
3033 if (nrFailed > 0) {
3034 debug(format("some references of path `%1%' could not be realised") % storePath);
3035 amDone(nrNoSubstituters > 0 || nrIncompleteClosure > 0 ? ecIncompleteClosure : ecFailed);
3036 return;
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());
3058 return;
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")
3067 % storePath);
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());
3076 return;
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);
3083 amDone(ecSuccess);
3084 return;
3087 printMsg(lvlInfo, format("fetching path `%1%'...") % storePath);
3089 outPipe.create();
3090 logPipe.create();
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. */
3101 Strings args;
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. */
3155 HashResult hash;
3156 try {
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());
3199 return;
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);
3217 outputLock.reset();
3219 worker.store.markContentsGood(storePath);
3221 printMsg(lvlChatty,
3222 format("substitution of path `%1%' succeeded") % storePath);
3224 if (settings.printBuildTrace)
3225 printMsg(lvlError, format("@ substituter-succeeded %1%") % storePath);
3227 amDone(ecSuccess);
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)
3254 : store(store)
3256 /* Debugging: prevent recursive workers. */
3257 if (working) abort();
3258 working = true;
3259 nrLocalBuilds = 0;
3260 lastWokenUp = 0;
3261 permanentFailure = false;
3262 timedOut = false;
3266 Worker::~Worker()
3268 working = 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). */
3274 topGoals.clear();
3278 GoalPtr Worker::makeDerivationGoal(const Path & path,
3279 const StringSet & wantedOutputs, BuildMode buildMode)
3281 GoalPtr goal = derivationGoals[path].lock();
3282 if (!goal) {
3283 goal = GoalPtr(new DerivationGoal(path, wantedOutputs, *this, buildMode));
3284 derivationGoals[path] = goal;
3285 wakeUp(goal);
3286 } else
3287 (dynamic_cast<DerivationGoal *>(goal.get()))->addWantedOutputs(wantedOutputs);
3288 return goal;
3292 GoalPtr Worker::makeSubstitutionGoal(const Path & path, bool repair)
3294 GoalPtr goal = substitutionGoals[path].lock();
3295 if (!goal) {
3296 goal = GoalPtr(new SubstitutionGoal(path, *this, repair));
3297 substitutionGoals[path] = goal;
3298 wakeUp(goal);
3300 return 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;
3311 goalMap.erase(i);
3312 i = j;
3314 else ++i;
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)
3327 topGoals.clear();
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)
3357 Child child;
3358 child.goal = goal;
3359 child.fds = fds;
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);
3377 nrLocalBuilds--;
3380 children.erase(pid);
3382 if (wakeSleepers) {
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 */
3400 else
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"));
3425 while (1) {
3427 checkInterrupt();
3429 /* Call every wake goal (in the ordering established by
3430 CompareGoalPtrs). */
3431 while (!awake.empty() && !topGoals.empty()) {
3432 Goals awake2;
3433 for (auto & i : awake) {
3434 GoalPtr goal = i.lock();
3435 if (goal) awake2.insert(goal);
3437 awake.clear();
3438 for (auto & goal : awake2) {
3439 checkInterrupt();
3440 goal->work();
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())
3449 waitForInput();
3450 else {
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
3475 terminated. */
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);
3496 useTimeout = true;
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()) {
3503 useTimeout = true;
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)
3513 includes EOF. */
3514 fd_set fds;
3515 FD_ZERO(&fds);
3516 int fdMax = 0;
3517 foreach (Children::iterator, i, children) {
3518 foreach (set<int>::iterator, j, i->second.fds) {
3519 FD_SET(*j, &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
3536 timedOut(). */
3537 set<pid_t> pids;
3538 foreach (Children::iterator, i, children) pids.insert(i->first);
3540 foreach (set<pid_t>::iterator, i, pids) {
3541 checkInterrupt();
3542 Children::iterator j = children.find(*i);
3543 if (j == children.end()) continue; // child destroyed
3544 GoalPtr goal = j->second.goal.lock();
3545 assert(goal);
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));
3552 if (rd == -1) {
3553 if (errno != EINTR)
3554 throw SysError(format("reading from %1%")
3555 % goal->getName());
3556 } else if (rd == 0) {
3557 debug(format("%1%: got EOF") % goal->getName());
3558 goal->handleEOF(*k);
3559 j->second.fds.erase(*k);
3560 } else {
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)
3575 printMsg(lvlError,
3576 format("%1% timed out after %2% seconds of silence")
3577 % goal->getName() % settings.maxSilentTime);
3578 goal->timedOut();
3581 else if (goal->getExitCode() == Goal::ecBusy &&
3582 settings.buildTimeout != 0 &&
3583 j->second.respectTimeouts &&
3584 after - j->second.timeStarted >= (time_t) settings.buildTimeout)
3586 printMsg(lvlError,
3587 format("%1% timed out after %2% seconds")
3588 % goal->getName() % settings.buildTimeout);
3589 goal->timedOut();
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);
3620 Goals goals;
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));
3625 else
3626 goals.insert(worker.makeSubstitutionGoal(*i, buildMode));
3629 worker.run(goals);
3631 PathSet failed;
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);
3653 worker.run(goals);
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);
3666 worker.run(goals);
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)) {
3673 goals.clear();
3674 goals.insert(worker.makeDerivationGoal(deriver, StringSet(), bmRepair));
3675 worker.run(goals);
3676 } else
3677 throw Error(format("cannot repair path `%1%'") % path, worker.exitStatus());