LTP GCOV extension - code coverage report
Current view: directory - src/libstore - build.cc
Test: app.info
Date: 2008-11-20 Instrumented lines: 1089
Code covered: 64.6 % Executed lines: 703

       1                 : #include "references.hh"
       2                 : #include "pathlocks.hh"
       3                 : #include "misc.hh"
       4                 : #include "globals.hh"
       5                 : #include "local-store.hh"
       6                 : #include "util.hh"
       7                 : 
       8                 : #include <map>
       9                 : #include <iostream>
      10                 : #include <sstream>
      11                 : #include <boost/shared_ptr.hpp>
      12                 : #include <boost/weak_ptr.hpp>
      13                 : #include <boost/enable_shared_from_this.hpp>
      14                 : 
      15                 : #include <time.h>
      16                 : #include <sys/time.h>
      17                 : #include <sys/types.h>
      18                 : #include <sys/stat.h>
      19                 : #include <fcntl.h>
      20                 : #include <unistd.h>
      21                 : #include <errno.h>
      22                 : 
      23                 : #include <pwd.h>
      24                 : #include <grp.h>
      25                 : 
      26                 : 
      27                 : /* Includes required for chroot support. */
      28                 : #include "config.h"
      29                 : 
      30                 : #if HAVE_SYS_PARAM_H
      31                 : #include <sys/param.h>
      32                 : #endif
      33                 : #if HAVE_SYS_MOUNT_H
      34                 : #include <sys/mount.h>
      35                 : #endif
      36                 : 
      37                 : #define CHROOT_ENABLED HAVE_CHROOT && HAVE_SYS_MOUNT_H && defined(MS_BIND)
      38                 : 
      39                 : 
      40                 : namespace nix {
      41                 : 
      42                 : using std::map;
      43                 :     
      44                 : 
      45                 : /* !!! TODO derivationFromPath shouldn't be used here */
      46                 : 
      47                 : 
      48            1704 : static string pathNullDevice = "/dev/null";
      49                 : 
      50                 : 
      51                 : static const uid_t rootUserId = 0;
      52                 : 
      53                 : 
      54                 : /* Forward definition. */
      55                 : class Worker;
      56                 : 
      57                 : 
      58                 : /* A pointer to a goal. */
      59                 : class Goal;
      60                 : typedef boost::shared_ptr<Goal> GoalPtr;
      61                 : typedef boost::weak_ptr<Goal> WeakGoalPtr;
      62                 : 
      63                 : /* Set of goals. */
      64                 : typedef set<GoalPtr> Goals;
      65                 : typedef set<WeakGoalPtr> WeakGoals;
      66                 : 
      67                 : /* A map of paths to goals (and the other way around). */
      68                 : typedef map<Path, WeakGoalPtr> WeakGoalMap;
      69                 : 
      70                 : 
      71                 : 
      72                 : class Goal : public boost::enable_shared_from_this<Goal>
      73                 : {
      74                 : public:
      75                 :     typedef enum {ecBusy, ecSuccess, ecFailed} ExitCode;
      76                 :     
      77                 : protected:
      78                 :     
      79                 :     /* Backlink to the worker. */
      80                 :     Worker & worker;
      81                 : 
      82                 :     /* Goals that this goal is waiting for. */
      83                 :     Goals waitees;
      84                 : 
      85                 :     /* Goals waiting for this one to finish.  Must use weak pointers
      86                 :        here to prevent cycles. */
      87                 :     WeakGoals waiters;
      88                 : 
      89                 :     /* Number of goals we are/were waiting for that have failed. */
      90                 :     unsigned int nrFailed;
      91                 : 
      92                 :     /* Name of this goal for debugging purposes. */
      93                 :     string name;
      94                 : 
      95                 :     /* Whether the goal is finished. */
      96                 :     ExitCode exitCode;
      97                 : 
      98             611 :     Goal(Worker & worker) : worker(worker)
      99                 :     {
     100             611 :         nrFailed = 0;
     101             611 :         exitCode = ecBusy;
     102             611 :         forceInputs = false;
     103             611 :     }
     104                 : 
     105             611 :     virtual ~Goal()
     106             611 :     {
     107             611 :         trace("goal destroyed");
     108             611 :     }
     109                 : 
     110                 :     bool forceInputs;
     111                 : 
     112                 : public:
     113                 :     virtual void work() = 0;
     114                 : 
     115                 :     void addWaitee(GoalPtr waitee);
     116                 : 
     117                 :     virtual void waiteeDone(GoalPtr waitee, ExitCode result);
     118                 : 
     119               0 :     virtual void handleChildOutput(int fd, const string & data)
     120                 :     {
     121               0 :         abort();
     122                 :     }
     123                 : 
     124               0 :     virtual void handleEOF(int fd)
     125                 :     {
     126               0 :         abort();
     127                 :     }
     128                 : 
     129                 :     void trace(const format & f);
     130                 : 
     131               0 :     string getName()
     132                 :     {
     133               0 :         return name;
     134                 :     }
     135                 :     
     136             334 :     ExitCode getExitCode()
     137                 :     {
     138             334 :         return exitCode;
     139                 :     }
     140                 : 
     141                 :     /* Cancel the goal.  It should wake up its waiters, get rid of any
     142                 :        running child processes that are being monitored by the worker
     143                 :        (important!), etc. */
     144                 :     virtual void cancel() = 0;
     145                 : 
     146              68 :     void setForceInputs(bool x)
     147                 :     {
     148              68 :        forceInputs = x;
     149              68 :     }
     150                 : 
     151                 : protected:
     152                 :     void amDone(ExitCode result);
     153                 : };
     154                 : 
     155                 : 
     156                 : /* A mapping used to remember for each child process to what goal it
     157                 :    belongs, and file descriptors for receiving log data and output
     158                 :    path creation commands. */
     159                 : struct Child
     160            1188 : {
     161                 :     WeakGoalPtr goal;
     162                 :     set<int> fds;
     163                 :     bool inBuildSlot;
     164                 :     time_t lastOutput; /* time we last got output on stdout/stderr */
     165                 : };
     166                 : 
     167                 : typedef map<pid_t, Child> Children;
     168                 : 
     169                 : 
     170                 : /* The worker class. */
     171                 : class Worker
     172                 : {
     173                 : private:
     174                 : 
     175                 :     /* Note: the worker should only have strong pointers to the
     176                 :        top-level goals. */
     177                 : 
     178                 :     /* The top-level goals of the worker. */
     179                 :     Goals topGoals;
     180                 : 
     181                 :     /* Goals that are ready to do some work. */
     182                 :     WeakGoals awake;
     183                 : 
     184                 :     /* Goals waiting for a build slot. */
     185                 :     WeakGoals wantingToBuild;
     186                 : 
     187                 :     /* Child processes currently running. */
     188                 :     Children children;
     189                 : 
     190                 :     /* Number of build slots occupied.  Not all child processes
     191                 :        (namely build hooks) count as occupied build slots. */
     192                 :     unsigned int nrChildren;
     193                 : 
     194                 :     /* Maps used to prevent multiple instantiations of a goal for the
     195                 :        same derivation / path. */
     196                 :     WeakGoalMap derivationGoals;
     197                 :     WeakGoalMap substitutionGoals;
     198                 : 
     199                 :     /* Goals waiting for busy paths to be unlocked. */
     200                 :     WeakGoals waitingForAnyGoal;
     201                 :     
     202                 : public:
     203                 : 
     204                 :     LocalStore & store;
     205                 : 
     206                 :     Worker(LocalStore & store);
     207                 :     ~Worker();
     208                 : 
     209                 :     /* Make a goal (with caching). */
     210                 :     GoalPtr makeDerivationGoal(const Path & drvPath);
     211                 :     GoalPtr makeSubstitutionGoal(const Path & storePath);
     212                 : 
     213                 :     /* Remove a dead goal. */
     214                 :     void removeGoal(GoalPtr goal);
     215                 : 
     216                 :     /* Wake up a goal (i.e., there is something for it to do). */
     217                 :     void wakeUp(GoalPtr goal);
     218                 : 
     219                 :     /* Can we start another child process? */
     220                 :     bool canBuildMore();
     221                 : 
     222                 :     /* Registers a running child process.  `inBuildSlot' means that
     223                 :        the process counts towards the jobs limit. */
     224                 :     void childStarted(GoalPtr goal, pid_t pid,
     225                 :         const set<int> & fds, bool inBuildSlot);
     226                 : 
     227                 :     /* Unregisters a running child process.  `wakeSleepers' should be
     228                 :        false if there is no sense in waking up goals that are sleeping
     229                 :        because they can't run yet (e.g., there is no free build slot,
     230                 :        or the hook would still say `postpone'). */
     231                 :     void childTerminated(pid_t pid, bool wakeSleepers = true);
     232                 : 
     233                 :     /* Put `goal' to sleep until a build slot becomes available (which
     234                 :        might be right away). */
     235                 :     void waitForBuildSlot(GoalPtr goal);
     236                 : 
     237                 :     /* Put `goal' to sleep until a child process terminates, i.e., a
     238                 :        call is made to childTerminate(..., true).  */
     239                 :     void waitForChildTermination(GoalPtr goal);
     240                 : 
     241                 :     /* Wait for any goal to finish.  Pretty indiscriminate way to
     242                 :        wait for some resource that some other goal is holding. */
     243                 :     void waitForAnyGoal(GoalPtr goal);
     244                 :     
     245                 :     /* Loop until the specified top-level goals have finished. */
     246                 :     void run(const Goals & topGoals);
     247                 : 
     248                 :     /* Wait for input to become available. */
     249                 :     void waitForInput();
     250                 :     
     251                 : };
     252                 : 
     253                 : 
     254               6 : MakeError(SubstError, Error)
     255              12 : MakeError(BuildError, Error)
     256                 : 
     257                 : 
     258                 : //////////////////////////////////////////////////////////////////////
     259                 : 
     260                 : 
     261             483 : void Goal::addWaitee(GoalPtr waitee)
     262                 : {
     263             483 :     waitees.insert(waitee);
     264             483 :     waitee->waiters.insert(shared_from_this());
     265             483 : }
     266                 : 
     267                 : 
     268             483 : void Goal::waiteeDone(GoalPtr waitee, ExitCode result)
     269                 : {
     270             483 :     assert(waitees.find(waitee) != waitees.end());
     271             483 :     waitees.erase(waitee);
     272                 : 
     273                 :     trace(format("waitee `%1%' done; %2% left") %
     274             483 :         waitee->name % waitees.size());
     275                 :     
     276             485 :     if (result == ecFailed) ++nrFailed;
     277                 :     
     278             483 :     if (waitees.empty() || (result == ecFailed && !keepGoing)) {
     279                 : 
     280                 :         /* If we failed and keepGoing is not set, we remove all
     281                 :            remaining waitees. */
     282             370 :         for (Goals::iterator i = waitees.begin(); i != waitees.end(); ++i) {
     283               0 :             GoalPtr goal = *i;
     284               0 :             WeakGoals waiters2;
     285               0 :             for (WeakGoals::iterator j = goal->waiters.begin();
     286                 :                  j != goal->waiters.end(); ++j)
     287               0 :                 if (j->lock() != shared_from_this())
     288               0 :                     waiters2.insert(*j);
     289               0 :             goal->waiters = waiters2;
     290                 :         }
     291             370 :         waitees.clear();
     292                 : 
     293             370 :         worker.wakeUp(shared_from_this());
     294                 :     }
     295             483 : }
     296                 : 
     297                 : 
     298             610 : void Goal::amDone(ExitCode result)
     299                 : {
     300             610 :     trace("done");
     301             610 :     assert(exitCode == ecBusy);
     302             610 :     assert(result == ecSuccess || result == ecFailed);
     303             610 :     exitCode = result;
     304            1093 :     for (WeakGoals::iterator i = waiters.begin(); i != waiters.end(); ++i) {
     305             483 :         GoalPtr goal = i->lock();
     306             483 :         if (goal) goal->waiteeDone(shared_from_this(), result);
     307                 :     }
     308             610 :     waiters.clear();
     309             610 :     worker.removeGoal(shared_from_this());
     310             610 : }
     311                 : 
     312                 : 
     313            5146 : void Goal::trace(const format & f)
     314                 : {
     315            5146 :     debug(format("%1%: %2%") % name % f);
     316            5146 : }
     317                 : 
     318                 : 
     319                 : 
     320                 : //////////////////////////////////////////////////////////////////////
     321                 : 
     322                 : 
     323                 : /* Common initialisation performed in child processes. */
     324               3 : void commonChildInit(Pipe & logPipe)
     325                 : {
     326                 :     /* Put the child in a separate session (and thus a separate
     327                 :        process group) so that it has no controlling terminal (meaning
     328                 :        that e.g. ssh cannot open /dev/tty) and it doesn't receive
     329                 :        terminal signals. */
     330               3 :     if (setsid() == -1)
     331               0 :         throw SysError(format("creating a new session"));
     332                 :     
     333                 :     /* Dup the write side of the logger pipe into stderr. */
     334               3 :     if (dup2(logPipe.writeSide, STDERR_FILENO) == -1)
     335               0 :         throw SysError("cannot pipe standard error into log file");
     336               3 :     logPipe.readSide.close();
     337                 :             
     338                 :     /* Dup stderr to stdout. */
     339               3 :     if (dup2(STDERR_FILENO, STDOUT_FILENO) == -1)
     340               0 :         throw SysError("cannot dup stderr into stdout");
     341                 : 
     342                 :     /* Reroute stdin to /dev/null. */
     343               3 :     int fdDevNull = open(pathNullDevice.c_str(), O_RDWR);
     344               3 :     if (fdDevNull == -1)
     345               0 :         throw SysError(format("cannot open `%1%'") % pathNullDevice);
     346               3 :     if (dup2(fdDevNull, STDIN_FILENO) == -1)
     347               0 :         throw SysError("cannot dup null device into stdin");
     348               3 : }
     349                 : 
     350                 : 
     351                 : /* Convert a string list to an array of char pointers.  Careful: the
     352                 :    string list should outlive the array. */
     353               0 : const char * * strings2CharPtrs(const Strings & ss)
     354                 : {
     355               0 :     const char * * arr = new const char * [ss.size() + 1];
     356               0 :     const char * * p = arr;
     357               0 :     for (Strings::const_iterator i = ss.begin(); i != ss.end(); ++i)
     358               0 :         *p++ = i->c_str();
     359               0 :     *p = 0;
     360               0 :     return arr;
     361                 : }
     362                 : 
     363                 : 
     364                 : /* Restore default handling of SIGPIPE, otherwise some programs will
     365                 :    randomly say "Broken pipe". */
     366               0 : static void restoreSIGPIPE() 
     367                 : {
     368                 :     struct sigaction act, oact;
     369               0 :     act.sa_handler = SIG_DFL;
     370               0 :     act.sa_flags = 0;
     371               0 :     if (sigaction(SIGPIPE, &act, &oact)) throw SysError("resetting SIGPIPE");
     372               0 : }
     373                 : 
     374                 : 
     375                 : //////////////////////////////////////////////////////////////////////
     376                 : 
     377                 : 
     378                 : class UserLock
     379                 : {
     380                 : private:
     381                 :     /* POSIX locks suck.  If we have a lock on a file, and we open and
     382                 :        close that file again (without closing the original file
     383                 :        descriptor), we lose the lock.  So we have to be *very* careful
     384                 :        not to open a lock file on which we are holding a lock. */
     385                 :     static PathSet lockedPaths; /* !!! not thread-safe */
     386                 : 
     387                 :     Path fnUserLock;
     388                 :     AutoCloseFD fdUserLock;
     389                 : 
     390                 :     string user;
     391                 :     uid_t uid;
     392                 :     gid_t gid;
     393                 :     
     394                 : public:
     395                 :     UserLock();
     396                 :     ~UserLock();
     397                 : 
     398                 :     void acquire();
     399                 :     void release();
     400                 : 
     401                 :     void kill();
     402                 : 
     403               0 :     string getUser() { return user; }
     404               0 :     uid_t getUID() { return uid; }
     405               0 :     uid_t getGID() { return gid; }
     406                 : 
     407             339 :     bool enabled() { return uid != 0; }
     408                 :         
     409                 : };
     410                 : 
     411                 : 
     412            1151 : PathSet UserLock::lockedPaths;
     413                 : 
     414                 : 
     415             220 : UserLock::UserLock()
     416                 : {
     417             220 :     uid = gid = 0;
     418             220 : }
     419                 : 
     420                 : 
     421             220 : UserLock::~UserLock()
     422                 : {
     423             220 :     release();
     424             220 : }
     425                 : 
     426                 : 
     427               0 : void UserLock::acquire()
     428                 : {
     429               0 :     assert(uid == 0);
     430                 : 
     431               0 :     string buildUsersGroup = querySetting("build-users-group", "");
     432               0 :     assert(buildUsersGroup != "");
     433                 : 
     434                 :     /* Get the members of the build-users-group. */
     435               0 :     struct group * gr = getgrnam(buildUsersGroup.c_str());
     436               0 :     if (!gr)
     437                 :         throw Error(format("the group `%1%' specified in `build-users-group' does not exist")
     438               0 :             % buildUsersGroup);
     439               0 :     gid = gr->gr_gid;
     440                 : 
     441                 :     /* Copy the result of getgrnam. */
     442               0 :     Strings users;
     443               0 :     for (char * * p = gr->gr_mem; *p; ++p) {
     444               0 :         debug(format("found build user `%1%'") % *p);
     445               0 :         users.push_back(*p);
     446                 :     }
     447                 : 
     448               0 :     if (users.empty())
     449                 :         throw Error(format("the build users group `%1%' has no members")
     450               0 :             % buildUsersGroup);
     451                 : 
     452                 :     /* Find a user account that isn't currently in use for another
     453                 :        build. */
     454               0 :     for (Strings::iterator i = users.begin(); i != users.end(); ++i) {
     455               0 :         debug(format("trying user `%1%'") % *i);
     456                 : 
     457               0 :         struct passwd * pw = getpwnam(i->c_str());
     458               0 :         if (!pw)
     459                 :             throw Error(format("the user `%1%' in the group `%2%' does not exist")
     460               0 :                 % *i % buildUsersGroup);
     461                 :         
     462               0 :         fnUserLock = (format("%1%/userpool/%2%") % nixStateDir % pw->pw_uid).str();
     463                 : 
     464               0 :         if (lockedPaths.find(fnUserLock) != lockedPaths.end())
     465                 :             /* We already have a lock on this one. */
     466               0 :             continue;
     467                 :         
     468               0 :         AutoCloseFD fd = open(fnUserLock.c_str(), O_RDWR | O_CREAT, 0600);
     469               0 :         if (fd == -1)
     470               0 :             throw SysError(format("opening user lock `%1%'") % fnUserLock);
     471                 : 
     472               0 :         if (lockFile(fd, ltWrite, false)) {
     473               0 :             fdUserLock = fd.borrow();
     474               0 :             lockedPaths.insert(fnUserLock);
     475               0 :             user = *i;
     476               0 :             uid = pw->pw_uid;
     477                 : 
     478                 :             /* Sanity check... */
     479               0 :             if (uid == getuid() || uid == geteuid())
     480                 :                 throw Error(format("the Nix user should not be a member of `%1%'")
     481               0 :                     % buildUsersGroup);
     482                 :             
     483                 :             return;
     484                 :         }
     485                 :     }
     486                 : 
     487                 :     throw BuildError(format("all build users are currently in use; "
     488                 :         "consider creating additional users and adding them to the `%1%' group")
     489               0 :         % buildUsersGroup);
     490                 : }
     491                 : 
     492                 : 
     493             327 : void UserLock::release()
     494                 : {
     495             327 :     if (uid == 0) return;
     496               0 :     fdUserLock.close(); /* releases lock */
     497               0 :     assert(lockedPaths.find(fnUserLock) != lockedPaths.end());
     498               0 :     lockedPaths.erase(fnUserLock);
     499               0 :     fnUserLock = "";
     500               0 :     uid = 0;
     501                 : }
     502                 : 
     503                 : 
     504                 : static void runSetuidHelper(const string & command,
     505               0 :     const string & arg)
     506                 : {
     507                 :     Path program = getEnv("NIX_SETUID_HELPER",
     508               0 :         nixLibexecDir + "/nix-setuid-helper");
     509                 :             
     510                 :     /* Fork. */
     511               0 :     Pid pid;
     512               0 :     pid = fork();
     513               0 :     switch (pid) {
     514                 : 
     515                 :     case -1:
     516               0 :         throw SysError("unable to fork");
     517                 : 
     518                 :     case 0: /* child */
     519                 :         try {
     520               0 :             std::vector<const char *> args; /* careful with c_str()! */
     521               0 :             args.push_back(program.c_str());
     522               0 :             args.push_back(command.c_str());
     523               0 :             args.push_back(arg.c_str());
     524               0 :             args.push_back(0);
     525                 : 
     526               0 :             restoreSIGPIPE();
     527                 :             
     528               0 :             execve(program.c_str(), (char * *) &args[0], 0);
     529               0 :             throw SysError(format("executing `%1%'") % program);
     530                 :         }
     531               0 :         catch (std::exception & e) {
     532               0 :             std::cerr << "error: " << e.what() << std::endl;
     533                 :         }
     534               0 :         quickExit(1);
     535                 :     }
     536                 : 
     537                 :     /* Parent. */
     538                 : 
     539                 :     /* Wait for the child to finish. */
     540               0 :     int status = pid.wait(true);
     541               0 :     if (!statusOk(status))
     542                 :         throw Error(format("program `%1%' %2%")
     543               0 :             % program % statusToString(status));
     544               0 : }
     545                 : 
     546                 : 
     547               0 : void UserLock::kill()
     548                 : {
     549               0 :     assert(enabled());
     550               0 :     if (amPrivileged())
     551               0 :         killUser(uid);
     552                 :     else
     553               0 :         runSetuidHelper("kill", user);
     554               0 : }
     555                 : 
     556                 : 
     557               0 : bool amPrivileged()
     558                 : {
     559               0 :     return geteuid() == 0;
     560                 : }
     561                 : 
     562                 : 
     563             112 : bool haveBuildUsers()
     564                 : {
     565             112 :     return querySetting("build-users-group", "") != "";
     566                 : }
     567                 : 
     568                 : 
     569               0 : void getOwnership(const Path & path)
     570                 : {
     571               0 :     runSetuidHelper("get-ownership", path);
     572               0 : }
     573                 : 
     574                 : 
     575                 : void deletePathWrapped(const Path & path,
     576            2790 :     unsigned long long & bytesFreed, unsigned long long & blocksFreed)
     577                 : {
     578                 :     try {
     579                 :         /* First try to delete it ourselves. */
     580            2790 :         deletePath(path, bytesFreed, blocksFreed);
     581               0 :     } catch (SysError & e) {
     582                 :         /* If this failed due to a permission error, then try it with
     583                 :            the setuid helper. */
     584               0 :         if (haveBuildUsers() && !amPrivileged()) {
     585               0 :             getOwnership(path);
     586               0 :             deletePath(path, bytesFreed, blocksFreed);
     587                 :         } else
     588               0 :             throw;
     589                 :     }
     590            2790 : }
     591                 : 
     592                 : 
     593             115 : void deletePathWrapped(const Path & path)
     594                 : {
     595                 :     unsigned long long dummy1, dummy2;
     596             115 :     deletePathWrapped(path, dummy1, dummy2);
     597             115 : }
     598                 : 
     599                 : 
     600                 : //////////////////////////////////////////////////////////////////////
     601                 : 
     602                 : 
     603                 : /* Helper RAII class for automatically unmounting bind-mounts in
     604                 :    chroots. */
     605                 : struct BindMount
     606                 : {
     607                 :     Path source, target;
     608                 :     Paths created;
     609                 : 
     610                 :     BindMount()
     611                 :     {
     612                 :     }
     613                 : 
     614               0 :     BindMount(const Path & source, const Path & target)
     615               0 :     {
     616               0 :         bind(source, target);
     617               0 :     }
     618                 : 
     619               0 :     ~BindMount()
     620                 :     {
     621                 :         try {
     622               0 :             unbind();
     623               0 :         } catch (...) {
     624               0 :             ignoreException();
     625                 :         }
     626               0 :     }
     627                 :     
     628               0 :     void bind(const Path & source, const Path & target)
     629                 :     {
     630                 : #if CHROOT_ENABLED        
     631               0 :         debug(format("bind mounting `%1%' to `%2%'") % source % target);
     632                 : 
     633               0 :         this->source = source;
     634               0 :         this->target = target;
     635                 :         
     636               0 :         created = createDirs(target);
     637                 :         
     638               0 :         if (mount(source.c_str(), target.c_str(), "", MS_BIND, 0) == -1)
     639               0 :             throw SysError(format("bind mount from `%1%' to `%2%' failed") % source % target);
     640                 : #endif
     641               0 :     }
     642                 : 
     643               0 :     void unbind()
     644                 :     {
     645                 : #if CHROOT_ENABLED
     646               0 :         if (source == "") return;
     647                 :         
     648               0 :         debug(format("unmount bind-mount `%1%'") % target);
     649                 : 
     650                 :         /* Urgh.  Unmount sometimes doesn't succeed right away because
     651                 :            the mount point is still busy.  It shouldn't be, because
     652                 :            we've killed all the build processes by now (at least when
     653                 :            using a build user; see the check in killUser()).  But
     654                 :            maybe this is because those processes are still zombies and
     655                 :            are keeping some kernel structures busy (open files,
     656                 :            current directories, etc.).  So retry a few times
     657                 :            (actually, a 1 second sleep is almost certainly enough for
     658                 :            the zombies to be cleaned up). */
     659               0 :         unsigned int tries = 0;
     660               0 :         while (umount(target.c_str()) == -1) {
     661               0 :             if (errno == EBUSY && ++tries < 10) {
     662               0 :                 printMsg(lvlError, format("unmounting `%1%' failed, retrying after 1 second...") % target);
     663               0 :                 sleep(1);
     664                 :             }
     665                 :             else
     666               0 :                 throw SysError(format("unmounting bind-mount `%1%' failed") % target);
     667                 :         }
     668                 : 
     669                 :         /* Get rid of the directories for the mount point created in
     670                 :            bind(). */
     671               0 :         for (Paths::reverse_iterator i = created.rbegin(); i != created.rend(); ++i) {
     672               0 :             debug(format("deleting `%1%'") % *i);
     673               0 :             if (remove(i->c_str()) == -1)
     674               0 :                 throw SysError(format("cannot unlink `%1%'") % *i);
     675                 :         }
     676                 : 
     677               0 :         source = "";
     678                 : #endif
     679                 :     }
     680                 : };
     681                 : 
     682                 : 
     683                 : //////////////////////////////////////////////////////////////////////
     684                 : 
     685                 : 
     686                 : class DerivationGoal : public Goal
     687                 : {
     688                 : private:
     689                 :     /* The path of the derivation. */
     690                 :     Path drvPath;
     691                 : 
     692                 :     /* The derivation stored at drvPath. */
     693                 :     Derivation drv;
     694                 :     
     695                 :     /* The remainder is state held during the build. */
     696                 : 
     697                 :     /* Locks on the output paths. */
     698                 :     PathLocks outputLocks;
     699                 : 
     700                 :     /* All input paths (that is, the union of FS closures of the
     701                 :        immediate input paths). */
     702                 :     PathSet inputPaths; 
     703                 : 
     704                 :     /* Referenceable paths (i.e., input and output paths). */
     705                 :     PathSet allPaths;
     706                 : 
     707                 :     /* User selected for running the builder. */
     708                 :     UserLock buildUser;
     709                 : 
     710                 :     /* The process ID of the builder. */
     711                 :     Pid pid;
     712                 : 
     713                 :     /* The temporary directory. */
     714                 :     Path tmpDir;
     715                 : 
     716                 :     /* File descriptor for the log file. */
     717                 :     AutoCloseFD fdLogFile;
     718                 : 
     719                 :     /* Pipe for the builder's standard output/error. */
     720                 :     Pipe logPipe;
     721                 : 
     722                 :     /* Pipes for talking to the build hook (if any). */
     723                 :     Pipe toHook;
     724                 :     Pipe fromHook;
     725                 : 
     726                 :     /* Whether we're currently doing a chroot build. */
     727                 :     bool useChroot;
     728                 : 
     729                 :     /* RAII objects to delete the chroot directory and its /tmp
     730                 :        directory.  Don't change the order: autoDelChrootTmp has to be
     731                 :        deleted before autoDelChrootRoot. */
     732                 :     boost::shared_ptr<AutoDelete> autoDelChrootRoot;
     733                 :     boost::shared_ptr<AutoDelete> autoDelChrootTmp;
     734                 :     
     735                 :     /* In chroot builds, the list of bind mounts currently active.
     736                 :        The destructor of BindMount will cause the binds to be
     737                 :        unmounted.  Keep this *below* autoDelChroot* just to be safe! */
     738                 :     list<boost::shared_ptr<BindMount> > bindMounts;
     739                 : 
     740                 :     typedef void (DerivationGoal::*GoalState)();
     741                 :     GoalState state;
     742                 :     
     743                 : public:
     744                 :     DerivationGoal(const Path & drvPath, Worker & worker);
     745                 :     ~DerivationGoal();
     746                 : 
     747                 :     void cancel();
     748                 :     
     749                 :     void work();
     750                 : 
     751               6 :     Path getDrvPath()
     752                 :     {
     753               6 :         return drvPath;
     754                 :     }
     755                 : 
     756                 : private:
     757                 :     /* The states. */
     758                 :     void init();
     759                 :     void haveDerivation();
     760                 :     void outputsSubstituted();
     761                 :     void inputsRealised();
     762                 :     void tryToBuild();
     763                 :     void buildDone();
     764                 : 
     765                 :     /* Is the build hook willing to perform the build? */
     766                 :     typedef enum {rpAccept, rpDecline, rpPostpone, rpDone, rpRestart} HookReply;
     767                 :     HookReply tryBuildHook();
     768                 : 
     769                 :     /* Synchronously wait for a build hook to finish. */
     770                 :     void terminateBuildHook(bool kill = false);
     771                 : 
     772                 :     /* Acquires locks on the output paths and gathers information
     773                 :        about the build (e.g., the input closures).  During this
     774                 :        process its possible that we find out that the build is
     775                 :        unnecessary, in which case we return prDone.  It's also
     776                 :        possible that some other goal is already building/substituting
     777                 :        the output paths, in which case we return prRestart (go back to
     778                 :        the haveDerivation() state).  Otherwise, prProceed is
     779                 :        returned. */
     780                 :     typedef enum {prProceed, prDone, prRestart} PrepareBuildReply;
     781                 :     PrepareBuildReply prepareBuild();
     782                 : 
     783                 :     /* Start building a derivation. */
     784                 :     void startBuilder();
     785                 : 
     786                 :     /* Must be called after the output paths have become valid (either
     787                 :        due to a successful build or hook, or because they already
     788                 :        were). */
     789                 :     void computeClosure();
     790                 : 
     791                 :     /* Open a log file and a pipe to it. */
     792                 :     Path openLogFile();
     793                 : 
     794                 :     /* Common initialisation to be performed in child processes (i.e.,
     795                 :        both in builders and in build hooks). */
     796                 :     void initChild();
     797                 :     
     798                 :     /* Delete the temporary directory, if we have one. */
     799                 :     void deleteTmpDir(bool force);
     800                 : 
     801                 :     /* Callback used by the worker to write to the log. */
     802                 :     void handleChildOutput(int fd, const string & data);
     803                 :     void handleEOF(int fd);
     804                 : 
     805                 :     /* Return the set of (in)valid paths. */
     806                 :     PathSet checkPathValidity(bool returnValid);
     807                 : 
     808                 :     /* Forcibly kill the child process, if any. */
     809                 :     void killChild();
     810                 : };
     811                 : 
     812                 : 
     813             220 : DerivationGoal::DerivationGoal(const Path & drvPath, Worker & worker)
     814             220 :     : Goal(worker)
     815                 : {
     816             220 :     this->drvPath = drvPath;
     817             220 :     state = &DerivationGoal::init;
     818             220 :     name = (format("building of `%1%'") % drvPath).str();
     819             220 :     trace("created");
     820             220 : }
     821                 : 
     822             220 : DerivationGoal::~DerivationGoal()
     823                 : {
     824                 :     /* Careful: we should never ever throw an exception from a
     825                 :        destructor. */
     826                 :     try {
     827             220 :         killChild();
     828             220 :         deleteTmpDir(false);
     829               0 :     } catch (...) {
     830               0 :         ignoreException();
     831                 :     }
     832             220 : }
     833                 : 
     834             220 : void DerivationGoal::killChild()
     835                 : {
     836             220 :     if (pid != -1) {
     837               0 :         worker.childTerminated(pid);
     838                 : 
     839               0 :         if (buildUser.enabled()) {
     840                 :             /* We can't use pid.kill(), since we may not have the
     841                 :                appropriate privilege.  I.e., if we're not root, then
     842                 :                setuid helper should do it).
     843                 : 
     844                 :                Also, if we're using a build user, then there is a
     845                 :                tricky race condition: if we kill the build user before
     846                 :                the child has done its setuid() to the build user uid,
     847                 :                then it won't be killed, and we'll potentially lock up
     848                 :                in pid.wait().  So also send a conventional kill to the
     849                 :                child. */
     850               0 :             ::kill(-pid, SIGKILL); /* ignore the result */
     851               0 :             buildUser.kill();
     852               0 :             pid.wait(true);
     853                 :         } else
     854               0 :             pid.kill();
     855                 :         
     856               0 :         assert(pid == -1);
     857                 :     }
     858             220 : }
     859                 : 
     860                 : 
     861               0 : void DerivationGoal::cancel()
     862                 : {
     863               0 :     killChild();
     864               0 :     amDone(ecFailed);
     865               0 : }
     866                 : 
     867                 : 
     868             890 : void DerivationGoal::work()
     869                 : {
     870             890 :     (this->*state)();
     871             889 : }
     872                 : 
     873                 : 
     874             220 : void DerivationGoal::init()
     875                 : {
     876             220 :     trace("init");
     877                 : 
     878                 :     /* The first thing to do is to make sure that the derivation
     879                 :        exists.  If it doesn't, it may be created through a
     880                 :        substitute. */
     881             440 :     addWaitee(worker.makeSubstitutionGoal(drvPath));
     882                 : 
     883             220 :     state = &DerivationGoal::haveDerivation;
     884             220 : }
     885                 : 
     886                 : 
     887             221 : void DerivationGoal::haveDerivation()
     888                 : {
     889             221 :     trace("loading derivation");
     890                 : 
     891             221 :     if (nrFailed != 0) {
     892               0 :         printMsg(lvlError,
     893                 :             format("cannot build missing derivation `%1%'")
     894                 :             % drvPath);
     895               0 :         amDone(ecFailed);
     896               0 :         return;
     897                 :     }
     898                 : 
     899             221 :     assert(worker.store.isValidPath(drvPath));
     900                 : 
     901                 :     /* Get the derivation. */
     902             221 :     drv = derivationFromPath(drvPath);
     903                 : 
     904             442 :     for (DerivationOutputs::iterator i = drv.outputs.begin();
     905                 :          i != drv.outputs.end(); ++i)
     906             221 :         worker.store.addTempRoot(i->second.path);
     907                 : 
     908                 :     /* Check what outputs paths are not already valid. */
     909             221 :     PathSet invalidOutputs = checkPathValidity(false);
     910                 : 
     911                 :     /* If they are all valid, then we're done. */
     912             221 :     if (invalidOutputs.size() == 0) {
     913              78 :         if(!forceInputs) {
     914              78 :             amDone(ecSuccess);
     915              78 :             return;
     916                 :         }
     917                 :     }
     918                 : 
     919                 :     /* If this is a fixed-output derivation, it is possible that some
     920                 :        other goal is already building the output paths.  (The case
     921                 :        where some other process is building it is handled through
     922                 :        normal locking mechanisms.)  So if any output paths are already
     923                 :        being built, put this goal to sleep. */
     924             286 :     for (PathSet::iterator i = invalidOutputs.begin();
     925                 :          i != invalidOutputs.end(); ++i)
     926             143 :         if (pathIsLockedByMe(*i)) {
     927                 :             /* Wait until any goal finishes (hopefully the one that is
     928                 :                locking *i), then retry haveDerivation(). */
     929               0 :             worker.waitForAnyGoal(shared_from_this());
     930                 :             return;
     931                 :         }
     932                 : 
     933                 :     /* We are first going to try to create the invalid output paths
     934                 :        through substitutes.  If that doesn't work, we'll build
     935                 :        them. */
     936             286 :     for (PathSet::iterator i = invalidOutputs.begin();
     937                 :          i != invalidOutputs.end(); ++i)
     938                 :         /* Don't bother creating a substitution goal if there are no
     939                 :            substitutes. */
     940             143 :         if (worker.store.hasSubstitutes(*i))
     941               5 :             addWaitee(worker.makeSubstitutionGoal(*i));
     942                 :     
     943             143 :     if (waitees.empty()) /* to prevent hang (no wake-up event) */
     944             138 :         outputsSubstituted();
     945                 :     else
     946               5 :         state = &DerivationGoal::outputsSubstituted;
     947                 : }
     948                 : 
     949                 : 
     950             143 : void DerivationGoal::outputsSubstituted()
     951                 : {
     952             143 :     trace("all outputs substituted (maybe)");
     953                 : 
     954             145 :     if (nrFailed > 0 && !tryFallback)
     955               1 :         throw Error(format("some substitutes for the outputs of derivation `%1%' failed; try `--fallback'") % drvPath);
     956                 : 
     957             142 :     nrFailed = 0;
     958                 : 
     959             142 :     if (checkPathValidity(false).size() == 0) {
     960               3 :         if (! forceInputs){
     961               3 :                 amDone(ecSuccess);
     962               3 :                 return;
     963                 :         }
     964                 :     }
     965                 : 
     966                 :     /* Otherwise, at least one of the output paths could not be
     967                 :        produced using a substitute.  So we have to build instead. */
     968                 : 
     969                 :     /* The inputs must be built before we can build this goal. */
     970                 :     /* !!! but if possible, only install the paths that we need */
     971             207 :     for (DerivationInputs::iterator i = drv.inputDrvs.begin();
     972                 :          i != drv.inputDrvs.end(); ++i){
     973              68 :         GoalPtr newGoal = worker.makeDerivationGoal(i->first);
     974              68 :         newGoal->setForceInputs(forceInputs);
     975              68 :         addWaitee(newGoal);
     976                 :     }
     977                 : 
     978             642 :     for (PathSet::iterator i = drv.inputSrcs.begin();
     979                 :          i != drv.inputSrcs.end(); ++i)
     980             182 :         addWaitee(worker.makeSubstitutionGoal(*i));
     981                 : 
     982                 :     /* Actually, I do some work twice just to be on the safe side */
     983             139 :     string s = drv.env["exportBuildReferencesGraph"]; 
     984             278 :     Strings ss = tokenizeString(s);
     985             278 :     if (ss.size() % 2 !=0)
     986               0 :         throw BuildError(format("odd number of tokens in `exportBuildReferencesGraph': `%1%'") % s); 
     987             139 :     for (Strings::iterator i = ss.begin(); i != ss.end(); ) {
     988               0 :         string fileName = *i++;
     989               0 :         Path storePath=*i++;
     990                 :         
     991               0 :         if (!isInStore(storePath))
     992                 :             throw BuildError(format("`exportBuildReferencesGraph' contains a non-store path `%1%'")
     993               0 :                 % storePath);
     994               0 :         storePath = toStorePath(storePath);
     995               0 :         if (!worker.store.isValidPath(storePath))
     996                 :             throw BuildError(format("`exportBuildReferencesGraph' contains an invalid path `%1%'")
     997               0 :                 % storePath);
     998                 :         
     999                 :         /* Build-time closure should be in dependencies 
    1000                 :          * We really want just derivation, its closure
    1001                 :          * and outputs. Looks like we should build it.
    1002                 :          * */
    1003                 : 
    1004               0 :         GoalPtr newGoal = worker.makeDerivationGoal(storePath);
    1005               0 :         newGoal->setForceInputs(true);
    1006               0 :         addWaitee(newGoal);
    1007                 :     }
    1008                 : 
    1009             139 :     state = &DerivationGoal::inputsRealised;
    1010                 : }
    1011                 : 
    1012                 : 
    1013             139 : void DerivationGoal::inputsRealised()
    1014                 : {
    1015             139 :     trace("all inputs realised");
    1016                 : 
    1017             139 :     if (nrFailed != 0) {
    1018               0 :         printMsg(lvlError,
    1019                 :             format("cannot build derivation `%1%': "
    1020                 :                 "%2% inputs could not be realised")
    1021                 :             % drvPath % nrFailed);
    1022               0 :         amDone(ecFailed);
    1023               0 :         return;
    1024                 :     }
    1025                 : 
    1026                 :     /* Maybe we just wanted to force build of inputs */
    1027             139 :     if (checkPathValidity(false).size() == 0) {
    1028               0 :         amDone(ecSuccess);
    1029               0 :         return;
    1030                 :     }
    1031                 : 
    1032                 :     /* Okay, try to build.  Note that here we don't wait for a build
    1033                 :        slot to become available, since we don't need one if there is a
    1034                 :        build hook. */
    1035             139 :     state = &DerivationGoal::tryToBuild;
    1036             139 :     worker.wakeUp(shared_from_this());
    1037                 : }
    1038                 : 
    1039                 : 
    1040             192 : void DerivationGoal::tryToBuild()
    1041                 : {
    1042             192 :     trace("trying to build");
    1043                 : 
    1044                 :     try {
    1045                 : 
    1046                 :         /* Is the build hook willing to accept this job? */
    1047             384 :         switch (tryBuildHook()) {
    1048                 :             case rpAccept:
    1049                 :                 /* Yes, it has started doing so.  Wait until we get
    1050                 :                    EOF from the hook. */
    1051               1 :                 state = &DerivationGoal::buildDone;
    1052               1 :                 return;
    1053                 :             case rpPostpone:
    1054                 :                 /* Not now; wait until at least one child finishes. */
    1055               0 :                 worker.waitForChildTermination(shared_from_this());
    1056               0 :                 return;
    1057                 :             case rpDecline:
    1058                 :                 /* We should do it ourselves. */
    1059             191 :                 break;
    1060                 :             case rpDone:
    1061                 :                 /* Somebody else did it. */
    1062               0 :                 amDone(ecSuccess);
    1063               0 :                 return;
    1064                 :             case rpRestart:
    1065                 :                 /* Somebody else is building this output path.
    1066                 :                    Restart from haveDerivation(). */
    1067               0 :                 state = &DerivationGoal::haveDerivation;
    1068               0 :                 worker.waitForAnyGoal(shared_from_this());
    1069               0 :                 return;
    1070                 :         }
    1071                 : 
    1072                 :         /* Make sure that we are allowed to start a build. */
    1073             191 :         if (!worker.canBuildMore()) {
    1074              53 :             worker.waitForBuildSlot(shared_from_this());
    1075              53 :             return;
    1076                 :         }
    1077                 : 
    1078                 :         /* Acquire locks and such.  If we then see that the build has
    1079                 :            been done by somebody else, we're done. */
    1080             138 :         PrepareBuildReply preply = prepareBuild();
    1081             138 :         if (preply == prDone) {
    1082              25 :             amDone(ecSuccess);
    1083              25 :             return;
    1084             113 :         } else if (preply == prRestart) {
    1085               1 :             state = &DerivationGoal::haveDerivation;
    1086               1 :             worker.waitForAnyGoal(shared_from_this());
    1087               1 :             return;
    1088                 :         }
    1089                 : 
    1090                 :         /* Okay, we have to build. */
    1091             112 :         startBuilder();
    1092                 : 
    1093               0 :     } catch (BuildError & e) {
    1094               0 :         printMsg(lvlError, e.msg());
    1095               0 :         if (printBuildTrace) {
    1096               0 :             printMsg(lvlError, format("@ build-failed %1% %2% %3% %4%")
    1097                 :                 % drvPath % drv.outputs["out"].path % 0 % e.msg());
    1098                 :         }
    1099               0 :         amDone(ecFailed);
    1100               0 :         return;
    1101                 :     }
    1102                 : 
    1103                 :     /* This state will be reached when we get EOF on the child's
    1104                 :        log pipe. */
    1105             112 :     state = &DerivationGoal::buildDone;
    1106                 : }
    1107                 : 
    1108                 : 
    1109             113 : void DerivationGoal::buildDone()
    1110                 : {
    1111             113 :     trace("build done");
    1112                 : 
    1113                 :     /* Since we got an EOF on the logger pipe, the builder is presumed
    1114                 :        to have terminated.  In fact, the builder could also have
    1115                 :        simply have closed its end of the pipe --- just don't do that
    1116                 :        :-) */
    1117                 :     /* !!! this could block! security problem! solution: kill the
    1118                 :        child */
    1119             113 :     pid_t savedPid = pid;
    1120             113 :     int status = pid.wait(true);
    1121                 : 
    1122             113 :     debug(format("builder process for `%1%' finished") % drvPath);
    1123                 : 
    1124                 :     /* So the child is gone now. */
    1125             113 :     worker.childTerminated(savedPid);
    1126                 : 
    1127                 :     /* Close the read side of the logger pipe. */
    1128             113 :     logPipe.readSide.close();
    1129                 : 
    1130                 :     /* Close the log file. */
    1131             113 :     fdLogFile.close();
    1132                 : 
    1133                 :     /* When running under a build user, make sure that all processes
    1134                 :        running under that uid are gone.  This is to prevent a
    1135                 :        malicious user from leaving behind a process that keeps files
    1136                 :        open and modifies them after they have been chown'ed to
    1137                 :        root. */
    1138             113 :     if (buildUser.enabled())
    1139               0 :         buildUser.kill();
    1140                 : 
    1141                 :     try {
    1142                 : 
    1143                 :         /* Some cleanup per path.  We do this here and not in
    1144                 :            computeClosure() for convenience when the build has
    1145                 :            failed. */
    1146             226 :         for (DerivationOutputs::iterator i = drv.outputs.begin(); 
    1147                 :              i != drv.outputs.end(); ++i)
    1148                 :         {
    1149             113 :             Path path = i->second.path;
    1150             113 :             if (!pathExists(path)) continue;
    1151                 : 
    1152                 :             struct stat st;
    1153             113 :             if (lstat(path.c_str(), &st) == -1)
    1154               0 :                 throw SysError(format("getting attributes of path `%1%'") % path);
    1155                 :             
    1156                 : #ifndef __CYGWIN__
    1157                 :             /* Check that the output is not group or world writable,
    1158                 :                as that means that someone else can have interfered
    1159                 :                with the build.  Also, the output should be owned by
    1160                 :                the build user. */
    1161             113 :             if ((!S_ISLNK(st.st_mode) && (st.st_mode & (S_IWGRP | S_IWOTH))) ||
    1162                 :                 (buildUser.enabled() && st.st_uid != buildUser.getUID()))
    1163               0 :                 throw BuildError(format("suspicious ownership or permission on `%1%'; rejecting this build output") % path);
    1164                 : #endif
    1165                 : 
    1166                 :             /* Gain ownership of the build result using the setuid
    1167                 :                wrapper if we're not root.  If we *are* root, then
    1168                 :                canonicalisePathMetaData() will take care of this later
    1169                 :                on. */
    1170             113 :             if (buildUser.enabled() && !amPrivileged())
    1171               0 :                 getOwnership(path);
    1172                 :         }
    1173                 :     
    1174                 :         /* Check the exit status. */
    1175             113 :         if (!statusOk(status)) {
    1176               3 :             deleteTmpDir(false);
    1177                 :             throw BuildError(format("builder for `%1%' %2%")
    1178               3 :                 % drvPath % statusToString(status));
    1179                 :         }
    1180                 :     
    1181             110 :         deleteTmpDir(true);
    1182                 : 
    1183                 :         /* In chroot builds, unmount the bind mounts ASAP. */
    1184             110 :         bindMounts.clear(); /* the destructors will do the rest */
    1185                 : 
    1186                 :         /* Compute the FS closure of the outputs and register them as
    1187                 :            being valid. */
    1188             110 :         computeClosure();
    1189                 : 
    1190              12 :     } catch (BuildError & e) {
    1191               6 :         printMsg(lvlError, e.msg());
    1192               6 :         if (printBuildTrace) {
    1193               0 :             printMsg(lvlError, format("@ build-failed %1% %2% %3% %4%")
    1194                 :                 % drvPath % drv.outputs["out"].path % status % e.msg());
    1195                 :         }
    1196               6 :         amDone(ecFailed);
    1197               6 :         return;
    1198                 :     }
    1199                 : 
    1200                 :     /* Release the build user, if applicable. */
    1201             107 :     buildUser.release();
    1202                 : 
    1203             107 :     if (printBuildTrace) {
    1204               0 :         printMsg(lvlError, format("@ build-succeeded %1% %2%")
    1205                 :             % drvPath % drv.outputs["out"].path);
    1206                 :     }
    1207                 :     
    1208             107 :     amDone(ecSuccess);
    1209                 : }
    1210                 : 
    1211                 : 
    1212               3 : static string readLine(int fd)
    1213                 : {
    1214               3 :     string s;
    1215              20 :     while (1) {
    1216              23 :         checkInterrupt();
    1217                 :         char ch;
    1218              23 :         ssize_t rd = read(fd, &ch, 1);
    1219              23 :         if (rd == -1) {
    1220               0 :             if (errno != EINTR)
    1221               0 :                 throw SysError("reading a line");
    1222              23 :         } else if (rd == 0)
    1223               0 :             throw Error("unexpected EOF reading a line");
    1224                 :         else {
    1225              23 :             if (ch == '\n') return s;
    1226              20 :             s += ch;
    1227                 :         }
    1228               0 :     }
    1229                 : }
    1230                 : 
    1231                 : 
    1232               1 : static void writeLine(int fd, string s)
    1233                 : {
    1234               1 :     s += '\n';
    1235               1 :     writeFull(fd, (const unsigned char *) s.c_str(), s.size());
    1236               1 : }
    1237                 : 
    1238                 : 
    1239                 : /* !!! ugly hack */
    1240               4 : static void drain(int fd)
    1241                 : {
    1242                 :     unsigned char buffer[1024];
    1243               2 :     while (1) {
    1244               4 :         checkInterrupt();
    1245               4 :         ssize_t rd = read(fd, buffer, sizeof buffer);
    1246               4 :         if (rd == -1) {
    1247               0 :             if (errno != EINTR)
    1248               0 :                 throw SysError("draining");
    1249               4 :         } else if (rd == 0) break;
    1250               2 :         else writeToStderr(buffer, rd);
    1251                 :     }
    1252               2 : }
    1253                 : 
    1254                 : 
    1255             389 : PathSet outputPaths(const DerivationOutputs & outputs)
    1256                 : {
    1257             389 :     PathSet paths;
    1258             778 :     for (DerivationOutputs::const_iterator i = outputs.begin();
    1259                 :          i != outputs.end(); ++i)
    1260             389 :         paths.insert(i->second.path);
    1261               0 :     return paths;
    1262                 : }
    1263                 : 
    1264                 : 
    1265             192 : DerivationGoal::HookReply DerivationGoal::tryBuildHook()
    1266                 : {
    1267             192 :     if (!useBuildHook) return rpDecline;
    1268             147 :     Path buildHook = getEnv("NIX_BUILD_HOOK");
    1269             441 :     if (buildHook == "") return rpDecline;
    1270               3 :     buildHook = absPath(buildHook);
    1271                 : 
    1272                 :     /* Create a directory where we will store files used for
    1273                 :        communication between us and the build hook. */
    1274               6 :     tmpDir = createTempDir();
    1275                 :     
    1276                 :     /* Create the log file and pipe. */
    1277               3 :     Path logFile = openLogFile();
    1278                 : 
    1279                 :     /* Create the communication pipes. */
    1280               3 :     toHook.create();
    1281               3 :     fromHook.create();
    1282                 : 
    1283                 :     /* Fork the hook. */
    1284               3 :     pid = fork();
    1285               6 :     switch (pid) {
    1286                 :         
    1287                 :     case -1:
    1288               0 :         throw SysError("unable to fork");
    1289                 : 
    1290                 :     case 0:
    1291                 :         try { /* child */
    1292                 : 
    1293               3 :             initChild();
    1294                 : 
    1295                 :             execl(buildHook.c_str(), buildHook.c_str(),
    1296                 :                 (worker.canBuildMore() ? (string) "1" : "0").c_str(),
    1297                 :                 thisSystem.c_str(),
    1298                 :                 drv.platform.c_str(),
    1299               3 :                 drvPath.c_str(), NULL);
    1300                 :             
    1301               0 :             throw SysError(format("executing `%1%'") % buildHook);
    1302                 :             
    1303               0 :         } catch (std::exception & e) {
    1304               0 :             std::cerr << format("build hook error: %1%") % e.what() << std::endl;
    1305                 :         }
    1306               0 :         quickExit(1);
    1307                 :     }
    1308                 :     
    1309                 :     /* parent */
    1310               3 :     pid.setSeparatePG(true);
    1311               3 :     pid.setKillSignal(SIGTERM);
    1312               3 :     logPipe.writeSide.close();
    1313                 :     worker.childStarted(shared_from_this(),
    1314               3 :         pid, singleton<set<int> >(logPipe.readSide), false);
    1315                 : 
    1316               3 :     fromHook.writeSide.close();
    1317               3 :     toHook.readSide.close();
    1318                 : 
    1319                 :     /* Read the first line of input, which should be a word indicating
    1320                 :        whether the hook wishes to perform the build.  !!! potential
    1321                 :        for deadlock here: we should also read from the child's logger
    1322                 :        pipe. */
    1323               3 :     string reply;
    1324                 :     try {
    1325               3 :         reply = readLine(fromHook.readSide);
    1326               0 :     } catch (Error & e) {
    1327               0 :         terminateBuildHook(true);
    1328               0 :         throw;
    1329                 :     }
    1330                 : 
    1331               3 :     debug(format("hook reply is `%1%'") % reply);
    1332                 : 
    1333               3 :     if (reply == "decline" || reply == "postpone") {
    1334                 :         /* Clean up the child.  !!! hacky / should verify */
    1335               2 :         terminateBuildHook();
    1336               2 :         return reply == "decline" ? rpDecline : rpPostpone;
    1337                 :     }
    1338                 : 
    1339               1 :     else if (reply == "accept") {
    1340                 : 
    1341                 :         /* Acquire locks and such.  If we then see that the output
    1342                 :            paths are now valid, we're done. */
    1343               1 :         PrepareBuildReply preply = prepareBuild();
    1344               1 :         if (preply == prDone || preply == prRestart) {
    1345                 :             /* Tell the hook to exit. */
    1346               0 :             writeLine(toHook.writeSide, "cancel");
    1347               0 :             terminateBuildHook();
    1348               0 :             return preply == prDone ? rpDone : rpRestart;
    1349                 :         }
    1350                 : 
    1351               1 :         printMsg(lvlInfo, format("running hook to build path(s) %1%")
    1352                 :             % showPaths(outputPaths(drv.outputs)));
    1353                 :         
    1354                 :         /* Write the information that the hook needs to perform the
    1355                 :            build, i.e., the set of input paths, the set of output
    1356                 :            paths, and the references (pointer graph) in the input
    1357                 :            paths. */
    1358                 :         
    1359               1 :         Path inputListFN = tmpDir + "/inputs";
    1360               1 :         Path outputListFN = tmpDir + "/outputs";
    1361               1 :         Path referencesFN = tmpDir + "/references";
    1362                 : 
    1363                 :         /* The `inputs' file lists all inputs that have to be copied
    1364                 :            to the remote system.  This unfortunately has to contain
    1365                 :            the entire derivation closure to ensure that the validity
    1366                 :            invariant holds on the remote system.  (I.e., it's
    1367                 :            unfortunate that we have to list it since the remote system
    1368                 :            *probably* already has it.) */
    1369               1 :         PathSet allInputs;
    1370               1 :         allInputs.insert(inputPaths.begin(), inputPaths.end());
    1371               1 :         computeFSClosure(drvPath, allInputs);
    1372                 :         
    1373               1 :         string s;
    1374               3 :         for (PathSet::iterator i = allInputs.begin();
    1375                 :              i != allInputs.end(); ++i)
    1376               2 :             s += *i + "\n";
    1377                 :         
    1378               1 :         writeStringToFile(inputListFN, s);
    1379                 : 
    1380                 :         /* The `outputs' file lists all outputs that have to be copied
    1381                 :            from the remote system. */
    1382               1 :         s = "";
    1383               2 :         for (DerivationOutputs::iterator i = drv.outputs.begin();
    1384                 :              i != drv.outputs.end(); ++i)
    1385               1 :             s += i->second.path + "\n";
    1386               1 :         writeStringToFile(outputListFN, s);
    1387                 : 
    1388                 :         /* The `references' file has exactly the format accepted by
    1389                 :            `nix-store --register-validity'. */
    1390                 :         writeStringToFile(referencesFN,
    1391               1 :             makeValidityRegistration(allInputs, true, false));
    1392                 : 
    1393                 :         /* Tell the hook to proceed. */ 
    1394               1 :         writeLine(toHook.writeSide, "okay");
    1395                 : 
    1396               1 :         if (printBuildTrace) {
    1397               0 :             printMsg(lvlError, format("@ build-started %1% %2% %3% %4%")
    1398                 :                 % drvPath % drv.outputs["out"].path % drv.platform % logFile);
    1399                 :         }
    1400                 :         
    1401               1 :         return rpAccept;
    1402                 :     }
    1403                 : 
    1404               0 :     else throw Error(format("bad hook reply `%1%'") % reply);
    1405                 : }
    1406                 : 
    1407                 : 
    1408               2 : void DerivationGoal::terminateBuildHook(bool kill)
    1409                 : {
    1410                 :     /* !!! drain stdout of hook */
    1411               2 :     debug("terminating build hook");
    1412               2 :     pid_t savedPid = pid;
    1413               2 :     if (kill)
    1414               0 :         pid.kill();
    1415                 :     else
    1416               2 :         pid.wait(true);
    1417                 :     /* `false' means don't wake up waiting goals, since we want to
    1418                 :        keep this build slot ourselves. */
    1419               2 :     worker.childTerminated(savedPid, false);
    1420               2 :     fromHook.readSide.close();
    1421               2 :     toHook.writeSide.close();
    1422               2 :     fdLogFile.close();
    1423               2 :     drain(logPipe.readSide);
    1424               2 :     logPipe.readSide.close();
    1425               2 :     deleteTmpDir(true); /* get rid of the hook's temporary directory */
    1426               2 : }
    1427                 : 
    1428                 : 
    1429             139 : DerivationGoal::PrepareBuildReply DerivationGoal::prepareBuild()
    1430                 : {
    1431                 :     /* Check for the possibility that some other goal in this process
    1432                 :        has locked the output since we checked in haveDerivation().
    1433                 :        (It can't happen between here and the lockPaths() call below
    1434                 :        because we're not allowing multi-threading.) */
    1435             277 :     for (DerivationOutputs::iterator i = drv.outputs.begin(); 
    1436                 :          i != drv.outputs.end(); ++i)
    1437             139 :         if (pathIsLockedByMe(i->second.path)) {
    1438               1 :             debug(format("restarting derivation `%1%' because `%2%' is locked by another goal")
    1439                 :                 % drvPath % i->second.path);
    1440               1 :             return prRestart;
    1441                 :         }
    1442                 :     
    1443                 :     /* Obtain locks on all output paths.  The locks are automatically
    1444                 :        released when we exit this function or Nix crashes. */
    1445                 :     /* !!! BUG: this could block, which is not allowed. */
    1446                 :     /* !!! and once we make this non-blocking, we should carefully
    1447                 :        consider the case where some but not all locks are required; we
    1448                 :        should then release the acquired locks so that the other
    1449                 :        processes and the pathIsLockedByMe() test don't get confused. */
    1450                 :     outputLocks.lockPaths(outputPaths(drv.outputs),
    1451             138 :         (format("waiting for lock on %1%") % showPaths(outputPaths(drv.outputs))).str());
    1452                 : 
    1453                 :     /* Now check again whether the outputs are valid.  This is because
    1454                 :        another process may have started building in parallel.  After
    1455                 :        it has finished and released the locks, we can (and should)
    1456                 :        reuse its results.  (Strictly speaking the first check can be
    1457                 :        omitted, but that would be less efficient.)  Note that since we
    1458                 :        now hold the locks on the output paths, no other process can
    1459                 :        build this derivation, so no further checks are necessary. */
    1460             138 :     PathSet validPaths = checkPathValidity(true);
    1461             276 :     if (validPaths.size() == drv.outputs.size()) {
    1462              25 :         debug(format("skipping build of derivation `%1%', someone beat us to it")
    1463                 :             % drvPath);
    1464              25 :         outputLocks.setDeletion(true);
    1465             163 :         return prDone;
    1466                 :     }
    1467                 : 
    1468             113 :     if (validPaths.size() > 0) {
    1469                 :         /* !!! fix this; try to delete valid paths */
    1470                 :         throw BuildError(
    1471                 :             format("derivation `%1%' is blocked by its output paths")
    1472               0 :             % drvPath);
    1473                 :     }
    1474                 : 
    1475                 :     /* If any of the outputs already exist but are not registered,
    1476                 :        delete them. */
    1477             226 :     for (DerivationOutputs::iterator i = drv.outputs.begin(); 
    1478                 :          i != drv.outputs.end(); ++i)
    1479                 :     {
    1480             113 :         Path path = i->second.path;
    1481             113 :         if (worker.store.isValidPath(path))
    1482               0 :             throw BuildError(format("obstructed build: path `%1%' exists") % path);
    1483             113 :         if (pathExists(path)) {
    1484               0 :             debug(format("removing unregistered path `%1%'") % path);
    1485               0 :             deletePathWrapped(path);
    1486                 :         }
    1487                 :     }
    1488                 : 
    1489                 :     /* Gather information necessary for computing the closure and/or
    1490                 :        running the build hook. */
    1491                 :     
    1492                 :     /* The outputs are referenceable paths. */
    1493             226 :     for (DerivationOutputs::iterator i = drv.outputs.begin();
    1494                 :          i != drv.outputs.end(); ++i)
    1495                 :     {
    1496             113 :         debug(format("building path `%1%'") % i->second.path);
    1497             113 :         allPaths.insert(i->second.path);
    1498                 :     }
    1499                 : 
    1500                 :     /* Determine the full set of input paths. */
    1501                 : 
    1502                 :     /* First, the input derivations. */
    1503             151 :     for (DerivationInputs::iterator i = drv.inputDrvs.begin();
    1504                 :          i != drv.inputDrvs.end(); ++i)
    1505                 :     {
    1506                 :         /* Add the relevant output closures of the input derivation
    1507                 :            `*i' as input paths.  Only add the closures of output paths
    1508                 :            that are specified as inputs. */
    1509              38 :         assert(worker.store.isValidPath(i->first));
    1510              38 :         Derivation inDrv = derivationFromPath(i->first);
    1511             152 :         for (StringSet::iterator j = i->second.begin();
    1512                 :              j != i->second.end(); ++j)
    1513              38 :             if (inDrv.outputs.find(*j) != inDrv.outputs.end())
    1514              38 :                 computeFSClosure(inDrv.outputs[*j].path, inputPaths);
    1515                 :             else
    1516                 :                 throw BuildError(
    1517                 :                     format("derivation `%1%' requires non-existent output `%2%' from input derivation `%3%'")
    1518               0 :                     % drvPath % *j % i->first);
    1519                 :     }
    1520                 : 
    1521                 :     /* Second, the input sources. */
    1522             269 :     for (PathSet::iterator i = drv.inputSrcs.begin();
    1523                 :          i != drv.inputSrcs.end(); ++i)
    1524             156 :         computeFSClosure(*i, inputPaths);
    1525                 : 
    1526             113 :     debug(format("added input paths %1%") % showPaths(inputPaths));
    1527                 : 
    1528             113 :     allPaths.insert(inputPaths.begin(), inputPaths.end());
    1529                 : 
    1530             113 :     return prProceed;
    1531                 : }
    1532                 : 
    1533                 : 
    1534             112 : void DerivationGoal::startBuilder()
    1535                 : {
    1536             112 :     startNest(nest, lvlInfo,
    1537                 :         format("building path(s) %1%") % showPaths(outputPaths(drv.outputs)))
    1538                 :     
    1539                 :     /* Right platform? */
    1540             112 :     if (drv.platform != thisSystem)
    1541                 :         throw BuildError(
    1542                 :             format("a `%1%' is required to build `%3%', but I am a `%2%'")
    1543               0 :             % drv.platform % thisSystem % drvPath);
    1544                 : 
    1545                 :     /* Construct the environment passed to the builder. */
    1546                 :     typedef map<string, string> Environment;
    1547             112 :     Environment env; 
    1548                 :     
    1549                 :     /* Most shells initialise PATH to some default (/bin:/usr/bin:...) when
    1550                 :        PATH is not set.  We don't want this, so we fill it in with some dummy
    1551                 :        value. */
    1552             112 :     env["PATH"] = "/path-not-set";
    1553                 : 
    1554                 :     /* Set HOME to a non-existing path to prevent certain programs from using
    1555                 :        /etc/passwd (or NIS, or whatever) to locate the home directory (for
    1556                 :        example, wget looks for ~/.wgetrc).  I.e., these tools use /etc/passwd
    1557                 :        if HOME is not set, but they will just assume that the settings file
    1558                 :        they are looking for does not exist if HOME is set but points to some
    1559                 :        non-existing path. */
    1560             224 :     env["HOME"] = "/homeless-shelter";
    1561                 : 
    1562                 :     /* Tell the builder where the Nix store is.  Usually they
    1563                 :        shouldn't care, but this is useful for purity checking (e.g.,
    1564                 :        the compiler or linker might only want to accept paths to files
    1565                 :        in the store or in the build directory). */
    1566             224 :     env["NIX_STORE"] = nixStore;
    1567                 : 
    1568                 :     /* Add all bindings specified in the derivation. */
    1569            1137 :     for (StringPairs::iterator i = drv.env.begin();
    1570                 :          i != drv.env.end(); ++i)
    1571             913 :         env[i->first] = i->second;
    1572                 : 
    1573                 :     /* Create a temporary directory where the build will take
    1574                 :        place. */
    1575             112 :     tmpDir = createTempDir("", "nix-build-" + baseNameOf(drvPath), false, false);
    1576                 : 
    1577                 :     /* For convenience, set an environment pointing to the top build
    1578                 :        directory. */
    1579             112 :     env["NIX_BUILD_TOP"] = tmpDir;
    1580                 : 
    1581                 :     /* Also set TMPDIR and variants to point to this directory. */
    1582             224 :     env["TMPDIR"] = env["TEMPDIR"] = env["TMP"] = env["TEMP"] = tmpDir;
    1583                 : 
    1584                 :     /* Explicitly set PWD to prevent problems with chroot builds.  In
    1585                 :        particular, dietlibc cannot figure out the cwd because the
    1586                 :        inode of the current directory doesn't appear in .. (because
    1587                 :        getdents returns the inode of the mount point). */
    1588             224 :     env["PWD"] = tmpDir;
    1589                 : 
    1590                 :     /* Compatibility hack with Nix <= 0.7: if this is a fixed-output
    1591                 :        derivation, tell the builder, so that for instance `fetchurl'
    1592                 :        can skip checking the output.  On older Nixes, this environment
    1593                 :        variable won't be set, so `fetchurl' will do the check. */
    1594             112 :     bool fixedOutput = true;
    1595             336 :     for (DerivationOutputs::iterator i = drv.outputs.begin(); 
    1596                 :          i != drv.outputs.end(); ++i)
    1597             112 :         if (i->second.hash == "") fixedOutput = false;
    1598             112 :     if (fixedOutput) 
    1599               7 :         env["NIX_OUTPUT_CHECKED"] = "1";
    1600                 : 
    1601                 :     /* *Only* if this is a fixed-output derivation, propagate the
    1602                 :        values of the environment variables specified in the
    1603                 :        `impureEnvVars' attribute to the builder.  This allows for
    1604                 :        instance environment variables for proxy configuration such as
    1605                 :        `http_proxy' to be easily passed to downloaders like
    1606                 :        `fetchurl'.  Passing such environment variables from the caller
    1607                 :        to the builder is generally impure, but the output of
    1608                 :        fixed-output derivations is by definition pure (since we
    1609                 :        already know the cryptographic hash of the output). */
    1610             112 :     if (fixedOutput) {
    1611               7 :         Strings varNames = tokenizeString(drv.env["impureEnvVars"]);
    1612              49 :         for (Strings::iterator i = varNames.begin(); i != varNames.end(); ++i)
    1613              21 :             env[*i] = getEnv(*i);
    1614                 :     }
    1615                 : 
    1616                 :     /* The `exportReferencesGraph' feature allows the references graph
    1617                 :        to be passed to a builder.  This attribute should be a list of
    1618                 :        pairs [name1 path1 name2 path2 ...].  The references graph of
    1619                 :        each `pathN' will be stored in a text file `nameN' in the
    1620                 :        temporary build directory.  The text files have the format used
    1621                 :        by `nix-store --register-validity'.  However, the deriver
    1622                 :        fields are left empty. */
    1623             112 :     string s = drv.env["exportReferencesGraph"];
    1624             224 :     Strings ss = tokenizeString(s);
    1625             224 :     if (ss.size() % 2 != 0)
    1626               0 :         throw BuildError(format("odd number of tokens in `exportReferencesGraph': `%1%'") % s);
    1627             112 :     for (Strings::iterator i = ss.begin(); i != ss.end(); ) {
    1628               0 :         string fileName = *i++;
    1629               0 :         checkStoreName(fileName); /* !!! abuse of this function */
    1630                 : 
    1631                 :         /* Check that the store path is valid. */
    1632               0 :         Path storePath = *i++;
    1633               0 :         if (!isInStore(storePath))
    1634                 :             throw BuildError(format("`exportReferencesGraph' contains a non-store path `%1%'")
    1635               0 :                 % storePath);
    1636               0 :         storePath = toStorePath(storePath);
    1637               0 :         if (!worker.store.isValidPath(storePath))
    1638                 :             throw BuildError(format("`exportReferencesGraph' contains an invalid path `%1%'")
    1639               0 :                 % storePath);
    1640                 : 
    1641                 :         /* Write closure info to `fileName'. */
    1642               0 :         PathSet refs;
    1643               0 :         computeFSClosure(storePath, refs);
    1644                 :         /* !!! in secure Nix, the writing should be done on the
    1645                 :            build uid for security (maybe). */
    1646                 :         writeStringToFile(tmpDir + "/" + fileName,
    1647               0 :             makeValidityRegistration(refs, false, false));
    1648                 :     }
    1649                 : 
    1650                 :     // The same for derivations
    1651                 :     // !!! urgh, cut&paste duplication
    1652             112 :     s = drv.env["exportBuildReferencesGraph"];
    1653             224 :     ss = tokenizeString(s);
    1654             224 :     if (ss.size() % 2 != 0)
    1655               0 :         throw BuildError(format("odd number of tokens in `exportBuildReferencesGraph': `%1%'") % s);
    1656             112 :     for (Strings::iterator i = ss.begin(); i != ss.end(); ) {
    1657               0 :         string fileName = *i++;
    1658               0 :         checkStoreName(fileName); /* !!! abuse of this function */
    1659                 : 
    1660                 :         /* Check that the store path is valid. */
    1661               0 :         Path storePath = *i++;
    1662               0 :         if (!isInStore(storePath))
    1663                 :             throw BuildError(format("`exportBuildReferencesGraph' contains a non-store path `%1%'")
    1664               0 :                 % storePath);
    1665               0 :         storePath = toStorePath(storePath);
    1666               0 :         if (!worker.store.isValidPath(storePath))
    1667                 :             throw BuildError(format("`exportBuildReferencesGraph' contains an invalid path `%1%'")
    1668               0 :                 % storePath);
    1669                 : 
    1670                 :         /* Write closure info to `fileName'. */
    1671               0 :         PathSet refs1,refs;
    1672               0 :         computeFSClosure(storePath, refs1);
    1673               0 :         for (PathSet::iterator j = refs1.begin(); j != refs1.end() ; j++) {
    1674               0 :                 refs.insert (*j);
    1675               0 :                 if (isDerivation (*j)) {
    1676               0 :                         Derivation deriv = derivationFromPath (*j);
    1677               0 :                         for (DerivationOutputs::iterator k=deriv.outputs.begin(); 
    1678                 :                             k != deriv.outputs.end(); k++) {
    1679               0 :                                 refs.insert(k->second.path);
    1680                 :                                 
    1681               0 :                         }
    1682                 :                 }
    1683                 :         }
    1684                 :         /* !!! in secure Nix, the writing should be done on the
    1685                 :            build uid for security (maybe). */
    1686                 :         writeStringToFile(tmpDir + "/" + fileName,
    1687               0 :             makeValidityRegistration(refs, false, false));
    1688                 :     }
    1689                 :     
    1690                 :     
    1691                 :     /* If `build-users-group' is not empty, then we have to build as
    1692                 :        one of the members of that group. */
    1693             112 :     if (haveBuildUsers()) {
    1694               0 :         buildUser.acquire();
    1695               0 :         assert(buildUser.getUID() != 0);
    1696               0 :         assert(buildUser.getGID() != 0);
    1697                 : 
    1698                 :         /* Make sure that no other processes are executing under this
    1699                 :            uid. */
    1700               0 :         buildUser.kill();
    1701                 :         
    1702                 :         /* Change ownership of the temporary build directory, if we're
    1703                 :            root.  If we're not root, then the setuid helper will do it
    1704                 :            just before it starts the builder. */
    1705               0 :         if (amPrivileged()) {
    1706               0 :             if (chown(tmpDir.c_str(), buildUser.getUID(), buildUser.getGID()) == -1)
    1707               0 :                 throw SysError(format("cannot change ownership of `%1%'") % tmpDir);
    1708                 :         }
    1709                 : 
    1710                 :         /* Check that the Nix store has the appropriate permissions,
    1711                 :            i.e., owned by root and mode 1775 (sticky bit on so that
    1712                 :            the builder can create its output but not mess with the
    1713                 :            outputs of other processes). */
    1714                 :         struct stat st;
    1715               0 :         if (stat(nixStore.c_str(), &st) == -1)
    1716               0 :             throw SysError(format("cannot stat `%1%'") % nixStore);
    1717               0 :         if (!(st.st_mode & S_ISVTX) ||
    1718                 :             ((st.st_mode & S_IRWXG) != S_IRWXG) ||
    1719                 :             (st.st_gid != buildUser.getGID()))
    1720                 :             throw Error(format(
    1721                 :                 "builder does not have write permission to `%2%'; "
    1722                 :                 "try `chgrp %1% %2%; chmod 1775 %2%'")
    1723               0 :                 % buildUser.getGID() % nixStore);
    1724                 :     }
    1725                 : 
    1726                 : 
    1727                 :     /* Are we doing a chroot build?  Note that fixed-output
    1728                 :        derivations are never done in a chroot, mainly so that
    1729                 :        functions like fetchurl (which needs a proper /etc/resolv.conf)
    1730                 :        work properly.  Purity checking for fixed-output derivations
    1731                 :        is somewhat pointless anyway. */
    1732             112 :     useChroot = queryBoolSetting("build-use-chroot", false);
    1733             112 :     Path chrootRootDir;
    1734                 : 
    1735             112 :     if (fixedOutput) useChroot = false;
    1736                 : 
    1737             112 :     if (useChroot) {
    1738                 : #if CHROOT_ENABLED
    1739                 :         /* Create a temporary directory in which we set up the chroot
    1740                 :            environment using bind-mounts.
    1741                 : 
    1742                 :            !!! Bind mounts are potentially dangerous: if the user
    1743                 :            cleans up his system by doing "rm -rf
    1744                 :            /nix/var/nix/chroots/*", this will recurse into /nix/store
    1745                 :            via the bind mounts (and potentially other parts of the
    1746                 :            filesystem, depending on the setting of the
    1747                 :            `build-chroot-dirs' option). */
    1748               0 :         chrootRootDir = createTempDir(nixChrootsDir, "chroot-nix");
    1749                 : 
    1750                 :         /* Clean up the chroot directory automatically, but don't
    1751                 :            recurse; that would be very very bad if the unmount of a
    1752                 :            bind-mount fails. Instead BindMount::unbind() unmounts and
    1753                 :            deletes exactly those directories that it created to
    1754                 :            produce the mount point, so that after all the BindMount
    1755                 :            destructors have run, chrootRootDir should be empty. */
    1756               0 :         autoDelChrootRoot = boost::shared_ptr<AutoDelete>(new AutoDelete(chrootRootDir, false));
    1757                 :         
    1758               0 :         printMsg(lvlChatty, format("setting up chroot environment in `%1%'") % chrootRootDir);
    1759                 : 
    1760                 :         /* Create a writable /tmp in the chroot.  Many builders need
    1761                 :            this.  (Of course they should really respect $TMPDIR
    1762                 :            instead.) */
    1763               0 :         Path chrootTmpDir = chrootRootDir + "/tmp";
    1764               0 :         createDirs(chrootTmpDir);
    1765                 : 
    1766               0 :         if (chmod(chrootTmpDir.c_str(), 01777) == -1)
    1767               0 :             throw SysError("creating /tmp in the chroot");
    1768                 : 
    1769                 :         /* When deleting this, do recurse (the builder might have left
    1770                 :            crap there).  As long as nothing important is bind-mounted
    1771                 :            under /tmp it's okay (and the bind-mounts are unmounted
    1772                 :            before autoDelChrootTmp's destructor runs, anyway).  */
    1773               0 :         autoDelChrootTmp = boost::shared_ptr<AutoDelete>(new AutoDelete(chrootTmpDir));
    1774                 : 
    1775                 :         /* Bind-mount a user-configurable set of directories from the
    1776                 :            host file system.  The `/dev/pts' directory must be mounted
    1777                 :            separately so that newly-created pseudo-terminals show
    1778                 :            up. */
    1779               0 :         Paths defaultDirs;
    1780               0 :         defaultDirs.push_back("/dev");
    1781               0 :         defaultDirs.push_back("/dev/pts");
    1782               0 :         defaultDirs.push_back("/proc");
    1783                 :         
    1784               0 :         Paths dirsInChroot = querySetting("build-chroot-dirs", defaultDirs);
    1785                 : 
    1786               0 :         dirsInChroot.push_front(nixStore);
    1787               0 :         dirsInChroot.push_front(tmpDir);
    1788                 : 
    1789                 :         /* Push BindMounts at the front of the list so that they get
    1790                 :            unmounted in LIFO order.  (!!! Does the C++ standard
    1791                 :            guarantee that list elements are destroyed in order?) */
    1792               0 :         for (Paths::iterator i = dirsInChroot.begin(); i != dirsInChroot.end(); ++i)
    1793               0 :             bindMounts.push_front(boost::shared_ptr<BindMount>(new BindMount(*i, chrootRootDir + *i)));
    1794                 :         
    1795                 : #else
    1796                 :         throw Error("chroot builds are not supported on this platform");
    1797                 : #endif
    1798                 :     }
    1799                 :     
    1800                 :     
    1801                 :     /* Run the builder. */
    1802             112 :     printMsg(lvlChatty, format("executing builder `%1%'") %
    1803                 :         drv.builder);
    1804                 : 
    1805                 :     /* Create the log file and pipe. */
    1806             112 :     Path logFile = openLogFile();
    1807                 :     
    1808                 :     /* Fork a child to build the package.  Note that while we
    1809                 :        currently use forks to run and wait for the children, it
    1810                 :        shouldn't be hard to use threads for this on systems where
    1811                 :        fork() is unavailable or inefficient. */
    1812             112 :     pid = fork();
    1813             112 :     switch (pid) {
    1814                 : 
    1815                 :     case -1:
    1816               0 :         throw SysError("unable to fork");
    1817                 : 
    1818                 :     case 0:
    1819                 : 
    1820                 :         /* Warning: in the child we should absolutely not make any
    1821                 :            Berkeley DB calls! */
    1822                 : 
    1823                 :         try { /* child */
    1824                 : 
    1825                 : #if CHROOT_ENABLED
    1826                 :             /* If building in a chroot, do the chroot right away.
    1827                 :                initChild() will do a chdir() to the temporary build
    1828                 :                directory to make sure the current directory is in the
    1829                 :                chroot.  (Actually the order doesn't matter, since due
    1830                 :                to the bind mount tmpDir and tmpRootDit/tmpDir are the
    1831                 :                same directories.) */
    1832               0 :             if (useChroot && chroot(chrootRootDir.c_str()) == -1)
    1833               0 :                 throw SysError(format("cannot change root directory to `%1%'") % chrootRootDir);
    1834                 : #endif
    1835                 :             
    1836               0 :             initChild();
    1837                 : 
    1838                 :             /* Fill in the environment. */
    1839               0 :             Strings envStrs;
    1840               0 :             for (Environment::const_iterator i = env.begin();
    1841                 :                  i != env.end(); ++i)
    1842               0 :                 envStrs.push_back(i->first + "=" + i->second);
    1843               0 :             const char * * envArr = strings2CharPtrs(envStrs);
    1844                 : 
    1845               0 :             Path program = drv.builder.c_str();
    1846               0 :             std::vector<const char *> args; /* careful with c_str()! */
    1847               0 :             string user; /* must be here for its c_str()! */
    1848                 :             
    1849                 :             /* If we are running in `build-users' mode, then switch to
    1850                 :                the user we allocated above.  Make sure that we drop
    1851                 :                all root privileges.  Note that initChild() above has
    1852                 :                closed all file descriptors except std*, so that's
    1853                 :                safe.  Also note that setuid() when run as root sets
    1854                 :                the real, effective and saved UIDs. */
    1855               0 :             if (buildUser.enabled()) {
    1856               0 :                 printMsg(lvlChatty, format("switching to user `%1%'") % buildUser.getUser());
    1857                 : 
    1858               0 :                 if (amPrivileged()) {
    1859                 :                     
    1860               0 :                     if (setgroups(0, 0) == -1)
    1861               0 :                         throw SysError("cannot clear the set of supplementary groups");
    1862                 :                 
    1863               0 :                     if (setgid(buildUser.getGID()) == -1 ||
    1864                 :                         getgid() != buildUser.getGID() ||
    1865                 :                         getegid() != buildUser.getGID())
    1866               0 :                         throw SysError("setgid failed");
    1867                 : 
    1868               0 :                     if (setuid(buildUser.getUID()) == -1 ||
    1869                 :                         getuid() != buildUser.getUID() ||
    1870                 :                         geteuid() != buildUser.getUID())
    1871               0 :                         throw SysError("setuid failed");
    1872                 :                     
    1873                 :                 } else {
    1874                 :                     /* Let the setuid helper take care of it. */
    1875               0 :                     program = nixLibexecDir + "/nix-setuid-helper";
    1876               0 :                     args.push_back(program.c_str());
    1877               0 :                     args.push_back("run-builder");
    1878               0 :                     user = buildUser.getUser().c_str();
    1879               0 :                     args.push_back(user.c_str());
    1880               0 :                     args.push_back(drv.builder.c_str());
    1881                 :                 }
    1882                 :             }
    1883                 :             
    1884                 :             /* Fill in the arguments. */
    1885               0 :             string builderBasename = baseNameOf(drv.builder);
    1886               0 :             args.push_back(builderBasename.c_str());
    1887               0 :             for (Strings::iterator i = drv.args.begin();
    1888                 :                  i != drv.args.end(); ++i)
    1889               0 :                 args.push_back(i->c_str());
    1890               0 :             args.push_back(0);
    1891                 : 
    1892               0 :             restoreSIGPIPE();
    1893                 : 
    1894                 :             /* Execute the program.  This should not return. */
    1895               0 :             execve(program.c_str(), (char * *) &args[0], (char * *) envArr);
    1896                 : 
    1897                 :             throw SysError(format("executing `%1%'")
    1898               0 :                 % drv.builder);
    1899                 :             
    1900               0 :         } catch (std::exception & e) {
    1901               0 :             std::cerr << format("build error: %1%") % e.what() << std::endl;
    1902                 :         }
    1903               0 :         quickExit(1);
    1904                 :     }
    1905                 : 
    1906                 :     
    1907                 :     /* parent */
    1908             112 :     pid.setSeparatePG(true);
    1909             112 :     logPipe.writeSide.close();
    1910                 :     worker.childStarted(shared_from_this(), pid,
    1911             112 :         singleton<set<int> >(logPipe.readSide), true);
    1912                 : 
    1913             112 :     if (printBuildTrace) {
    1914               0 :         printMsg(lvlError, format("@ build-started %1% %2% %3% %4%")
    1915                 :             % drvPath % drv.outputs["out"].path % drv.platform % logFile);
    1916             112 :     }
    1917             112 : }
    1918                 : 
    1919                 : 
    1920                 : /* Parse a list of reference specifiers.  Each element must either be
    1921                 :    a store path, or the symbolic name of the output of the derivation
    1922                 :    (such as `out'). */
    1923               5 : PathSet parseReferenceSpecifiers(const Derivation & drv, string attr)
    1924                 : {
    1925               5 :     PathSet result;
    1926               5 :     Paths paths = tokenizeString(attr);
    1927              12 :     for (Strings::iterator i = paths.begin(); i != paths.end(); ++i) {
    1928               2 :         if (isStorePath(*i))
    1929               1 :             result.insert(*i);
    1930               1 :         else if (drv.outputs.find(*i) != drv.outputs.end())
    1931               1 :             result.insert(drv.outputs.find(*i)->second.path);
    1932                 :         else throw BuildError(
    1933                 :             format("derivation contains an illegal reference specifier `%1%'")
    1934               0 :             % *i);
    1935                 :     }
    1936               5 :     return result;
    1937                 : }
    1938                 : 
    1939                 : 
    1940             110 : void DerivationGoal::computeClosure()
    1941                 : {
    1942             110 :     map<Path, PathSet> allReferences;
    1943             110 :     map<Path, Hash> contentHashes;
    1944                 :     
    1945                 :     /* Check whether the output paths were created, and grep each
    1946                 :        output path to determine what other paths it references.  Also make all
    1947                 :        output paths read-only. */
    1948             217 :     for (DerivationOutputs::iterator i = drv.outputs.begin(); 
    1949                 :          i != drv.outputs.end(); ++i)
    1950                 :     {
    1951             110 :         Path path = i->second.path;
    1952             110 :         if (!pathExists(path)) {
    1953                 :             throw BuildError(
    1954                 :                 format("builder for `%1%' failed to produce output path `%2%'")
    1955               0 :                 % drvPath % path);
    1956                 :         }
    1957                 : 
    1958                 :         struct stat st;
    1959             110 :         if (lstat(path.c_str(), &st) == -1)
    1960               0 :             throw SysError(format("getting attributes of path `%1%'") % path);
    1961                 :             
    1962             110 :         startNest(nest, lvlTalkative,
    1963                 :             format("scanning for references inside `%1%'") % path);
    1964                 : 
    1965                 :         /* Check that fixed-output derivations produced the right
    1966                 :            outputs (i.e., the content hash should match the specified
    1967                 :            hash). */ 
    1968             110 :         if (i->second.hash != "") {
    1969                 : 
    1970               7 :             bool recursive = false;
    1971               7 :             string algo = i->second.hashAlgo;
    1972                 :             
    1973               7 :             if (string(algo, 0, 2) == "r:") {
    1974               4 :                 recursive = true;
    1975               4 :                 algo = string(algo, 2);
    1976                 :             }
    1977                 : 
    1978               7 :             if (!recursive) {
    1979                 :                 /* The output path should be a regular file without
    1980                 :                    execute permission. */
    1981               3 :                 if (!S_ISREG(st.st_mode) || (st.st_mode & S_IXUSR) != 0)
    1982                 :                     throw BuildError(
    1983                 :                         format("output path `%1% should be a non-executable regular file")
    1984               0 :                         % path);
    1985                 :             }
    1986                 : 
    1987                 :             /* Check the hash. */
    1988               7 :             HashType ht = parseHashType(algo);
    1989               7 :             if (ht == htUnknown)
    1990               0 :                 throw BuildError(format("unknown hash algorithm `%1%'") % algo);
    1991               7 :             Hash h = parseHash(ht, i->second.hash);
    1992               7 :             Hash h2 = recursive ? hashPath(ht, path) : hashFile(ht, path);
    1993               7 :             if (h != h2)
    1994                 :                 throw BuildError(
    1995                 :                     format("output path `%1%' should have %2% hash `%3%', instead has `%4%'")
    1996               1 :                     % path % algo % printHash(h) % printHash(h2));
    1997                 :         }
    1998                 : 
    1999                 :         /* Get rid of all weird permissions. */
    2000             109 :         canonicalisePathMetaData(path);
    2001                 : 
    2002                 :         /* For this output path, find the references to other paths contained
    2003                 :            in it. */
    2004             109 :         PathSet references = scanForReferences(path, allPaths);
    2005                 : 
    2006                 :         /* For debugging, print out the referenced and unreferenced
    2007                 :            paths. */
    2008             339 :         for (PathSet::iterator i = inputPaths.begin();
    2009                 :              i != inputPaths.end(); ++i)
    2010                 :         {
    2011             230 :             PathSet::iterator j = references.find(*i);
    2012             230 :             if (j == references.end())
    2013             153 :                 debug(format("unreferenced input: `%1%'") % *i);
    2014                 :             else
    2015              77 :                 debug(format("referenced input: `%1%'") % *i);
    2016                 :         }
    2017                 : 
    2018             109 :         allReferences[path] = references;
    2019                 : 
    2020                 :         /* If the derivation specifies an `allowedReferences'
    2021                 :            attribute (containing a list of paths that the output may
    2022                 :            refer to), check that all references are in that list.  !!!
    2023                 :            allowedReferences should really be per-output. */
    2024             109 :         if (drv.env.find("allowedReferences") != drv.env.end()) {
    2025               5 :             PathSet allowed = parseReferenceSpecifiers(drv, drv.env["allowedReferences"]);
    2026              12 :             for (PathSet::iterator i = references.begin(); i != references.end(); ++i)
    2027               4 :                 if (allowed.find(*i) == allowed.end())
    2028               5 :                     throw BuildError(format("output is not allowed to refer to path `%1%'") % *i);
    2029                 :         }
    2030                 :         
    2031                 :         /* Hash the contents of the path.  The hash is stored in the
    2032                 :            database so that we can verify later on whether nobody has
    2033                 :            messed with the store.  !!! inefficient: it would be nice
    2034                 :            if we could combine this with filterReferences(). */
    2035             107 :         contentHashes[path] = hashPath(htSHA256, path);
    2036                 :     }
    2037                 : 
    2038                 :     /* Register each output path as valid, and register the sets of
    2039                 :        paths referenced by each of them.  !!! this should be
    2040                 :        atomic so that either all paths are registered as valid, or
    2041                 :        none are. */
    2042             214 :     for (DerivationOutputs::iterator i = drv.outputs.begin(); 
    2043                 :          i != drv.outputs.end(); ++i)
    2044                 :     {
    2045                 :         worker.store.registerValidPath(i->second.path,
    2046                 :             contentHashes[i->second.path],
    2047                 :             allReferences[i->second.path],
    2048             107 :             drvPath);
    2049                 :     }
    2050                 : 
    2051                 :     /* It is now safe to delete the lock files, since all future
    2052                 :        lockers will see that the output paths are valid; they will not
    2053                 :        create new lock files with the same names as the old (unlinked)
    2054                 :        lock files. */
    2055             110 :     outputLocks.setDeletion(true);
    2056             107 : }
    2057                 : 
    2058                 : 
    2059            1704 : string drvsLogDir = "drvs";
    2060                 : 
    2061                 : 
    2062             115 : Path DerivationGoal::openLogFile()
    2063                 : {
    2064                 :     /* Create a log file. */
    2065             115 :     Path dir = (format("%1%/%2%") % nixLogDir % drvsLogDir).str();
    2066             230 :     createDirs(dir);
    2067                 :     
    2068             115 :     Path logFileName = (format("%1%/%2%") % dir % baseNameOf(drvPath)).str();
    2069                 :     fdLogFile = open(logFileName.c_str(),
    2070             115 :         O_CREAT | O_WRONLY | O_TRUNC, 0666);
    2071             115 :     if (fdLogFile == -1)
    2072               0 :         throw SysError(format("creating log file `%1%'") % logFileName);
    2073                 : 
    2074                 :     /* Create a pipe to get the output of the child. */
    2075             115 :     logPipe.create();
    2076                 : 
    2077             115 :     return logFileName;
    2078                 : }
    2079                 : 
    2080                 : 
    2081               3 : void DerivationGoal::initChild()
    2082                 : {
    2083               3 :     commonChildInit(logPipe);
    2084                 :     
    2085               3 :     if (chdir(tmpDir.c_str()) == -1)
    2086               0 :         throw SysError(format("changing into `%1%'") % tmpDir);
    2087                 : 
    2088                 :     /* When running a hook, dup the communication pipes. */
    2089               3 :     bool inHook = fromHook.writeSide.isOpen();
    2090               3 :     if (inHook) {
    2091               3 :         fromHook.readSide.close();
    2092               3 :         if (dup2(fromHook.writeSide, 3) == -1)
    2093               0 :             throw SysError("dupping from-hook write side");
    2094                 : 
    2095               3 :         toHook.writeSide.close();
    2096               3 :         if (dup2(toHook.readSide, 4) == -1)
    2097               0 :             throw SysError("dupping to-hook read side");
    2098                 :     }
    2099                 : 
    2100                 :     /* Close all other file descriptors. */
    2101               3 :     set<int> exceptions;
    2102               3 :     if (inHook) {
    2103               3 :         exceptions.insert(3);
    2104               3 :         exceptions.insert(4);
    2105                 :     }
    2106               3 :     closeMostFDs(exceptions);
    2107               3 : }
    2108                 : 
    2109                 : 
    2110             335 : void DerivationGoal::deleteTmpDir(bool force)
    2111                 : {
    2112             335 :     if (tmpDir != "") {
    2113             115 :         if (keepFailed && !force) {
    2114               0 :             printMsg(lvlError, 
    2115                 :                 format("builder for `%1%' failed; keeping build directory `%2%'")
    2116                 :                 % drvPath % tmpDir);
    2117               0 :             if (buildUser.enabled() && !amPrivileged())
    2118               0 :                 getOwnership(tmpDir);
    2119                 :         }
    2120                 :         else
    2121             115 :             deletePathWrapped(tmpDir);
    2122             115 :         tmpDir = "";
    2123                 :     }
    2124             335 : }
    2125                 : 
    2126                 : 
    2127             352 : void DerivationGoal::handleChildOutput(int fd, const string & data)
    2128                 : {
    2129             352 :     if (fd == logPipe.readSide) {
    2130             352 :         if (verbosity >= buildVerbosity)
    2131             352 :             writeToStderr((unsigned char *) data.c_str(), data.size());
    2132             352 :         writeFull(fdLogFile, (unsigned char *) data.c_str(), data.size());
    2133                 :     }
    2134                 : 
    2135               0 :     else abort();
    2136             352 : }
    2137                 : 
    2138                 : 
    2139             113 : void DerivationGoal::handleEOF(int fd)
    2140                 : {
    2141             113 :     if (fd == logPipe.readSide) worker.wakeUp(shared_from_this());
    2142             113 : }
    2143                 : 
    2144                 : 
    2145             640 : PathSet DerivationGoal::checkPathValidity(bool returnValid)
    2146                 : {
    2147             640 :     PathSet result;
    2148            1280 :     for (DerivationOutputs::iterator i = drv.outputs.begin();
    2149                 :          i != drv.outputs.end(); ++i)
    2150             640 :         if (worker.store.isValidPath(i->second.path)) {
    2151             106 :             if (returnValid) result.insert(i->second.path);
    2152                 :         } else {
    2153             534 :             if (!returnValid) result.insert(i->second.path);
    2154                 :         }
    2155               0 :     return result;
    2156                 : }
    2157                 : 
    2158                 : 
    2159                 : 
    2160                 : //////////////////////////////////////////////////////////////////////
    2161                 : 
    2162                 : 
    2163                 : class SubstitutionGoal : public Goal
    2164                 : {
    2165                 :     friend class Worker;
    2166                 :     
    2167                 : private:
    2168                 :     /* The store path that should be realised through a substitute. */
    2169                 :     Path storePath;
    2170                 : 
    2171                 :     /* The remaining substituters. */
    2172                 :     Paths subs;
    2173                 : 
    2174                 :     /* The current substituter. */
    2175                 :     Path sub;
    2176                 : 
    2177                 :     /* Path info returned by the substituter's query info operation. */
    2178                 :     SubstitutablePathInfo info;
    2179                 : 
    2180                 :     /* Pipe for the substitute's standard output/error. */
    2181                 :     Pipe logPipe;
    2182                 : 
    2183                 :     /* The process ID of the builder. */
    2184                 :     Pid pid;
    2185                 : 
    2186                 :     /* Lock on the store path. */
    2187                 :     boost::shared_ptr<PathLocks> outputLock;
    2188                 :     
    2189                 :     typedef void (SubstitutionGoal::*GoalState)();
    2190                 :     GoalState state;
    2191                 : 
    2192                 : public:
    2193                 :     SubstitutionGoal(const Path & storePath, Worker & worker);
    2194                 :     ~SubstitutionGoal();
    2195                 : 
    2196                 :     void cancel();
    2197                 :     
    2198                 :     void work();
    2199                 : 
    2200                 :     /* The states. */
    2201                 :     void init();
    2202                 :     void tryNext();
    2203                 :     void gotInfo();
    2204                 :     void referencesValid();
    2205                 :     void tryToRun();
    2206                 :     void finished();
    2207                 : 
    2208                 :     /* Callback used by the worker to write to the log. */
    2209                 :     void handleChildOutput(int fd, const string & data);
    2210                 :     void handleEOF(int fd);
    2211                 : };
    2212                 : 
    2213                 : 
    2214             391 : SubstitutionGoal::SubstitutionGoal(const Path & storePath, Worker & worker)
    2215             391 :     : Goal(worker)
    2216                 : {
    2217             391 :     this->storePath = storePath;
    2218             391 :     state = &SubstitutionGoal::init;
    2219             391 :     name = (format("substitution of `%1%'") % storePath).str();
    2220             391 :     trace("created");
    2221             391 : }
    2222                 : 
    2223                 : 
    2224             391 : SubstitutionGoal::~SubstitutionGoal()
    2225                 : {
    2226                 :     /* !!! Once we let substitution goals run under a build user, we
    2227                 :        need to do use the setuid helper just as in ~DerivationGoal().
    2228                 :        Idem for cancel. */
    2229             391 :     if (pid != -1) worker.childTerminated(pid);
    2230             391 : }
    2231                 : 
    2232                 : 
    2233               0 : void SubstitutionGoal::cancel()
    2234                 : {
    2235               0 :     if (pid != -1) {
    2236               0 :         pid_t savedPid = pid;
    2237               0 :         pid.kill();
    2238               0 :         worker.childTerminated(savedPid);
    2239                 :     }
    2240               0 :     amDone(ecFailed);
    2241               0 : }
    2242                 : 
    2243                 : 
    2244             437 : void SubstitutionGoal::work()
    2245                 : {
    2246             437 :     (this->*state)();
    2247             437 : }
    2248                 : 
    2249                 : 
    2250             391 : void SubstitutionGoal::init()
    2251                 : {
    2252             391 :     trace("init");
    2253                 : 
    2254             391 :     worker.store.addTempRoot(storePath);
    2255                 :     
    2256                 :     /* If the path already exists we're done. */
    2257             391 :     if (worker.store.isValidPath(storePath)) {
    2258             375 :         amDone(ecSuccess);
    2259             375 :         return;
    2260                 :     }
    2261                 : 
    2262              16 :     subs = substituters;
    2263                 :     
    2264              16 :     tryNext();
    2265                 : }
    2266                 : 
    2267                 : 
    2268              31 : void SubstitutionGoal::tryNext()
    2269                 : {
    2270              31 :     trace("trying next substituter");
    2271                 : 
    2272              31 :     if (subs.size() == 0) {
    2273                 :         /* None left.  Terminate this goal and let someone else deal
    2274                 :            with it. */
    2275               2 :         printMsg(lvlError,
    2276                 :             format("path `%1%' is required, but there is no substituter that can build it")
    2277                 :             % storePath);
    2278               2 :         amDone(ecFailed);
    2279               2 :         return;
    2280                 :     }
    2281                 : 
    2282              29 :     sub = subs.front();
    2283              29 :     subs.pop_front();
    2284                 : 
    2285              29 :     if (!worker.store.querySubstitutablePathInfo(sub, storePath, info)) {
    2286              12 :         tryNext();
    2287              12 :         return;
    2288                 :     }
    2289                 : 
    2290                 :     /* To maintain the closure invariant, we first have to realise the
    2291                 :        paths referenced by this one. */
    2292              28 :     foreach (PathSet::iterator, i, info.references)
    2293              11 :         if (*i != storePath) /* ignore self-references */
    2294               8 :             addWaitee(worker.makeSubstitutionGoal(*i));
    2295                 : 
    2296              17 :     if (waitees.empty()) /* to prevent hang (no wake-up event) */
    2297              11 :         referencesValid();
    2298                 :     else
    2299               6 :         state = &SubstitutionGoal::referencesValid;
    2300                 : }
    2301                 : 
    2302                 : 
    2303              17 : void SubstitutionGoal::referencesValid()
    2304                 : {
    2305              17 :     trace("all references realised");
    2306                 : 
    2307              17 :     if (nrFailed > 0) {
    2308               0 :         printMsg(lvlError,
    2309                 :             format("some references of path `%1%' could not be realised") % storePath);
    2310               0 :         amDone(ecFailed);
    2311               0 :         return;
    2312                 :     }
    2313                 : 
    2314              28 :     foreach (PathSet::iterator, i, info.references)
    2315              11 :         if (*i != storePath) /* ignore self-references */
    2316               8 :             assert(worker.store.isValidPath(*i));
    2317                 : 
    2318              17 :     state = &SubstitutionGoal::tryToRun;
    2319              17 :     worker.waitForBuildSlot(shared_from_this());
    2320                 : }
    2321                 : 
    2322                 : 
    2323              20 : void SubstitutionGoal::tryToRun()
    2324                 : {
    2325              20 :     trace("trying to run");
    2326                 : 
    2327                 :     /* Make sure that we are allowed to start a build. */
    2328              20 :     if (!worker.canBuildMore()) {
    2329               3 :         worker.waitForBuildSlot(shared_from_this());
    2330               3 :         return;
    2331                 :     }
    2332                 : 
    2333                 :     /* Maybe a derivation goal has already locked this path
    2334                 :        (exceedingly unlikely, since it should have used a substitute
    2335                 :        first, but let's be defensive). */
    2336              17 :     outputLock.reset(); // make sure this goal's lock is gone
    2337              17 :     if (pathIsLockedByMe(storePath)) {
    2338               0 :         debug(format("restarting substitution of `%1%' because it's locked by another goal")
    2339                 :             % storePath);
    2340               0 :         worker.waitForAnyGoal(shared_from_this());
    2341               0 :         return; /* restart in the tryToRun() state when another goal finishes */
    2342                 :     }
    2343                 :     
    2344                 :     /* Acquire a lock on the output path. */
    2345              17 :     outputLock = boost::shared_ptr<PathLocks>(new PathLocks);
    2346                 :     outputLock->lockPaths(singleton<PathSet>(storePath),
    2347              34 :         (format("waiting for lock on `%1%'") % storePath).str());
    2348                 : 
    2349                 :     /* Check again whether the path is invalid. */
    2350              17 :     if (worker.store.isValidPath(storePath)) {
    2351               0 :         debug(format("store path `%1%' has become valid") % storePath);
    2352               0 :         outputLock->setDeletion(true);
    2353               0 :         amDone(ecSuccess);
    2354               0 :         return;
    2355                 :     }
    2356                 : 
    2357              17 :     printMsg(lvlInfo,
    2358                 :         format("substituting path `%1%' using substituter `%2%'")
    2359                 :         % storePath % sub);
    2360                 :     
    2361              17 :     logPipe.create();
    2362                 : 
    2363                 :     /* Remove the (stale) output path if it exists. */
    2364              17 :     if (pathExists(storePath))
    2365               0 :         deletePathWrapped(storePath);
    2366                 : 
    2367                 :     /* Fork the substitute program. */
    2368              17 :     pid = fork();
    2369              17 :     switch (pid) {
    2370                 :         
    2371                 :     case -1:
    2372               0 :         throw SysError("unable to fork");
    2373                 : 
    2374                 :     case 0:
    2375                 :         try { /* child */
    2376                 : 
    2377               0 :             logPipe.readSide.close();
    2378                 : 
    2379                 :             /* !!! close other handles */
    2380                 : 
    2381               0 :             commonChildInit(logPipe);
    2382                 : 
    2383                 :             /* Fill in the arguments. */
    2384               0 :             Strings args;
    2385               0 :             args.push_back(baseNameOf(sub));
    2386               0 :             args.push_back("--substitute");
    2387               0 :             args.push_back(storePath);
    2388               0 :             const char * * argArr = strings2CharPtrs(args);
    2389                 : 
    2390               0 :             execv(sub.c_str(), (char * *) argArr);
    2391                 :             
    2392               0 :             throw SysError(format("executing `%1%'") % sub);
    2393                 :             
    2394               0 :         } catch (std::exception & e) {
    2395               0 :             std::cerr << format("substitute error: %1%") % e.what() << std::endl;
    2396                 :         }
    2397               0 :         quickExit(1);
    2398                 :     }
    2399                 :     
    2400                 :     /* parent */
    2401              17 :     pid.setSeparatePG(true);
    2402              17 :     pid.setKillSignal(SIGTERM);
    2403              17 :     logPipe.writeSide.close();
    2404                 :     worker.childStarted(shared_from_this(),
    2405              17 :         pid, singleton<set<int> >(logPipe.readSide), true);
    2406                 : 
    2407              17 :     state = &SubstitutionGoal::finished;
    2408                 : 
    2409              17 :     if (printBuildTrace) {
    2410               0 :         printMsg(lvlError, format("@ substituter-started %1% %2%")
    2411                 :             % storePath % sub);
    2412                 :     }
    2413                 : }
    2414                 : 
    2415                 : 
    2416              17 : void SubstitutionGoal::finished()
    2417                 : {
    2418              17 :     trace("substitute finished");
    2419                 : 
    2420                 :     /* Since we got an EOF on the logger pipe, the substitute is
    2421                 :        presumed to have terminated.  */
    2422                 :     /* !!! this could block! */
    2423              17 :     pid_t savedPid = pid;
    2424              17 :     int status = pid.wait(true);
    2425                 : 
    2426                 :     /* So the child is gone now. */
    2427              17 :     worker.childTerminated(savedPid);
    2428                 : 
    2429                 :     /* Close the read side of the logger pipe. */
    2430              17 :     logPipe.readSide.close();
    2431                 : 
    2432              17 :     debug(format("substitute for `%1%' finished") % storePath);
    2433                 : 
    2434                 :     /* Check the exit status and the build result. */
    2435                 :     try {
    2436                 :         
    2437              17 :         if (!statusOk(status))
    2438                 :             throw SubstError(format("builder for `%1%' %2%")
    2439               3 :                 % storePath % statusToString(status));
    2440                 : 
    2441              14 :         if (!pathExists(storePath))
    2442                 :             throw SubstError(
    2443                 :                 format("substitute did not produce path `%1%'")
    2444               0 :                 % storePath);
    2445                 :         
    2446               6 :     } catch (SubstError & e) {
    2447                 : 
    2448               3 :         printMsg(lvlInfo,
    2449                 :             format("substitution of path `%1%' using substituter `%2%' failed: %3%")
    2450                 :             % storePath % sub % e.msg());
    2451                 :         
    2452               3 :         if (printBuildTrace) {
    2453               0 :             printMsg(lvlError, format("@ substituter-failed %1% %2% %3%")
    2454                 :                 % storePath % status % e.msg());
    2455                 :         }
    2456                 :         
    2457                 :         /* Try the next substitute. */
    2458               3 :         state = &SubstitutionGoal::tryNext;
    2459               3 :         worker.wakeUp(shared_from_this());
    2460               3 :         return;
    2461                 :     }
    2462                 : 
    2463              14 :     canonicalisePathMetaData(storePath);
    2464                 : 
    2465              14 :     Hash contentHash = hashPath(htSHA256, storePath);
    2466                 : 
    2467                 :     worker.store.registerValidPath(storePath, contentHash,
    2468              14 :         info.references, info.deriver);
    2469                 : 
    2470              14 :     outputLock->setDeletion(true);
    2471                 :     
    2472              16 :     printMsg(lvlChatty,
    2473                 :         format("substitution of path `%1%' succeeded") % storePath);
    2474                 : 
    2475              14 :     if (printBuildTrace) {
    2476               0 :         printMsg(lvlError, format("@ substituter-succeeded %1%") % storePath);
    2477                 :     }
    2478                 :     
    2479              14 :     amDone(ecSuccess);
    2480                 : }
    2481                 : 
    2482                 : 
    2483            2465 : void SubstitutionGoal::handleChildOutput(int fd, const string & data)
    2484                 : {
    2485            2465 :     assert(fd == logPipe.readSide);
    2486            2465 :     if (verbosity >= buildVerbosity)
    2487            2465 :         writeToStderr((unsigned char *) data.c_str(), data.size());
    2488                 :     /* Don't write substitution output to a log file for now.  We
    2489                 :        probably should, though. */
    2490            2465 : }
    2491                 : 
    2492                 : 
    2493              17 : void SubstitutionGoal::handleEOF(int fd)
    2494                 : {
    2495              17 :     if (fd == logPipe.readSide) worker.wakeUp(shared_from_this());
    2496              17 : }
    2497                 : 
    2498                 : 
    2499                 : 
    2500                 : //////////////////////////////////////////////////////////////////////
    2501                 : 
    2502                 : 
    2503                 : static bool working = false;
    2504                 : 
    2505                 : 
    2506             174 : Worker::Worker(LocalStore & store)
    2507             174 :     : store(store)
    2508                 : {
    2509                 :     /* Debugging: prevent recursive workers. */ 
    2510             174 :     if (working) abort();
    2511             174 :     working = true;
    2512             174 :     nrChildren = 0;
    2513             174 : }
    2514                 : 
    2515                 : 
    2516             174 : Worker::~Worker()
    2517                 : {
    2518             174 :     working = false;
    2519                 : 
    2520                 :     /* Explicitly get rid of all strong pointers now.  After this all
    2521                 :        goals that refer to this worker should be gone.  (Otherwise we
    2522                 :        are in trouble, since goals may call childTerminated() etc. in
    2523                 :        their destructors). */
    2524             174 :     topGoals.clear();
    2525             174 : }
    2526                 : 
    2527                 : 
    2528                 : template<class T>
    2529                 : static GoalPtr addGoal(const Path & path,
    2530             651 :     Worker & worker, WeakGoalMap & goalMap)
    2531                 : {
    2532             651 :     GoalPtr goal = goalMap[path].lock();
    2533             651 :     if (!goal) {
    2534             611 :         goal = GoalPtr(new T(path, worker));
    2535             611 :         goalMap[path] = goal;
    2536             611 :         worker.wakeUp(goal);
    2537                 :     }
    2538               0 :     return goal;
    2539                 : }
    2540                 : 
    2541                 : 
    2542             234 : GoalPtr Worker::makeDerivationGoal(const Path & nePath)
    2543                 : {
    2544             234 :     return addGoal<DerivationGoal>(nePath, *this, derivationGoals);
    2545                 : }
    2546                 : 
    2547                 : 
    2548             417 : GoalPtr Worker::makeSubstitutionGoal(const Path & storePath)
    2549                 : {
    2550             417 :     return addGoal<SubstitutionGoal>(storePath, *this, substitutionGoals);
    2551                 : }
    2552                 : 
    2553                 : 
    2554            1220 : static void removeGoal(GoalPtr goal, WeakGoalMap & goalMap)
    2555                 : {
    2556                 :     /* !!! inefficient */
    2557            4296 :     for (WeakGoalMap::iterator i = goalMap.begin();
    2558                 :          i != goalMap.end(); )
    2559            1856 :         if (i->second.lock() == goal) {
    2560             610 :             WeakGoalMap::iterator j = i; ++j;
    2561             610 :             goalMap.erase(i);
    2562             610 :             i = j;
    2563                 :         }
    2564            1246 :         else ++i;
    2565            1220 : }
    2566                 : 
    2567                 : 
    2568             610 : void Worker::removeGoal(GoalPtr goal)
    2569                 : {
    2570             610 :     nix::removeGoal(goal, derivationGoals);
    2571            1220 :     nix::removeGoal(goal, substitutionGoals);
    2572             610 :     if (topGoals.find(goal) != topGoals.end()) {
    2573             167 :         topGoals.erase(goal);
    2574                 :         /* If a top-level goal failed, then kill all other goals
    2575                 :            (unless keepGoing was set). */
    2576             167 :         if (goal->getExitCode() == Goal::ecFailed && !keepGoing)
    2577               6 :             topGoals.clear();
    2578                 :     }
    2579                 : 
    2580                 :     /* Wake up goals waiting for any goal to finish. */
    2581             611 :     for (WeakGoals::iterator i = waitingForAnyGoal.begin();
    2582                 :          i != waitingForAnyGoal.end(); ++i)
    2583                 :     {
    2584               1 :         GoalPtr goal = i->lock();
    2585               1 :         if (goal) wakeUp(goal);
    2586                 :     }
    2587                 : 
    2588             610 :     waitingForAnyGoal.clear();
    2589             610 : }
    2590                 : 
    2591                 : 
    2592            1327 : void Worker::wakeUp(GoalPtr goal)
    2593                 : {
    2594            1327 :     goal->trace("woken up");
    2595            2654 :     awake.insert(goal);
    2596            1327 : }
    2597                 : 
    2598                 : 
    2599             287 : bool Worker::canBuildMore()
    2600                 : {
    2601             287 :     return nrChildren < maxBuildJobs;
    2602                 : }
    2603                 : 
    2604                 : 
    2605                 : void Worker::childStarted(GoalPtr goal,
    2606             132 :     pid_t pid, const set<int> & fds, bool inBuildSlot)
    2607                 : {
    2608             132 :     Child child;
    2609             132 :     child.goal = goal;
    2610             132 :     child.fds = fds;
    2611             132 :     child.lastOutput = time(0);
    2612             132 :     child.inBuildSlot = inBuildSlot;
    2613             132 :     children[pid] = child;
    2614             132 :     if (inBuildSlot) nrChildren++;
    2615             132 : }
    2616                 : 
    2617                 : 
    2618             132 : void Worker::childTerminated(pid_t pid, bool wakeSleepers)
    2619                 : {
    2620             132 :     assert(pid != -1); /* common mistake */
    2621                 :     
    2622             132 :     Children::iterator i = children.find(pid);
    2623             132 :     assert(i != children.end());
    2624                 : 
    2625             132 :     if (i->second.inBuildSlot) {
    2626             129 :         assert(nrChildren > 0);
    2627             129 :         nrChildren--;
    2628                 :     }
    2629                 : 
    2630             132 :     children.erase(pid);
    2631                 : 
    2632             132 :     if (wakeSleepers) {
    2633                 :         
    2634                 :         /* Wake up goals waiting for a build slot. */
    2635             188 :         for (WeakGoals::iterator i = wantingToBuild.begin();
    2636                 :              i != wantingToBuild.end(); ++i)
    2637                 :         {
    2638              58 :             GoalPtr goal = i->lock();
    2639              58 :             if (goal) wakeUp(goal);
    2640                 :         }
    2641                 : 
    2642             130 :         wantingToBuild.clear();
    2643                 :     }
    2644             132 : }
    2645                 : 
    2646                 : 
    2647              73 : void Worker::waitForBuildSlot(GoalPtr goal)
    2648                 : {
    2649              73 :     debug("wait for build slot");
    2650              73 :     if (canBuildMore())
    2651              15 :         wakeUp(goal); /* we can do it right away */
    2652                 :     else
    2653              58 :         wantingToBuild.insert(goal);
    2654              73 : }
    2655                 : 
    2656                 : 
    2657               0 : void Worker::waitForChildTermination(GoalPtr goal)
    2658                 : {
    2659               0 :     debug("wait for child termination");
    2660               0 :     if (children.size() == 0)
    2661                 :         throw Error("waiting for a build slot, yet there are no running children - "
    2662               0 :             "maybe the build hook gave an inappropriate `postpone' reply?");
    2663               0 :     wantingToBuild.insert(goal);
    2664               0 : }
    2665                 : 
    2666                 : 
    2667               1 : void Worker::waitForAnyGoal(GoalPtr goal)
    2668                 : {
    2669               1 :     debug("wait for any goal");
    2670               1 :     waitingForAnyGoal.insert(goal);
    2671               1 : }
    2672                 : 
    2673                 : 
    2674             174 : void Worker::run(const Goals & _topGoals)
    2675                 : {
    2676             342 :     for (Goals::iterator i = _topGoals.begin();
    2677                 :          i != _topGoals.end(); ++i)
    2678             168 :         topGoals.insert(*i);
    2679                 :     
    2680             174 :     startNest(nest, lvlDebug, format("entered goal loop"));
    2681                 : 
    2682            2945 :     while (1) {
    2683                 : 
    2684            3119 :         checkInterrupt();
    2685                 : 
    2686                 :         /* Call every wake goal. */
    2687            4138 :         while (!awake.empty() && !topGoals.empty()) {
    2688            1020 :             WeakGoals awake2(awake);
    2689            1020 :             awake.clear();
    2690            2199 :             for (WeakGoals::iterator i = awake2.begin(); i != awake2.end(); ++i) {
    2691            1327 :                 checkInterrupt();
    2692            1327 :                 GoalPtr goal = i->lock();
    2693            1327 :                 if (goal) goal->work();
    2694            1474 :                 if (topGoals.empty()) break;
    2695                 :             }
    2696                 :         }
    2697                 : 
    2698            3118 :         if (topGoals.empty()) break;
    2699                 : 
    2700                 :         /* Wait for input. */
    2701            2945 :         if (!children.empty())
    2702            2945 :             waitForInput();
    2703                 :         else
    2704                 :             /* !!! not when we're polling */
    2705               0 :             assert(!awake.empty());
    2706                 :     }
    2707                 : 
    2708                 :     /* If --keep-going is not set, it's possible that the main goal
    2709                 :        exited while some of its subgoals were still active.  But if
    2710                 :        --keep-going *is* set, then they must all be finished now. */
    2711             173 :     assert(!keepGoing || awake.empty());
    2712             173 :     assert(!keepGoing || wantingToBuild.empty());
    2713             174 :     assert(!keepGoing || children.empty());
    2714             173 : }
    2715                 : 
    2716                 : 
    2717            2945 : void Worker::waitForInput()
    2718                 : {
    2719            2945 :     printMsg(lvlVomit, "waiting for children");
    2720                 : 
    2721                 :     /* Process output from the file descriptors attached to the
    2722                 :        children, namely log output and output path creation commands.
    2723                 :        We also use this to detect child termination: if we get EOF on
    2724                 :        the logger pipe of a build, we assume that the builder has
    2725                 :        terminated. */
    2726                 : 
    2727                 :     /* If we're monitoring for silence on stdout/stderr, sleep until
    2728                 :        the first deadline for any child. */
    2729                 :     struct timeval timeout;
    2730            2945 :     if (maxSilentTime != 0) {
    2731               0 :         time_t oldest = 0;
    2732               0 :         for (Children::iterator i = children.begin();
    2733                 :              i != children.end(); ++i)
    2734                 :         {
    2735                 :             oldest = oldest == 0 || i->second.lastOutput < oldest
    2736               0 :                 ? i->second.lastOutput : oldest;
    2737                 :         }
    2738               0 :         time_t now = time(0);
    2739                 :         timeout.tv_sec = (time_t) (oldest + maxSilentTime) <= now ? 0 :
    2740               0 :             oldest + maxSilentTime - now;
    2741               0 :         timeout.tv_usec = 0;
    2742               0 :         printMsg(lvlVomit, format("sleeping %1% seconds") % timeout.tv_sec);
    2743                 :     }
    2744                 : 
    2745                 :     /* Use select() to wait for the input side of any logger pipe to
    2746                 :        become `available'.  Note that `available' (i.e., non-blocking)
    2747                 :        includes EOF. */
    2748                 :     fd_set fds;
    2749            2945 :     FD_ZERO(&fds);
    2750            2945 :     int fdMax = 0;
    2751            5957 :     for (Children::iterator i = children.begin();
    2752                 :          i != children.end(); ++i)
    2753                 :     {
    2754            6024 :         for (set<int>::iterator j = i->second.fds.begin();
    2755                 :              j != i->second.fds.end(); ++j)
    2756                 :         {
    2757            3012 :             FD_SET(*j, &fds);
    2758            3012 :             if (*j >= fdMax) fdMax = *j + 1;
    2759                 :         }
    2760                 :     }
    2761                 : 
    2762            2945 :     if (select(fdMax, &fds, 0, 0, maxSilentTime != 0 ? &timeout : 0) == -1) {
    2763               0 :         if (errno == EINTR) return;
    2764               0 :         throw SysError("waiting for input");
    2765                 :     }
    2766                 : 
    2767            2945 :     time_t now = time(0);
    2768                 : 
    2769                 :     /* Process all available file descriptors. */
    2770                 : 
    2771                 :     /* Since goals may be canceled from inside the loop below (causing
    2772                 :        them go be erased from the `children' map), we have to be
    2773                 :        careful that we don't keep iterators alive across calls to
    2774                 :        cancel(). */
    2775            2945 :     set<pid_t> pids;
    2776            5957 :     for (Children::iterator i = children.begin();
    2777                 :          i != children.end(); ++i)
    2778            3012 :         pids.insert(i->first);
    2779                 :             
    2780            5957 :     for (set<pid_t>::iterator i = pids.begin();
    2781                 :          i != pids.end(); ++i)
    2782                 :     {
    2783            3012 :         checkInterrupt();
    2784            3012 :         Children::iterator j = children.find(*i);
    2785            3012 :         if (j == children.end()) continue; // child destroyed
    2786            3012 :         GoalPtr goal = j->second.goal.lock();
    2787            3012 :         assert(goal);
    2788                 : 
    2789            3012 :         set<int> fds2(j->second.fds);
    2790            6024 :         for (set<int>::iterator k = fds2.begin(); k != fds2.end(); ++k) {
    2791            3012 :             if (FD_ISSET(*k, &fds)) {
    2792                 :                 unsigned char buffer[4096];
    2793            2947 :                 ssize_t rd = read(*k, buffer, sizeof(buffer));
    2794            2947 :                 if (rd == -1) {
    2795               0 :                     if (errno != EINTR)
    2796                 :                         throw SysError(format("reading from %1%")
    2797               0 :                             % goal->getName());
    2798            2947 :                 } else if (rd == 0) {
    2799             130 :                     debug(format("%1%: got EOF") % goal->getName());
    2800             130 :                     goal->handleEOF(*k);
    2801             130 :                     j->second.fds.erase(*k);
    2802                 :                 } else {
    2803            2817 :                     printMsg(lvlVomit, format("%1%: read %2% bytes")
    2804                 :                         % goal->getName() % rd);
    2805            2817 :                     string data((char *) buffer, rd);
    2806            2817 :                     goal->handleChildOutput(*k, data);
    2807            2817 :                     j->second.lastOutput = now;
    2808                 :                 }
    2809                 :             }
    2810                 :         }
    2811                 : 
    2812            3012 :         if (maxSilentTime != 0 &&
    2813                 :             now - j->second.lastOutput >= (time_t) maxSilentTime)
    2814                 :         {
    2815               0 :             printMsg(lvlError,
    2816                 :                 format("%1% timed out after %2% seconds of silence")
    2817                 :                 % goal->getName() % maxSilentTime);
    2818               0 :             goal->cancel();
    2819                 :         }
    2820            2945 :     }
    2821                 : }
    2822                 : 
    2823                 : 
    2824                 : 
    2825                 : //////////////////////////////////////////////////////////////////////
    2826                 : 
    2827                 : 
    2828             172 : void LocalStore::buildDerivations(const PathSet & drvPaths)
    2829                 : {
    2830             172 :     startNest(nest, lvlDebug,
    2831                 :         format("building %1%") % showPaths(drvPaths));
    2832                 : 
    2833             172 :     Worker worker(*this);
    2834                 : 
    2835             172 :     Goals goals;
    2836             338 :     for (PathSet::const_iterator i = drvPaths.begin();
    2837                 :          i != drvPaths.end(); ++i)
    2838             166 :         goals.insert(worker.makeDerivationGoal(*i));
    2839                 : 
    2840             172 :     worker.run(goals);
    2841                 : 
    2842             171 :     PathSet failed;
    2843             336 :     for (Goals::iterator i = goals.begin(); i != goals.end(); ++i)
    2844             165 :         if ((*i)->getExitCode() == Goal::ecFailed) {
    2845               6 :             DerivationGoal * i2 = dynamic_cast<DerivationGoal *>(i->get());
    2846               6 :             assert(i2);
    2847               6 :             failed.insert(i2->getDrvPath());
    2848                 :         }
    2849                 :             
    2850             171 :     if (!failed.empty())
    2851              13 :         throw Error(format("build of %1% failed") % showPaths(failed));
    2852             165 : }
    2853                 : 
    2854                 : 
    2855             483 : void LocalStore::ensurePath(const Path & path)
    2856                 : {
    2857                 :     /* If the path is already valid, we're done. */
    2858             483 :     if (isValidPath(path)) return;
    2859                 : 
    2860               2 :     Worker worker(*this);
    2861               2 :     GoalPtr goal = worker.makeSubstitutionGoal(path);
    2862               2 :     Goals goals = singleton<Goals>(goal);
    2863                 : 
    2864               2 :     worker.run(goals);
    2865                 : 
    2866               2 :     if (goal->getExitCode() != Goal::ecSuccess)
    2867               0 :         throw Error(format("path `%1%' does not exist and cannot be created") % path);
    2868                 : }
    2869                 : 
    2870               0 :  
    2871            1106 : }

Generated by: LTP GCOV extension version 1.6