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