news: Add entry for the further reduced (aka Scheme-only) bootstrap.
[jackhill/guix/guix.git] / nix / libstore / build.cc
1 #include "config.h"
2
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"
12
13 #include <map>
14 #include <sstream>
15 #include <algorithm>
16
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>
30
31 #include <pwd.h>
32 #include <grp.h>
33
34 #include <zlib.h>
35
36 #if HAVE_BZLIB_H
37 # include <bzlib.h>
38 #endif
39
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
53
54
55 #define CHROOT_ENABLED HAVE_CHROOT && HAVE_SYS_MOUNT_H && defined(MS_BIND) && defined(MS_PRIVATE)
56 #define CLONE_ENABLED defined(CLONE_NEWNS)
57
58 #if defined(SYS_pivot_root)
59 #define pivot_root(new_root, put_old) (syscall(SYS_pivot_root, new_root,put_old))
60 #endif
61
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
68
69 #if __linux__
70 #include <sys/personality.h>
71 #endif
72
73 #if HAVE_STATVFS
74 #include <sys/statvfs.h>
75 #endif
76
77
78 namespace nix {
79
80 using std::map;
81
82
83 static string pathNullDevice = "/dev/null";
84
85
86 /* Forward definition. */
87 class Worker;
88 struct HookInstance;
89
90
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;
96
97 struct CompareGoalPtrs {
98 bool operator() (const GoalPtr & a, const GoalPtr & b);
99 };
100
101 /* Set of goals. */
102 typedef set<GoalPtr, CompareGoalPtrs> Goals;
103 typedef list<WeakGoalPtr> WeakGoals;
104
105 /* A map of paths to goals (and the other way around). */
106 typedef map<Path, WeakGoalPtr> WeakGoalMap;
107
108
109
110 class Goal : public std::enable_shared_from_this<Goal>
111 {
112 public:
113 typedef enum {ecBusy, ecSuccess, ecFailed, ecNoSubstituters, ecIncompleteClosure} ExitCode;
114
115 protected:
116
117 /* Backlink to the worker. */
118 Worker & worker;
119
120 /* Goals that this goal is waiting for. */
121 Goals waitees;
122
123 /* Goals waiting for this one to finish. Must use weak pointers
124 here to prevent cycles. */
125 WeakGoals waiters;
126
127 /* Number of goals we are/were waiting for that have failed. */
128 unsigned int nrFailed;
129
130 /* Number of substitution goals we are/were waiting for that
131 failed because there are no substituters. */
132 unsigned int nrNoSubstituters;
133
134 /* Number of substitution goals we are/were waiting for that
135 failed because othey had unsubstitutable references. */
136 unsigned int nrIncompleteClosure;
137
138 /* Name of this goal for debugging purposes. */
139 string name;
140
141 /* Whether the goal is finished. */
142 ExitCode exitCode;
143
144 Goal(Worker & worker) : worker(worker)
145 {
146 nrFailed = nrNoSubstituters = nrIncompleteClosure = 0;
147 exitCode = ecBusy;
148 }
149
150 virtual ~Goal()
151 {
152 trace("goal destroyed");
153 }
154
155 public:
156 virtual void work() = 0;
157
158 void addWaitee(GoalPtr waitee);
159
160 virtual void waiteeDone(GoalPtr waitee, ExitCode result);
161
162 virtual void handleChildOutput(int fd, const string & data)
163 {
164 abort();
165 }
166
167 virtual void handleEOF(int fd)
168 {
169 abort();
170 }
171
172 void trace(const format & f);
173
174 string getName()
175 {
176 return name;
177 }
178
179 ExitCode getExitCode()
180 {
181 return exitCode;
182 }
183
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;
188
189 virtual string key() = 0;
190
191 protected:
192 void amDone(ExitCode result);
193 };
194
195
196 bool CompareGoalPtrs::operator() (const GoalPtr & a, const GoalPtr & b) {
197 string s1 = a->key();
198 string s2 = b->key();
199 return s1 < s2;
200 }
201
202
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
207 {
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;
214 };
215
216 typedef map<pid_t, Child> Children;
217
218
219 /* The worker class. */
220 class Worker
221 {
222 private:
223
224 /* Note: the worker should only have strong pointers to the
225 top-level goals. */
226
227 /* The top-level goals of the worker. */
228 Goals topGoals;
229
230 /* Goals that are ready to do some work. */
231 WeakGoals awake;
232
233 /* Goals waiting for a build slot. */
234 WeakGoals wantingToBuild;
235
236 /* Child processes currently running. */
237 Children children;
238
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;
242
243 /* Maps used to prevent multiple instantiations of a goal for the
244 same derivation / path. */
245 WeakGoalMap derivationGoals;
246 WeakGoalMap substitutionGoals;
247
248 /* Goals waiting for busy paths to be unlocked. */
249 WeakGoals waitingForAnyGoal;
250
251 /* Goals sleeping for a few seconds (polling a lock). */
252 WeakGoals waitingForAWhile;
253
254 /* Last time the goals in `waitingForAWhile' where woken up. */
255 time_t lastWokenUp;
256
257 public:
258
259 /* Set if at least one derivation had a BuildError (i.e. permanent
260 failure). */
261 bool permanentFailure;
262
263 /* Set if at least one derivation had a timeout. */
264 bool timedOut;
265
266 LocalStore & store;
267
268 std::shared_ptr<HookInstance> hook;
269
270 Worker(LocalStore & store);
271 ~Worker();
272
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);
276
277 /* Remove a dead goal. */
278 void removeGoal(GoalPtr goal);
279
280 /* Wake up a goal (i.e., there is something for it to do). */
281 void wakeUp(GoalPtr goal);
282
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();
287
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);
292
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);
298
299 /* Put `goal' to sleep until a build slot becomes available (which
300 might be right away). */
301 void waitForBuildSlot(GoalPtr goal);
302
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);
306
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);
312
313 /* Loop until the specified top-level goals have finished. */
314 void run(const Goals & topGoals);
315
316 /* Wait for input to become available. */
317 void waitForInput();
318
319 unsigned int exitStatus();
320 };
321
322
323 //////////////////////////////////////////////////////////////////////
324
325
326 void addToWeakGoals(WeakGoals & goals, GoalPtr p)
327 {
328 // FIXME: necessary?
329 // FIXME: O(n)
330 foreach (WeakGoals::iterator, i, goals)
331 if (i->lock() == p) return;
332 goals.push_back(p);
333 }
334
335
336 void Goal::addWaitee(GoalPtr waitee)
337 {
338 waitees.insert(waitee);
339 addToWeakGoals(waitee->waiters, shared_from_this());
340 }
341
342
343 void Goal::waiteeDone(GoalPtr waitee, ExitCode result)
344 {
345 assert(waitees.find(waitee) != waitees.end());
346 waitees.erase(waitee);
347
348 trace(format("waitee `%1%' done; %2% left") %
349 waitee->name % waitees.size());
350
351 if (result == ecFailed || result == ecNoSubstituters || result == ecIncompleteClosure) ++nrFailed;
352
353 if (result == ecNoSubstituters) ++nrNoSubstituters;
354
355 if (result == ecIncompleteClosure) ++nrIncompleteClosure;
356
357 if (waitees.empty() || (result == ecFailed && !settings.keepGoing)) {
358
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;
367 }
368 waitees.clear();
369
370 worker.wakeUp(shared_from_this());
371 }
372 }
373
374
375 void Goal::amDone(ExitCode result)
376 {
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);
384 }
385 waiters.clear();
386 worker.removeGoal(shared_from_this());
387 }
388
389
390 void Goal::trace(const format & f)
391 {
392 debug(format("%1%: %2%") % name % f);
393 }
394
395
396
397 //////////////////////////////////////////////////////////////////////
398
399
400 /* Common initialisation performed in child processes. */
401 static void commonChildInit(Pipe & logPipe)
402 {
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"));
409
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");
413
414 /* Dup stderr to stdout. */
415 if (dup2(STDERR_FILENO, STDOUT_FILENO) == -1)
416 throw SysError("cannot dup stderr into stdout");
417
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);
425 }
426
427 /* Restore default handling of SIGPIPE, otherwise some programs will
428 randomly say "Broken pipe". */
429 static void restoreSIGPIPE()
430 {
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");
436 }
437
438
439 //////////////////////////////////////////////////////////////////////
440
441
442 class UserLock
443 {
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 */
450
451 Path fnUserLock;
452 AutoCloseFD fdUserLock;
453
454 string user;
455 uid_t uid;
456 gid_t gid;
457 std::vector<gid_t> supplementaryGIDs;
458
459 public:
460 UserLock();
461 ~UserLock();
462
463 void acquire();
464 void release();
465
466 void kill();
467
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; }
472
473 bool enabled() { return uid != 0; }
474
475 };
476
477
478 PathSet UserLock::lockedPaths;
479
480
481 UserLock::UserLock()
482 {
483 uid = gid = 0;
484 }
485
486
487 UserLock::~UserLock()
488 {
489 release();
490 }
491
492
493 void UserLock::acquire()
494 {
495 assert(uid == 0);
496
497 assert(settings.buildUsersGroup != "");
498
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;
505
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);
511 }
512
513 if (users.empty())
514 throw Error(format("the build users group `%1%' has no members")
515 % settings.buildUsersGroup);
516
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);
521
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);
526
527 createDirs(settings.nixStateDir + "/userpool");
528
529 fnUserLock = (format("%1%/userpool/%2%") % settings.nixStateDir % pw->pw_uid).str();
530
531 if (lockedPaths.find(fnUserLock) != lockedPaths.end())
532 /* We already have a lock on this one. */
533 continue;
534
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);
539
540 if (lockFile(fd, ltWrite, false)) {
541 fdUserLock = fd.borrow();
542 lockedPaths.insert(fnUserLock);
543 user = *i;
544 uid = pw->pw_uid;
545
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);
550
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);
559
560 supplementaryGIDs.resize(ngroups);
561
562 return;
563 }
564 }
565
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);
569 }
570
571
572 void UserLock::release()
573 {
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;
580 }
581
582
583 void UserLock::kill()
584 {
585 assert(enabled());
586 killUser(uid);
587 }
588
589
590 //////////////////////////////////////////////////////////////////////
591
592
593 struct HookInstance
594 {
595 /* Pipes for talking to the build hook. */
596 Pipe toHook;
597
598 /* Pipe for the hook's standard output/error. */
599 Pipe fromHook;
600
601 /* Pipe for the builder's standard output/error. */
602 Pipe builderOut;
603
604 /* The process ID of the hook. */
605 Pid pid;
606
607 HookInstance();
608
609 ~HookInstance();
610 };
611
612
613 HookInstance::HookInstance()
614 {
615 debug("starting build hook");
616
617 const Path &buildHook = settings.guixProgram;
618
619 /* Create a pipe to get the output of the child. */
620 fromHook.create();
621
622 /* Create the communication pipes. */
623 toHook.create();
624
625 /* Create a pipe to get the output of the builder. */
626 builderOut.create();
627
628 /* Fork the hook. */
629 pid = startProcess([&]() {
630
631 commonChildInit(fromHook);
632
633 if (chdir("/") == -1) throw SysError("changing into `/");
634
635 /* Dup the communication pipes. */
636 if (dup2(toHook.readSide, STDIN_FILENO) == -1)
637 throw SysError("dupping to-hook read side");
638
639 /* Use fd 4 for the builder's stdout/stderr. */
640 if (dup2(builderOut.writeSide, 4) == -1)
641 throw SysError("dupping builder's stdout/stderr");
642
643 execl(buildHook.c_str(), buildHook.c_str(), "offload",
644 settings.thisSystem.c_str(),
645 (format("%1%") % settings.maxSilentTime).str().c_str(),
646 (format("%1%") % settings.printBuildTrace).str().c_str(),
647 (format("%1%") % settings.buildTimeout).str().c_str(),
648 NULL);
649
650 throw SysError(format("executing `%1% offload'") % buildHook);
651 });
652
653 pid.setSeparatePG(true);
654 fromHook.writeSide.close();
655 toHook.readSide.close();
656 }
657
658
659 HookInstance::~HookInstance()
660 {
661 try {
662 toHook.writeSide.close();
663 pid.kill(true);
664 } catch (...) {
665 ignoreException();
666 }
667 }
668
669
670 //////////////////////////////////////////////////////////////////////
671
672
673 typedef map<string, string> HashRewrites;
674
675
676 string rewriteHashes(string s, const HashRewrites & rewrites)
677 {
678 foreach (HashRewrites::const_iterator, i, rewrites) {
679 assert(i->first.size() == i->second.size());
680 size_t j = 0;
681 while ((j = s.find(i->first, j)) != string::npos) {
682 debug(format("rewriting @ %1%") % j);
683 s.replace(j, i->second.size(), i->second);
684 }
685 }
686 return s;
687 }
688
689
690 //////////////////////////////////////////////////////////////////////
691
692
693 typedef enum {rpAccept, rpDecline, rpPostpone} HookReply;
694
695 class SubstitutionGoal;
696
697 class DerivationGoal : public Goal
698 {
699 private:
700 /* The path of the derivation. */
701 Path drvPath;
702
703 /* The specific outputs that we need to build. Empty means all of
704 them. */
705 StringSet wantedOutputs;
706
707 /* Whether additional wanted outputs have been added. */
708 bool needRestart;
709
710 /* Whether to retry substituting the outputs after building the
711 inputs. */
712 bool retrySubstitution;
713
714 /* The derivation stored at drvPath. */
715 Derivation drv;
716
717 /* The remainder is state held during the build. */
718
719 /* Locks on the output paths. */
720 PathLocks outputLocks;
721
722 /* All input paths (that is, the union of FS closures of the
723 immediate input paths). */
724 PathSet inputPaths;
725
726 /* Referenceable paths (i.e., input and output paths). */
727 PathSet allPaths;
728
729 /* Outputs that are already valid. If we're repairing, these are
730 the outputs that are valid *and* not corrupt. */
731 PathSet validPaths;
732
733 /* Outputs that are corrupt or not valid. */
734 PathSet missingPaths;
735
736 /* User selected for running the builder. */
737 UserLock buildUser;
738
739 /* The process ID of the builder. */
740 Pid pid;
741
742 /* The temporary directory. */
743 Path tmpDir;
744
745 /* The path of the temporary directory in the sandbox. */
746 Path tmpDirInSandbox;
747
748 /* File descriptor for the log file. */
749 FILE * fLogFile;
750 gzFile gzLogFile;
751 #if HAVE_BZLIB_H
752 BZFILE * bzLogFile;
753 #endif
754 AutoCloseFD fdLogFile;
755
756 /* Number of bytes received from the builder's stdout/stderr. */
757 unsigned long logSize;
758
759 /* Pipe for the builder's standard output/error. */
760 Pipe builderOut;
761
762 /* The build hook. */
763 std::shared_ptr<HookInstance> hook;
764
765 /* Whether we're currently doing a chroot build. */
766 bool useChroot;
767
768 Path chrootRootDir;
769
770 /* RAII object to delete the chroot directory. */
771 std::shared_ptr<AutoDelete> autoDelChroot;
772
773 /* All inputs that are regular files. */
774 PathSet regularInputPaths;
775
776 /* Whether this is a fixed-output derivation. */
777 bool fixedOutput;
778
779 typedef void (DerivationGoal::*GoalState)();
780 GoalState state;
781
782 /* Stuff we need to pass to runChild(). */
783 typedef map<Path, Path> DirsInChroot; // maps target path to source path
784 DirsInChroot dirsInChroot;
785 typedef map<string, string> Environment;
786 Environment env;
787
788 /* Hash rewriting. */
789 HashRewrites rewritesToTmp, rewritesFromTmp;
790 typedef map<Path, Path> RedirectedOutputs;
791 RedirectedOutputs redirectedOutputs;
792
793 BuildMode buildMode;
794
795 /* If we're repairing without a chroot, there may be outputs that
796 are valid but corrupt. So we redirect these outputs to
797 temporary paths. */
798 PathSet redirectedBadOutputs;
799
800 /* The current round, if we're building multiple times. */
801 unsigned int curRound = 1;
802
803 unsigned int nrRounds;
804
805 /* Path registration info from the previous round, if we're
806 building multiple times. Since this contains the hash, it
807 allows us to compare whether two rounds produced the same
808 result. */
809 ValidPathInfos prevInfos;
810
811 BuildResult result;
812
813 public:
814 DerivationGoal(const Path & drvPath, const StringSet & wantedOutputs, Worker & worker, BuildMode buildMode = bmNormal);
815 ~DerivationGoal();
816
817 void timedOut() override;
818
819 string key()
820 {
821 /* Ensure that derivations get built in order of their name,
822 i.e. a derivation named "aardvark" always comes before
823 "baboon". And substitution goals always happen before
824 derivation goals (due to "b$"). */
825 return "b$" + storePathToName(drvPath) + "$" + drvPath;
826 }
827
828 void work();
829
830 Path getDrvPath()
831 {
832 return drvPath;
833 }
834
835 /* Add wanted outputs to an already existing derivation goal. */
836 void addWantedOutputs(const StringSet & outputs);
837
838 BuildResult getResult() { return result; }
839
840 private:
841 /* The states. */
842 void init();
843 void haveDerivation();
844 void outputsSubstituted();
845 void closureRepaired();
846 void inputsRealised();
847 void tryToBuild();
848 void buildDone();
849
850 /* Is the build hook willing to perform the build? */
851 HookReply tryBuildHook();
852
853 /* Start building a derivation. */
854 void startBuilder();
855
856 /* Run the builder's process. */
857 void runChild();
858
859 friend int childEntry(void *);
860
861 /* Check that the derivation outputs all exist and register them
862 as valid. */
863 void registerOutputs();
864
865 /* Open a log file and a pipe to it. */
866 Path openLogFile();
867
868 /* Close the log file. */
869 void closeLogFile();
870
871 /* Delete the temporary directory, if we have one. */
872 void deleteTmpDir(bool force);
873
874 /* Callback used by the worker to write to the log. */
875 void handleChildOutput(int fd, const string & data);
876 void handleEOF(int fd);
877
878 /* Return the set of (in)valid paths. */
879 PathSet checkPathValidity(bool returnValid, bool checkHash);
880
881 /* Abort the goal if `path' failed to build. */
882 bool pathFailed(const Path & path);
883
884 /* Forcibly kill the child process, if any. */
885 void killChild();
886
887 Path addHashRewrite(const Path & path);
888
889 void repairClosure();
890
891 void done(BuildResult::Status status, const string & msg = "");
892 };
893
894
895 DerivationGoal::DerivationGoal(const Path & drvPath, const StringSet & wantedOutputs, Worker & worker, BuildMode buildMode)
896 : Goal(worker)
897 , wantedOutputs(wantedOutputs)
898 , needRestart(false)
899 , retrySubstitution(false)
900 , fLogFile(0)
901 , gzLogFile(0)
902 #if HAVE_BZLIB_H
903 , bzLogFile(0)
904 #endif
905 , useChroot(false)
906 , buildMode(buildMode)
907 {
908 this->drvPath = drvPath;
909 state = &DerivationGoal::init;
910 name = (format("building of `%1%'") % drvPath).str();
911 trace("created");
912
913 /* Prevent the .chroot directory from being
914 garbage-collected. (See isActiveTempFile() in gc.cc.) */
915 worker.store.addTempRoot(drvPath);
916 }
917
918
919 DerivationGoal::~DerivationGoal()
920 {
921 /* Careful: we should never ever throw an exception from a
922 destructor. */
923 try { killChild(); } catch (...) { ignoreException(); }
924 try { deleteTmpDir(false); } catch (...) { ignoreException(); }
925 try { closeLogFile(); } catch (...) { ignoreException(); }
926 }
927
928
929 void DerivationGoal::killChild()
930 {
931 if (pid != -1) {
932 worker.childTerminated(pid);
933
934 if (buildUser.enabled()) {
935 /* If we're using a build user, then there is a tricky
936 race condition: if we kill the build user before the
937 child has done its setuid() to the build user uid, then
938 it won't be killed, and we'll potentially lock up in
939 pid.wait(). So also send a conventional kill to the
940 child. */
941 ::kill(-pid, SIGKILL); /* ignore the result */
942 buildUser.kill();
943 pid.wait(true);
944 } else
945 pid.kill();
946
947 assert(pid == -1);
948 }
949
950 /* If there was a build hook involved, remove it from the worker's
951 children. */
952 if (hook && hook->pid != -1) {
953 worker.childTerminated(hook->pid);
954 }
955 hook.reset();
956 }
957
958
959 void DerivationGoal::timedOut()
960 {
961 if (settings.printBuildTrace)
962 printMsg(lvlError, format("@ build-failed %1% - timeout") % drvPath);
963 killChild();
964 done(BuildResult::TimedOut);
965 }
966
967
968 void DerivationGoal::work()
969 {
970 (this->*state)();
971 }
972
973
974 void DerivationGoal::addWantedOutputs(const StringSet & outputs)
975 {
976 /* If we already want all outputs, there is nothing to do. */
977 if (wantedOutputs.empty()) return;
978
979 if (outputs.empty()) {
980 wantedOutputs.clear();
981 needRestart = true;
982 } else
983 foreach (StringSet::const_iterator, i, outputs)
984 if (wantedOutputs.find(*i) == wantedOutputs.end()) {
985 wantedOutputs.insert(*i);
986 needRestart = true;
987 }
988 }
989
990
991 void DerivationGoal::init()
992 {
993 trace("init");
994
995 if (settings.readOnlyMode)
996 throw Error(format("cannot build derivation `%1%' - no write access to the store") % drvPath);
997
998 /* The first thing to do is to make sure that the derivation
999 exists. If it doesn't, it may be created through a
1000 substitute. */
1001 if (buildMode == bmNormal && worker.store.isValidPath(drvPath)) {
1002 haveDerivation();
1003 return;
1004 }
1005
1006 addWaitee(worker.makeSubstitutionGoal(drvPath));
1007
1008 state = &DerivationGoal::haveDerivation;
1009 }
1010
1011
1012 void DerivationGoal::haveDerivation()
1013 {
1014 trace("loading derivation");
1015
1016 if (nrFailed != 0) {
1017 printMsg(lvlError, format("cannot build missing derivation ‘%1%’") % drvPath);
1018 done(BuildResult::MiscFailure);
1019 return;
1020 }
1021
1022 /* `drvPath' should already be a root, but let's be on the safe
1023 side: if the user forgot to make it a root, we wouldn't want
1024 things being garbage collected while we're busy. */
1025 worker.store.addTempRoot(drvPath);
1026
1027 assert(worker.store.isValidPath(drvPath));
1028
1029 /* Get the derivation. */
1030 drv = derivationFromPath(worker.store, drvPath);
1031
1032 foreach (DerivationOutputs::iterator, i, drv.outputs)
1033 worker.store.addTempRoot(i->second.path);
1034
1035 /* Check what outputs paths are not already valid. */
1036 PathSet invalidOutputs = checkPathValidity(false, buildMode == bmRepair);
1037
1038 /* If they are all valid, then we're done. */
1039 if (invalidOutputs.size() == 0 && buildMode == bmNormal) {
1040 done(BuildResult::AlreadyValid);
1041 return;
1042 }
1043
1044 /* Check whether any output previously failed to build. If so,
1045 don't bother. */
1046 foreach (PathSet::iterator, i, invalidOutputs)
1047 if (pathFailed(*i)) return;
1048
1049 /* We are first going to try to create the invalid output paths
1050 through substitutes. If that doesn't work, we'll build
1051 them. */
1052 if (settings.useSubstitutes && substitutesAllowed(drv))
1053 foreach (PathSet::iterator, i, invalidOutputs)
1054 addWaitee(worker.makeSubstitutionGoal(*i, buildMode == bmRepair));
1055
1056 if (waitees.empty()) /* to prevent hang (no wake-up event) */
1057 outputsSubstituted();
1058 else
1059 state = &DerivationGoal::outputsSubstituted;
1060 }
1061
1062
1063 void DerivationGoal::outputsSubstituted()
1064 {
1065 trace("all outputs substituted (maybe)");
1066
1067 if (nrFailed > 0 && nrFailed > nrNoSubstituters + nrIncompleteClosure && !settings.tryFallback)
1068 throw Error(format("some substitutes for the outputs of derivation `%1%' failed (usually happens due to networking issues); try `--fallback' to build derivation from source ") % drvPath);
1069
1070 /* If the substitutes form an incomplete closure, then we should
1071 build the dependencies of this derivation, but after that, we
1072 can still use the substitutes for this derivation itself. */
1073 if (nrIncompleteClosure > 0 && !retrySubstitution) retrySubstitution = true;
1074
1075 nrFailed = nrNoSubstituters = nrIncompleteClosure = 0;
1076
1077 if (needRestart) {
1078 needRestart = false;
1079 haveDerivation();
1080 return;
1081 }
1082
1083 unsigned int nrInvalid = checkPathValidity(false, buildMode == bmRepair).size();
1084 if (buildMode == bmNormal && nrInvalid == 0) {
1085 done(BuildResult::Substituted);
1086 return;
1087 }
1088 if (buildMode == bmRepair && nrInvalid == 0) {
1089 repairClosure();
1090 return;
1091 }
1092 if (buildMode == bmCheck && nrInvalid > 0)
1093 throw Error(format("some outputs of `%1%' are not valid, so checking is not possible") % drvPath);
1094
1095 /* Otherwise, at least one of the output paths could not be
1096 produced using a substitute. So we have to build instead. */
1097
1098 /* Make sure checkPathValidity() from now on checks all
1099 outputs. */
1100 wantedOutputs = PathSet();
1101
1102 /* The inputs must be built before we can build this goal. */
1103 foreach (DerivationInputs::iterator, i, drv.inputDrvs)
1104 addWaitee(worker.makeDerivationGoal(i->first, i->second, buildMode == bmRepair ? bmRepair : bmNormal));
1105
1106 foreach (PathSet::iterator, i, drv.inputSrcs)
1107 addWaitee(worker.makeSubstitutionGoal(*i));
1108
1109 if (waitees.empty()) /* to prevent hang (no wake-up event) */
1110 inputsRealised();
1111 else
1112 state = &DerivationGoal::inputsRealised;
1113 }
1114
1115
1116 void DerivationGoal::repairClosure()
1117 {
1118 /* If we're repairing, we now know that our own outputs are valid.
1119 Now check whether the other paths in the outputs closure are
1120 good. If not, then start derivation goals for the derivations
1121 that produced those outputs. */
1122
1123 /* Get the output closure. */
1124 PathSet outputClosure;
1125 foreach (DerivationOutputs::iterator, i, drv.outputs) {
1126 if (!wantOutput(i->first, wantedOutputs)) continue;
1127 computeFSClosure(worker.store, i->second.path, outputClosure);
1128 }
1129
1130 /* Filter out our own outputs (which we have already checked). */
1131 foreach (DerivationOutputs::iterator, i, drv.outputs)
1132 outputClosure.erase(i->second.path);
1133
1134 /* Get all dependencies of this derivation so that we know which
1135 derivation is responsible for which path in the output
1136 closure. */
1137 PathSet inputClosure;
1138 computeFSClosure(worker.store, drvPath, inputClosure);
1139 std::map<Path, Path> outputsToDrv;
1140 foreach (PathSet::iterator, i, inputClosure)
1141 if (isDerivation(*i)) {
1142 Derivation drv = derivationFromPath(worker.store, *i);
1143 foreach (DerivationOutputs::iterator, j, drv.outputs)
1144 outputsToDrv[j->second.path] = *i;
1145 }
1146
1147 /* Check each path (slow!). */
1148 PathSet broken;
1149 foreach (PathSet::iterator, i, outputClosure) {
1150 if (worker.store.pathContentsGood(*i)) continue;
1151 printMsg(lvlError, format("found corrupted or missing path `%1%' in the output closure of `%2%'") % *i % drvPath);
1152 Path drvPath2 = outputsToDrv[*i];
1153 if (drvPath2 == "")
1154 addWaitee(worker.makeSubstitutionGoal(*i, true));
1155 else
1156 addWaitee(worker.makeDerivationGoal(drvPath2, PathSet(), bmRepair));
1157 }
1158
1159 if (waitees.empty()) {
1160 done(BuildResult::AlreadyValid);
1161 return;
1162 }
1163
1164 state = &DerivationGoal::closureRepaired;
1165 }
1166
1167
1168 void DerivationGoal::closureRepaired()
1169 {
1170 trace("closure repaired");
1171 if (nrFailed > 0)
1172 throw Error(format("some paths in the output closure of derivation ‘%1%’ could not be repaired") % drvPath);
1173 done(BuildResult::AlreadyValid);
1174 }
1175
1176
1177 void DerivationGoal::inputsRealised()
1178 {
1179 trace("all inputs realised");
1180
1181 if (nrFailed != 0) {
1182 printMsg(lvlError,
1183 format("cannot build derivation `%1%': %2% dependencies couldn't be built")
1184 % drvPath % nrFailed);
1185 done(BuildResult::DependencyFailed);
1186 return;
1187 }
1188
1189 if (retrySubstitution) {
1190 haveDerivation();
1191 return;
1192 }
1193
1194 /* Gather information necessary for computing the closure and/or
1195 running the build hook. */
1196
1197 /* The outputs are referenceable paths. */
1198 foreach (DerivationOutputs::iterator, i, drv.outputs) {
1199 debug(format("building path `%1%'") % i->second.path);
1200 allPaths.insert(i->second.path);
1201 }
1202
1203 /* Determine the full set of input paths. */
1204
1205 /* First, the input derivations. */
1206 foreach (DerivationInputs::iterator, i, drv.inputDrvs) {
1207 /* Add the relevant output closures of the input derivation
1208 `*i' as input paths. Only add the closures of output paths
1209 that are specified as inputs. */
1210 assert(worker.store.isValidPath(i->first));
1211 Derivation inDrv = derivationFromPath(worker.store, i->first);
1212 foreach (StringSet::iterator, j, i->second)
1213 if (inDrv.outputs.find(*j) != inDrv.outputs.end())
1214 computeFSClosure(worker.store, inDrv.outputs[*j].path, inputPaths);
1215 else
1216 throw Error(
1217 format("derivation `%1%' requires non-existent output `%2%' from input derivation `%3%'")
1218 % drvPath % *j % i->first);
1219 }
1220
1221 /* Second, the input sources. */
1222 foreach (PathSet::iterator, i, drv.inputSrcs)
1223 computeFSClosure(worker.store, *i, inputPaths);
1224
1225 debug(format("added input paths %1%") % showPaths(inputPaths));
1226
1227 allPaths.insert(inputPaths.begin(), inputPaths.end());
1228
1229 /* Is this a fixed-output derivation? */
1230 fixedOutput = true;
1231 for (auto & i : drv.outputs)
1232 if (i.second.hash == "") fixedOutput = false;
1233
1234 /* Don't repeat fixed-output derivations since they're already
1235 verified by their output hash.*/
1236 nrRounds = fixedOutput ? 1 : settings.get("build-repeat", 0) + 1;
1237
1238 /* Okay, try to build. Note that here we don't wait for a build
1239 slot to become available, since we don't need one if there is a
1240 build hook. */
1241 state = &DerivationGoal::tryToBuild;
1242 worker.wakeUp(shared_from_this());
1243 }
1244
1245
1246 static bool canBuildLocally(const string & platform)
1247 {
1248 return platform == settings.thisSystem
1249 #if __linux__
1250 || (platform == "i686-linux" && settings.thisSystem == "x86_64-linux")
1251 || (platform == "armhf-linux" && settings.thisSystem == "aarch64-linux")
1252 #endif
1253 ;
1254 }
1255
1256
1257 static string get(const StringPairs & map, const string & key, const string & def = "")
1258 {
1259 StringPairs::const_iterator i = map.find(key);
1260 return i == map.end() ? def : i->second;
1261 }
1262
1263
1264 bool willBuildLocally(const Derivation & drv)
1265 {
1266 return get(drv.env, "preferLocalBuild") == "1" && canBuildLocally(drv.platform);
1267 }
1268
1269
1270 bool substitutesAllowed(const Derivation & drv)
1271 {
1272 return get(drv.env, "allowSubstitutes", "1") == "1";
1273 }
1274
1275
1276 void DerivationGoal::tryToBuild()
1277 {
1278 trace("trying to build");
1279
1280 /* Check for the possibility that some other goal in this process
1281 has locked the output since we checked in haveDerivation().
1282 (It can't happen between here and the lockPaths() call below
1283 because we're not allowing multi-threading.) If so, put this
1284 goal to sleep until another goal finishes, then try again. */
1285 foreach (DerivationOutputs::iterator, i, drv.outputs)
1286 if (pathIsLockedByMe(i->second.path)) {
1287 debug(format("putting derivation `%1%' to sleep because `%2%' is locked by another goal")
1288 % drvPath % i->second.path);
1289 worker.waitForAnyGoal(shared_from_this());
1290 return;
1291 }
1292
1293 /* Obtain locks on all output paths. The locks are automatically
1294 released when we exit this function or the client crashes. If we
1295 can't acquire the lock, then continue; hopefully some other
1296 goal can start a build, and if not, the main loop will sleep a
1297 few seconds and then retry this goal. */
1298 if (!outputLocks.lockPaths(outputPaths(drv), "", false)) {
1299 worker.waitForAWhile(shared_from_this());
1300 return;
1301 }
1302
1303 /* Now check again whether the outputs are valid. This is because
1304 another process may have started building in parallel. After
1305 it has finished and released the locks, we can (and should)
1306 reuse its results. (Strictly speaking the first check can be
1307 omitted, but that would be less efficient.) Note that since we
1308 now hold the locks on the output paths, no other process can
1309 build this derivation, so no further checks are necessary. */
1310 validPaths = checkPathValidity(true, buildMode == bmRepair);
1311 if (buildMode != bmCheck && validPaths.size() == drv.outputs.size()) {
1312 debug(format("skipping build of derivation `%1%', someone beat us to it") % drvPath);
1313 outputLocks.setDeletion(true);
1314 done(BuildResult::AlreadyValid);
1315 return;
1316 }
1317
1318 missingPaths = outputPaths(drv);
1319 if (buildMode != bmCheck)
1320 foreach (PathSet::iterator, i, validPaths) missingPaths.erase(*i);
1321
1322 /* If any of the outputs already exist but are not valid, delete
1323 them. */
1324 foreach (DerivationOutputs::iterator, i, drv.outputs) {
1325 Path path = i->second.path;
1326 if (worker.store.isValidPath(path)) continue;
1327 if (!pathExists(path)) continue;
1328 debug(format("removing invalid path `%1%'") % path);
1329 deletePath(path);
1330 }
1331
1332 /* Check again whether any output previously failed to build,
1333 because some other process may have tried and failed before we
1334 acquired the lock. */
1335 foreach (DerivationOutputs::iterator, i, drv.outputs)
1336 if (pathFailed(i->second.path)) return;
1337
1338 /* Don't do a remote build if the derivation has the attribute
1339 `preferLocalBuild' set. Also, check and repair modes are only
1340 supported for local builds. */
1341 bool buildLocally = buildMode != bmNormal || willBuildLocally(drv);
1342
1343 /* Is the build hook willing to accept this job? */
1344 if (!buildLocally) {
1345 switch (tryBuildHook()) {
1346 case rpAccept:
1347 /* Yes, it has started doing so. Wait until we get
1348 EOF from the hook. */
1349 state = &DerivationGoal::buildDone;
1350 return;
1351 case rpPostpone:
1352 /* Not now; wait until at least one child finishes or
1353 the wake-up timeout expires. */
1354 worker.waitForAWhile(shared_from_this());
1355 outputLocks.unlock();
1356 return;
1357 case rpDecline:
1358 /* We should do it ourselves. */
1359 break;
1360 }
1361 }
1362
1363 /* Make sure that we are allowed to start a build. If this
1364 derivation prefers to be done locally, do it even if
1365 maxBuildJobs is 0. */
1366 unsigned int curBuilds = worker.getNrLocalBuilds();
1367 if (curBuilds >= settings.maxBuildJobs && !(buildLocally && curBuilds == 0)) {
1368 worker.waitForBuildSlot(shared_from_this());
1369 outputLocks.unlock();
1370 return;
1371 }
1372
1373 try {
1374
1375 /* Okay, we have to build. */
1376 startBuilder();
1377
1378 } catch (BuildError & e) {
1379 printMsg(lvlError, e.msg());
1380 outputLocks.unlock();
1381 buildUser.release();
1382 if (settings.printBuildTrace)
1383 printMsg(lvlError, format("@ build-failed %1% - %2% %3%")
1384 % drvPath % 0 % e.msg());
1385 worker.permanentFailure = true;
1386 done(BuildResult::InputRejected, e.msg());
1387 return;
1388 }
1389
1390 /* This state will be reached when we get EOF on the child's
1391 log pipe. */
1392 state = &DerivationGoal::buildDone;
1393 }
1394
1395
1396 void replaceValidPath(const Path & storePath, const Path tmpPath)
1397 {
1398 /* We can't atomically replace storePath (the original) with
1399 tmpPath (the replacement), so we have to move it out of the
1400 way first. We'd better not be interrupted here, because if
1401 we're repairing (say) Glibc, we end up with a broken system. */
1402 Path oldPath = (format("%1%.old-%2%-%3%") % storePath % getpid() % rand()).str();
1403 if (pathExists(storePath))
1404 rename(storePath.c_str(), oldPath.c_str());
1405 if (rename(tmpPath.c_str(), storePath.c_str()) == -1)
1406 throw SysError(format("moving `%1%' to `%2%'") % tmpPath % storePath);
1407 if (pathExists(oldPath))
1408 deletePath(oldPath);
1409 }
1410
1411
1412 MakeError(NotDeterministic, BuildError)
1413
1414
1415 void DerivationGoal::buildDone()
1416 {
1417 trace("build done");
1418
1419 /* Since we got an EOF on the logger pipe, the builder is presumed
1420 to have terminated. In fact, the builder could also have
1421 simply have closed its end of the pipe --- just don't do that
1422 :-) */
1423 int status;
1424 pid_t savedPid;
1425 if (hook) {
1426 savedPid = hook->pid;
1427 status = hook->pid.wait(true);
1428 } else {
1429 /* !!! this could block! security problem! solution: kill the
1430 child */
1431 savedPid = pid;
1432 status = pid.wait(true);
1433 }
1434
1435 debug(format("builder process for `%1%' finished") % drvPath);
1436
1437 /* So the child is gone now. */
1438 worker.childTerminated(savedPid);
1439
1440 /* Close the read side of the logger pipe. */
1441 if (hook) {
1442 hook->builderOut.readSide.close();
1443 hook->fromHook.readSide.close();
1444 }
1445 else builderOut.readSide.close();
1446
1447 /* Close the log file. */
1448 closeLogFile();
1449
1450 /* When running under a build user, make sure that all processes
1451 running under that uid are gone. This is to prevent a
1452 malicious user from leaving behind a process that keeps files
1453 open and modifies them after they have been chown'ed to
1454 root. */
1455 if (buildUser.enabled()) buildUser.kill();
1456
1457 bool diskFull = false;
1458
1459 try {
1460
1461 /* Check the exit status. */
1462 if (!statusOk(status)) {
1463
1464 /* Heuristically check whether the build failure may have
1465 been caused by a disk full condition. We have no way
1466 of knowing whether the build actually got an ENOSPC.
1467 So instead, check if the disk is (nearly) full now. If
1468 so, we don't mark this build as a permanent failure. */
1469 #if HAVE_STATVFS
1470 unsigned long long required = 8ULL * 1024 * 1024; // FIXME: make configurable
1471 struct statvfs st;
1472 if (statvfs(settings.nixStore.c_str(), &st) == 0 &&
1473 (unsigned long long) st.f_bavail * st.f_bsize < required)
1474 diskFull = true;
1475 if (statvfs(tmpDir.c_str(), &st) == 0 &&
1476 (unsigned long long) st.f_bavail * st.f_bsize < required)
1477 diskFull = true;
1478 #endif
1479
1480 deleteTmpDir(false);
1481
1482 /* Move paths out of the chroot for easier debugging of
1483 build failures. */
1484 if (useChroot && buildMode == bmNormal)
1485 foreach (PathSet::iterator, i, missingPaths)
1486 if (pathExists(chrootRootDir + *i))
1487 rename((chrootRootDir + *i).c_str(), i->c_str());
1488
1489 if (diskFull)
1490 printMsg(lvlError, "note: build failure may have been caused by lack of free disk space");
1491
1492 throw BuildError(format("builder for `%1%' %2%")
1493 % drvPath % statusToString(status));
1494 }
1495
1496 /* Compute the FS closure of the outputs and register them as
1497 being valid. */
1498 registerOutputs();
1499
1500 if (buildMode == bmCheck) {
1501 done(BuildResult::Built);
1502 return;
1503 }
1504
1505 /* Delete unused redirected outputs (when doing hash rewriting). */
1506 foreach (RedirectedOutputs::iterator, i, redirectedOutputs)
1507 if (pathExists(i->second)) deletePath(i->second);
1508
1509 /* Delete the chroot (if we were using one). */
1510 autoDelChroot.reset(); /* this runs the destructor */
1511
1512 deleteTmpDir(true);
1513
1514 /* Repeat the build if necessary. */
1515 if (curRound++ < nrRounds) {
1516 outputLocks.unlock();
1517 buildUser.release();
1518 state = &DerivationGoal::tryToBuild;
1519 worker.wakeUp(shared_from_this());
1520 return;
1521 }
1522
1523 /* It is now safe to delete the lock files, since all future
1524 lockers will see that the output paths are valid; they will
1525 not create new lock files with the same names as the old
1526 (unlinked) lock files. */
1527 outputLocks.setDeletion(true);
1528 outputLocks.unlock();
1529
1530 } catch (BuildError & e) {
1531 if (!hook)
1532 printMsg(lvlError, e.msg());
1533 outputLocks.unlock();
1534 buildUser.release();
1535
1536 BuildResult::Status st = BuildResult::MiscFailure;
1537
1538 if (hook && WIFEXITED(status) && WEXITSTATUS(status) == 101) {
1539 if (settings.printBuildTrace)
1540 printMsg(lvlError, format("@ build-failed %1% - timeout") % drvPath);
1541 st = BuildResult::TimedOut;
1542 }
1543
1544 else if (hook && (!WIFEXITED(status) || WEXITSTATUS(status) != 100)) {
1545 if (settings.printBuildTrace)
1546 printMsg(lvlError, format("@ hook-failed %1% - %2% %3%")
1547 % drvPath % status % e.msg());
1548 }
1549
1550 else {
1551 if (settings.printBuildTrace)
1552 printMsg(lvlError, format("@ build-failed %1% - %2% %3%")
1553 % drvPath % 1 % e.msg());
1554
1555 st =
1556 statusOk(status) ? BuildResult::OutputRejected :
1557 fixedOutput || diskFull ? BuildResult::TransientFailure :
1558 BuildResult::PermanentFailure;
1559
1560 /* Register the outputs of this build as "failed" so we
1561 won't try to build them again (negative caching).
1562 However, don't do this for fixed-output derivations,
1563 since they're likely to fail for transient reasons
1564 (e.g., fetchurl not being able to access the network).
1565 Hook errors (like communication problems with the
1566 remote machine) shouldn't be cached either. */
1567 if (settings.cacheFailure && !fixedOutput && !diskFull)
1568 foreach (DerivationOutputs::iterator, i, drv.outputs)
1569 worker.store.registerFailedPath(i->second.path);
1570 }
1571
1572 done(st, e.msg());
1573 return;
1574 }
1575
1576 /* Release the build user, if applicable. */
1577 buildUser.release();
1578
1579 if (settings.printBuildTrace)
1580 printMsg(lvlError, format("@ build-succeeded %1% -") % drvPath);
1581
1582 done(BuildResult::Built);
1583 }
1584
1585
1586 HookReply DerivationGoal::tryBuildHook()
1587 {
1588 if (!settings.useBuildHook) return rpDecline;
1589
1590 if (!worker.hook)
1591 worker.hook = std::shared_ptr<HookInstance>(new HookInstance);
1592
1593 /* Tell the hook about system features (beyond the system type)
1594 required from the build machine. (The hook could parse the
1595 drv file itself, but this is easier.) */
1596 Strings features = tokenizeString<Strings>(get(drv.env, "requiredSystemFeatures"));
1597 foreach (Strings::iterator, i, features) checkStoreName(*i); /* !!! abuse */
1598
1599 /* Send the request to the hook. */
1600 writeLine(worker.hook->toHook.writeSide, (format("%1% %2% %3% %4%")
1601 % (worker.getNrLocalBuilds() < settings.maxBuildJobs ? "1" : "0")
1602 % drv.platform % drvPath % concatStringsSep(",", features)).str());
1603
1604 /* Read the first line of input, which should be a word indicating
1605 whether the hook wishes to perform the build. */
1606 string reply;
1607 while (true) {
1608 string s = readLine(worker.hook->fromHook.readSide);
1609 if (string(s, 0, 2) == "# ") {
1610 reply = string(s, 2);
1611 break;
1612 }
1613 s += "\n";
1614 writeToStderr(s);
1615 }
1616
1617 debug(format("hook reply is `%1%'") % reply);
1618
1619 if (reply == "decline" || reply == "postpone")
1620 return reply == "decline" ? rpDecline : rpPostpone;
1621 else if (reply != "accept")
1622 throw Error(format("bad hook reply `%1%'") % reply);
1623
1624 printMsg(lvlTalkative, format("using hook to build path(s) %1%") % showPaths(missingPaths));
1625
1626 hook = worker.hook;
1627 worker.hook.reset();
1628
1629 /* Tell the hook all the inputs that have to be copied to the
1630 remote system. This unfortunately has to contain the entire
1631 derivation closure to ensure that the validity invariant holds
1632 on the remote system. (I.e., it's unfortunate that we have to
1633 list it since the remote system *probably* already has it.) */
1634 PathSet allInputs;
1635 allInputs.insert(inputPaths.begin(), inputPaths.end());
1636 computeFSClosure(worker.store, drvPath, allInputs);
1637
1638 string s;
1639 foreach (PathSet::iterator, i, allInputs) { s += *i; s += ' '; }
1640 writeLine(hook->toHook.writeSide, s);
1641
1642 /* Tell the hooks the missing outputs that have to be copied back
1643 from the remote system. */
1644 s = "";
1645 foreach (PathSet::iterator, i, missingPaths) { s += *i; s += ' '; }
1646 writeLine(hook->toHook.writeSide, s);
1647
1648 hook->toHook.writeSide.close();
1649
1650 /* Create the log file and pipe. */
1651 Path logFile = openLogFile();
1652
1653 set<int> fds;
1654 fds.insert(hook->fromHook.readSide);
1655 fds.insert(hook->builderOut.readSide);
1656 worker.childStarted(shared_from_this(), hook->pid, fds, false, true);
1657
1658 if (settings.printBuildTrace)
1659 printMsg(lvlError, format("@ build-started %1% - %2% %3% %4%")
1660 % drvPath % drv.platform % logFile % hook->pid);
1661
1662 return rpAccept;
1663 }
1664
1665
1666 void chmod_(const Path & path, mode_t mode)
1667 {
1668 if (chmod(path.c_str(), mode) == -1)
1669 throw SysError(format("setting permissions on `%1%'") % path);
1670 }
1671
1672
1673 int childEntry(void * arg)
1674 {
1675 ((DerivationGoal *) arg)->runChild();
1676 return 1;
1677 }
1678
1679
1680 void DerivationGoal::startBuilder()
1681 {
1682 auto f = format(
1683 buildMode == bmRepair ? "repairing path(s) %1%" :
1684 buildMode == bmCheck ? "checking path(s) %1%" :
1685 nrRounds > 1 ? "building path(s) %1% (round %2%/%3%)" :
1686 "building path(s) %1%");
1687 f.exceptions(boost::io::all_error_bits ^ boost::io::too_many_args_bit);
1688 startNest(nest, lvlInfo, f % showPaths(missingPaths) % curRound % nrRounds);
1689
1690 /* Note: built-in builders are *not* running in a chroot environment so
1691 that we can easily implement them in Guile without having it as a
1692 derivation input (they are running under a separate build user,
1693 though). */
1694 useChroot = settings.useChroot && !isBuiltin(drv);
1695
1696 /* Construct the environment passed to the builder. */
1697 env.clear();
1698
1699 /* Most shells initialise PATH to some default (/bin:/usr/bin:...) when
1700 PATH is not set. We don't want this, so we fill it in with some dummy
1701 value. */
1702 env["PATH"] = "/path-not-set";
1703
1704 /* Set HOME to a non-existing path to prevent certain programs from using
1705 /etc/passwd (or NIS, or whatever) to locate the home directory (for
1706 example, wget looks for ~/.wgetrc). I.e., these tools use /etc/passwd
1707 if HOME is not set, but they will just assume that the settings file
1708 they are looking for does not exist if HOME is set but points to some
1709 non-existing path. */
1710 Path homeDir = "/homeless-shelter";
1711 env["HOME"] = homeDir;
1712
1713 /* Tell the builder where the store is. Usually they
1714 shouldn't care, but this is useful for purity checking (e.g.,
1715 the compiler or linker might only want to accept paths to files
1716 in the store or in the build directory). */
1717 env["NIX_STORE"] = settings.nixStore;
1718
1719 /* The maximum number of cores to utilize for parallel building. */
1720 env["NIX_BUILD_CORES"] = (format("%d") % settings.buildCores).str();
1721
1722 /* Add all bindings specified in the derivation. */
1723 foreach (StringPairs::iterator, i, drv.env)
1724 env[i->first] = i->second;
1725
1726 /* Create a temporary directory where the build will take
1727 place. */
1728 auto drvName = storePathToName(drvPath);
1729 tmpDir = createTempDir("", "guix-build-" + drvName, false, false, 0700);
1730
1731 /* In a sandbox, for determinism, always use the same temporary
1732 directory. */
1733 tmpDirInSandbox = useChroot ? canonPath("/tmp", true) + "/guix-build-" + drvName + "-0" : tmpDir;
1734
1735 /* For convenience, set an environment pointing to the top build
1736 directory. */
1737 env["NIX_BUILD_TOP"] = tmpDirInSandbox;
1738
1739 /* Also set TMPDIR and variants to point to this directory. */
1740 env["TMPDIR"] = env["TEMPDIR"] = env["TMP"] = env["TEMP"] = tmpDirInSandbox;
1741
1742 /* Explicitly set PWD to prevent problems with chroot builds. In
1743 particular, dietlibc cannot figure out the cwd because the
1744 inode of the current directory doesn't appear in .. (because
1745 getdents returns the inode of the mount point). */
1746 env["PWD"] = tmpDirInSandbox;
1747
1748 /* Compatibility hack with Nix <= 0.7: if this is a fixed-output
1749 derivation, tell the builder, so that for instance `fetchurl'
1750 can skip checking the output. On older Nixes, this environment
1751 variable won't be set, so `fetchurl' will do the check. */
1752 if (fixedOutput) env["NIX_OUTPUT_CHECKED"] = "1";
1753
1754 /* *Only* if this is a fixed-output derivation, propagate the
1755 values of the environment variables specified in the
1756 `impureEnvVars' attribute to the builder. This allows for
1757 instance environment variables for proxy configuration such as
1758 `http_proxy' to be easily passed to downloaders like
1759 `fetchurl'. Passing such environment variables from the caller
1760 to the builder is generally impure, but the output of
1761 fixed-output derivations is by definition pure (since we
1762 already know the cryptographic hash of the output). */
1763 if (fixedOutput) {
1764 Strings varNames = tokenizeString<Strings>(get(drv.env, "impureEnvVars"));
1765 foreach (Strings::iterator, i, varNames) env[*i] = getEnv(*i);
1766 }
1767
1768 /* The `exportReferencesGraph' feature allows the references graph
1769 to be passed to a builder. This attribute should be a list of
1770 pairs [name1 path1 name2 path2 ...]. The references graph of
1771 each `pathN' will be stored in a text file `nameN' in the
1772 temporary build directory. The text files have the format used
1773 by `nix-store --register-validity'. However, the deriver
1774 fields are left empty. */
1775 string s = get(drv.env, "exportReferencesGraph");
1776 Strings ss = tokenizeString<Strings>(s);
1777 if (ss.size() % 2 != 0)
1778 throw BuildError(format("odd number of tokens in `exportReferencesGraph': `%1%'") % s);
1779 for (Strings::iterator i = ss.begin(); i != ss.end(); ) {
1780 string fileName = *i++;
1781 checkStoreName(fileName); /* !!! abuse of this function */
1782
1783 /* Check that the store path is valid. */
1784 Path storePath = *i++;
1785 if (!isInStore(storePath))
1786 throw BuildError(format("`exportReferencesGraph' contains a non-store path `%1%'")
1787 % storePath);
1788 storePath = toStorePath(storePath);
1789 if (!worker.store.isValidPath(storePath))
1790 throw BuildError(format("`exportReferencesGraph' contains an invalid path `%1%'")
1791 % storePath);
1792
1793 /* If there are derivations in the graph, then include their
1794 outputs as well. This is useful if you want to do things
1795 like passing all build-time dependencies of some path to a
1796 derivation that builds a NixOS DVD image. */
1797 PathSet paths, paths2;
1798 computeFSClosure(worker.store, storePath, paths);
1799 paths2 = paths;
1800
1801 foreach (PathSet::iterator, j, paths2) {
1802 if (isDerivation(*j)) {
1803 Derivation drv = derivationFromPath(worker.store, *j);
1804 foreach (DerivationOutputs::iterator, k, drv.outputs)
1805 computeFSClosure(worker.store, k->second.path, paths);
1806 }
1807 }
1808
1809 /* Write closure info to `fileName'. */
1810 writeFile(tmpDir + "/" + fileName,
1811 worker.store.makeValidityRegistration(paths, false, false));
1812 }
1813
1814
1815 /* If `build-users-group' is not empty, then we have to build as
1816 one of the members of that group. */
1817 if (settings.buildUsersGroup != "") {
1818 buildUser.acquire();
1819 assert(buildUser.getUID() != 0);
1820 assert(buildUser.getGID() != 0);
1821
1822 /* Make sure that no other processes are executing under this
1823 uid. */
1824 buildUser.kill();
1825
1826 /* Change ownership of the temporary build directory. */
1827 if (chown(tmpDir.c_str(), buildUser.getUID(), buildUser.getGID()) == -1)
1828 throw SysError(format("cannot change ownership of '%1%'") % tmpDir);
1829 }
1830
1831 if (useChroot) {
1832 #if CHROOT_ENABLED
1833 /* Create a temporary directory in which we set up the chroot
1834 environment using bind-mounts. We put it in the store
1835 to ensure that we can create hard-links to non-directory
1836 inputs in the fake store in the chroot (see below). */
1837 chrootRootDir = drvPath + ".chroot";
1838 if (pathExists(chrootRootDir)) deletePath(chrootRootDir);
1839
1840 /* Clean up the chroot directory automatically. */
1841 autoDelChroot = std::shared_ptr<AutoDelete>(new AutoDelete(chrootRootDir));
1842
1843 printMsg(lvlChatty, format("setting up chroot environment in `%1%'") % chrootRootDir);
1844
1845 if (mkdir(chrootRootDir.c_str(), 0750) == -1)
1846 throw SysError(format("cannot create ‘%1%’") % chrootRootDir);
1847
1848 if (chown(chrootRootDir.c_str(), 0, buildUser.getGID()) == -1)
1849 throw SysError(format("cannot change ownership of ‘%1%’") % chrootRootDir);
1850
1851 /* Create a writable /tmp in the chroot. Many builders need
1852 this. (Of course they should really respect $TMPDIR
1853 instead.) */
1854 Path chrootTmpDir = chrootRootDir + "/tmp";
1855 createDirs(chrootTmpDir);
1856 chmod_(chrootTmpDir, 01777);
1857
1858 /* Create a /etc/passwd with entries for the build user and the
1859 nobody account. The latter is kind of a hack to support
1860 Samba-in-QEMU. */
1861 createDirs(chrootRootDir + "/etc");
1862
1863 writeFile(chrootRootDir + "/etc/passwd",
1864 (format(
1865 "nixbld:x:%1%:%2%:Nix build user:/:/noshell\n"
1866 "nobody:x:65534:65534:Nobody:/:/noshell\n")
1867 % (buildUser.enabled() ? buildUser.getUID() : getuid())
1868 % (buildUser.enabled() ? buildUser.getGID() : getgid())).str());
1869
1870 /* Declare the build user's group so that programs get a consistent
1871 view of the system (e.g., "id -gn"). */
1872 writeFile(chrootRootDir + "/etc/group",
1873 (format("nixbld:!:%1%:\n")
1874 % (buildUser.enabled() ? buildUser.getGID() : getgid())).str());
1875
1876 /* Create /etc/hosts with localhost entry. */
1877 if (!fixedOutput)
1878 writeFile(chrootRootDir + "/etc/hosts", "127.0.0.1 localhost\n");
1879
1880 /* Bind-mount a user-configurable set of directories from the
1881 host file system. */
1882 PathSet dirs = tokenizeString<StringSet>(settings.get("build-chroot-dirs", string(DEFAULT_CHROOT_DIRS)));
1883 PathSet dirs2 = tokenizeString<StringSet>(settings.get("build-extra-chroot-dirs", string("")));
1884 dirs.insert(dirs2.begin(), dirs2.end());
1885 for (auto & i : dirs) {
1886 size_t p = i.find('=');
1887 if (p == string::npos)
1888 dirsInChroot[i] = i;
1889 else
1890 dirsInChroot[string(i, 0, p)] = string(i, p + 1);
1891 }
1892 dirsInChroot[tmpDirInSandbox] = tmpDir;
1893
1894 /* Make the closure of the inputs available in the chroot,
1895 rather than the whole store. This prevents any access
1896 to undeclared dependencies. Directories are bind-mounted,
1897 while other inputs are hard-linked (since only directories
1898 can be bind-mounted). !!! As an extra security
1899 precaution, make the fake store only writable by the
1900 build user. */
1901 Path chrootStoreDir = chrootRootDir + settings.nixStore;
1902 createDirs(chrootStoreDir);
1903 chmod_(chrootStoreDir, 01775);
1904
1905 if (chown(chrootStoreDir.c_str(), 0, buildUser.getGID()) == -1)
1906 throw SysError(format("cannot change ownership of ‘%1%’") % chrootStoreDir);
1907
1908 foreach (PathSet::iterator, i, inputPaths) {
1909 struct stat st;
1910 if (lstat(i->c_str(), &st))
1911 throw SysError(format("getting attributes of path `%1%'") % *i);
1912 if (S_ISDIR(st.st_mode))
1913 dirsInChroot[*i] = *i;
1914 else {
1915 Path p = chrootRootDir + *i;
1916 if (link(i->c_str(), p.c_str()) == -1) {
1917 /* Hard-linking fails if we exceed the maximum
1918 link count on a file (e.g. 32000 of ext3),
1919 which is quite possible after a `nix-store
1920 --optimise'. */
1921 if (errno != EMLINK)
1922 throw SysError(format("linking `%1%' to `%2%'") % p % *i);
1923 StringSink sink;
1924 dumpPath(*i, sink);
1925 StringSource source(sink.s);
1926 restorePath(p, source);
1927 }
1928
1929 regularInputPaths.insert(*i);
1930 }
1931 }
1932
1933 /* If we're repairing, checking or rebuilding part of a
1934 multiple-outputs derivation, it's possible that we're
1935 rebuilding a path that is in settings.dirsInChroot
1936 (typically the dependencies of /bin/sh). Throw them
1937 out. */
1938 for (auto & i : drv.outputs)
1939 dirsInChroot.erase(i.second.path);
1940
1941 #else
1942 throw Error("chroot builds are not supported on this platform");
1943 #endif
1944 }
1945
1946 else {
1947
1948 if (pathExists(homeDir))
1949 throw Error(format("directory `%1%' exists; please remove it") % homeDir);
1950
1951 /* We're not doing a chroot build, but we have some valid
1952 output paths. Since we can't just overwrite or delete
1953 them, we have to do hash rewriting: i.e. in the
1954 environment/arguments passed to the build, we replace the
1955 hashes of the valid outputs with unique dummy strings;
1956 after the build, we discard the redirected outputs
1957 corresponding to the valid outputs, and rewrite the
1958 contents of the new outputs to replace the dummy strings
1959 with the actual hashes. */
1960 if (validPaths.size() > 0)
1961 foreach (PathSet::iterator, i, validPaths)
1962 addHashRewrite(*i);
1963
1964 /* If we're repairing, then we don't want to delete the
1965 corrupt outputs in advance. So rewrite them as well. */
1966 if (buildMode == bmRepair)
1967 foreach (PathSet::iterator, i, missingPaths)
1968 if (worker.store.isValidPath(*i) && pathExists(*i)) {
1969 addHashRewrite(*i);
1970 redirectedBadOutputs.insert(*i);
1971 }
1972 }
1973
1974
1975 /* Run the builder. */
1976 printMsg(lvlChatty, format("executing builder `%1%'") % drv.builder);
1977
1978 /* Create the log file. */
1979 Path logFile = openLogFile();
1980
1981 /* Create a pipe to get the output of the builder. */
1982 builderOut.create();
1983
1984 /* Fork a child to build the package. Note that while we
1985 currently use forks to run and wait for the children, it
1986 shouldn't be hard to use threads for this on systems where
1987 fork() is unavailable or inefficient.
1988
1989 If we're building in a chroot, then also set up private
1990 namespaces for the build:
1991
1992 - The PID namespace causes the build to start as PID 1.
1993 Processes outside of the chroot are not visible to those on
1994 the inside, but processes inside the chroot are visible from
1995 the outside (though with different PIDs).
1996
1997 - The private mount namespace ensures that all the bind mounts
1998 we do will only show up in this process and its children, and
1999 will disappear automatically when we're done.
2000
2001 - The private network namespace ensures that the builder cannot
2002 talk to the outside world (or vice versa). It only has a
2003 private loopback interface.
2004
2005 - The IPC namespace prevents the builder from communicating
2006 with outside processes using SysV IPC mechanisms (shared
2007 memory, message queues, semaphores). It also ensures that
2008 all IPC objects are destroyed when the builder exits.
2009
2010 - The UTS namespace ensures that builders see a hostname of
2011 localhost rather than the actual hostname.
2012 */
2013 #if __linux__
2014 if (useChroot) {
2015 char stack[32 * 1024];
2016 int flags = CLONE_NEWPID | CLONE_NEWNS | CLONE_NEWIPC | CLONE_NEWUTS | SIGCHLD;
2017 if (!fixedOutput) flags |= CLONE_NEWNET;
2018 /* Ensure proper alignment on the stack. On aarch64, it has to be 16
2019 bytes. */
2020 pid = clone(childEntry,
2021 (char *)(((uintptr_t)stack + sizeof(stack) - 8) & ~(uintptr_t)0xf),
2022 flags, this);
2023 if (pid == -1)
2024 throw SysError("cloning builder process");
2025 } else
2026 #endif
2027 {
2028 pid = fork();
2029 if (pid == 0) runChild();
2030 }
2031
2032 if (pid == -1) throw SysError("unable to fork");
2033
2034 /* parent */
2035 pid.setSeparatePG(true);
2036 builderOut.writeSide.close();
2037 worker.childStarted(shared_from_this(), pid,
2038 singleton<set<int> >(builderOut.readSide), true, true);
2039
2040 /* Check if setting up the build environment failed. */
2041 string msg = readLine(builderOut.readSide);
2042 if (!msg.empty()) throw Error(msg);
2043
2044 if (settings.printBuildTrace) {
2045 printMsg(lvlError, format("@ build-started %1% - %2% %3% %4%")
2046 % drvPath % drv.platform % logFile % pid);
2047 }
2048
2049 }
2050
2051
2052 void DerivationGoal::runChild()
2053 {
2054 /* Warning: in the child we should absolutely not make any SQLite
2055 calls! */
2056
2057 try { /* child */
2058
2059 _writeToStderr = 0;
2060
2061 restoreAffinity();
2062
2063 commonChildInit(builderOut);
2064
2065 #if CHROOT_ENABLED
2066 if (useChroot) {
2067 /* Initialise the loopback interface. */
2068 AutoCloseFD fd(socket(PF_INET, SOCK_DGRAM, IPPROTO_IP));
2069 if (fd == -1) throw SysError("cannot open IP socket");
2070
2071 struct ifreq ifr;
2072 strcpy(ifr.ifr_name, "lo");
2073 ifr.ifr_flags = IFF_UP | IFF_LOOPBACK | IFF_RUNNING;
2074 if (ioctl(fd, SIOCSIFFLAGS, &ifr) == -1)
2075 throw SysError("cannot set loopback interface flags");
2076
2077 fd.close();
2078
2079 /* Set the hostname etc. to fixed values. */
2080 char hostname[] = "localhost";
2081 if (sethostname(hostname, sizeof(hostname)) == -1)
2082 throw SysError("cannot set host name");
2083 char domainname[] = "(none)"; // kernel default
2084 if (setdomainname(domainname, sizeof(domainname)) == -1)
2085 throw SysError("cannot set domain name");
2086
2087 /* Make all filesystems private. This is necessary
2088 because subtrees may have been mounted as "shared"
2089 (MS_SHARED). (Systemd does this, for instance.) Even
2090 though we have a private mount namespace, mounting
2091 filesystems on top of a shared subtree still propagates
2092 outside of the namespace. Making a subtree private is
2093 local to the namespace, though, so setting MS_PRIVATE
2094 does not affect the outside world. */
2095 if (mount(0, "/", 0, MS_REC|MS_PRIVATE, 0) == -1) {
2096 throw SysError("unable to make ‘/’ private mount");
2097 }
2098
2099 /* Bind-mount chroot directory to itself, to treat it as a
2100 different filesystem from /, as needed for pivot_root. */
2101 if (mount(chrootRootDir.c_str(), chrootRootDir.c_str(), 0, MS_BIND, 0) == -1)
2102 throw SysError(format("unable to bind mount ‘%1%’") % chrootRootDir);
2103
2104 /* Set up a nearly empty /dev, unless the user asked to
2105 bind-mount the host /dev. */
2106 Strings ss;
2107 if (dirsInChroot.find("/dev") == dirsInChroot.end()) {
2108 createDirs(chrootRootDir + "/dev/shm");
2109 createDirs(chrootRootDir + "/dev/pts");
2110 ss.push_back("/dev/full");
2111 #ifdef __linux__
2112 if (pathExists("/dev/kvm"))
2113 ss.push_back("/dev/kvm");
2114 #endif
2115 ss.push_back("/dev/null");
2116 ss.push_back("/dev/random");
2117 ss.push_back("/dev/tty");
2118 ss.push_back("/dev/urandom");
2119 ss.push_back("/dev/zero");
2120 createSymlink("/proc/self/fd", chrootRootDir + "/dev/fd");
2121 createSymlink("/proc/self/fd/0", chrootRootDir + "/dev/stdin");
2122 createSymlink("/proc/self/fd/1", chrootRootDir + "/dev/stdout");
2123 createSymlink("/proc/self/fd/2", chrootRootDir + "/dev/stderr");
2124 }
2125
2126 /* Fixed-output derivations typically need to access the
2127 network, so give them access to /etc/resolv.conf and so
2128 on. */
2129 if (fixedOutput) {
2130 ss.push_back("/etc/resolv.conf");
2131 ss.push_back("/etc/nsswitch.conf");
2132 ss.push_back("/etc/services");
2133 ss.push_back("/etc/hosts");
2134 }
2135
2136 for (auto & i : ss) dirsInChroot[i] = i;
2137
2138 /* Bind-mount all the directories from the "host"
2139 filesystem that we want in the chroot
2140 environment. */
2141 foreach (DirsInChroot::iterator, i, dirsInChroot) {
2142 struct stat st;
2143 Path source = i->second;
2144 Path target = chrootRootDir + i->first;
2145 if (source == "/proc") continue; // backwards compatibility
2146 debug(format("bind mounting `%1%' to `%2%'") % source % target);
2147 if (stat(source.c_str(), &st) == -1)
2148 throw SysError(format("getting attributes of path `%1%'") % source);
2149 if (S_ISDIR(st.st_mode))
2150 createDirs(target);
2151 else {
2152 createDirs(dirOf(target));
2153 writeFile(target, "");
2154 }
2155 if (mount(source.c_str(), target.c_str(), "", MS_BIND, 0) == -1)
2156 throw SysError(format("bind mount from `%1%' to `%2%' failed") % source % target);
2157 }
2158
2159 /* Bind a new instance of procfs on /proc to reflect our
2160 private PID namespace. */
2161 createDirs(chrootRootDir + "/proc");
2162 if (mount("none", (chrootRootDir + "/proc").c_str(), "proc", 0, 0) == -1)
2163 throw SysError("mounting /proc");
2164
2165 /* Mount a new tmpfs on /dev/shm to ensure that whatever
2166 the builder puts in /dev/shm is cleaned up automatically. */
2167 if (pathExists("/dev/shm") && mount("none", (chrootRootDir + "/dev/shm").c_str(), "tmpfs", 0, 0) == -1)
2168 throw SysError("mounting /dev/shm");
2169
2170 /* Mount a new devpts on /dev/pts. Note that this
2171 requires the kernel to be compiled with
2172 CONFIG_DEVPTS_MULTIPLE_INSTANCES=y (which is the case
2173 if /dev/ptx/ptmx exists). */
2174 if (pathExists("/dev/pts/ptmx") &&
2175 !pathExists(chrootRootDir + "/dev/ptmx")
2176 && dirsInChroot.find("/dev/pts") == dirsInChroot.end())
2177 {
2178 if (mount("none", (chrootRootDir + "/dev/pts").c_str(), "devpts", 0, "newinstance,mode=0620") == -1)
2179 throw SysError("mounting /dev/pts");
2180 createSymlink("/dev/pts/ptmx", chrootRootDir + "/dev/ptmx");
2181
2182 /* Make sure /dev/pts/ptmx is world-writable. With some
2183 Linux versions, it is created with permissions 0. */
2184 chmod_(chrootRootDir + "/dev/pts/ptmx", 0666);
2185 }
2186
2187 /* Do the chroot(). */
2188 if (chdir(chrootRootDir.c_str()) == -1)
2189 throw SysError(format("cannot change directory to '%1%'") % chrootRootDir);
2190
2191 if (mkdir("real-root", 0) == -1)
2192 throw SysError("cannot create real-root directory");
2193
2194 if (pivot_root(".", "real-root") == -1)
2195 throw SysError(format("cannot pivot old root directory onto '%1%'") % (chrootRootDir + "/real-root"));
2196
2197 if (chroot(".") == -1)
2198 throw SysError(format("cannot change root directory to '%1%'") % chrootRootDir);
2199
2200 if (umount2("real-root", MNT_DETACH) == -1)
2201 throw SysError("cannot unmount real root filesystem");
2202
2203 if (rmdir("real-root") == -1)
2204 throw SysError("cannot remove real-root directory");
2205 }
2206 #endif
2207
2208 if (chdir(tmpDirInSandbox.c_str()) == -1)
2209 throw SysError(format("changing into `%1%'") % tmpDir);
2210
2211 /* Close all other file descriptors. */
2212 closeMostFDs(set<int>());
2213
2214 #if __linux__
2215 /* Change the personality to 32-bit if we're doing an
2216 i686-linux build on an x86_64-linux machine. */
2217 struct utsname utsbuf;
2218 uname(&utsbuf);
2219 if (drv.platform == "i686-linux" &&
2220 (settings.thisSystem == "x86_64-linux" ||
2221 (!strcmp(utsbuf.sysname, "Linux") && !strcmp(utsbuf.machine, "x86_64")))) {
2222 if (personality(PER_LINUX32) == -1)
2223 throw SysError("cannot set i686-linux personality");
2224 }
2225
2226 if (drv.platform == "armhf-linux" &&
2227 (settings.thisSystem == "aarch64-linux" ||
2228 (!strcmp(utsbuf.sysname, "Linux") && !strcmp(utsbuf.machine, "aarch64")))) {
2229 if (personality(PER_LINUX32) == -1)
2230 throw SysError("cannot set armhf-linux personality");
2231 }
2232
2233 /* Impersonate a Linux 2.6 machine to get some determinism in
2234 builds that depend on the kernel version. */
2235 if ((drv.platform == "i686-linux" || drv.platform == "x86_64-linux") && settings.impersonateLinux26) {
2236 int cur = personality(0xffffffff);
2237 if (cur != -1) personality(cur | 0x0020000 /* == UNAME26 */);
2238 }
2239
2240 /* Disable address space randomization for improved
2241 determinism. */
2242 int cur = personality(0xffffffff);
2243 if (cur != -1) personality(cur | ADDR_NO_RANDOMIZE);
2244 #endif
2245
2246 /* Fill in the environment. */
2247 Strings envStrs;
2248 foreach (Environment::const_iterator, i, env)
2249 envStrs.push_back(rewriteHashes(i->first + "=" + i->second, rewritesToTmp));
2250
2251 /* If we are running in `build-users' mode, then switch to the
2252 user we allocated above. Make sure that we drop all root
2253 privileges. Note that above we have closed all file
2254 descriptors except std*, so that's safe. Also note that
2255 setuid() when run as root sets the real, effective and
2256 saved UIDs. */
2257 if (buildUser.enabled()) {
2258 /* Preserve supplementary groups of the build user, to allow
2259 admins to specify groups such as "kvm". */
2260 if (setgroups(buildUser.getSupplementaryGIDs().size(),
2261 buildUser.getSupplementaryGIDs().data()) == -1)
2262 throw SysError("cannot set supplementary groups of build user");
2263
2264 if (setgid(buildUser.getGID()) == -1 ||
2265 getgid() != buildUser.getGID() ||
2266 getegid() != buildUser.getGID())
2267 throw SysError("setgid failed");
2268
2269 if (setuid(buildUser.getUID()) == -1 ||
2270 getuid() != buildUser.getUID() ||
2271 geteuid() != buildUser.getUID())
2272 throw SysError("setuid failed");
2273 }
2274
2275 restoreSIGPIPE();
2276
2277 /* Indicate that we managed to set up the build environment. */
2278 writeFull(STDERR_FILENO, "\n");
2279
2280 /* Execute the program. This should not return. */
2281 if (isBuiltin(drv)) {
2282 try {
2283 logType = ltFlat;
2284
2285 auto buildDrv = lookupBuiltinBuilder(drv.builder);
2286 if (buildDrv != NULL) {
2287 /* Check what the output file name is. When doing a
2288 'bmCheck' build, the output file name is different from
2289 that specified in DRV due to hash rewriting. */
2290 Path output = drv.outputs["out"].path;
2291 auto redirected = redirectedOutputs.find(output);
2292 if (redirected != redirectedOutputs.end())
2293 output = redirected->second;
2294
2295 buildDrv(drv, drvPath, output);
2296 }
2297 else
2298 throw Error(format("unsupported builtin function '%1%'") % string(drv.builder, 8));
2299 _exit(0);
2300 } catch (std::exception & e) {
2301 writeFull(STDERR_FILENO, "error: " + string(e.what()) + "\n");
2302 _exit(1);
2303 }
2304 }
2305
2306 /* Fill in the arguments. */
2307 Strings args;
2308 string builderBasename = baseNameOf(drv.builder);
2309 args.push_back(builderBasename);
2310 foreach (Strings::iterator, i, drv.args)
2311 args.push_back(rewriteHashes(*i, rewritesToTmp));
2312
2313 execve(drv.builder.c_str(), stringsToCharPtrs(args).data(), stringsToCharPtrs(envStrs).data());
2314
2315 int error = errno;
2316
2317 /* Right platform? Check this after we've tried 'execve' to allow for
2318 transparent emulation of different platforms with binfmt_misc
2319 handlers that invoke QEMU. */
2320 if (error == ENOEXEC && !canBuildLocally(drv.platform)) {
2321 if (settings.printBuildTrace)
2322 printMsg(lvlError, format("@ unsupported-platform %1% %2%") % drvPath % drv.platform);
2323 throw Error(
2324 format("a `%1%' is required to build `%3%', but I am a `%2%'")
2325 % drv.platform % settings.thisSystem % drvPath);
2326 }
2327
2328 errno = error;
2329 throw SysError(format("executing `%1%'") % drv.builder);
2330
2331 } catch (std::exception & e) {
2332 writeFull(STDERR_FILENO, "while setting up the build environment: " + string(e.what()) + "\n");
2333 _exit(1);
2334 }
2335
2336 abort(); /* never reached */
2337 }
2338
2339
2340 /* Parse a list of reference specifiers. Each element must either be
2341 a store path, or the symbolic name of the output of the derivation
2342 (such as `out'). */
2343 PathSet parseReferenceSpecifiers(const Derivation & drv, string attr)
2344 {
2345 PathSet result;
2346 Paths paths = tokenizeString<Paths>(attr);
2347 foreach (Strings::iterator, i, paths) {
2348 if (isStorePath(*i))
2349 result.insert(*i);
2350 else if (drv.outputs.find(*i) != drv.outputs.end())
2351 result.insert(drv.outputs.find(*i)->second.path);
2352 else throw BuildError(
2353 format("derivation contains an invalid reference specifier `%1%'")
2354 % *i);
2355 }
2356 return result;
2357 }
2358
2359
2360 void DerivationGoal::registerOutputs()
2361 {
2362 /* When using a build hook, the build hook can register the output
2363 as valid (by doing `nix-store --import'). If so we don't have
2364 to do anything here. */
2365 if (hook) {
2366 bool allValid = true;
2367 foreach (DerivationOutputs::iterator, i, drv.outputs)
2368 if (!worker.store.isValidPath(i->second.path)) allValid = false;
2369 if (allValid) return;
2370 }
2371
2372 ValidPathInfos infos;
2373
2374 /* Set of inodes seen during calls to canonicalisePathMetaData()
2375 for this build's outputs. This needs to be shared between
2376 outputs to allow hard links between outputs. */
2377 InodesSeen inodesSeen;
2378
2379 Path checkSuffix = "-check";
2380
2381 /* Check whether the output paths were created, and grep each
2382 output path to determine what other paths it references. Also make all
2383 output paths read-only. */
2384 foreach (DerivationOutputs::iterator, i, drv.outputs) {
2385 Path path = i->second.path;
2386 if (missingPaths.find(path) == missingPaths.end()) continue;
2387
2388 Path actualPath = path;
2389 if (useChroot) {
2390 actualPath = chrootRootDir + path;
2391 if (pathExists(actualPath)) {
2392 /* Move output paths from the chroot to the store. */
2393 if (buildMode == bmRepair)
2394 replaceValidPath(path, actualPath);
2395 else
2396 if (buildMode != bmCheck && rename(actualPath.c_str(), path.c_str()) == -1)
2397 throw SysError(format("moving build output `%1%' from the chroot to the store") % path);
2398 }
2399 if (buildMode != bmCheck) actualPath = path;
2400 } else {
2401 Path redirected = redirectedOutputs[path];
2402 if (buildMode == bmRepair
2403 && redirectedBadOutputs.find(path) != redirectedBadOutputs.end()
2404 && pathExists(redirected))
2405 replaceValidPath(path, redirected);
2406 if (buildMode == bmCheck && redirected != "")
2407 actualPath = redirected;
2408 }
2409
2410 struct stat st;
2411 if (lstat(actualPath.c_str(), &st) == -1) {
2412 if (errno == ENOENT)
2413 throw BuildError(
2414 format("builder for `%1%' failed to produce output path `%2%'")
2415 % drvPath % path);
2416 throw SysError(format("getting attributes of path `%1%'") % actualPath);
2417 }
2418
2419 #ifndef __CYGWIN__
2420 /* Check that the output is not group or world writable, as
2421 that means that someone else can have interfered with the
2422 build. Also, the output should be owned by the build
2423 user. */
2424 if ((!S_ISLNK(st.st_mode) && (st.st_mode & (S_IWGRP | S_IWOTH))) ||
2425 (buildUser.enabled() && st.st_uid != buildUser.getUID()))
2426 throw BuildError(format("suspicious ownership or permission on `%1%'; rejecting this build output") % path);
2427 #endif
2428
2429 /* Apply hash rewriting if necessary. */
2430 bool rewritten = false;
2431 if (!rewritesFromTmp.empty()) {
2432 printMsg(lvlError, format("warning: rewriting hashes in `%1%'; cross fingers") % path);
2433
2434 /* Canonicalise first. This ensures that the path we're
2435 rewriting doesn't contain a hard link to /etc/shadow or
2436 something like that. */
2437 canonicalisePathMetaData(actualPath, buildUser.enabled() ? buildUser.getUID() : -1, inodesSeen);
2438
2439 /* FIXME: this is in-memory. */
2440 StringSink sink;
2441 dumpPath(actualPath, sink);
2442 deletePath(actualPath);
2443 sink.s = rewriteHashes(sink.s, rewritesFromTmp);
2444 StringSource source(sink.s);
2445 restorePath(actualPath, source);
2446
2447 rewritten = true;
2448 }
2449
2450 startNest(nest, lvlTalkative,
2451 format("scanning for references inside `%1%'") % path);
2452
2453 /* Check that fixed-output derivations produced the right
2454 outputs (i.e., the content hash should match the specified
2455 hash). */
2456 if (i->second.hash != "") {
2457
2458 bool recursive; HashType ht; Hash h;
2459 i->second.parseHashInfo(recursive, ht, h);
2460
2461 if (!recursive) {
2462 /* The output path should be a regular file without
2463 execute permission. */
2464 if (!S_ISREG(st.st_mode) || (st.st_mode & S_IXUSR) != 0)
2465 throw BuildError(
2466 format("output path `%1% should be a non-executable regular file") % path);
2467 }
2468
2469 /* Check the hash. */
2470 Hash h2 = recursive ? hashPath(ht, actualPath).first : hashFile(ht, actualPath);
2471 if (h != h2) {
2472 if (settings.printBuildTrace)
2473 printMsg(lvlError, format("@ hash-mismatch %1% %2% %3% %4%")
2474 % path % i->second.hashAlgo
2475 % printHash16or32(h) % printHash16or32(h2));
2476 throw BuildError(format("hash mismatch for store item '%1%'") % path);
2477 }
2478 }
2479
2480 /* Get rid of all weird permissions. This also checks that
2481 all files are owned by the build user, if applicable. */
2482 canonicalisePathMetaData(actualPath,
2483 buildUser.enabled() && !rewritten ? buildUser.getUID() : -1, inodesSeen);
2484
2485 /* For this output path, find the references to other paths
2486 contained in it. Compute the SHA-256 NAR hash at the same
2487 time. The hash is stored in the database so that we can
2488 verify later on whether nobody has messed with the store. */
2489 HashResult hash;
2490 PathSet references = scanForReferences(actualPath, allPaths, hash);
2491
2492 if (buildMode == bmCheck) {
2493 if (!store->isValidPath(path)) continue;
2494 ValidPathInfo info = worker.store.queryPathInfo(path);
2495 if (hash.first != info.hash) {
2496 if (settings.keepFailed) {
2497 Path dst = path + checkSuffix;
2498 if (pathExists(dst)) deletePath(dst);
2499 if (rename(actualPath.c_str(), dst.c_str()))
2500 throw SysError(format("renaming `%1%' to `%2%'") % actualPath % dst);
2501 throw Error(format("derivation `%1%' may not be deterministic: output `%2%' differs from ‘%3%’")
2502 % drvPath % path % dst);
2503 } else
2504 throw Error(format("derivation `%1%' may not be deterministic: output `%2%' differs")
2505 % drvPath % path);
2506 }
2507
2508 if (settings.printBuildTrace)
2509 printMsg(lvlError, format("@ build-succeeded %1% -") % drvPath);
2510
2511 continue;
2512 }
2513
2514 /* For debugging, print out the referenced and unreferenced
2515 paths. */
2516 foreach (PathSet::iterator, i, inputPaths) {
2517 PathSet::iterator j = references.find(*i);
2518 if (j == references.end())
2519 debug(format("unreferenced input: `%1%'") % *i);
2520 else
2521 debug(format("referenced input: `%1%'") % *i);
2522 }
2523
2524 /* Enforce `allowedReferences' and friends. */
2525 auto checkRefs = [&](const string & attrName, bool allowed, bool recursive) {
2526 if (drv.env.find(attrName) == drv.env.end()) return;
2527
2528 PathSet spec = parseReferenceSpecifiers(drv, get(drv.env, attrName));
2529
2530 PathSet used;
2531 if (recursive) {
2532 /* Our requisites are the union of the closures of our references. */
2533 for (auto & i : references)
2534 /* Don't call computeFSClosure on ourselves. */
2535 if (actualPath != i)
2536 computeFSClosure(worker.store, i, used);
2537 } else
2538 used = references;
2539
2540 for (auto & i : used)
2541 if (allowed) {
2542 if (spec.find(i) == spec.end())
2543 throw BuildError(format("output (`%1%') is not allowed to refer to path `%2%'") % actualPath % i);
2544 } else {
2545 if (spec.find(i) != spec.end())
2546 throw BuildError(format("output (`%1%') is not allowed to refer to path `%2%'") % actualPath % i);
2547 }
2548 };
2549
2550 checkRefs("allowedReferences", true, false);
2551 checkRefs("allowedRequisites", true, true);
2552 checkRefs("disallowedReferences", false, false);
2553 checkRefs("disallowedRequisites", false, true);
2554
2555 if (curRound == nrRounds) {
2556 worker.store.optimisePath(path); // FIXME: combine with scanForReferences()
2557
2558 worker.store.markContentsGood(path);
2559 }
2560
2561 ValidPathInfo info;
2562 info.path = path;
2563 info.hash = hash.first;
2564 info.narSize = hash.second;
2565 info.references = references;
2566 info.deriver = drvPath;
2567 infos.push_back(info);
2568 }
2569
2570 if (buildMode == bmCheck) return;
2571
2572 /* Compare the result with the previous round, and report which
2573 path is different, if any.*/
2574 if (curRound > 1 && prevInfos != infos) {
2575 assert(prevInfos.size() == infos.size());
2576 for (auto i = prevInfos.begin(), j = infos.begin(); i != prevInfos.end(); ++i, ++j)
2577 if (!(*i == *j)) {
2578 Path prev = i->path + checkSuffix;
2579 if (pathExists(prev))
2580 throw NotDeterministic(
2581 format("output ‘%1%’ of ‘%2%’ differs from ‘%3%’ from previous round")
2582 % i->path % drvPath % prev);
2583 else
2584 throw NotDeterministic(
2585 format("output ‘%1%’ of ‘%2%’ differs from previous round")
2586 % i->path % drvPath);
2587 }
2588 assert(false); // shouldn't happen
2589 }
2590
2591 if (settings.keepFailed) {
2592 for (auto & i : drv.outputs) {
2593 Path prev = i.second.path + checkSuffix;
2594 if (pathExists(prev)) deletePath(prev);
2595 if (curRound < nrRounds) {
2596 Path dst = i.second.path + checkSuffix;
2597 if (rename(i.second.path.c_str(), dst.c_str()))
2598 throw SysError(format("renaming ‘%1%’ to ‘%2%’") % i.second.path % dst);
2599 }
2600 }
2601
2602 }
2603
2604 if (curRound < nrRounds) {
2605 prevInfos = infos;
2606 return;
2607 }
2608
2609 /* Register each output path as valid, and register the sets of
2610 paths referenced by each of them. If there are cycles in the
2611 outputs, this will fail. */
2612 worker.store.registerValidPaths(infos);
2613 }
2614
2615
2616 string drvsLogDir = "drvs";
2617
2618
2619 Path DerivationGoal::openLogFile()
2620 {
2621 logSize = 0;
2622
2623 if (!settings.keepLog) return "";
2624
2625 string baseName = baseNameOf(drvPath);
2626
2627 /* Create a log file. */
2628 Path dir = (format("%1%/%2%/%3%/") % settings.nixLogDir % drvsLogDir % string(baseName, 0, 2)).str();
2629 createDirs(dir);
2630
2631 switch (settings.logCompression)
2632 {
2633 case COMPRESSION_GZIP: {
2634 Path logFileName = (format("%1%/%2%.gz") % dir % string(baseName, 2)).str();
2635 AutoCloseFD fd = open(logFileName.c_str(), O_CREAT | O_WRONLY | O_TRUNC, 0666);
2636 if (fd == -1) throw SysError(format("creating log file `%1%'") % logFileName);
2637 closeOnExec(fd);
2638
2639 /* Note: FD will be closed by 'gzclose'. */
2640 if (!(gzLogFile = gzdopen(fd.borrow(), "w")))
2641 throw Error(format("cannot open compressed log file `%1%'") % logFileName);
2642
2643 gzbuffer(gzLogFile, 32768);
2644 gzsetparams(gzLogFile, Z_BEST_COMPRESSION, Z_DEFAULT_STRATEGY);
2645
2646 return logFileName;
2647 }
2648
2649 #if HAVE_BZLIB_H
2650 case COMPRESSION_BZIP2: {
2651 Path logFileName = (format("%1%/%2%.bz2") % dir % string(baseName, 2)).str();
2652 AutoCloseFD fd = open(logFileName.c_str(), O_CREAT | O_WRONLY | O_TRUNC, 0666);
2653 if (fd == -1) throw SysError(format("creating log file `%1%'") % logFileName);
2654 closeOnExec(fd);
2655
2656 if (!(fLogFile = fdopen(fd.borrow(), "w")))
2657 throw SysError(format("opening file `%1%'") % logFileName);
2658
2659 int err;
2660 if (!(bzLogFile = BZ2_bzWriteOpen(&err, fLogFile, 9, 0, 0)))
2661 throw Error(format("cannot open compressed log file `%1%'") % logFileName);
2662
2663 return logFileName;
2664 }
2665 #endif
2666
2667 case COMPRESSION_NONE: {
2668 Path logFileName = (format("%1%/%2%") % dir % string(baseName, 2)).str();
2669 fdLogFile = open(logFileName.c_str(), O_CREAT | O_WRONLY | O_TRUNC, 0666);
2670 if (fdLogFile == -1) throw SysError(format("creating log file `%1%'") % logFileName);
2671 closeOnExec(fdLogFile);
2672 return logFileName;
2673 }
2674 }
2675
2676 abort();
2677 }
2678
2679
2680 void DerivationGoal::closeLogFile()
2681 {
2682 if (gzLogFile) {
2683 int err;
2684 err = gzclose(gzLogFile);
2685 gzLogFile = NULL;
2686 if (err != Z_OK) throw Error(format("cannot close compressed log file (gzip error = %1%)") % err);
2687 }
2688 #if HAVE_BZLIB_H
2689 else if (bzLogFile) {
2690 int err;
2691 BZ2_bzWriteClose(&err, bzLogFile, 0, 0, 0);
2692 bzLogFile = 0;
2693 if (err != BZ_OK) throw Error(format("cannot close compressed log file (BZip2 error = %1%)") % err);
2694 }
2695 #endif
2696
2697 if (fLogFile) {
2698 fclose(fLogFile);
2699 fLogFile = 0;
2700 }
2701
2702 fdLogFile.close();
2703 }
2704
2705
2706 static void _chown(const Path & path, uid_t uid, gid_t gid)
2707 {
2708 checkInterrupt();
2709
2710 if (lchown(path.c_str(), uid, gid) == -1) {
2711 throw SysError(format("change owner and group of `%1%'") % path);
2712 }
2713 struct stat st = lstat(path);
2714 if (S_ISDIR(st.st_mode)) {
2715 for (auto & i : readDirectory(path))
2716 _chown(path + "/" + i.name, uid, gid);
2717 }
2718 }
2719
2720
2721 void DerivationGoal::deleteTmpDir(bool force)
2722 {
2723 if (tmpDir != "") {
2724 if (settings.keepFailed && !force) {
2725 printMsg(lvlError,
2726 format("note: keeping build directory `%2%'")
2727 % drvPath % tmpDir);
2728 chmod(tmpDir.c_str(), 0755);
2729 // Change the ownership if clientUid is set. Never change the
2730 // ownership or the group to "root" for security reasons.
2731 if (settings.clientUid != (uid_t) -1 && settings.clientUid != 0) {
2732 _chown(tmpDir, settings.clientUid,
2733 settings.clientGid != 0 ? settings.clientGid : -1);
2734 }
2735 }
2736 else
2737 deletePath(tmpDir);
2738 tmpDir = "";
2739 }
2740 }
2741
2742
2743 void DerivationGoal::handleChildOutput(int fd, const string & data)
2744 {
2745 string prefix;
2746
2747 if (settings.multiplexedBuildOutput) {
2748 /* Print a prefix that allows clients to determine whether a message
2749 comes from the daemon or from a build process, and in the latter
2750 case, which build process it comes from. The PID here matches the
2751 one given in "@ build-started" traces; it's shorter that the
2752 derivation file name, hence this choice. */
2753 prefix = "@ build-log "
2754 + std::to_string(pid < 0 ? hook->pid : pid)
2755 + " " + std::to_string(data.size()) + "\n";
2756 }
2757
2758 if ((hook && fd == hook->builderOut.readSide) ||
2759 (!hook && fd == builderOut.readSide))
2760 {
2761 logSize += data.size();
2762 if (settings.maxLogSize && logSize > settings.maxLogSize) {
2763 printMsg(lvlError,
2764 format("%1% killed after writing more than %2% bytes of log output")
2765 % getName() % settings.maxLogSize);
2766 timedOut(); // not really a timeout, but close enough
2767 return;
2768 }
2769 if (verbosity >= settings.buildVerbosity)
2770 writeToStderr(prefix + data);
2771
2772 if (gzLogFile) {
2773 if (data.size() > 0) {
2774 int count, err;
2775 count = gzwrite(gzLogFile, data.data(), data.size());
2776 if (count == 0) throw Error(format("cannot write to compressed log file (gzip error = %1%)") % gzerror(gzLogFile, &err));
2777 }
2778 #if HAVE_BZLIB_H
2779 } else if (bzLogFile) {
2780 int err;
2781 BZ2_bzWrite(&err, bzLogFile, (unsigned char *) data.data(), data.size());
2782 if (err != BZ_OK) throw Error(format("cannot write to compressed log file (BZip2 error = %1%)") % err);
2783 #endif
2784 } else if (fdLogFile != -1)
2785 writeFull(fdLogFile, data);
2786 }
2787
2788 if (hook && fd == hook->fromHook.readSide)
2789 writeToStderr(prefix + data);
2790 }
2791
2792
2793 void DerivationGoal::handleEOF(int fd)
2794 {
2795 worker.wakeUp(shared_from_this());
2796 }
2797
2798
2799 PathSet DerivationGoal::checkPathValidity(bool returnValid, bool checkHash)
2800 {
2801 PathSet result;
2802 foreach (DerivationOutputs::iterator, i, drv.outputs) {
2803 if (!wantOutput(i->first, wantedOutputs)) continue;
2804 bool good =
2805 worker.store.isValidPath(i->second.path) &&
2806 (!checkHash || worker.store.pathContentsGood(i->second.path));
2807 if (good == returnValid) result.insert(i->second.path);
2808 }
2809 return result;
2810 }
2811
2812
2813 bool DerivationGoal::pathFailed(const Path & path)
2814 {
2815 if (!settings.cacheFailure) return false;
2816
2817 if (!worker.store.hasPathFailed(path)) return false;
2818
2819 printMsg(lvlError, format("builder for `%1%' failed previously (cached)") % path);
2820
2821 if (settings.printBuildTrace)
2822 printMsg(lvlError, format("@ build-failed %1% - cached") % drvPath);
2823
2824 done(BuildResult::CachedFailure);
2825
2826 return true;
2827 }
2828
2829
2830 Path DerivationGoal::addHashRewrite(const Path & path)
2831 {
2832 string h1 = string(path, settings.nixStore.size() + 1, 32);
2833 string h2 = string(printHash32(hashString(htSHA256, "rewrite:" + drvPath + ":" + path)), 0, 32);
2834 Path p = settings.nixStore + "/" + h2 + string(path, settings.nixStore.size() + 33);
2835 if (pathExists(p)) deletePath(p);
2836 assert(path.size() == p.size());
2837 rewritesToTmp[h1] = h2;
2838 rewritesFromTmp[h2] = h1;
2839 redirectedOutputs[path] = p;
2840 printMsg(lvlChatty, format("output '%1%' redirected to '%2%'")
2841 % path % p);
2842 return p;
2843 }
2844
2845
2846 void DerivationGoal::done(BuildResult::Status status, const string & msg)
2847 {
2848 result.status = status;
2849 result.errorMsg = msg;
2850 amDone(result.success() ? ecSuccess : ecFailed);
2851 if (result.status == BuildResult::TimedOut)
2852 worker.timedOut = true;
2853 if (result.status == BuildResult::PermanentFailure || result.status == BuildResult::CachedFailure)
2854 worker.permanentFailure = true;
2855 }
2856
2857
2858 //////////////////////////////////////////////////////////////////////
2859
2860
2861 class SubstitutionGoal : public Goal
2862 {
2863 friend class Worker;
2864
2865 private:
2866 /* The store path that should be realised through a substitute. */
2867 Path storePath;
2868
2869 /* Path info returned by the substituter's query info operation. */
2870 SubstitutablePathInfo info;
2871
2872 /* Pipe for the substituter's standard output. */
2873 Pipe outPipe;
2874
2875 /* Pipe for the substituter's standard error. */
2876 Pipe logPipe;
2877
2878 /* The process ID of the builder. */
2879 Pid pid;
2880
2881 /* Lock on the store path. */
2882 std::shared_ptr<PathLocks> outputLock;
2883
2884 /* Whether to try to repair a valid path. */
2885 bool repair;
2886
2887 /* Location where we're downloading the substitute. Differs from
2888 storePath when doing a repair. */
2889 Path destPath;
2890
2891 typedef void (SubstitutionGoal::*GoalState)();
2892 GoalState state;
2893
2894 void tryNext();
2895
2896 public:
2897 SubstitutionGoal(const Path & storePath, Worker & worker, bool repair = false);
2898 ~SubstitutionGoal();
2899
2900 void timedOut();
2901
2902 string key()
2903 {
2904 /* "a$" ensures substitution goals happen before derivation
2905 goals. */
2906 return "a$" + storePathToName(storePath) + "$" + storePath;
2907 }
2908
2909 void work();
2910
2911 /* The states. */
2912 void init();
2913 void gotInfo();
2914 void referencesValid();
2915 void tryToRun();
2916 void finished();
2917
2918 /* Callback used by the worker to write to the log. */
2919 void handleChildOutput(int fd, const string & data);
2920 void handleEOF(int fd);
2921
2922 Path getStorePath() { return storePath; }
2923 };
2924
2925
2926 SubstitutionGoal::SubstitutionGoal(const Path & storePath, Worker & worker, bool repair)
2927 : Goal(worker)
2928 , repair(repair)
2929 {
2930 this->storePath = storePath;
2931 state = &SubstitutionGoal::init;
2932 name = (format("substitution of `%1%'") % storePath).str();
2933 trace("created");
2934 }
2935
2936
2937 SubstitutionGoal::~SubstitutionGoal()
2938 {
2939 if (pid != -1) worker.childTerminated(pid);
2940 }
2941
2942
2943 void SubstitutionGoal::timedOut()
2944 {
2945 if (settings.printBuildTrace)
2946 printMsg(lvlError, format("@ substituter-failed %1% timeout") % storePath);
2947 if (pid != -1) {
2948 pid_t savedPid = pid;
2949 pid.kill();
2950 worker.childTerminated(savedPid);
2951 }
2952 amDone(ecFailed);
2953 }
2954
2955
2956 void SubstitutionGoal::work()
2957 {
2958 (this->*state)();
2959 }
2960
2961
2962 void SubstitutionGoal::init()
2963 {
2964 trace("init");
2965
2966 worker.store.addTempRoot(storePath);
2967
2968 /* If the path already exists we're done. */
2969 if (!repair && worker.store.isValidPath(storePath)) {
2970 amDone(ecSuccess);
2971 return;
2972 }
2973
2974 if (settings.readOnlyMode)
2975 throw Error(format("cannot substitute path `%1%' - no write access to the store") % storePath);
2976
2977 tryNext();
2978 }
2979
2980
2981 void SubstitutionGoal::tryNext()
2982 {
2983 trace("trying substituter");
2984
2985 SubstitutablePathInfos infos;
2986 PathSet dummy(singleton<PathSet>(storePath));
2987 worker.store.querySubstitutablePathInfos(dummy, infos);
2988 SubstitutablePathInfos::iterator k = infos.find(storePath);
2989 if (k == infos.end()) {
2990 /* None left. Terminate this goal and let someone else deal
2991 with it. */
2992 debug(format("path `%1%' is required, but there is no substituter that can build it") % storePath);
2993 /* Hack: don't indicate failure if there were no substituters.
2994 In that case the calling derivation should just do a
2995 build. */
2996 amDone(ecNoSubstituters);
2997 return;
2998 }
2999
3000 /* Found a substitute. */
3001 info = k->second;
3002
3003 /* To maintain the closure invariant, we first have to realise the
3004 paths referenced by this one. */
3005 foreach (PathSet::iterator, i, info.references)
3006 if (*i != storePath) /* ignore self-references */
3007 addWaitee(worker.makeSubstitutionGoal(*i));
3008
3009 if (waitees.empty()) /* to prevent hang (no wake-up event) */
3010 referencesValid();
3011 else
3012 state = &SubstitutionGoal::referencesValid;
3013 }
3014
3015
3016 void SubstitutionGoal::referencesValid()
3017 {
3018 trace("all references realised");
3019
3020 if (nrFailed > 0) {
3021 debug(format("some references of path `%1%' could not be realised") % storePath);
3022 amDone(nrNoSubstituters > 0 || nrIncompleteClosure > 0 ? ecIncompleteClosure : ecFailed);
3023 return;
3024 }
3025
3026 foreach (PathSet::iterator, i, info.references)
3027 if (*i != storePath) /* ignore self-references */
3028 assert(worker.store.isValidPath(*i));
3029
3030 state = &SubstitutionGoal::tryToRun;
3031 worker.wakeUp(shared_from_this());
3032 }
3033
3034
3035 void SubstitutionGoal::tryToRun()
3036 {
3037 trace("trying to run");
3038
3039 /* Make sure that we are allowed to start a build. Note that even
3040 is maxBuildJobs == 0 (no local builds allowed), we still allow
3041 a substituter to run. This is because substitutions cannot be
3042 distributed to another machine via the build hook. */
3043 if (worker.getNrLocalBuilds() >= (settings.maxBuildJobs == 0 ? 1 : settings.maxBuildJobs)) {
3044 worker.waitForBuildSlot(shared_from_this());
3045 return;
3046 }
3047
3048 /* Maybe a derivation goal has already locked this path
3049 (exceedingly unlikely, since it should have used a substitute
3050 first, but let's be defensive). */
3051 outputLock.reset(); // make sure this goal's lock is gone
3052 if (pathIsLockedByMe(storePath)) {
3053 debug(format("restarting substitution of `%1%' because it's locked by another goal")
3054 % storePath);
3055 worker.waitForAnyGoal(shared_from_this());
3056 return; /* restart in the tryToRun() state when another goal finishes */
3057 }
3058
3059 /* Acquire a lock on the output path. */
3060 outputLock = std::shared_ptr<PathLocks>(new PathLocks);
3061 if (!outputLock->lockPaths(singleton<PathSet>(storePath), "", false)) {
3062 worker.waitForAWhile(shared_from_this());
3063 return;
3064 }
3065
3066 /* Check again whether the path is invalid. */
3067 if (!repair && worker.store.isValidPath(storePath)) {
3068 debug(format("store path `%1%' has become valid") % storePath);
3069 outputLock->setDeletion(true);
3070 amDone(ecSuccess);
3071 return;
3072 }
3073
3074 printMsg(lvlInfo, format("fetching path `%1%'...") % storePath);
3075
3076 outPipe.create();
3077 logPipe.create();
3078
3079 destPath = repair ? storePath + ".tmp" : storePath;
3080
3081 /* Remove the (stale) output path if it exists. */
3082 if (pathExists(destPath))
3083 deletePath(destPath);
3084
3085 worker.store.setSubstituterEnv();
3086
3087 /* Fill in the arguments. */
3088 Strings args;
3089 args.push_back("guix");
3090 args.push_back("substitute");
3091 args.push_back("--substitute");
3092 args.push_back(storePath);
3093 args.push_back(destPath);
3094
3095 /* Fork the substitute program. */
3096 pid = startProcess([&]() {
3097
3098 commonChildInit(logPipe);
3099
3100 if (dup2(outPipe.writeSide, STDOUT_FILENO) == -1)
3101 throw SysError("cannot dup output pipe into stdout");
3102
3103 execv(settings.guixProgram.c_str(), stringsToCharPtrs(args).data());
3104
3105 throw SysError(format("executing `%1% substitute'") % settings.guixProgram);
3106 });
3107
3108 pid.setSeparatePG(true);
3109 pid.setKillSignal(SIGTERM);
3110 outPipe.writeSide.close();
3111 logPipe.writeSide.close();
3112 worker.childStarted(shared_from_this(),
3113 pid, singleton<set<int> >(logPipe.readSide), true, true);
3114
3115 state = &SubstitutionGoal::finished;
3116
3117 if (settings.printBuildTrace)
3118 /* The second element in the message used to be the name of the
3119 substituter but we're left with only one. */
3120 printMsg(lvlError, format("@ substituter-started %1% substitute") % storePath);
3121 }
3122
3123
3124 void SubstitutionGoal::finished()
3125 {
3126 trace("substitute finished");
3127
3128 /* Since we got an EOF on the logger pipe, the substitute is
3129 presumed to have terminated. */
3130 pid_t savedPid = pid;
3131 int status = pid.wait(true);
3132
3133 /* So the child is gone now. */
3134 worker.childTerminated(savedPid);
3135
3136 /* Close the read side of the logger pipe. */
3137 logPipe.readSide.close();
3138
3139 /* Get the hash info from stdout. */
3140 string dummy = readLine(outPipe.readSide);
3141 string expectedHashStr = statusOk(status) ? readLine(outPipe.readSide) : "";
3142 outPipe.readSide.close();
3143
3144 /* Check the exit status and the build result. */
3145 HashResult hash;
3146 try {
3147
3148 if (!statusOk(status))
3149 throw SubstError(format("fetching path `%1%' %2%")
3150 % storePath % statusToString(status));
3151
3152 if (!pathExists(destPath))
3153 throw SubstError(format("substitute did not produce path `%1%'") % destPath);
3154
3155 hash = hashPath(htSHA256, destPath);
3156
3157 /* Verify the expected hash we got from the substituer. */
3158 if (expectedHashStr != "") {
3159 size_t n = expectedHashStr.find(':');
3160 if (n == string::npos)
3161 throw Error(format("bad hash from substituter: %1%") % expectedHashStr);
3162 HashType hashType = parseHashType(string(expectedHashStr, 0, n));
3163 if (hashType == htUnknown)
3164 throw Error(format("unknown hash algorithm in `%1%'") % expectedHashStr);
3165 Hash expectedHash = parseHash16or32(hashType, string(expectedHashStr, n + 1));
3166 Hash actualHash = hashType == htSHA256 ? hash.first : hashPath(hashType, destPath).first;
3167 if (expectedHash != actualHash) {
3168 if (settings.printBuildTrace)
3169 printMsg(lvlError, format("@ hash-mismatch %1% %2% %3% %4%")
3170 % storePath % "sha256"
3171 % printHash16or32(expectedHash)
3172 % printHash16or32(actualHash));
3173 throw SubstError(format("hash mismatch for substituted item `%1%'") % storePath);
3174 }
3175 }
3176
3177 } catch (SubstError & e) {
3178
3179 printMsg(lvlInfo, e.msg());
3180
3181 if (settings.printBuildTrace) {
3182 printMsg(lvlError, format("@ substituter-failed %1% %2% %3%")
3183 % storePath % status % e.msg());
3184 }
3185
3186 amDone(ecFailed);
3187 return;
3188 }
3189
3190 if (repair) replaceValidPath(storePath, destPath);
3191
3192 canonicalisePathMetaData(storePath, -1);
3193
3194 worker.store.optimisePath(storePath); // FIXME: combine with hashPath()
3195
3196 ValidPathInfo info2;
3197 info2.path = storePath;
3198 info2.hash = hash.first;
3199 info2.narSize = hash.second;
3200 info2.references = info.references;
3201 info2.deriver = info.deriver;
3202 worker.store.registerValidPath(info2);
3203
3204 outputLock->setDeletion(true);
3205 outputLock.reset();
3206
3207 worker.store.markContentsGood(storePath);
3208
3209 printMsg(lvlChatty,
3210 format("substitution of path `%1%' succeeded") % storePath);
3211
3212 if (settings.printBuildTrace)
3213 printMsg(lvlError, format("@ substituter-succeeded %1%") % storePath);
3214
3215 amDone(ecSuccess);
3216 }
3217
3218
3219 void SubstitutionGoal::handleChildOutput(int fd, const string & data)
3220 {
3221 assert(fd == logPipe.readSide);
3222 if (verbosity >= settings.buildVerbosity) writeToStderr(data);
3223 /* Don't write substitution output to a log file for now. We
3224 probably should, though. */
3225 }
3226
3227
3228 void SubstitutionGoal::handleEOF(int fd)
3229 {
3230 if (fd == logPipe.readSide) worker.wakeUp(shared_from_this());
3231 }
3232
3233
3234
3235 //////////////////////////////////////////////////////////////////////
3236
3237
3238 static bool working = false;
3239
3240
3241 Worker::Worker(LocalStore & store)
3242 : store(store)
3243 {
3244 /* Debugging: prevent recursive workers. */
3245 if (working) abort();
3246 working = true;
3247 nrLocalBuilds = 0;
3248 lastWokenUp = 0;
3249 permanentFailure = false;
3250 timedOut = false;
3251 }
3252
3253
3254 Worker::~Worker()
3255 {
3256 working = false;
3257
3258 /* Explicitly get rid of all strong pointers now. After this all
3259 goals that refer to this worker should be gone. (Otherwise we
3260 are in trouble, since goals may call childTerminated() etc. in
3261 their destructors). */
3262 topGoals.clear();
3263 }
3264
3265
3266 GoalPtr Worker::makeDerivationGoal(const Path & path,
3267 const StringSet & wantedOutputs, BuildMode buildMode)
3268 {
3269 GoalPtr goal = derivationGoals[path].lock();
3270 if (!goal) {
3271 goal = GoalPtr(new DerivationGoal(path, wantedOutputs, *this, buildMode));
3272 derivationGoals[path] = goal;
3273 wakeUp(goal);
3274 } else
3275 (dynamic_cast<DerivationGoal *>(goal.get()))->addWantedOutputs(wantedOutputs);
3276 return goal;
3277 }
3278
3279
3280 GoalPtr Worker::makeSubstitutionGoal(const Path & path, bool repair)
3281 {
3282 GoalPtr goal = substitutionGoals[path].lock();
3283 if (!goal) {
3284 goal = GoalPtr(new SubstitutionGoal(path, *this, repair));
3285 substitutionGoals[path] = goal;
3286 wakeUp(goal);
3287 }
3288 return goal;
3289 }
3290
3291
3292 static void removeGoal(GoalPtr goal, WeakGoalMap & goalMap)
3293 {
3294 /* !!! inefficient */
3295 for (WeakGoalMap::iterator i = goalMap.begin();
3296 i != goalMap.end(); )
3297 if (i->second.lock() == goal) {
3298 WeakGoalMap::iterator j = i; ++j;
3299 goalMap.erase(i);
3300 i = j;
3301 }
3302 else ++i;
3303 }
3304
3305
3306 void Worker::removeGoal(GoalPtr goal)
3307 {
3308 nix::removeGoal(goal, derivationGoals);
3309 nix::removeGoal(goal, substitutionGoals);
3310 if (topGoals.find(goal) != topGoals.end()) {
3311 topGoals.erase(goal);
3312 /* If a top-level goal failed, then kill all other goals
3313 (unless keepGoing was set). */
3314 if (goal->getExitCode() == Goal::ecFailed && !settings.keepGoing)
3315 topGoals.clear();
3316 }
3317
3318 /* Wake up goals waiting for any goal to finish. */
3319 foreach (WeakGoals::iterator, i, waitingForAnyGoal) {
3320 GoalPtr goal = i->lock();
3321 if (goal) wakeUp(goal);
3322 }
3323
3324 waitingForAnyGoal.clear();
3325 }
3326
3327
3328 void Worker::wakeUp(GoalPtr goal)
3329 {
3330 goal->trace("woken up");
3331 addToWeakGoals(awake, goal);
3332 }
3333
3334
3335 unsigned Worker::getNrLocalBuilds()
3336 {
3337 return nrLocalBuilds;
3338 }
3339
3340
3341 void Worker::childStarted(GoalPtr goal,
3342 pid_t pid, const set<int> & fds, bool inBuildSlot,
3343 bool respectTimeouts)
3344 {
3345 Child child;
3346 child.goal = goal;
3347 child.fds = fds;
3348 child.timeStarted = child.lastOutput = time(0);
3349 child.inBuildSlot = inBuildSlot;
3350 child.respectTimeouts = respectTimeouts;
3351 children[pid] = child;
3352 if (inBuildSlot) nrLocalBuilds++;
3353 }
3354
3355
3356 void Worker::childTerminated(pid_t pid, bool wakeSleepers)
3357 {
3358 assert(pid != -1); /* common mistake */
3359
3360 Children::iterator i = children.find(pid);
3361 assert(i != children.end());
3362
3363 if (i->second.inBuildSlot) {
3364 assert(nrLocalBuilds > 0);
3365 nrLocalBuilds--;
3366 }
3367
3368 children.erase(pid);
3369
3370 if (wakeSleepers) {
3371
3372 /* Wake up goals waiting for a build slot. */
3373 foreach (WeakGoals::iterator, i, wantingToBuild) {
3374 GoalPtr goal = i->lock();
3375 if (goal) wakeUp(goal);
3376 }
3377
3378 wantingToBuild.clear();
3379 }
3380 }
3381
3382
3383 void Worker::waitForBuildSlot(GoalPtr goal)
3384 {
3385 debug("wait for build slot");
3386 if (getNrLocalBuilds() < settings.maxBuildJobs)
3387 wakeUp(goal); /* we can do it right away */
3388 else
3389 addToWeakGoals(wantingToBuild, goal);
3390 }
3391
3392
3393 void Worker::waitForAnyGoal(GoalPtr goal)
3394 {
3395 debug("wait for any goal");
3396 addToWeakGoals(waitingForAnyGoal, goal);
3397 }
3398
3399
3400 void Worker::waitForAWhile(GoalPtr goal)
3401 {
3402 debug("wait for a while");
3403 addToWeakGoals(waitingForAWhile, goal);
3404 }
3405
3406
3407 void Worker::run(const Goals & _topGoals)
3408 {
3409 foreach (Goals::iterator, i, _topGoals) topGoals.insert(*i);
3410
3411 startNest(nest, lvlDebug, format("entered goal loop"));
3412
3413 while (1) {
3414
3415 checkInterrupt();
3416
3417 /* Call every wake goal (in the ordering established by
3418 CompareGoalPtrs). */
3419 while (!awake.empty() && !topGoals.empty()) {
3420 Goals awake2;
3421 for (auto & i : awake) {
3422 GoalPtr goal = i.lock();
3423 if (goal) awake2.insert(goal);
3424 }
3425 awake.clear();
3426 for (auto & goal : awake2) {
3427 checkInterrupt();
3428 goal->work();
3429 if (topGoals.empty()) break; // stuff may have been cancelled
3430 }
3431 }
3432
3433 if (topGoals.empty()) break;
3434
3435 /* Wait for input. */
3436 if (!children.empty() || !waitingForAWhile.empty())
3437 waitForInput();
3438 else {
3439 if (awake.empty() && settings.maxBuildJobs == 0) throw Error(
3440 "unable to start any build; either increase `--max-jobs' "
3441 "or enable distributed builds");
3442 assert(!awake.empty());
3443 }
3444 }
3445
3446 /* If --keep-going is not set, it's possible that the main goal
3447 exited while some of its subgoals were still active. But if
3448 --keep-going *is* set, then they must all be finished now. */
3449 assert(!settings.keepGoing || awake.empty());
3450 assert(!settings.keepGoing || wantingToBuild.empty());
3451 assert(!settings.keepGoing || children.empty());
3452 }
3453
3454
3455 void Worker::waitForInput()
3456 {
3457 printMsg(lvlVomit, "waiting for children");
3458
3459 /* Process output from the file descriptors attached to the
3460 children, namely log output and output path creation commands.
3461 We also use this to detect child termination: if we get EOF on
3462 the logger pipe of a build, we assume that the builder has
3463 terminated. */
3464
3465 bool useTimeout = false;
3466 struct timeval timeout;
3467 timeout.tv_usec = 0;
3468 time_t before = time(0);
3469
3470 /* If we're monitoring for silence on stdout/stderr, or if there
3471 is a build timeout, then wait for input until the first
3472 deadline for any child. */
3473 assert(sizeof(time_t) >= sizeof(long));
3474 time_t nearest = LONG_MAX; // nearest deadline
3475 foreach (Children::iterator, i, children) {
3476 if (!i->second.respectTimeouts) continue;
3477 if (settings.maxSilentTime != 0)
3478 nearest = std::min(nearest, i->second.lastOutput + settings.maxSilentTime);
3479 if (settings.buildTimeout != 0)
3480 nearest = std::min(nearest, i->second.timeStarted + settings.buildTimeout);
3481 }
3482 if (nearest != LONG_MAX) {
3483 timeout.tv_sec = std::max((time_t) 1, nearest - before);
3484 useTimeout = true;
3485 printMsg(lvlVomit, format("sleeping %1% seconds") % timeout.tv_sec);
3486 }
3487
3488 /* If we are polling goals that are waiting for a lock, then wake
3489 up after a few seconds at most. */
3490 if (!waitingForAWhile.empty()) {
3491 useTimeout = true;
3492 if (lastWokenUp == 0)
3493 printMsg(lvlError, "waiting for locks or build slots...");
3494 if (lastWokenUp == 0 || lastWokenUp > before) lastWokenUp = before;
3495 timeout.tv_sec = std::max((time_t) 1, (time_t) (lastWokenUp + settings.pollInterval - before));
3496 } else lastWokenUp = 0;
3497
3498 using namespace std;
3499 /* Use select() to wait for the input side of any logger pipe to
3500 become `available'. Note that `available' (i.e., non-blocking)
3501 includes EOF. */
3502 fd_set fds;
3503 FD_ZERO(&fds);
3504 int fdMax = 0;
3505 foreach (Children::iterator, i, children) {
3506 foreach (set<int>::iterator, j, i->second.fds) {
3507 FD_SET(*j, &fds);
3508 if (*j >= fdMax) fdMax = *j + 1;
3509 }
3510 }
3511
3512 if (select(fdMax, &fds, 0, 0, useTimeout ? &timeout : 0) == -1) {
3513 if (errno == EINTR) return;
3514 throw SysError("waiting for input");
3515 }
3516
3517 time_t after = time(0);
3518
3519 /* Process all available file descriptors. */
3520
3521 /* Since goals may be canceled from inside the loop below (causing
3522 them go be erased from the `children' map), we have to be
3523 careful that we don't keep iterators alive across calls to
3524 timedOut(). */
3525 set<pid_t> pids;
3526 foreach (Children::iterator, i, children) pids.insert(i->first);
3527
3528 foreach (set<pid_t>::iterator, i, pids) {
3529 checkInterrupt();
3530 Children::iterator j = children.find(*i);
3531 if (j == children.end()) continue; // child destroyed
3532 GoalPtr goal = j->second.goal.lock();
3533 assert(goal);
3534
3535 set<int> fds2(j->second.fds);
3536 foreach (set<int>::iterator, k, fds2) {
3537 if (FD_ISSET(*k, &fds)) {
3538 unsigned char buffer[4096];
3539 ssize_t rd = read(*k, buffer, sizeof(buffer));
3540 if (rd == -1) {
3541 if (errno != EINTR)
3542 throw SysError(format("reading from %1%")
3543 % goal->getName());
3544 } else if (rd == 0) {
3545 debug(format("%1%: got EOF") % goal->getName());
3546 goal->handleEOF(*k);
3547 j->second.fds.erase(*k);
3548 } else {
3549 printMsg(lvlVomit, format("%1%: read %2% bytes")
3550 % goal->getName() % rd);
3551 string data((char *) buffer, rd);
3552 j->second.lastOutput = after;
3553 goal->handleChildOutput(*k, data);
3554 }
3555 }
3556 }
3557
3558 if (goal->getExitCode() == Goal::ecBusy &&
3559 settings.maxSilentTime != 0 &&
3560 j->second.respectTimeouts &&
3561 after - j->second.lastOutput >= (time_t) settings.maxSilentTime)
3562 {
3563 printMsg(lvlError,
3564 format("%1% timed out after %2% seconds of silence")
3565 % goal->getName() % settings.maxSilentTime);
3566 goal->timedOut();
3567 }
3568
3569 else if (goal->getExitCode() == Goal::ecBusy &&
3570 settings.buildTimeout != 0 &&
3571 j->second.respectTimeouts &&
3572 after - j->second.timeStarted >= (time_t) settings.buildTimeout)
3573 {
3574 printMsg(lvlError,
3575 format("%1% timed out after %2% seconds")
3576 % goal->getName() % settings.buildTimeout);
3577 goal->timedOut();
3578 }
3579 }
3580
3581 if (!waitingForAWhile.empty() && lastWokenUp + settings.pollInterval <= after) {
3582 lastWokenUp = after;
3583 foreach (WeakGoals::iterator, i, waitingForAWhile) {
3584 GoalPtr goal = i->lock();
3585 if (goal) wakeUp(goal);
3586 }
3587 waitingForAWhile.clear();
3588 }
3589 }
3590
3591
3592 unsigned int Worker::exitStatus()
3593 {
3594 return timedOut ? 101 : (permanentFailure ? 100 : 1);
3595 }
3596
3597
3598 //////////////////////////////////////////////////////////////////////
3599
3600
3601 void LocalStore::buildPaths(const PathSet & drvPaths, BuildMode buildMode)
3602 {
3603 startNest(nest, lvlDebug,
3604 format("building %1%") % showPaths(drvPaths));
3605
3606 Worker worker(*this);
3607
3608 Goals goals;
3609 foreach (PathSet::const_iterator, i, drvPaths) {
3610 DrvPathWithOutputs i2 = parseDrvPathWithOutputs(*i);
3611 if (isDerivation(i2.first))
3612 goals.insert(worker.makeDerivationGoal(i2.first, i2.second, buildMode));
3613 else
3614 goals.insert(worker.makeSubstitutionGoal(*i, buildMode));
3615 }
3616
3617 worker.run(goals);
3618
3619 PathSet failed;
3620 foreach (Goals::iterator, i, goals)
3621 if ((*i)->getExitCode() == Goal::ecFailed) {
3622 DerivationGoal * i2 = dynamic_cast<DerivationGoal *>(i->get());
3623 if (i2) failed.insert(i2->getDrvPath());
3624 else failed.insert(dynamic_cast<SubstitutionGoal *>(i->get())->getStorePath());
3625 }
3626
3627 if (!failed.empty())
3628 throw Error(format("build of %1% failed") % showPaths(failed), worker.exitStatus());
3629 }
3630
3631
3632 void LocalStore::ensurePath(const Path & path)
3633 {
3634 /* If the path is already valid, we're done. */
3635 if (isValidPath(path)) return;
3636
3637 Worker worker(*this);
3638 GoalPtr goal = worker.makeSubstitutionGoal(path);
3639 Goals goals = singleton<Goals>(goal);
3640
3641 worker.run(goals);
3642
3643 if (goal->getExitCode() != Goal::ecSuccess)
3644 throw Error(format("path `%1%' does not exist and cannot be created") % path, worker.exitStatus());
3645 }
3646
3647
3648 void LocalStore::repairPath(const Path & path)
3649 {
3650 Worker worker(*this);
3651 GoalPtr goal = worker.makeSubstitutionGoal(path, true);
3652 Goals goals = singleton<Goals>(goal);
3653
3654 worker.run(goals);
3655
3656 if (goal->getExitCode() != Goal::ecSuccess) {
3657 /* Since substituting the path didn't work, if we have a valid
3658 deriver, then rebuild the deriver. */
3659 Path deriver = queryDeriver(path);
3660 if (deriver != "" && isValidPath(deriver)) {
3661 goals.clear();
3662 goals.insert(worker.makeDerivationGoal(deriver, StringSet(), bmRepair));
3663 worker.run(goals);
3664 } else
3665 throw Error(format("cannot repair path `%1%'") % path, worker.exitStatus());
3666 }
3667 }
3668
3669
3670 }