LTP GCOV extension - code coverage report
Current view: directory - src/libstore - build.cc
Test: app.info
Date: 2006-10-11 Instrumented lines: 784
Code covered: 78.8 % Executed lines: 618

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

Generated by: LTP GCOV extension version 1.1