1 : #include "build.hh"
2 : #include "references.hh"
3 : #include "pathlocks.hh"
4 : #include "misc.hh"
5 : #include "globals.hh"
6 : #include "gc.hh"
7 : #include "store.hh"
8 : #include "db.hh"
9 : #include "util.hh"
10 :
11 : #include <map>
12 : #include <iostream>
13 : #include <sstream>
14 : #include <boost/shared_ptr.hpp>
15 : #include <boost/weak_ptr.hpp>
16 : #include <boost/enable_shared_from_this.hpp>
17 :
18 : #include <sys/time.h>
19 : #include <sys/types.h>
20 : #include <sys/stat.h>
21 : #include <fcntl.h>
22 : #include <unistd.h>
23 : #include <errno.h>
24 :
25 : #include <pwd.h>
26 : #include <grp.h>
27 :
28 :
29 : namespace nix {
30 :
31 : using std::map;
32 :
33 :
34 : /* !!! TODO derivationFromPath shouldn't be used here */
35 :
36 :
37 690 : static string pathNullDevice = "/dev/null";
38 :
39 :
40 : static const uid_t rootUserId = 0;
41 :
42 :
43 : /* Forward definition. */
44 : class Worker;
45 :
46 :
47 : /* A pointer to a goal. */
48 : class Goal;
49 : typedef boost::shared_ptr<Goal> GoalPtr;
50 : typedef boost::weak_ptr<Goal> WeakGoalPtr;
51 :
52 : /* Set of goals. */
53 : typedef set<GoalPtr> Goals;
54 : typedef set<WeakGoalPtr> WeakGoals;
55 :
56 : /* A map of paths to goals (and the other way around). */
57 : typedef map<Path, WeakGoalPtr> WeakGoalMap;
58 :
59 :
60 :
61 : class Goal : public boost::enable_shared_from_this<Goal>
62 : {
63 : public:
64 : typedef enum {ecBusy, ecSuccess, ecFailed} ExitCode;
65 :
66 : protected:
67 :
68 : /* Backlink to the worker. */
69 : Worker & worker;
70 :
71 : /* Goals that this goal is waiting for. */
72 : Goals waitees;
73 :
74 : /* Goals waiting for this one to finish. Must use weak pointers
75 : here to prevent cycles. */
76 : WeakGoals waiters;
77 :
78 : /* Number of goals we are/were waiting for that have failed. */
79 : unsigned int nrFailed;
80 :
81 : /* Name of this goal for debugging purposes. */
82 : string name;
83 :
84 : /* Whether the goal is finished. */
85 : ExitCode exitCode;
86 :
87 333 : Goal(Worker & worker) : worker(worker)
88 3331 : {
89 333 : nrFailed = 0;
90 333 : exitCode = ecBusy;
91 : }
92 :
93 : virtual ~Goal()
94 666 : {
95 333 : trace("goal destroyed");
96 333 : }
97 :
98 : public:
99 : virtual void work() = 0;
100 :
101 : void addWaitee(GoalPtr waitee);
102 :
103 : virtual void waiteeDone(GoalPtr waitee, bool success);
104 :
105 : virtual void handleChildOutput(int fd, const string & data)
106 0 : {
107 0 : abort();
108 : }
109 :
110 : virtual void handleEOF(int fd)
111 0 : {
112 0 : abort();
113 : }
114 :
115 : void trace(const format & f);
116 698 :
117 : string getName()
118 0 : {
119 0 : return name;
120 : }
121 :
122 : ExitCode getExitCode()
123 156 : {
124 156 : return exitCode;
125 : }
126 :
127 : protected:
128 : void amDone(bool success = true);
129 : };
130 :
131 :
132 : /* A mapping used to remember for each child process to what goal it
133 : belongs, and file descriptors for receiving log data and output
134 : path creation commands. */
135 : struct Child
136 : {
137 : WeakGoalPtr goal;
138 : set<int> fds;
139 : bool inBuildSlot;
140 : };
141 :
142 : typedef map<pid_t, Child> Children;
143 :
144 :
145 : /* The worker class. */
146 : class Worker
147 : {
148 : private:
149 :
150 : /* Note: the worker should only have strong pointers to the
151 : top-level goals. */
152 :
153 : /* The top-level goals of the worker. */
154 : Goals topGoals;
155 :
156 : /* Goals that are ready to do some work. */
157 : WeakGoals awake;
158 :
159 : /* Goals waiting for a build slot. */
160 : WeakGoals wantingToBuild;
161 :
162 : /* Child processes currently running. */
163 : Children children;
164 :
165 : /* Number of build slots occupied. Not all child processes
166 : (namely build hooks) count as occupied build slots. */
167 : unsigned int nrChildren;
168 :
169 : /* Maps used to prevent multiple instantiations of a goal for the
170 : same derivation / path. */
171 : WeakGoalMap derivationGoals;
172 : WeakGoalMap substitutionGoals;
173 :
174 : public:
175 :
176 : Worker();
177 : ~Worker();
178 :
179 : /* Make a goal (with caching). */
180 : GoalPtr makeDerivationGoal(const Path & drvPath);
181 : GoalPtr makeSubstitutionGoal(const Path & storePath);
182 :
183 : /* Remove a dead goal. */
184 : void removeGoal(GoalPtr goal);
185 :
186 : /* Wake up a goal (i.e., there is something for it to do). */
187 : void wakeUp(GoalPtr goal);
188 :
189 : /* Can we start another child process? */
190 : bool canBuildMore();
191 :
192 : /* Registers / unregisters a running child process. */
193 : void childStarted(GoalPtr goal, pid_t pid,
194 : const set<int> & fds, bool inBuildSlot);
195 : void childTerminated(pid_t pid, bool wakeSleepers = true);
196 :
197 : /* Add a goal to the set of goals waiting for a build slot. */
198 : void waitForBuildSlot(GoalPtr goal, bool reallyWait = false);
199 :
200 : /* Loop until the specified top-level goals have finished. */
201 : void run(const Goals & topGoals);
202 :
203 : /* Wait for input to become available. */
204 : void waitForInput();
205 : };
206 :
207 :
208 : class SubstError : public Error
209 : {
210 : public:
211 4 : SubstError(const format & f) : Error(f) { };
212 : };
213 :
214 :
215 : class BuildError : public Error
216 : {
217 : public:
218 0 : BuildError(const format & f) : Error(f) { };
219 : };
220 :
221 :
222 :
223 : //////////////////////////////////////////////////////////////////////
224 :
225 :
226 : void Goal::addWaitee(GoalPtr waitee)
227 291 : {
228 291 : waitees.insert(waitee);
229 33119 : waitee->waiters.insert(shared_from_this());
230 : }
231 :
232 :
233 : void Goal::waiteeDone(GoalPtr waitee, bool success)
234 291 : {
235 291 : assert(waitees.find(waitee) != waitees.end());
236 291 : waitees.erase(waitee);
237 :
238 291 : trace(format("waitee `%1%' done; %2% left") %
239 : waitee->name % waitees.size());
240 :
241 291 : if (!success) ++nrFailed;
242 :
243 291 : if (waitees.empty() || (!success && !keepGoing)) {
244 :
245 : /* If we failed and keepGoing is not set, we remove all
246 : remaining waitees. */
247 214 : for (Goals::iterator i = waitees.begin(); i != waitees.end(); ++i) {
248 0 : GoalPtr goal = *i;
249 0 : WeakGoals waiters2;
250 0 : for (WeakGoals::iterator j = goal->waiters.begin();
251 : j != goal->waiters.end(); ++j)
252 0 : if (j->lock() != shared_from_this())
253 0 : waiters2.insert(*j);
254 0 : goal->waiters = waiters2;
255 : }
256 214 : waitees.clear();
257 :
258 214 : worker.wakeUp(shared_from_this());
259 : }
260 : }
261 :
262 :
263 : void Goal::amDone(bool success)
264 332 : {
265 332 : trace("done");
266 332 : assert(exitCode == ecBusy);
267 332 : exitCode = success ? ecSuccess : ecFailed;
268 623 : for (WeakGoals::iterator i = waiters.begin(); i != waiters.end(); ++i) {
269 291 : GoalPtr goal = i->lock();
270 291 : if (goal) goal->waiteeDone(shared_from_this(), success);
271 : }
272 332 : waiters.clear();
273 332 : worker.removeGoal(shared_from_this());
274 : }
275 :
276 :
277 : void Goal::trace(const format & f)
278 2962 : {
279 2962 : debug(format("%1%: %2%") % name % f);
280 : }
281 :
282 :
283 :
284 : //////////////////////////////////////////////////////////////////////
285 :
286 :
287 : /* Common initialisation performed in child processes. */
288 : void commonChildInit(Pipe & logPipe)
289 80 : {
290 : /* Put the child in a separate session (and thus a separate
291 : process group) so that it has no controlling terminal (meaning
292 : that e.g. ssh cannot open /dev/tty) and it doesn't receive
293 : terminal signals. */
294 80 : if (setsid() == -1)
295 0 : throw SysError(format("creating a new session"));
296 :
297 : /* Dup the write side of the logger pipe into stderr. */
298 80 : if (dup2(logPipe.writeSide, STDERR_FILENO) == -1)
299 0 : throw SysError("cannot pipe standard error into log file");
300 80 : logPipe.readSide.close();
301 :
302 : /* Dup stderr to stdout. */
303 80 : if (dup2(STDERR_FILENO, STDOUT_FILENO) == -1)
304 0 : throw SysError("cannot dup stderr into stdout");
305 :
306 : /* Reroute stdin to /dev/null. */
307 80 : int fdDevNull = open(pathNullDevice.c_str(), O_RDWR);
308 80 : if (fdDevNull == -1)
309 0 : throw SysError(format("cannot open `%1%'") % pathNullDevice);
310 80 : if (dup2(fdDevNull, STDIN_FILENO) == -1)
311 0 : throw SysError("cannot dup null device into stdin");
312 : }
313 :
314 :
315 : /* Convert a string list to an array of char pointers. Careful: the
316 : string list should outlive the array. */
317 : const char * * strings2CharPtrs(const Strings & ss)
318 138 : {
319 138 : const char * * arr = new const char * [ss.size() + 1];
320 138 : const char * * p = arr;
321 1209 : for (Strings::const_iterator i = ss.begin(); i != ss.end(); ++i)
322 1071 : *p++ = i->c_str();
323 138 : *p = 0;
324 138 : return arr;
325 : }
326 :
327 :
328 :
329 :
330 : //////////////////////////////////////////////////////////////////////
331 :
332 :
333 : class UserLock
334 : {
335 : private:
336 : /* POSIX locks suck. If we have a lock on a file, and we open and
337 : close that file again (without closing the original file
338 : descriptor), we lose the lock. So we have to be *very* careful
339 : not to open a lock file on which we are holding a lock. */
340 : static PathSet lockedPaths; /* !!! not thread-safe */
341 :
342 : Path fnUserLock;
343 : AutoCloseFD fdUserLock;
344 :
345 : uid_t uid;
346 :
347 : public:
348 : UserLock();
349 : ~UserLock();
350 :
351 : void acquire();
352 : void release();
353 :
354 : uid_t getUID();
355 : };
356 :
357 :
358 690 : PathSet UserLock::lockedPaths;
359 :
360 :
361 : UserLock::UserLock()
362 236 : {
363 118 : uid = 0;
364 : }
365 :
366 :
367 : UserLock::~UserLock()
368 236 : {
369 118 : release();
370 : }
371 :
372 :
373 : void UserLock::acquire()
374 0 : {
375 0 : assert(uid == 0);
376 :
377 0 : Strings buildUsers = querySetting("build-users", Strings());
378 :
379 0 : if (buildUsers.empty())
380 0 : throw Error(
381 : "cannot build as `root'; you must define "
382 : "one or more users for building in `build-users' "
383 : "in the Nix configuration file");
384 :
385 0 : for (Strings::iterator i = buildUsers.begin(); i != buildUsers.end(); ++i) {
386 0 : printMsg(lvlError, format("trying user `%1%'") % *i);
387 :
388 0 : struct passwd * pw = getpwnam(i->c_str());
389 0 : if (!pw)
390 0 : throw Error(format("the user `%1%' listed in `build-users' does not exist") % *i);
391 :
392 0 : fnUserLock = (format("%1%/userpool/%2%") % nixStateDir % pw->pw_uid).str();
393 :
394 0 : if (lockedPaths.find(fnUserLock) != lockedPaths.end())
395 : /* We already have a lock on this one. */
396 0 : continue;
397 :
398 0 : AutoCloseFD fd = open(fnUserLock.c_str(), O_RDWR | O_CREAT, 0600);
399 0 : if (fd == -1)
400 0 : throw SysError(format("opening user lock `%1%'") % fnUserLock);
401 :
402 0 : if (lockFile(fd, ltWrite, false)) {
403 0 : fdUserLock = fd.borrow();
404 0 : lockedPaths.insert(fnUserLock);
405 0 : uid = pw->pw_uid;
406 0 : return;
407 : }
408 : }
409 :
410 0 : throw Error("all build users are currently in use; "
411 : "consider expanding the `build-users' field "
412 : "in the Nix configuration file");
413 : }
414 :
415 :
416 : void UserLock::release()
417 178 : {
418 178 : if (uid == 0) return;
419 0 : fdUserLock.close(); /* releases lock */
420 0 : assert(lockedPaths.find(fnUserLock) != lockedPaths.end());
421 0 : lockedPaths.erase(fnUserLock);
422 0 : fnUserLock = "";
423 0 : uid = 0;
424 : }
425 :
426 :
427 : uid_t UserLock::getUID()
428 183 : {
429 183 : return uid;
430 : }
431 :
432 :
433 : static void killUser(uid_t uid)
434 0 : {
435 0 : debug(format("killing all processes running under uid `%1%'") % uid);
436 :
437 0 : assert(uid != rootUserId); /* just to be safe... */
438 :
439 : /* The system call kill(-1, sig) sends the signal `sig' to all
440 : users to which the current process can send signals. So we
441 : fork a process, switch to uid, and send a mass kill. */
442 :
443 0 : Pid pid;
444 0 : pid = fork();
445 0 : switch (pid) {
446 :
447 : case -1:
448 0 : throw SysError("unable to fork");
449 :
450 : case 0:
451 0 : try { /* child */
452 :
453 0 : if (setuid(uid) == -1) abort();
454 :
455 0 : if (kill(-1, SIGKILL) == -1)
456 0 : throw SysError(format("cannot kill processes for UID `%1%'") % uid);
457 :
458 0 : } catch (std::exception & e) {
459 0 : std::cerr << format("build error: %1%\n") % e.what();
460 0 : quickExit(1);
461 : }
462 0 : quickExit(0);
463 : }
464 :
465 : /* parent */
466 0 : if (pid.wait(true) != 0)
467 0 : throw Error(format("cannot kill processes for UID `%1%'") % uid);
468 :
469 : /* !!! We should really do some check to make sure that there are
470 : no processes left running under `uid', but there is no portable
471 : way to do so (I think). The most reliable way may be `ps -eo
472 : uid | grep -q $uid'. */
473 : }
474 :
475 :
476 :
477 : //////////////////////////////////////////////////////////////////////
478 :
479 :
480 : class DerivationGoal : public Goal
481 : {
482 : private:
483 : /* The path of the derivation. */
484 : Path drvPath;
485 :
486 : /* The derivation stored at drvPath. */
487 : Derivation drv;
488 :
489 : /* The remainder is state held during the build. */
490 :
491 : /* Locks on the output paths. */
492 : PathLocks outputLocks;
493 :
494 : /* All input paths (that is, the union of FS closures of the
495 : immediate input paths). */
496 : PathSet inputPaths;
497 :
498 : /* Referenceable paths (i.e., input and output paths). */
499 : PathSet allPaths;
500 :
501 : /* User selected for running the builder. */
502 : UserLock buildUser;
503 :
504 : /* The process ID of the builder. */
505 : Pid pid;
506 :
507 : /* The temporary directory. */
508 : Path tmpDir;
509 :
510 : /* File descriptor for the log file. */
511 : AutoCloseFD fdLogFile;
512 :
513 : /* Pipe for the builder's standard output/error. */
514 : Pipe logPipe;
515 :
516 : /* Pipes for talking to the build hook (if any). */
517 : Pipe toHook;
518 : Pipe fromHook;
519 :
520 : typedef void (DerivationGoal::*GoalState)();
521 : GoalState state;
522 :
523 : public:
524 : DerivationGoal(const Path & drvPath, Worker & worker);
525 : ~DerivationGoal();
526 :
527 : void work();
528 :
529 : Path getDrvPath()
530 1 : {
531 1 : return drvPath;
532 : }
533 :
534 : private:
535 : /* The states. */
536 : void init();
537 : void haveStoreExpr();
538 : void outputsSubstituted();
539 : void inputsRealised();
540 : void tryToBuild();
541 : void buildDone();
542 :
543 : /* Is the build hook willing to perform the build? */
544 : typedef enum {rpAccept, rpDecline, rpPostpone, rpDone} HookReply;
545 : HookReply tryBuildHook();
546 :
547 : /* Synchronously wait for a build hook to finish. */
548 : void terminateBuildHook();
549 :
550 : /* Acquires locks on the output paths and gathers information
551 : about the build (e.g., the input closures). During this
552 : process its possible that we find out that the build is
553 : unnecessary, in which case we return false (this is not an
554 : error condition!). */
555 : bool prepareBuild();
556 :
557 : /* Start building a derivation. */
558 : void startBuilder();
559 :
560 : /* Must be called after the output paths have become valid (either
561 : due to a successful build or hook, or because they already
562 : were). */
563 : void computeClosure();
564 :
565 : /* Open a log file and a pipe to it. */
566 : void openLogFile();
567 :
568 : /* Common initialisation to be performed in child processes (i.e.,
569 : both in builders and in build hooks. */
570 : void initChild();
571 :
572 : /* Delete the temporary directory, if we have one. */
573 : void deleteTmpDir(bool force);
574 :
575 : /* Callback used by the worker to write to the log. */
576 : void handleChildOutput(int fd, const string & data);
577 : void handleEOF(int fd);
578 :
579 : /* Return the set of (in)valid paths. */
580 : PathSet checkPathValidity(bool returnValid);
581 : };
582 :
583 :
584 : DerivationGoal::DerivationGoal(const Path & drvPath, Worker & worker)
585 118 : : Goal(worker)
586 2398 : {
587 118 : this->drvPath = drvPath;
588 118 : state = &DerivationGoal::init;
589 118 : name = (format("building of `%1%'") % drvPath).str();
590 118 : trace("created");
591 : }
592 :
593 :
594 : DerivationGoal::~DerivationGoal()
595 236 : {
596 118 : if (pid != -1) worker.childTerminated(pid);
597 :
598 : /* Careful: we should never ever throw an exception from a
599 : destructor. */
600 118 : try {
601 118 : deleteTmpDir(false);
602 0 : } catch (Error & e) {
603 0 : printMsg(lvlError, format("error (ignored): %1%") % e.msg());
604 : }
605 118 : }
606 :
607 :
608 : void DerivationGoal::work()
609 525 : {
610 525 : (this->*state)();
611 : }
612 :
613 :
614 : void DerivationGoal::init()
615 118 : {
616 118 : trace("init");
617 :
618 : /* The first thing to do is to make sure that the derivation
619 : exists. If it doesn't, it may be created through a
620 : substitute. */
621 118 : addWaitee(worker.makeSubstitutionGoal(drvPath));
622 :
623 118 : state = &DerivationGoal::haveStoreExpr;
624 : }
625 :
626 :
627 : void DerivationGoal::haveStoreExpr()
628 118 : {
629 118 : trace("loading derivation");
630 :
631 118 : if (nrFailed != 0) {
632 0 : printMsg(lvlError,
633 : format("cannot build missing derivation `%1%'")
634 : % drvPath);
635 0 : amDone(false);
636 0 : return;
637 : }
638 :
639 118 : assert(isValidPath(drvPath));
640 :
641 : /* Get the derivation. */
642 354 : drv = derivationFromPath(drvPath);
643 :
644 236 : for (DerivationOutputs::iterator i = drv.outputs.begin();
645 : i != drv.outputs.end(); ++i)
646 118 : addTempRoot(i->second.path);
647 :
648 : /* Check what outputs paths are not already valid. */
649 118 : PathSet invalidOutputs = checkPathValidity(false);
650 :
651 : /* If they are all valid, then we're done. */
652 118 : if (invalidOutputs.size() == 0) {
653 29 : amDone(true);
654 29 : return;
655 : }
656 :
657 : /* We are first going to try to create the invalid output paths
658 : through substitutes. If that doesn't work, we'll build
659 : them. */
660 178 : for (PathSet::iterator i = invalidOutputs.begin();
661 : i != invalidOutputs.end(); ++i)
662 : /* Don't bother creating a substitution goal if there are no
663 : substitutes. */
664 89 : if (querySubstitutes(noTxn, *i).size() > 0)
665 4 : addWaitee(worker.makeSubstitutionGoal(*i));
666 :
667 89 : if (waitees.empty()) /* to prevent hang (no wake-up event) */
668 85 : outputsSubstituted();
669 : else
670 4 : state = &DerivationGoal::outputsSubstituted;
671 : }
672 :
673 :
674 : void DerivationGoal::outputsSubstituted()
675 89 : {
676 89 : trace("all outputs substituted (maybe)");
677 :
678 89 : if (nrFailed > 0 && !tryFallback)
679 0 : throw Error(format("some substitutes for the outputs of derivation `%1%' failed; try `--fallback'") % drvPath);
680 :
681 89 : nrFailed = 0;
682 :
683 89 : if (checkPathValidity(false).size() == 0) {
684 3 : amDone(true);
685 3 : return;
686 : }
687 :
688 : /* Otherwise, at least one of the output paths could not be
689 : produced using a substitute. So we have to build instead. */
690 :
691 : /* The inputs must be built before we can build this goal. */
692 : /* !!! but if possible, only install the paths that we need */
693 140 : for (DerivationInputs::iterator i = drv.inputDrvs.begin();
694 : i != drv.inputDrvs.end(); ++i)
695 54 : addWaitee(worker.makeDerivationGoal(i->first));
696 :
697 193 : for (PathSet::iterator i = drv.inputSrcs.begin();
698 : i != drv.inputSrcs.end(); ++i)
699 107 : addWaitee(worker.makeSubstitutionGoal(*i));
700 :
701 86 : state = &DerivationGoal::inputsRealised;
702 : }
703 :
704 :
705 : void DerivationGoal::inputsRealised()
706 86 : {
707 86 : trace("all inputs realised");
708 :
709 86 : if (nrFailed != 0) {
710 0 : printMsg(lvlError,
711 : format("cannot build derivation `%1%': "
712 : "%2% inputs could not be realised")
713 : % drvPath % nrFailed);
714 0 : amDone(false);
715 0 : return;
716 : }
717 :
718 : /* Okay, try to build. Note that here we don't wait for a build
719 : slot to become available, since we don't need one if there is a
720 : build hook. */
721 86 : state = &DerivationGoal::tryToBuild;
722 86 : worker.wakeUp(shared_from_this());
723 : }
724 :
725 :
726 : void DerivationGoal::tryToBuild()
727 137 : {
728 137 : trace("trying to build");
729 :
730 137 : try {
731 :
732 : /* Is the build hook willing to accept this job? */
733 137 : switch (tryBuildHook()) {
734 : case rpAccept:
735 : /* Yes, it has started doing so. Wait until we get
736 : EOF from the hook. */
737 1 : state = &DerivationGoal::buildDone;
738 1 : return;
739 : case rpPostpone:
740 : /* Not now; wait until at least one child finishes. */
741 0 : worker.waitForBuildSlot(shared_from_this(), true);
742 0 : return;
743 : case rpDecline:
744 : /* We should do it ourselves. */
745 0 : break;
746 : case rpDone:
747 : /* Somebody else did it. */
748 0 : amDone();
749 0 : return;
750 : }
751 :
752 : /* Make sure that we are allowed to start a build. */
753 136 : if (!worker.canBuildMore()) {
754 51 : worker.waitForBuildSlot(shared_from_this());
755 51 : return;
756 : }
757 :
758 : /* Acquire locks and such. If we then see that the build has
759 : been done by somebody else, we're done. */
760 85 : if (!prepareBuild()) {
761 24 : amDone();
762 24 : return;
763 : }
764 :
765 : /* Okay, we have to build. */
766 61 : startBuilder();
767 :
768 0 : } catch (BuildError & e) {
769 0 : printMsg(lvlError, e.msg());
770 0 : amDone(false);
771 0 : return;
772 : }
773 :
774 : /* This state will be reached when we get EOF on the child's
775 : log pipe. */
776 61 : state = &DerivationGoal::buildDone;
777 : }
778 :
779 :
780 : void DerivationGoal::buildDone()
781 62 : {
782 62 : trace("build done");
783 :
784 : /* Since we got an EOF on the logger pipe, the builder is presumed
785 : to have terminated. In fact, the builder could also have
786 : simply have closed its end of the pipe --- just don't do that
787 : :-) */
788 : /* !!! this could block! security problem! solution: kill the
789 : child */
790 62 : pid_t savedPid = pid;
791 62 : int status = pid.wait(true);
792 :
793 : /* So the child is gone now. */
794 62 : worker.childTerminated(savedPid);
795 :
796 : /* When running under a build user, make sure that all processes
797 : running under that uid are gone. This is to prevent a
798 : malicious user from leaving behind a process that keeps files
799 : open and modifies them after they have been chown'ed to
800 : root. */
801 62 : if (buildUser.getUID() != 0)
802 0 : killUser(buildUser.getUID());
803 :
804 : /* Close the read side of the logger pipe. */
805 62 : logPipe.readSide.close();
806 :
807 : /* Close the log file. */
808 62 : fdLogFile.close();
809 :
810 62 : debug(format("builder process for `%1%' finished") % drvPath);
811 :
812 : /* Check the exit status. */
813 62 : if (!statusOk(status)) {
814 1 : deleteTmpDir(false);
815 1 : printMsg(lvlError, format("builder for `%1%' %2%")
816 : % drvPath % statusToString(status));
817 1 : amDone(false);
818 1 : return;
819 : }
820 :
821 61 : deleteTmpDir(true);
822 :
823 : /* Compute the FS closure of the outputs and register them as
824 : being valid. */
825 61 : try {
826 61 : computeClosure();
827 0 : } catch (BuildError & e) {
828 0 : printMsg(lvlError, e.msg());
829 0 : amDone(false);
830 0 : return;
831 : }
832 :
833 : /* Release the build user, if applicable. */
834 60 : buildUser.release();
835 :
836 60 : amDone();
837 : }
838 :
839 :
840 : static string readLine(int fd)
841 3 : {
842 3 : string s;
843 23 : while (1) {
844 23 : char ch;
845 23 : ssize_t rd = read(fd, &ch, 1);
846 23 : if (rd == -1) {
847 0 : if (errno != EINTR)
848 0 : throw SysError("reading a line");
849 23 : } else if (rd == 0)
850 0 : throw Error("unexpected EOF reading a line");
851 : else {
852 23 : if (ch == '\n') return s;
853 20 : s += ch;
854 : }
855 : }
856 : }
857 :
858 :
859 : static void writeLine(int fd, string s)
860 1 : {
861 1 : s += '\n';
862 1 : writeFull(fd, (const unsigned char *) s.c_str(), s.size());
863 : }
864 :
865 :
866 : /* !!! ugly hack */
867 : static void drain(int fd)
868 4 : {
869 4 : unsigned char buffer[1024];
870 4 : while (1) {
871 4 : ssize_t rd = read(fd, buffer, sizeof buffer);
872 4 : if (rd == -1) {
873 0 : if (errno != EINTR)
874 0 : throw SysError("draining");
875 4 : } else if (rd == 0) break;
876 2 : else writeFull(STDERR_FILENO, buffer, rd);
877 : }
878 : }
879 :
880 :
881 : PathSet outputPaths(const DerivationOutputs & outputs)
882 234 : {
883 234 : PathSet paths;
884 468 : for (DerivationOutputs::const_iterator i = outputs.begin();
885 : i != outputs.end(); ++i)
886 234 : paths.insert(i->second.path);
887 0 : return paths;
888 : }
889 :
890 :
891 : string showPaths(const PathSet & paths)
892 149 : {
893 149 : string s;
894 298 : for (PathSet::const_iterator i = paths.begin();
895 : i != paths.end(); ++i)
896 : {
897 149 : if (s.size() != 0) s += ", ";
898 149 : s += "`" + *i + "'";
899 : }
900 0 : return s;
901 : }
902 :
903 :
904 : DerivationGoal::HookReply DerivationGoal::tryBuildHook()
905 137 : {
906 137 : Path buildHook = getEnv("NIX_BUILD_HOOK");
907 137 : if (buildHook == "") return rpDecline;
908 3 : buildHook = absPath(buildHook);
909 :
910 : /* Create a directory where we will store files used for
911 : communication between us and the build hook. */
912 3 : tmpDir = createTempDir();
913 :
914 : /* Create the log file and pipe. */
915 3 : openLogFile();
916 :
917 : /* Create the communication pipes. */
918 3 : toHook.create();
919 3 : fromHook.create();
920 :
921 : /* Fork the hook. */
922 3 : pid = fork();
923 6 : switch (pid) {
924 :
925 : case -1:
926 0 : throw SysError("unable to fork");
927 :
928 : case 0:
929 3 : try { /* child */
930 :
931 3 : initChild();
932 :
933 3 : execl(buildHook.c_str(), buildHook.c_str(),
934 : (worker.canBuildMore() ? (string) "1" : "0").c_str(),
935 : thisSystem.c_str(),
936 : drv.platform.c_str(),
937 : drvPath.c_str(), NULL);
938 :
939 0 : throw SysError(format("executing `%1%'") % buildHook);
940 :
941 0 : } catch (std::exception & e) {
942 0 : std::cerr << format("build error: %1%\n") % e.what();
943 : }
944 0 : quickExit(1);
945 : }
946 :
947 : /* parent */
948 3 : pid.setSeparatePG(true);
949 3 : logPipe.writeSide.close();
950 3 : worker.childStarted(shared_from_this(),
951 9142 : pid, singleton<set<int> >(logPipe.readSide), false);
952 :
953 3 : fromHook.writeSide.close();
954 3 : toHook.readSide.close();
955 :
956 : /* Read the first line of input, which should be a word indicating
957 : whether the hook wishes to perform the build. !!! potential
958 : for deadlock here: we should also read from the child's logger
959 : pipe. */
960 3 : string reply;
961 3 : try {
962 3 : reply = readLine(fromHook.readSide);
963 0 : } catch (Error & e) {
964 0 : drain(logPipe.readSide);
965 0 : throw;
966 : }
967 :
968 3 : debug(format("hook reply is `%1%'") % reply);
969 :
970 3 : if (reply == "decline" || reply == "postpone") {
971 : /* Clean up the child. !!! hacky / should verify */
972 2 : drain(logPipe.readSide);
973 2 : terminateBuildHook();
974 2 : return reply == "decline" ? rpDecline : rpPostpone;
975 : }
976 :
977 1 : else if (reply == "accept") {
978 :
979 : /* Acquire locks and such. If we then see that the output
980 : paths are now valid, we're done. */
981 1 : if (!prepareBuild()) {
982 : /* Tell the hook to exit. */
983 0 : writeLine(toHook.writeSide, "cancel");
984 0 : terminateBuildHook();
985 0 : return rpDone;
986 : }
987 :
988 1 : printMsg(lvlInfo, format("running hook to build path(s) %1%")
989 : % showPaths(outputPaths(drv.outputs)));
990 :
991 : /* Write the information that the hook needs to perform the
992 : build, i.e., the set of input paths, the set of output
993 : paths, and the references (pointer graph) in the input
994 : paths. */
995 :
996 1 : Path inputListFN = tmpDir + "/inputs";
997 1 : Path outputListFN = tmpDir + "/outputs";
998 1 : Path referencesFN = tmpDir + "/references";
999 :
1000 : /* The `inputs' file lists all inputs that have to be copied
1001 : to the remote system. This unfortunately has to contain
1002 : the entire derivation closure to ensure that the validity
1003 : invariant holds on the remote system. (I.e., it's
1004 : unfortunate that we have to list it since the remote system
1005 : *probably* already has it.) */
1006 1 : PathSet allInputs;
1007 1 : allInputs.insert(inputPaths.begin(), inputPaths.end());
1008 1 : computeFSClosure(drvPath, allInputs);
1009 :
1010 1 : string s;
1011 3 : for (PathSet::iterator i = allInputs.begin();
1012 : i != allInputs.end(); ++i)
1013 2 : s += *i + "\n";
1014 :
1015 1 : writeStringToFile(inputListFN, s);
1016 :
1017 : /* The `outputs' file lists all outputs that have to be copied
1018 : from the remote system. */
1019 1 : s = "";
1020 2 : for (DerivationOutputs::iterator i = drv.outputs.begin();
1021 : i != drv.outputs.end(); ++i)
1022 1 : s += i->second.path + "\n";
1023 1 : writeStringToFile(outputListFN, s);
1024 :
1025 : /* The `references' file has exactly the format accepted by
1026 : `nix-store --register-validity'. */
1027 1 : s = "";
1028 3 : for (PathSet::iterator i = allInputs.begin();
1029 : i != allInputs.end(); ++i)
1030 : {
1031 2 : s += *i + "\n";
1032 :
1033 2 : Path deriver = queryDeriver(noTxn, *i);
1034 2 : s += deriver + "\n";
1035 :
1036 2 : PathSet references;
1037 2 : queryReferences(noTxn, *i, references);
1038 :
1039 2 : s += (format("%1%\n") % references.size()).str();
1040 :
1041 3 : for (PathSet::iterator j = references.begin();
1042 : j != references.end(); ++j)
1043 1 : s += *j + "\n";
1044 : }
1045 :
1046 1 : writeStringToFile(referencesFN, s);
1047 :
1048 : /* Tell the hook to proceed. */
1049 1 : writeLine(toHook.writeSide, "okay");
1050 :
1051 1 : return rpAccept;
1052 : }
1053 :
1054 0 : else throw Error(format("bad hook reply `%1%'") % reply);
1055 : }
1056 :
1057 :
1058 : void DerivationGoal::terminateBuildHook()
1059 2 : {
1060 : /* !!! drain stdout of hook */
1061 2 : debug("terminating build hook");
1062 2 : pid_t savedPid = pid;
1063 2 : pid.wait(true);
1064 2 : worker.childTerminated(savedPid, false);
1065 2 : fromHook.readSide.close();
1066 2 : toHook.writeSide.close();
1067 2 : fdLogFile.close();
1068 2 : logPipe.readSide.close();
1069 2 : deleteTmpDir(true); /* get rid of the hook's temporary directory */
1070 : }
1071 :
1072 :
1073 : bool DerivationGoal::prepareBuild()
1074 86 : {
1075 : /* Obtain locks on all output paths. The locks are automatically
1076 : released when we exit this function or Nix crashes. */
1077 : /* !!! BUG: this could block, which is not allowed. */
1078 86 : outputLocks.lockPaths(outputPaths(drv.outputs),
1079 : (format("waiting for lock on %1%") % showPaths(outputPaths(drv.outputs))).str());
1080 :
1081 : /* Now check again whether the outputs are valid. This is because
1082 : another process may have started building in parallel. After
1083 : it has finished and released the locks, we can (and should)
1084 : reuse its results. (Strictly speaking the first check can be
1085 : omitted, but that would be less efficient.) Note that since we
1086 : now hold the locks on the output paths, no other process can
1087 : build this derivation, so no further checks are necessary. */
1088 86 : PathSet validPaths = checkPathValidity(true);
1089 86 : if (validPaths.size() == drv.outputs.size()) {
1090 24 : debug(format("skipping build of derivation `%1%', someone beat us to it")
1091 : % drvPath);
1092 24 : outputLocks.setDeletion(true);
1093 24 : return false;
1094 : }
1095 :
1096 62 : if (validPaths.size() > 0) {
1097 : /* !!! fix this; try to delete valid paths */
1098 0 : throw Error(
1099 : format("derivation `%1%' is blocked by its output paths")
1100 : % drvPath);
1101 : }
1102 :
1103 : /* Gather information necessary for computing the closure and/or
1104 : running the build hook. */
1105 :
1106 : /* The outputs are referenceable paths. */
1107 124 : for (DerivationOutputs::iterator i = drv.outputs.begin();
1108 : i != drv.outputs.end(); ++i)
1109 : {
1110 62 : debug(format("building path `%1%'") % i->second.path);
1111 62 : allPaths.insert(i->second.path);
1112 : }
1113 :
1114 : /* Determine the full set of input paths. */
1115 :
1116 : /* First, the input derivations. */
1117 86 : for (DerivationInputs::iterator i = drv.inputDrvs.begin();
1118 : i != drv.inputDrvs.end(); ++i)
1119 : {
1120 : /* Add the relevant output closures of the input derivation
1121 : `*i' as input paths. Only add the closures of output paths
1122 : that are specified as inputs. */
1123 24 : assert(isValidPath(i->first));
1124 24 : Derivation inDrv = derivationFromPath(i->first);
1125 48 : for (StringSet::iterator j = i->second.begin();
1126 : j != i->second.end(); ++j)
1127 24 : if (inDrv.outputs.find(*j) != inDrv.outputs.end())
1128 24 : computeFSClosure(inDrv.outputs[*j].path, inputPaths);
1129 : else
1130 0 : throw Error(
1131 : format("derivation `%1%' requires non-existent output `%2%' from input derivation `%3%'")
1132 : % drvPath % *j % i->first);
1133 : }
1134 :
1135 : /* Second, the input sources. */
1136 145 : for (PathSet::iterator i = drv.inputSrcs.begin();
1137 : i != drv.inputSrcs.end(); ++i)
1138 83 : computeFSClosure(*i, inputPaths);
1139 :
1140 62 : debug(format("added input paths %1%") % showPaths(inputPaths));
1141 :
1142 62 : allPaths.insert(inputPaths.begin(), inputPaths.end());
1143 :
1144 62 : return true;
1145 : }
1146 :
1147 :
1148 : void DerivationGoal::startBuilder()
1149 61 : {
1150 61 : startNest(nest, lvlInfo,
1151 : format("building path(s) %1%") % showPaths(outputPaths(drv.outputs)))
1152 :
1153 : /* Right platform? */
1154 61 : if (drv.platform != thisSystem)
1155 0 : throw BuildError(
1156 : format("a `%1%' is required to build `%3%', but I am a `%2%'")
1157 0 : % drv.platform % thisSystem % drvPath);
1158 :
1159 : /* If any of the outputs already exist but are not registered,
1160 : delete them. */
1161 122 : for (DerivationOutputs::iterator i = drv.outputs.begin();
1162 : i != drv.outputs.end(); ++i)
1163 : {
1164 61 : Path path = i->second.path;
1165 61 : if (isValidPath(path))
1166 0 : throw Error(format("obstructed build: path `%1%' exists") % path);
1167 61 : if (pathExists(path)) {
1168 0 : debug(format("removing unregistered path `%1%'") % path);
1169 0 : deletePath(path);
1170 : }
1171 : }
1172 :
1173 : /* Construct the environment passed to the builder. */
1174 : typedef map<string, string> Environment;
1175 61 : Environment env;
1176 :
1177 : /* Most shells initialise PATH to some default (/bin:/usr/bin:...) when
1178 : PATH is not set. We don't want this, so we fill it in with some dummy
1179 : value. */
1180 61 : env["PATH"] = "/path-not-set";
1181 :
1182 : /* Set HOME to a non-existing path to prevent certain programs from using
1183 : /etc/passwd (or NIS, or whatever) to locate the home directory (for
1184 : example, wget looks for ~/.wgetrc). I.e., these tools use /etc/passwd
1185 : if HOME is not set, but they will just assume that the settings file
1186 : they are looking for does not exist if HOME is set but points to some
1187 : non-existing path. */
1188 61 : env["HOME"] = "/homeless-shelter";
1189 :
1190 : /* Tell the builder where the Nix store is. Usually they
1191 : shouldn't care, but this is useful for purity checking (e.g.,
1192 : the compiler or linker might only want to accept paths to files
1193 : in the store or in the build directory). */
1194 61 : env["NIX_STORE"] = nixStore;
1195 :
1196 : /* Add all bindings specified in the derivation. */
1197 454 : for (StringPairs::iterator i = drv.env.begin();
1198 : i != drv.env.end(); ++i)
1199 393 : env[i->first] = i->second;
1200 :
1201 : /* Create a temporary directory where the build will take
1202 : place. */
1203 61 : tmpDir = createTempDir();
1204 :
1205 : /* For convenience, set an environment pointing to the top build
1206 : directory. */
1207 61 : env["NIX_BUILD_TOP"] = tmpDir;
1208 :
1209 : /* Also set TMPDIR and variants to point to this directory. */
1210 61 : env["TMPDIR"] = env["TEMPDIR"] = env["TMP"] = env["TEMP"] = tmpDir;
1211 :
1212 : /* Compatibility hack with Nix <= 0.7: if this is a fixed-output
1213 : derivation, tell the builder, so that for instance `fetchurl'
1214 : can skip checking the output. On older Nixes, this environment
1215 : variable won't be set, so `fetchurl' will do the check. */
1216 61 : bool fixedOutput = true;
1217 122 : for (DerivationOutputs::iterator i = drv.outputs.begin();
1218 : i != drv.outputs.end(); ++i)
1219 61 : if (i->second.hash == "") fixedOutput = false;
1220 61 : if (fixedOutput)
1221 6 : env["NIX_OUTPUT_CHECKED"] = "1";
1222 :
1223 : /* *Only* if this is a fixed-output derivation, propagate the
1224 : values of the environment variables specified in the
1225 : `impureEnvVars' attribute to the builder. This allows for
1226 : instance environment variables for proxy configuration such as
1227 : `http_proxy' to be easily passed to downloaders like
1228 : `fetchurl'. Passing such environment variables from the caller
1229 : to the builder is generally impure, but the output of
1230 : fixed-output derivations is by definition pure (since we
1231 : already know the cryptographic hash of the output). */
1232 61 : if (fixedOutput) {
1233 6 : Strings varNames = tokenizeString(drv.env["impureEnvVars"]);
1234 6 : for (Strings::iterator i = varNames.begin(); i != varNames.end(); ++i)
1235 0 : env[*i] = getEnv(*i);
1236 : }
1237 :
1238 :
1239 : /* If we are running as root, and the `build-allow-root' setting
1240 : is `false', then we have to build as one of the users listed in
1241 : `build-users'. */
1242 61 : if (!queryBoolSetting("build-allow-root", true) &&
1243 : getuid() == rootUserId)
1244 : {
1245 0 : buildUser.acquire();
1246 0 : assert(buildUser.getUID() != 0);
1247 :
1248 : /* Make sure that no other processes are executing under this
1249 : uid. */
1250 0 : killUser(buildUser.getUID());
1251 :
1252 : /* Change ownership of the temporary build directory. !!! gid */
1253 0 : if (chown(tmpDir.c_str(), buildUser.getUID(), (gid_t) -1) == -1)
1254 0 : throw SysError(format("cannot change ownership of `%1%'") % tmpDir);
1255 :
1256 : /* Check that the Nix store has the appropriate permissions,
1257 : i.e., owned by root and mode 1777 (sticky bit on so that
1258 : the builder can create its output but not mess with the
1259 : outputs of other processes). */
1260 0 : struct stat st;
1261 0 : if (stat(nixStore.c_str(), &st) == -1)
1262 0 : throw SysError(format("cannot stat `%1%'") % nixStore);
1263 0 : if (st.st_uid != rootUserId)
1264 0 : throw Error(format("`%1%' is not owned by root") % nixStore);
1265 0 : if (!(st.st_mode & S_ISVTX) ||
1266 : ((st.st_mode & S_IRWXO) != S_IRWXO))
1267 0 : throw Error(format(
1268 : "builder does not have write permission to `%1%'; "
1269 : "try `chmod 1777 %1%'") % nixStore);
1270 : }
1271 :
1272 :
1273 : /* Run the builder. */
1274 61 : printMsg(lvlChatty, format("executing builder `%1%'") %
1275 : drv.builder);
1276 :
1277 : /* Create the log file and pipe. */
1278 61 : openLogFile();
1279 :
1280 : /* Fork a child to build the package. Note that while we
1281 : currently use forks to run and wait for the children, it
1282 : shouldn't be hard to use threads for this on systems where
1283 : fork() is unavailable or inefficient. */
1284 61 : pid = fork();
1285 122 : switch (pid) {
1286 :
1287 : case -1:
1288 0 : throw SysError("unable to fork");
1289 :
1290 : case 0:
1291 :
1292 : /* Warning: in the child we should absolutely not make any
1293 : Berkeley DB calls! */
1294 :
1295 61 : try { /* child */
1296 :
1297 61 : initChild();
1298 :
1299 : /* Fill in the arguments. */
1300 61 : Strings args(drv.args);
1301 61 : args.push_front(baseNameOf(drv.builder));
1302 61 : const char * * argArr = strings2CharPtrs(args);
1303 :
1304 : /* Fill in the environment. */
1305 61 : Strings envStrs;
1306 910 : for (Environment::const_iterator i = env.begin();
1307 : i != env.end(); ++i)
1308 849 : envStrs.push_back(i->first + "=" + i->second);
1309 61 : const char * * envArr = strings2CharPtrs(envStrs);
1310 :
1311 : /* If we are running as root and `build-allow-root' is
1312 : `false', then switch to the user we allocated above.
1313 : Make sure that we drop all root privileges. Note that
1314 : initChild() above has closed all file descriptors
1315 : except std*, so that's safe. Also note that setuid()
1316 : when run as root sets the real, effective and saved
1317 : UIDs. */
1318 61 : if (buildUser.getUID() != 0) {
1319 0 : printMsg(lvlError, format("switching to uid `%1%'") % buildUser.getUID());
1320 :
1321 : /* !!! setgid also */
1322 0 : if (setgroups(0, 0) == -1)
1323 0 : throw SysError("cannot clear the set of supplementary groups");
1324 0 : setuid(buildUser.getUID());
1325 0 : assert(getuid() == buildUser.getUID());
1326 0 : assert(geteuid() == buildUser.getUID());
1327 :
1328 : }
1329 :
1330 : /* Execute the program. This should not return. */
1331 61 : execve(drv.builder.c_str(),
1332 : (char * *) argArr, (char * *) envArr);
1333 :
1334 0 : throw SysError(format("executing `%1%'")
1335 : % drv.builder);
1336 :
1337 0 : } catch (std::exception & e) {
1338 0 : std::cerr << format("build error: %1%\n") % e.what();
1339 : }
1340 0 : quickExit(1);
1341 : }
1342 :
1343 :
1344 : /* parent */
1345 61 : pid.setSeparatePG(true);
1346 61 : logPipe.writeSide.close();
1347 61 : worker.childStarted(shared_from_this(), pid,
1348 : singleton<set<int> >(logPipe.readSide), true);
1349 : }
1350 :
1351 :
1352 : void DerivationGoal::computeClosure()
1353 61 : {
1354 61 : map<Path, PathSet> allReferences;
1355 147 : map<Path, Hash> contentHashes;
1356 :
1357 : /* Check whether the output paths were created, and grep each
1358 : output path to determine what other paths it references. Also make all
1359 : output paths read-only. */
1360 121 : for (DerivationOutputs::iterator i = drv.outputs.begin();
1361 : i != drv.outputs.end(); ++i)
1362 : {
1363 61 : Path path = i->second.path;
1364 61 : if (!pathExists(path)) {
1365 0 : throw BuildError(
1366 : format("builder for `%1%' failed to produce output path `%2%'")
1367 : % drvPath % path);
1368 : }
1369 :
1370 61 : struct stat st;
1371 61 : if (lstat(path.c_str(), &st))
1372 0 : throw SysError(format("getting attributes of path `%1%'") % path);
1373 :
1374 61 : startNest(nest, lvlTalkative,
1375 : format("scanning for references inside `%1%'") % path);
1376 :
1377 : /* Check that fixed-output derivations produced the right
1378 : outputs (i.e., the content hash should match the specified
1379 : hash). */
1380 61 : if (i->second.hash != "") {
1381 :
1382 6 : bool recursive = false;
1383 6 : string algo = i->second.hashAlgo;
1384 :
1385 6 : if (string(algo, 0, 2) == "r:") {
1386 3 : recursive = true;
1387 3 : algo = string(algo, 2);
1388 : }
1389 :
1390 6 : if (!recursive) {
1391 : /* The output path should be a regular file without
1392 : execute permission. */
1393 3 : if (!S_ISREG(st.st_mode) || (st.st_mode & S_IXUSR) != 0)
1394 0 : throw Error(
1395 : format("output path `%1% should be a non-executable regular file")
1396 : % path);
1397 : }
1398 :
1399 : /* Check the hash. */
1400 6 : HashType ht = parseHashType(algo);
1401 6 : if (ht == htUnknown)
1402 0 : throw Error(format("unknown hash algorithm `%1%'") % algo);
1403 6 : Hash h = parseHash(ht, i->second.hash);
1404 6 : Hash h2 = recursive ? hashPath(ht, path) : hashFile(ht, path);
1405 6 : if (h != h2)
1406 1 : throw Error(
1407 : format("output path `%1%' should have %2% hash `%3%', instead has `%4%'")
1408 : % path % algo % printHash(h) % printHash(h2));
1409 : }
1410 :
1411 : #ifndef __CYGWIN__
1412 : /* Check that the output is not group or world writable, as
1413 : that means that someone else can have interfered with the
1414 : build. Also, the output should be owned by the build
1415 : user. */
1416 60 : if ((st.st_mode & (S_IWGRP | S_IWOTH)) ||
1417 : (buildUser.getUID() != 0 && st.st_uid != buildUser.getUID()))
1418 0 : throw Error(format("suspicious ownership or permission on `%1%'; rejecting this build output") % path);
1419 : #endif
1420 :
1421 : /* Get rid of all weird permissions. */
1422 60 : canonicalisePathMetaData(path);
1423 :
1424 : /* For this output path, find the references to other paths contained
1425 : in it. */
1426 60 : PathSet references;
1427 60 : if (!pathExists(path + "/nix-support/no-scan")) {
1428 60 : references = scanForReferences(path, allPaths);
1429 :
1430 : /* For debugging, print out the referenced and
1431 : unreferenced paths. */
1432 185 : for (PathSet::iterator i = inputPaths.begin();
1433 : i != inputPaths.end(); ++i)
1434 : {
1435 125 : PathSet::iterator j = references.find(*i);
1436 125 : if (j == references.end())
1437 97 : debug(format("unreferenced input: `%1%'") % *i);
1438 : else
1439 28 : debug(format("referenced input: `%1%'") % *i);
1440 : }
1441 : }
1442 :
1443 60 : allReferences[path] = references;
1444 :
1445 : /* Hash the contents of the path. The hash is stored in the
1446 : database so that we can verify later on whether nobody has
1447 : messed with the store. !!! inefficient: it would be nice
1448 : if we could combine this with filterReferences(). */
1449 60 : contentHashes[path] = hashPath(htSHA256, path);
1450 : }
1451 :
1452 : /* Register each output path as valid, and register the sets of
1453 : paths referenced by each of them. This is wrapped in one
1454 : database transaction to ensure that if we crash, either
1455 : everything is registered or nothing is. This is for
1456 : recoverability: unregistered paths in the store can be deleted
1457 : arbitrarily, while registered paths can only be deleted by
1458 : running the garbage collector.
1459 :
1460 : The reason that we do the transaction here and not on the fly
1461 : while we are scanning (above) is so that we don't hold database
1462 : locks for too long. */
1463 60 : Transaction txn;
1464 60 : createStoreTransaction(txn);
1465 120 : for (DerivationOutputs::iterator i = drv.outputs.begin();
1466 : i != drv.outputs.end(); ++i)
1467 : {
1468 60 : registerValidPath(txn, i->second.path,
1469 : contentHashes[i->second.path],
1470 : allReferences[i->second.path],
1471 : drvPath);
1472 : }
1473 60 : txn.commit();
1474 :
1475 : /* It is now safe to delete the lock files, since all future
1476 : lockers will see that the output paths are valid; they will not
1477 : create new lock files with the same names as the old (unlinked)
1478 : lock files. */
1479 60 : outputLocks.setDeletion(true);
1480 : }
1481 :
1482 :
1483 345 : static string drvsLogDir = "drvs";
1484 :
1485 :
1486 : void DerivationGoal::openLogFile()
1487 64 : {
1488 : /* Create a log file. */
1489 64 : Path dir = (format("%1%/%2%") % nixLogDir % drvsLogDir).str();
1490 64 : createDirs(dir);
1491 :
1492 64 : Path logFileName = (format("%1%/%2%") % dir % baseNameOf(drvPath)).str();
1493 64 : fdLogFile = open(logFileName.c_str(),
1494 : O_CREAT | O_WRONLY | O_TRUNC, 0666);
1495 64 : if (fdLogFile == -1)
1496 0 : throw SysError(format("creating log file `%1%'") % logFileName);
1497 :
1498 : /* Create a pipe to get the output of the child. */
1499 64 : logPipe.create();
1500 : }
1501 :
1502 :
1503 : void DerivationGoal::initChild()
1504 64 : {
1505 64 : commonChildInit(logPipe);
1506 :
1507 64 : if (chdir(tmpDir.c_str()) == -1)
1508 0 : throw SysError(format("changing into `%1%'") % tmpDir);
1509 :
1510 : /* When running a hook, dup the communication pipes. */
1511 64 : bool inHook = fromHook.writeSide.isOpen();
1512 64 : if (inHook) {
1513 3 : fromHook.readSide.close();
1514 3 : if (dup2(fromHook.writeSide, 3) == -1)
1515 0 : throw SysError("dupping from-hook write side");
1516 :
1517 3 : toHook.writeSide.close();
1518 3 : if (dup2(toHook.readSide, 4) == -1)
1519 0 : throw SysError("dupping to-hook read side");
1520 : }
1521 :
1522 : /* Close all other file descriptors. */
1523 64 : int maxFD = 0;
1524 64 : maxFD = sysconf(_SC_OPEN_MAX);
1525 65600 : for (int fd = 0; fd < maxFD; ++fd)
1526 65536 : if (fd != STDIN_FILENO && fd != STDOUT_FILENO && fd != STDERR_FILENO
1527 : && (!inHook || (fd != 3 && fd != 4)))
1528 65338 : close(fd); /* ignore result */
1529 : }
1530 :
1531 :
1532 : void DerivationGoal::deleteTmpDir(bool force)
1533 182 : {
1534 182 : if (tmpDir != "") {
1535 64 : if (keepFailed && !force)
1536 0 : printMsg(lvlError,
1537 : format("builder for `%1%' failed; keeping build directory `%2%'")
1538 : % drvPath % tmpDir);
1539 : else
1540 64 : deletePath(tmpDir);
1541 64 : tmpDir = "";
1542 : }
1543 : }
1544 :
1545 :
1546 : void DerivationGoal::handleChildOutput(int fd, const string & data)
1547 193 : {
1548 193 : if (fd == logPipe.readSide) {
1549 193 : if (verbosity >= buildVerbosity)
1550 193 : writeFull(STDERR_FILENO, (unsigned char *) data.c_str(), data.size());
1551 193 : writeFull(fdLogFile, (unsigned char *) data.c_str(), data.size());
1552 : }
1553 :
1554 0 : else abort();
1555 : }
1556 :
1557 :
1558 : void DerivationGoal::handleEOF(int fd)
1559 62 : {
1560 62 : if (fd == logPipe.readSide) worker.wakeUp(shared_from_this());
1561 : }
1562 :
1563 :
1564 : PathSet DerivationGoal::checkPathValidity(bool returnValid)
1565 293 : {
1566 293 : PathSet result;
1567 586 : for (DerivationOutputs::iterator i = drv.outputs.begin();
1568 : i != drv.outputs.end(); ++i)
1569 293 : if (isValidPath(i->second.path)) {
1570 56 : if (returnValid) result.insert(i->second.path);
1571 : } else {
1572 237 : if (!returnValid) result.insert(i->second.path);
1573 : }
1574 0 : return result;
1575 : }
1576 :
1577 :
1578 :
1579 : //////////////////////////////////////////////////////////////////////
1580 :
1581 :
1582 : class SubstitutionGoal : public Goal
1583 : {
1584 : private:
1585 : /* The store path that should be realised through a substitute. */
1586 : Path storePath;
1587 :
1588 : /* The remaining substitutes for this path. */
1589 : Substitutes subs;
1590 :
1591 : /* The current substitute. */
1592 : Substitute sub;
1593 :
1594 : /* Outgoing references for this path. */
1595 : PathSet references;
1596 :
1597 : /* Pipe for the substitute's standard output/error. */
1598 : Pipe logPipe;
1599 :
1600 : /* The process ID of the builder. */
1601 : Pid pid;
1602 :
1603 : /* Lock on the store path. */
1604 : boost::shared_ptr<PathLocks> outputLock;
1605 :
1606 : typedef void (SubstitutionGoal::*GoalState)();
1607 : GoalState state;
1608 :
1609 : public:
1610 : SubstitutionGoal(const Path & storePath, Worker & worker);
1611 : ~SubstitutionGoal();
1612 :
1613 : void work();
1614 :
1615 : /* The states. */
1616 : void init();
1617 : void referencesValid();
1618 : void tryNext();
1619 : void tryToRun();
1620 : void finished();
1621 :
1622 : /* Callback used by the worker to write to the log. */
1623 : void handleChildOutput(int fd, const string & data);
1624 : void handleEOF(int fd);
1625 : };
1626 :
1627 :
1628 : SubstitutionGoal::SubstitutionGoal(const Path & storePath, Worker & worker)
1629 215 : : Goal(worker)
1630 677 : {
1631 215 : this->storePath = storePath;
1632 215 : state = &SubstitutionGoal::init;
1633 215 : name = (format("substitution of `%1%'") % storePath).str();
1634 215 : trace("created");
1635 : }
1636 :
1637 :
1638 : SubstitutionGoal::~SubstitutionGoal()
1639 430 : {
1640 215 : if (pid != -1) worker.childTerminated(pid);
1641 215 : }
1642 :
1643 :
1644 : void SubstitutionGoal::work()
1645 257 : {
1646 257 : (this->*state)();
1647 : }
1648 :
1649 :
1650 : void SubstitutionGoal::init()
1651 215 : {
1652 215 : trace("init");
1653 :
1654 215 : addTempRoot(storePath);
1655 :
1656 : /* If the path already exists we're done. */
1657 215 : if (isValidPath(storePath)) {
1658 200 : amDone();
1659 200 : return;
1660 : }
1661 :
1662 : /* Read the substitutes. */
1663 15 : subs = querySubstitutes(noTxn, storePath);
1664 :
1665 : /* To maintain the closure invariant, we first have to realise the
1666 : paths referenced by this one. */
1667 15 : queryReferences(noTxn, storePath, references);
1668 :
1669 26 : for (PathSet::iterator i = references.begin();
1670 : i != references.end(); ++i)
1671 11 : if (*i != storePath) /* ignore self-references */
1672 8 : addWaitee(worker.makeSubstitutionGoal(*i));
1673 :
1674 15 : if (waitees.empty()) /* to prevent hang (no wake-up event) */
1675 9 : referencesValid();
1676 : else
1677 6 : state = &SubstitutionGoal::referencesValid;
1678 : }
1679 :
1680 :
1681 : void SubstitutionGoal::referencesValid()
1682 15 : {
1683 15 : trace("all referenced realised");
1684 :
1685 15 : if (nrFailed > 0)
1686 0 : throw Error(format("some references of path `%1%' could not be realised") % storePath);
1687 :
1688 26 : for (PathSet::iterator i = references.begin();
1689 : i != references.end(); ++i)
1690 11 : if (*i != storePath) /* ignore self-references */
1691 8 : assert(isValidPath(*i));
1692 :
1693 15 : tryNext();
1694 : }
1695 :
1696 :
1697 : void SubstitutionGoal::tryNext()
1698 17 : {
1699 17 : trace("trying next substitute");
1700 :
1701 17 : if (subs.size() == 0) {
1702 : /* None left. Terminate this goal and let someone else deal
1703 : with it. */
1704 1 : printMsg(lvlError,
1705 : format("path `%1%' is required, but it has no (remaining) substitutes")
1706 : % storePath);
1707 1 : amDone(false);
1708 1 : return;
1709 : }
1710 16 : sub = subs.front();
1711 16 : subs.pop_front();
1712 :
1713 : /* Wait until we can run the substitute program. */
1714 16 : state = &SubstitutionGoal::tryToRun;
1715 16 : worker.waitForBuildSlot(shared_from_this());
1716 : }
1717 :
1718 :
1719 : void SubstitutionGoal::tryToRun()
1720 18 : {
1721 18 : trace("trying to run");
1722 :
1723 : /* Make sure that we are allowed to start a build. */
1724 18 : if (!worker.canBuildMore()) {
1725 2 : worker.waitForBuildSlot(shared_from_this());
1726 2 : return;
1727 : }
1728 :
1729 : /* Acquire a lock on the output path. */
1730 16 : outputLock = boost::shared_ptr<PathLocks>(new PathLocks);
1731 16 : outputLock->lockPaths(singleton<PathSet>(storePath),
1732 : (format("waiting for lock on `%1%'") % storePath).str());
1733 :
1734 : /* Check again whether the path is invalid. */
1735 16 : if (isValidPath(storePath)) {
1736 0 : debug(format("store path `%1%' has become valid") % storePath);
1737 0 : outputLock->setDeletion(true);
1738 0 : amDone();
1739 0 : return;
1740 : }
1741 :
1742 16 : printMsg(lvlInfo,
1743 : format("substituting path `%1%' using substituter `%2%'")
1744 : % storePath % sub.program);
1745 :
1746 16 : logPipe.create();
1747 :
1748 : /* Remove the (stale) output path if it exists. */
1749 16 : if (pathExists(storePath))
1750 0 : deletePath(storePath);
1751 :
1752 : /* Fork the substitute program. */
1753 16 : pid = fork();
1754 32 : switch (pid) {
1755 :
1756 : case -1:
1757 0 : throw SysError("unable to fork");
1758 :
1759 : case 0:
1760 16 : try { /* child */
1761 :
1762 16 : logPipe.readSide.close();
1763 :
1764 : /* !!! close other handles */
1765 :
1766 16 : commonChildInit(logPipe);
1767 :
1768 : /* Fill in the arguments. */
1769 16 : Strings args(sub.args);
1770 16 : args.push_front(storePath);
1771 16 : args.push_front(baseNameOf(sub.program));
1772 16 : const char * * argArr = strings2CharPtrs(args);
1773 :
1774 16 : execv(sub.program.c_str(), (char * *) argArr);
1775 :
1776 0 : throw SysError(format("executing `%1%'") % sub.program);
1777 :
1778 0 : } catch (std::exception & e) {
1779 0 : std::cerr << format("substitute error: %1%\n") % e.what();
1780 : }
1781 0 : quickExit(1);
1782 : }
1783 :
1784 : /* parent */
1785 16 : pid.setSeparatePG(true);
1786 16 : logPipe.writeSide.close();
1787 16 : worker.childStarted(shared_from_this(),
1788 : pid, singleton<set<int> >(logPipe.readSide), true);
1789 :
1790 16 : state = &SubstitutionGoal::finished;
1791 : }
1792 :
1793 :
1794 : void SubstitutionGoal::finished()
1795 16 : {
1796 16 : trace("substitute finished");
1797 :
1798 : /* Since we got an EOF on the logger pipe, the substitute is
1799 : presumed to have terminated. */
1800 : /* !!! this could block! */
1801 16 : pid_t savedPid = pid;
1802 16 : int status = pid.wait(true);
1803 :
1804 : /* So the child is gone now. */
1805 16 : worker.childTerminated(savedPid);
1806 :
1807 : /* Close the read side of the logger pipe. */
1808 16 : logPipe.readSide.close();
1809 :
1810 16 : debug(format("substitute for `%1%' finished") % storePath);
1811 :
1812 : /* Check the exit status and the build result. */
1813 16 : try {
1814 :
1815 16 : if (!statusOk(status))
1816 2 : throw SubstError(format("builder for `%1%' %2%")
1817 4 : % storePath % statusToString(status));
1818 :
1819 14 : if (!pathExists(storePath))
1820 0 : throw SubstError(
1821 : format("substitute did not produce path `%1%'")
1822 : % storePath);
1823 :
1824 2 : } catch (SubstError & e) {
1825 :
1826 2 : printMsg(lvlInfo,
1827 : format("substitution of path `%1%' using substituter `%2%' failed: %3%")
1828 : % storePath % sub.program % e.msg());
1829 :
1830 : /* Try the next substitute. */
1831 2 : state = &SubstitutionGoal::tryNext;
1832 2 : worker.wakeUp(shared_from_this());
1833 2 : return;
1834 : }
1835 :
1836 14 : canonicalisePathMetaData(storePath);
1837 :
1838 14 : Hash contentHash = hashPath(htSHA256, storePath);
1839 :
1840 14 : Transaction txn;
1841 14 : createStoreTransaction(txn);
1842 14 : registerValidPath(txn, storePath, contentHash,
1843 : references, sub.deriver);
1844 14 : txn.commit();
1845 :
1846 14 : outputLock->setDeletion(true);
1847 :
1848 14 : printMsg(lvlChatty,
1849 : format("substitution of path `%1%' succeeded") % storePath);
1850 :
1851 14 : amDone();
1852 : }
1853 :
1854 :
1855 : void SubstitutionGoal::handleChildOutput(int fd, const string & data)
1856 3846 : {
1857 3846 : assert(fd == logPipe.readSide);
1858 3846 : if (verbosity >= buildVerbosity)
1859 3846 : writeFull(STDERR_FILENO, (unsigned char *) data.c_str(), data.size());
1860 : /* Don't write substitution output to a log file for now. We
1861 : probably should, though. */
1862 : }
1863 :
1864 :
1865 : void SubstitutionGoal::handleEOF(int fd)
1866 16 : {
1867 16 : if (fd == logPipe.readSide) worker.wakeUp(shared_from_this());
1868 : }
1869 :
1870 :
1871 :
1872 : //////////////////////////////////////////////////////////////////////
1873 :
1874 :
1875 : static bool working = false;
1876 :
1877 :
1878 : Worker::Worker()
1879 584 : {
1880 : /* Debugging: prevent recursive workers. */
1881 73 : if (working) abort();
1882 73 : working = true;
1883 73 : nrChildren = 0;
1884 : }
1885 :
1886 :
1887 : Worker::~Worker()
1888 146 : {
1889 73 : working = false;
1890 :
1891 : /* Explicitly get rid of all strong pointers now. After this all
1892 : goals that refer to this worker should be gone. (Otherwise we
1893 : are in trouble, since goals may call childTerminated() etc. in
1894 : their destructors). */
1895 73 : topGoals.clear();
1896 : }
1897 :
1898 :
1899 : template<class T>
1900 : static GoalPtr addGoal(const Path & path,
1901 : Worker & worker, WeakGoalMap & goalMap)
1902 370 : {
1903 370 : GoalPtr goal = goalMap[path].lock();
1904 370 : if (!goal) {
1905 333 : goal = GoalPtr(new T(path, worker));
1906 333 : goalMap[path] = goal;
1907 333 : worker.wakeUp(goal);
1908 : }
1909 333 : return goal;
1910 : }
1911 :
1912 :
1913 : GoalPtr Worker::makeDerivationGoal(const Path & nePath)
1914 131 : {
1915 131 : return addGoal<DerivationGoal>(nePath, *this, derivationGoals);
1916 : }
1917 :
1918 :
1919 : GoalPtr Worker::makeSubstitutionGoal(const Path & storePath)
1920 239 : {
1921 239 : return addGoal<SubstitutionGoal>(storePath, *this, substitutionGoals);
1922 : }
1923 :
1924 :
1925 : static void removeGoal(GoalPtr goal, WeakGoalMap & goalMap)
1926 664 : {
1927 : /* !!! inefficient */
1928 1578 : for (WeakGoalMap::iterator i = goalMap.begin();
1929 : i != goalMap.end(); )
1930 1246 : if (i->second.lock() == goal) {
1931 332 : WeakGoalMap::iterator j = i; ++j;
1932 332 : goalMap.erase(i);
1933 332 : i = j;
1934 : }
1935 914 : else ++i;
1936 : }
1937 :
1938 :
1939 : void Worker::removeGoal(GoalPtr goal)
1940 332 : {
1941 332 : nix::removeGoal(goal, derivationGoals);
1942 332 : nix::removeGoal(goal, substitutionGoals);
1943 332 : if (topGoals.find(goal) != topGoals.end()) {
1944 78 : topGoals.erase(goal);
1945 : /* If a top-level goal failed, then kill all other goals
1946 : (unless keepGoing was set). */
1947 78 : if (goal->getExitCode() == Goal::ecFailed && !keepGoing)
1948 1 : topGoals.clear();
1949 : }
1950 : }
1951 :
1952 :
1953 : void Worker::wakeUp(GoalPtr goal)
1954 782 : {
1955 782 : goal->trace("woken up");
1956 782 : awake.insert(goal);
1957 : }
1958 :
1959 :
1960 : bool Worker::canBuildMore()
1961 223 : {
1962 223 : return nrChildren < maxBuildJobs;
1963 : }
1964 :
1965 :
1966 : void Worker::childStarted(GoalPtr goal,
1967 : pid_t pid, const set<int> & fds, bool inBuildSlot)
1968 80 : {
1969 1040 : Child child;
1970 80 : child.goal = goal;
1971 80 : child.fds = fds;
1972 80 : child.inBuildSlot = inBuildSlot;
1973 400 : children[pid] = child;
1974 80 : if (inBuildSlot) nrChildren++;
1975 : }
1976 :
1977 :
1978 : void Worker::childTerminated(pid_t pid, bool wakeSleepers)
1979 80 : {
1980 80 : Children::iterator i = children.find(pid);
1981 80 : assert(i != children.end());
1982 :
1983 80 : if (i->second.inBuildSlot) {
1984 77 : assert(nrChildren > 0);
1985 77 : nrChildren--;
1986 : }
1987 :
1988 80 : children.erase(pid);
1989 :
1990 80 : if (wakeSleepers) {
1991 :
1992 : /* Wake up goals waiting for a build slot. */
1993 134 : for (WeakGoals::iterator i = wantingToBuild.begin();
1994 : i != wantingToBuild.end(); ++i)
1995 : {
1996 56 : GoalPtr goal = i->lock();
1997 56 : if (goal) wakeUp(goal);
1998 : }
1999 :
2000 78 : wantingToBuild.clear();
2001 :
2002 : }
2003 : }
2004 :
2005 :
2006 : void Worker::waitForBuildSlot(GoalPtr goal, bool reallyWait)
2007 69 : {
2008 69 : debug("wait for build slot");
2009 69 : if (reallyWait && children.size() == 0)
2010 0 : throw Error("waiting for a build slot, yet there are no children - "
2011 : "maybe the build hook gave an inappropriate `postpone' reply?");
2012 69 : if (!reallyWait && canBuildMore())
2013 13 : wakeUp(goal); /* we can do it right away */
2014 : else
2015 56 : wantingToBuild.insert(goal);
2016 : }
2017 :
2018 :
2019 : void Worker::run(const Goals & _topGoals)
2020 73 : {
2021 152 : for (Goals::iterator i = _topGoals.begin();
2022 : i != _topGoals.end(); ++i)
2023 79 : topGoals.insert(*i);
2024 :
2025 73 : startNest(nest, lvlDebug, format("entered goal loop"));
2026 :
2027 4189 : while (1) {
2028 :
2029 4189 : checkInterrupt();
2030 :
2031 : /* Call every wake goal. */
2032 4729 : while (!awake.empty() && !topGoals.empty()) {
2033 541 : WeakGoals awake2(awake);
2034 541 : awake.clear();
2035 1258 : for (WeakGoals::iterator i = awake2.begin(); i != awake2.end(); ++i) {
2036 782 : checkInterrupt();
2037 782 : GoalPtr goal = i->lock();
2038 782 : if (goal) goal->work();
2039 781 : if (topGoals.empty()) break;
2040 : }
2041 : }
2042 :
2043 4188 : if (topGoals.empty()) break;
2044 :
2045 : /* !!! not when we're polling */
2046 4116 : assert(!children.empty());
2047 :
2048 : /* Wait for input. */
2049 4116 : waitForInput();
2050 : }
2051 :
2052 : /* If --keep-going is not set, it's possible that the main goal
2053 : exited while some of its subgoals were still active. But if
2054 : --keep-going *is* set, then they must all be finished now. */
2055 72 : assert(!keepGoing || awake.empty());
2056 72 : assert(!keepGoing || wantingToBuild.empty());
2057 72 : assert(!keepGoing || children.empty());
2058 : }
2059 :
2060 :
2061 : void Worker::waitForInput()
2062 4116 : {
2063 4116 : printMsg(lvlVomit, "waiting for children");
2064 :
2065 : /* Process output from the file descriptors attached to the
2066 : children, namely log output and output path creation commands.
2067 : We also use this to detect child termination: if we get EOF on
2068 : the logger pipe of a build, we assume that the builder has
2069 : terminated. */
2070 :
2071 : /* Use select() to wait for the input side of any logger pipe to
2072 : become `available'. Note that `available' (i.e., non-blocking)
2073 : includes EOF. */
2074 4116 : fd_set fds;
2075 4116 : FD_ZERO(&fds);
2076 4116 : int fdMax = 0;
2077 8287 : for (Children::iterator i = children.begin();
2078 : i != children.end(); ++i)
2079 : {
2080 8342 : for (set<int>::iterator j = i->second.fds.begin();
2081 : j != i->second.fds.end(); ++j)
2082 : {
2083 4171 : FD_SET(*j, &fds);
2084 4171 : if (*j >= fdMax) fdMax = *j + 1;
2085 : }
2086 : }
2087 :
2088 4116 : if (select(fdMax, &fds, 0, 0, 0) == -1) {
2089 0 : if (errno == EINTR) return;
2090 0 : throw SysError("waiting for input");
2091 : }
2092 :
2093 : /* Process all available file descriptors. */
2094 8287 : for (Children::iterator i = children.begin();
2095 : i != children.end(); ++i)
2096 : {
2097 4171 : checkInterrupt();
2098 4171 : GoalPtr goal = i->second.goal.lock();
2099 4171 : assert(goal);
2100 4171 : set<int> fds2(i->second.fds);
2101 8342 : for (set<int>::iterator j = fds2.begin(); j != fds2.end(); ++j)
2102 : {
2103 4171 : if (FD_ISSET(*j, &fds)) {
2104 4117 : unsigned char buffer[4096];
2105 4117 : ssize_t rd = read(*j, buffer, sizeof(buffer));
2106 4117 : if (rd == -1) {
2107 0 : if (errno != EINTR)
2108 0 : throw SysError(format("reading from %1%")
2109 : % goal->getName());
2110 4117 : } else if (rd == 0) {
2111 78 : debug(format("%1%: got EOF") % goal->getName());
2112 78 : goal->handleEOF(*j);
2113 78 : i->second.fds.erase(*j);
2114 : } else {
2115 4039 : printMsg(lvlVomit, format("%1%: read %2% bytes")
2116 : % goal->getName() % rd);
2117 4039 : string data((char *) buffer, rd);
2118 4039 : goal->handleChildOutput(*j, data);
2119 : }
2120 : }
2121 : }
2122 : }
2123 : }
2124 :
2125 :
2126 :
2127 : //////////////////////////////////////////////////////////////////////
2128 :
2129 :
2130 : void buildDerivations(const PathSet & drvPaths)
2131 71 : {
2132 71 : startNest(nest, lvlDebug,
2133 : format("building %1%") % showPaths(drvPaths));
2134 :
2135 71 : Worker worker;
2136 :
2137 71 : Goals goals;
2138 148 : for (PathSet::const_iterator i = drvPaths.begin();
2139 : i != drvPaths.end(); ++i)
2140 77 : goals.insert(worker.makeDerivationGoal(*i));
2141 :
2142 71 : worker.run(goals);
2143 :
2144 70 : PathSet failed;
2145 146 : for (Goals::iterator i = goals.begin(); i != goals.end(); ++i)
2146 76 : if ((*i)->getExitCode() == Goal::ecFailed) {
2147 1 : DerivationGoal * i2 = dynamic_cast<DerivationGoal *>(i->get());
2148 1 : assert(i2);
2149 1 : failed.insert(i2->getDrvPath());
2150 : }
2151 :
2152 70 : if (!failed.empty())
2153 1 : throw Error(format("build of %1% failed") % showPaths(failed));
2154 : }
2155 :
2156 :
2157 : void ensurePath(const Path & path)
2158 209 : {
2159 : /* If the path is already valid, we're done. */
2160 209 : if (isValidPath(path)) return;
2161 :
2162 2 : Worker worker;
2163 2 : GoalPtr goal = worker.makeSubstitutionGoal(path);
2164 2 : Goals goals = singleton<Goals>(goal);
2165 :
2166 2 : worker.run(goals);
2167 :
2168 2 : if (goal->getExitCode() != Goal::ecSuccess)
2169 0 : throw Error(format("path `%1%' does not exist and cannot be created") % path);
2170 : }
2171 :
2172 :
2173 345 : }
|