about summary refs log tree commit diff
path: root/src/libstore
diff options
context:
space:
mode:
Diffstat (limited to 'src/libstore')
-rw-r--r--src/libstore/build.cc176
-rw-r--r--src/libstore/derivations.cc22
-rw-r--r--src/libstore/gc.cc78
-rw-r--r--src/libstore/globals.cc201
-rw-r--r--src/libstore/globals.hh221
-rw-r--r--src/libstore/local-store.cc64
-rw-r--r--src/libstore/local-store.hh49
-rw-r--r--src/libstore/misc.cc24
-rw-r--r--src/libstore/optimise-store.cc28
-rw-r--r--src/libstore/remote-store.cc24
-rw-r--r--src/libstore/store-api.cc16
11 files changed, 475 insertions, 428 deletions
diff --git a/src/libstore/build.cc b/src/libstore/build.cc
index 4a2bc5218b69..0972d6e19364 100644
--- a/src/libstore/build.cc
+++ b/src/libstore/build.cc
@@ -229,8 +229,6 @@ private:
 
 public:
 
-    bool cacheFailure;
-
     /* Set if at least one derivation had a BuildError (i.e. permanent
        failure). */
     bool permanentFailure;
@@ -314,7 +312,7 @@ void Goal::waiteeDone(GoalPtr waitee, ExitCode result)
 
     if (result == ecNoSubstituters) ++nrNoSubstituters;
 
-    if (waitees.empty() || (result == ecFailed && !keepGoing)) {
+    if (waitees.empty() || (result == ecFailed && !settings.keepGoing)) {
 
         /* If we failed and keepGoing is not set, we remove all
            remaining waitees. */
@@ -466,14 +464,13 @@ void UserLock::acquire()
 {
     assert(uid == 0);
 
-    string buildUsersGroup = querySetting("build-users-group", "");
-    assert(buildUsersGroup != "");
+    assert(settings.buildUsersGroup != "");
 
     /* Get the members of the build-users-group. */
-    struct group * gr = getgrnam(buildUsersGroup.c_str());
+    struct group * gr = getgrnam(settings.buildUsersGroup.c_str());
     if (!gr)
         throw Error(format("the group `%1%' specified in `build-users-group' does not exist")
-            % buildUsersGroup);
+            % settings.buildUsersGroup);
     gid = gr->gr_gid;
 
     /* Copy the result of getgrnam. */
@@ -485,7 +482,7 @@ void UserLock::acquire()
 
     if (users.empty())
         throw Error(format("the build users group `%1%' has no members")
-            % buildUsersGroup);
+            % settings.buildUsersGroup);
 
     /* Find a user account that isn't currently in use for another
        build. */
@@ -495,11 +492,11 @@ void UserLock::acquire()
         struct passwd * pw = getpwnam(i->c_str());
         if (!pw)
             throw Error(format("the user `%1%' in the group `%2%' does not exist")
-                % *i % buildUsersGroup);
+                % *i % settings.buildUsersGroup);
 
-        createDirs(nixStateDir + "/userpool");
+        createDirs(settings.nixStateDir + "/userpool");
 
-        fnUserLock = (format("%1%/userpool/%2%") % nixStateDir % pw->pw_uid).str();
+        fnUserLock = (format("%1%/userpool/%2%") % settings.nixStateDir % pw->pw_uid).str();
 
         if (lockedPaths.find(fnUserLock) != lockedPaths.end())
             /* We already have a lock on this one. */
@@ -519,7 +516,7 @@ void UserLock::acquire()
             /* Sanity check... */
             if (uid == getuid() || uid == geteuid())
                 throw Error(format("the Nix user should not be a member of `%1%'")
-                    % buildUsersGroup);
+                    % settings.buildUsersGroup);
 
             return;
         }
@@ -527,7 +524,7 @@ void UserLock::acquire()
 
     throw Error(format("all build users are currently in use; "
         "consider creating additional users and adding them to the `%1%' group")
-        % buildUsersGroup);
+        % settings.buildUsersGroup);
 }
 
 
@@ -546,7 +543,7 @@ static void runSetuidHelper(const string & command,
     const string & arg)
 {
     Path program = getEnv("NIX_SETUID_HELPER",
-        nixLibexecDir + "/nix-setuid-helper");
+        settings.nixLibexecDir + "/nix-setuid-helper");
 
     /* Fork. */
     Pid pid;
@@ -601,12 +598,6 @@ bool amPrivileged()
 }
 
 
-bool haveBuildUsers()
-{
-    return querySetting("build-users-group", "") != "";
-}
-
-
 void getOwnership(const Path & path)
 {
     runSetuidHelper("get-ownership", path);
@@ -622,7 +613,7 @@ void deletePathWrapped(const Path & path,
     } catch (SysError & e) {
         /* If this failed due to a permission error, then try it with
            the setuid helper. */
-        if (haveBuildUsers() && !amPrivileged()) {
+        if (settings.buildUsersGroup != "" && !amPrivileged()) {
             getOwnership(path);
             deletePath(path, bytesFreed, blocksFreed);
         } else
@@ -701,9 +692,9 @@ HookInstance::HookInstance()
                 throw SysError("dupping builder's stdout/stderr");
 
             /* XXX: Pass `buildTimeout' to the hook?  */
-            execl(buildHook.c_str(), buildHook.c_str(), thisSystem.c_str(),
-                (format("%1%") % maxSilentTime).str().c_str(),
-                (format("%1%") % printBuildTrace).str().c_str(),
+            execl(buildHook.c_str(), buildHook.c_str(), settings.thisSystem.c_str(),
+                (format("%1%") % settings.maxSilentTime).str().c_str(),
+                (format("%1%") % settings.printBuildTrace).str().c_str(),
                 NULL);
 
             throw SysError(format("executing `%1%'") % buildHook);
@@ -943,7 +934,7 @@ void DerivationGoal::init()
 {
     trace("init");
 
-    if (readOnlyMode)
+    if (settings.readOnlyMode)
         throw Error(format("cannot build derivation `%1%' - no write access to the Nix store") % drvPath);
 
     /* The first thing to do is to make sure that the derivation
@@ -995,7 +986,7 @@ void DerivationGoal::haveDerivation()
     /* We are first going to try to create the invalid output paths
        through substitutes.  If that doesn't work, we'll build
        them. */
-    if (queryBoolSetting("build-use-substitutes", true))
+    if (settings.useSubstitutes)
         foreach (PathSet::iterator, i, invalidOutputs)
             addWaitee(worker.makeSubstitutionGoal(*i));
 
@@ -1010,7 +1001,7 @@ void DerivationGoal::outputsSubstituted()
 {
     trace("all outputs substituted (maybe)");
 
-    if (nrFailed > 0 && nrFailed > nrNoSubstituters && !tryFallback)
+    if (nrFailed > 0 && nrFailed > nrNoSubstituters && !settings.tryFallback)
         throw Error(format("some substitutes for the outputs of derivation `%1%' failed; try `--fallback'") % drvPath);
 
     nrFailed = nrNoSubstituters = 0;
@@ -1109,9 +1100,9 @@ PathSet outputPaths(const DerivationOutputs & outputs)
 
 static bool canBuildLocally(const string & platform)
 {
-    return platform == thisSystem
+    return platform == settings.thisSystem
 #ifdef CAN_DO_LINUX32_BUILDS
-        || (platform == "i686-linux" && thisSystem == "x86_64-linux")
+        || (platform == "i686-linux" && settings.thisSystem == "x86_64-linux")
 #endif
         ;
 }
@@ -1213,7 +1204,7 @@ void DerivationGoal::tryToBuild()
        derivation prefers to be done locally, do it even if
        maxBuildJobs is 0. */
     unsigned int curBuilds = worker.getNrLocalBuilds();
-    if (curBuilds >= maxBuildJobs && !(preferLocalBuild && curBuilds == 0)) {
+    if (curBuilds >= settings.maxBuildJobs && !(preferLocalBuild && curBuilds == 0)) {
         worker.waitForBuildSlot(shared_from_this());
         outputLocks.unlock();
         return;
@@ -1228,7 +1219,7 @@ void DerivationGoal::tryToBuild()
         printMsg(lvlError, e.msg());
         outputLocks.unlock();
         buildUser.release();
-        if (printBuildTrace)
+        if (settings.printBuildTrace)
             printMsg(lvlError, format("@ build-failed %1% %2% %3% %4%")
                 % drvPath % drv.outputs["out"].path % 0 % e.msg());
         worker.permanentFailure = true;
@@ -1360,7 +1351,7 @@ void DerivationGoal::buildDone()
         bool hookError = hook &&
             (!WIFEXITED(status) || WEXITSTATUS(status) != 100);
 
-        if (printBuildTrace) {
+        if (settings.printBuildTrace) {
             if (hook && hookError)
                 printMsg(lvlError, format("@ hook-failed %1% %2% %3% %4%")
                     % drvPath % drv.outputs["out"].path % status % e.msg());
@@ -1376,7 +1367,7 @@ void DerivationGoal::buildDone()
            able to access the network).  Hook errors (like
            communication problems with the remote machine) shouldn't
            be cached either. */
-        if (worker.cacheFailure && !hookError && !fixedOutput)
+        if (settings.cacheFailure && !hookError && !fixedOutput)
             foreach (DerivationOutputs::iterator, i, drv.outputs)
                 worker.store.registerFailedPath(i->second.path);
 
@@ -1388,7 +1379,7 @@ void DerivationGoal::buildDone()
     /* Release the build user, if applicable. */
     buildUser.release();
 
-    if (printBuildTrace) {
+    if (settings.printBuildTrace) {
         printMsg(lvlError, format("@ build-succeeded %1% %2%")
             % drvPath % drv.outputs["out"].path);
     }
@@ -1399,7 +1390,7 @@ void DerivationGoal::buildDone()
 
 HookReply DerivationGoal::tryBuildHook()
 {
-    if (!useBuildHook || getEnv("NIX_BUILD_HOOK") == "") return rpDecline;
+    if (!settings.useBuildHook || getEnv("NIX_BUILD_HOOK") == "") return rpDecline;
 
     if (!worker.hook)
         worker.hook = boost::shared_ptr<HookInstance>(new HookInstance);
@@ -1412,7 +1403,7 @@ HookReply DerivationGoal::tryBuildHook()
 
     /* Send the request to the hook. */
     writeLine(worker.hook->toHook.writeSide, (format("%1% %2% %3% %4%")
-        % (worker.getNrLocalBuilds() < maxBuildJobs ? "1" : "0")
+        % (worker.getNrLocalBuilds() < settings.maxBuildJobs ? "1" : "0")
         % drv.platform % drvPath % concatStringsSep(",", features)).str());
 
     /* Read the first line of input, which should be a word indicating
@@ -1471,7 +1462,7 @@ HookReply DerivationGoal::tryBuildHook()
     fds.insert(hook->builderOut.readSide);
     worker.childStarted(shared_from_this(), hook->pid, fds, false, false);
 
-    if (printBuildTrace)
+    if (settings.printBuildTrace)
         printMsg(lvlError, format("@ build-started %1% %2% %3% %4%")
             % drvPath % drv.outputs["out"].path % drv.platform % logFile);
 
@@ -1502,7 +1493,7 @@ void DerivationGoal::startBuilder()
     if (!canBuildLocally(drv.platform))
         throw Error(
             format("a `%1%' is required to build `%3%', but I am a `%2%'")
-            % drv.platform % thisSystem % drvPath);
+            % drv.platform % settings.thisSystem % drvPath);
 
     /* Construct the environment passed to the builder. */
 
@@ -1523,10 +1514,10 @@ void DerivationGoal::startBuilder()
        shouldn't care, but this is useful for purity checking (e.g.,
        the compiler or linker might only want to accept paths to files
        in the store or in the build directory). */
-    env["NIX_STORE"] = nixStore;
+    env["NIX_STORE"] = settings.nixStore;
 
     /* The maximum number of cores to utilize for parallel building. */
-    env["NIX_BUILD_CORES"] = (format("%d") % buildCores).str();
+    env["NIX_BUILD_CORES"] = (format("%d") % settings.buildCores).str();
 
     /* Add all bindings specified in the derivation. */
     foreach (StringPairs::iterator, i, drv.env)
@@ -1618,7 +1609,7 @@ void DerivationGoal::startBuilder()
 
     /* If `build-users-group' is not empty, then we have to build as
        one of the members of that group. */
-    if (haveBuildUsers()) {
+    if (settings.buildUsersGroup != "") {
         buildUser.acquire();
         assert(buildUser.getUID() != 0);
         assert(buildUser.getGID() != 0);
@@ -1640,15 +1631,15 @@ void DerivationGoal::startBuilder()
            the builder can create its output but not mess with the
            outputs of other processes). */
         struct stat st;
-        if (stat(nixStore.c_str(), &st) == -1)
-            throw SysError(format("cannot stat `%1%'") % nixStore);
+        if (stat(settings.nixStore.c_str(), &st) == -1)
+            throw SysError(format("cannot stat `%1%'") % settings.nixStore);
         if (!(st.st_mode & S_ISVTX) ||
             ((st.st_mode & S_IRWXG) != S_IRWXG) ||
             (st.st_gid != buildUser.getGID()))
             throw Error(format(
                 "builder does not have write permission to `%2%'; "
                 "try `chgrp %1% %2%; chmod 1775 %2%'")
-                % buildUser.getGID() % nixStore);
+                % buildUser.getGID() % settings.nixStore);
     }
 
 
@@ -1657,7 +1648,7 @@ void DerivationGoal::startBuilder()
        functions like fetchurl (which needs a proper /etc/resolv.conf)
        work properly.  Purity checking for fixed-output derivations
        is somewhat pointless anyway. */
-    useChroot = queryBoolSetting("build-use-chroot", false);
+    useChroot = settings.useChroot;
 
     if (fixedOutput) useChroot = false;
 
@@ -1707,16 +1698,8 @@ void DerivationGoal::startBuilder()
         writeFile(chrootRootDir + "/etc/hosts", "127.0.0.1 localhost\n");
 
         /* Bind-mount a user-configurable set of directories from the
-           host file system.  The `/dev/pts' directory must be mounted
-           separately so that newly-created pseudo-terminals show
-           up. */
-        Paths defaultDirs;
-        defaultDirs.push_back("/dev");
-        defaultDirs.push_back("/dev/pts");
-
-        Paths dirsInChroot_ = querySetting("build-chroot-dirs", defaultDirs);
-        dirsInChroot.insert(dirsInChroot_.begin(), dirsInChroot_.end());
-
+           host file system. */
+        dirsInChroot = settings.dirsInChroot;
         dirsInChroot.insert(tmpDir);
 
         /* Make the closure of the inputs available in the chroot,
@@ -1726,8 +1709,8 @@ void DerivationGoal::startBuilder()
            can be bind-mounted).  !!! As an extra security
            precaution, make the fake Nix store only writable by the
            build user. */
-        createDirs(chrootRootDir + nixStore);
-        chmod(chrootRootDir + nixStore, 01777);
+        createDirs(chrootRootDir + settings.nixStore);
+        chmod(chrootRootDir + settings.nixStore, 01777);
 
         foreach (PathSet::iterator, i, inputPaths) {
             struct stat st;
@@ -1827,7 +1810,7 @@ void DerivationGoal::startBuilder()
     worker.childStarted(shared_from_this(), pid,
         singleton<set<int> >(builderOut.readSide), true, true);
 
-    if (printBuildTrace) {
+    if (settings.printBuildTrace) {
         printMsg(lvlError, format("@ build-started %1% %2% %3% %4%")
             % drvPath % drv.outputs["out"].path % drv.platform % logFile);
     }
@@ -1907,16 +1890,14 @@ void DerivationGoal::initChild()
 #ifdef CAN_DO_LINUX32_BUILDS
         /* Change the personality to 32-bit if we're doing an
            i686-linux build on an x86_64-linux machine. */
-        if (drv.platform == "i686-linux" && thisSystem == "x86_64-linux") {
+        if (drv.platform == "i686-linux" && settings.thisSystem == "x86_64-linux") {
             if (personality(0x0008 | 0x8000000 /* == PER_LINUX32_3GB */) == -1)
                 throw SysError("cannot set i686-linux personality");
         }
 
         /* Impersonate a Linux 2.6 machine to get some determinism in
            builds that depend on the kernel version. */
-        if ((drv.platform == "i686-linux" || drv.platform == "x86_64-linux") &&
-            queryBoolSetting("build-impersonate-linux-26", true))
-        {
+        if ((drv.platform == "i686-linux" || drv.platform == "x86_64-linux") && settings.impersonateLinux26) {
             int cur = personality(0xffffffff);
             if (cur != -1) personality(cur | 0x0020000 /* == UNAME26 */);
         }
@@ -1958,7 +1939,7 @@ void DerivationGoal::initChild()
 
             } else {
                 /* Let the setuid helper take care of it. */
-                program = nixLibexecDir + "/nix-setuid-helper";
+                program = settings.nixLibexecDir + "/nix-setuid-helper";
                 args.push_back(program.c_str());
                 args.push_back("run-builder");
                 user = buildUser.getUser().c_str();
@@ -2126,13 +2107,13 @@ string drvsLogDir = "drvs";
 
 Path DerivationGoal::openLogFile()
 {
-    if (!queryBoolSetting("build-keep-log", true)) return "";
+    if (!settings.keepLog) return "";
 
     /* Create a log file. */
-    Path dir = (format("%1%/%2%") % nixLogDir % drvsLogDir).str();
+    Path dir = (format("%1%/%2%") % settings.nixLogDir % drvsLogDir).str();
     createDirs(dir);
 
-    if (queryBoolSetting("build-compress-log", true)) {
+    if (settings.compressLog) {
 
         Path logFileName = (format("%1%/%2%.bz2") % dir % baseNameOf(drvPath)).str();
         AutoCloseFD fd = open(logFileName.c_str(), O_CREAT | O_WRONLY | O_TRUNC, 0666);
@@ -2179,7 +2160,7 @@ void DerivationGoal::closeLogFile()
 void DerivationGoal::deleteTmpDir(bool force)
 {
     if (tmpDir != "") {
-        if (keepFailed && !force) {
+        if (settings.keepFailed && !force) {
             printMsg(lvlError,
                 format("builder for `%1%' failed; keeping build directory `%2%'")
                 % drvPath % tmpDir);
@@ -2199,7 +2180,7 @@ void DerivationGoal::handleChildOutput(int fd, const string & data)
     if ((hook && fd == hook->builderOut.readSide) ||
         (!hook && fd == builderOut.readSide))
     {
-        if (verbosity >= buildVerbosity)
+        if (verbosity >= settings.buildVerbosity)
             writeToStderr((unsigned char *) data.data(), data.size());
         if (bzLogFile) {
             int err;
@@ -2235,13 +2216,13 @@ PathSet DerivationGoal::checkPathValidity(bool returnValid)
 
 bool DerivationGoal::pathFailed(const Path & path)
 {
-    if (!worker.cacheFailure) return false;
+    if (!settings.cacheFailure) return false;
 
     if (!worker.store.hasPathFailed(path)) return false;
 
     printMsg(lvlError, format("builder for `%1%' failed previously (cached)") % path);
 
-    if (printBuildTrace)
+    if (settings.printBuildTrace)
         printMsg(lvlError, format("@ build-failed %1% %2% cached") % drvPath % path);
 
     worker.permanentFailure = true;
@@ -2362,10 +2343,10 @@ void SubstitutionGoal::init()
         return;
     }
 
-    if (readOnlyMode)
+    if (settings.readOnlyMode)
         throw Error(format("cannot substitute path `%1%' - no write access to the Nix store") % storePath);
 
-    subs = substituters;
+    subs = settings.substituters;
 
     tryNext();
 }
@@ -2437,7 +2418,7 @@ void SubstitutionGoal::tryToRun()
        is maxBuildJobs == 0 (no local builds allowed), we still allow
        a substituter to run.  This is because substitutions cannot be
        distributed to another machine via the build hook. */
-    if (worker.getNrLocalBuilds() >= (maxBuildJobs == 0 ? 1 : maxBuildJobs)) {
+    if (worker.getNrLocalBuilds() >= (settings.maxBuildJobs == 0 ? 1 : settings.maxBuildJobs)) {
         worker.waitForBuildSlot(shared_from_this());
         return;
     }
@@ -2496,7 +2477,7 @@ void SubstitutionGoal::tryToRun()
 
             /* Pass configuration options (including those overriden
                with --option) to the substituter. */
-            setenv("_NIX_OPTIONS", packSettings().c_str(), 1);
+            setenv("_NIX_OPTIONS", settings.pack().c_str(), 1);
 
             /* Fill in the arguments. */
             Strings args;
@@ -2525,7 +2506,7 @@ void SubstitutionGoal::tryToRun()
 
     state = &SubstitutionGoal::finished;
 
-    if (printBuildTrace) {
+    if (settings.printBuildTrace) {
         printMsg(lvlError, format("@ substituter-started %1% %2%")
             % storePath % sub);
     }
@@ -2583,7 +2564,7 @@ void SubstitutionGoal::finished()
 
         printMsg(lvlInfo, e.msg());
 
-        if (printBuildTrace) {
+        if (settings.printBuildTrace) {
             printMsg(lvlError, format("@ substituter-failed %1% %2% %3%")
                 % storePath % status % e.msg());
         }
@@ -2611,7 +2592,7 @@ void SubstitutionGoal::finished()
     printMsg(lvlChatty,
         format("substitution of path `%1%' succeeded") % storePath);
 
-    if (printBuildTrace) {
+    if (settings.printBuildTrace) {
         printMsg(lvlError, format("@ substituter-succeeded %1%") % storePath);
     }
 
@@ -2622,7 +2603,7 @@ void SubstitutionGoal::finished()
 void SubstitutionGoal::handleChildOutput(int fd, const string & data)
 {
     assert(fd == logPipe.readSide);
-    if (verbosity >= buildVerbosity)
+    if (verbosity >= settings.buildVerbosity)
         writeToStderr((unsigned char *) data.data(), data.size());
     /* Don't write substitution output to a log file for now.  We
        probably should, though. */
@@ -2650,7 +2631,6 @@ Worker::Worker(LocalStore & store)
     working = true;
     nrLocalBuilds = 0;
     lastWokenUp = 0;
-    cacheFailure = queryBoolSetting("build-cache-failure", false);
     permanentFailure = false;
 }
 
@@ -2715,7 +2695,7 @@ void Worker::removeGoal(GoalPtr goal)
         topGoals.erase(goal);
         /* If a top-level goal failed, then kill all other goals
            (unless keepGoing was set). */
-        if (goal->getExitCode() == Goal::ecFailed && !keepGoing)
+        if (goal->getExitCode() == Goal::ecFailed && !settings.keepGoing)
             topGoals.clear();
     }
 
@@ -2787,7 +2767,7 @@ void Worker::childTerminated(pid_t pid, bool wakeSleepers)
 void Worker::waitForBuildSlot(GoalPtr goal)
 {
     debug("wait for build slot");
-    if (getNrLocalBuilds() < maxBuildJobs)
+    if (getNrLocalBuilds() < settings.maxBuildJobs)
         wakeUp(goal); /* we can do it right away */
     else
         wantingToBuild.insert(goal);
@@ -2836,7 +2816,7 @@ void Worker::run(const Goals & _topGoals)
         if (!children.empty() || !waitingForAWhile.empty())
             waitForInput();
         else {
-            if (awake.empty() && maxBuildJobs == 0) throw Error(
+            if (awake.empty() && settings.maxBuildJobs == 0) throw Error(
                 "unable to start any build; either increase `--max-jobs' "
                 "or enable distributed builds");
             assert(!awake.empty());
@@ -2846,9 +2826,9 @@ void Worker::run(const Goals & _topGoals)
     /* If --keep-going is not set, it's possible that the main goal
        exited while some of its subgoals were still active.  But if
        --keep-going *is* set, then they must all be finished now. */
-    assert(!keepGoing || awake.empty());
-    assert(!keepGoing || wantingToBuild.empty());
-    assert(!keepGoing || children.empty());
+    assert(!settings.keepGoing || awake.empty());
+    assert(!settings.keepGoing || wantingToBuild.empty());
+    assert(!settings.keepGoing || children.empty());
 }
 
 
@@ -2868,15 +2848,15 @@ void Worker::waitForInput()
     time_t before = time(0);
 
     /* If a global timeout has been set, sleep until it's done.  */
-    if (buildTimeout != 0) {
+    if (settings.buildTimeout != 0) {
         useTimeout = true;
         if (lastWait == 0 || lastWait > before) lastWait = before;
-        timeout.tv_sec = std::max((time_t) 0, lastWait + buildTimeout - before);
+        timeout.tv_sec = std::max((time_t) 0, lastWait + settings.buildTimeout - before);
     }
 
     /* If we're monitoring for silence on stdout/stderr, sleep until
        the first deadline for any child. */
-    if (maxSilentTime != 0) {
+    if (settings.maxSilentTime != 0) {
         time_t oldest = 0;
         foreach (Children::iterator, i, children) {
             if (i->second.monitorForSilence) {
@@ -2885,7 +2865,7 @@ void Worker::waitForInput()
             }
         }
         if (oldest) {
-            time_t silenceTimeout = std::max((time_t) 0, oldest + maxSilentTime - before);
+            time_t silenceTimeout = std::max((time_t) 0, oldest + settings.maxSilentTime - before);
             timeout.tv_sec = useTimeout
                 ? std::min(silenceTimeout, timeout.tv_sec)
                 : silenceTimeout;
@@ -2896,14 +2876,12 @@ void Worker::waitForInput()
 
     /* If we are polling goals that are waiting for a lock, then wake
        up after a few seconds at most. */
-    int wakeUpInterval = queryIntSetting("build-poll-interval", 5);
-
     if (!waitingForAWhile.empty()) {
         useTimeout = true;
         if (lastWokenUp == 0)
             printMsg(lvlError, "waiting for locks or build slots...");
         if (lastWokenUp == 0 || lastWokenUp > before) lastWokenUp = before;
-        timeout.tv_sec = std::max((time_t) 0, lastWokenUp + wakeUpInterval - before);
+        timeout.tv_sec = std::max((time_t) 0, lastWokenUp + settings.pollInterval - before);
     } else lastWokenUp = 0;
 
     using namespace std;
@@ -2969,27 +2947,27 @@ void Worker::waitForInput()
             }
         }
 
-        if (maxSilentTime != 0 &&
+        if (settings.maxSilentTime != 0 &&
             j->second.monitorForSilence &&
-            after - j->second.lastOutput >= (time_t) maxSilentTime)
+            after - j->second.lastOutput >= (time_t) settings.maxSilentTime)
         {
             printMsg(lvlError,
                 format("%1% timed out after %2% seconds of silence")
-                % goal->getName() % maxSilentTime);
+                % goal->getName() % settings.maxSilentTime);
             goal->cancel();
         }
 
-        if (buildTimeout != 0 &&
-            after - before >= (time_t) buildTimeout)
+        if (settings.buildTimeout != 0 &&
+            after - before >= (time_t) settings.buildTimeout)
         {
             printMsg(lvlError,
                 format("%1% timed out after %2% seconds of activity")
-                % goal->getName() % buildTimeout);
+                % goal->getName() % settings.buildTimeout);
             goal->cancel();
         }
     }
 
-    if (!waitingForAWhile.empty() && lastWokenUp + wakeUpInterval <= after) {
+    if (!waitingForAWhile.empty() && lastWokenUp + settings.pollInterval <= after) {
         lastWokenUp = after;
         foreach (WeakGoals::iterator, i, waitingForAWhile) {
             GoalPtr goal = i->lock();
diff --git a/src/libstore/derivations.cc b/src/libstore/derivations.cc
index 97343d57d478..73047c7538a0 100644
--- a/src/libstore/derivations.cc
+++ b/src/libstore/derivations.cc
@@ -12,7 +12,7 @@ void DerivationOutput::parseHashInfo(bool & recursive, HashType & hashType, Hash
 {
     recursive = false;
     string algo = hashAlgo;
-        
+
     if (string(algo, 0, 2) == "r:") {
         recursive = true;
         algo = string(algo, 2);
@@ -21,7 +21,7 @@ void DerivationOutput::parseHashInfo(bool & recursive, HashType & hashType, Hash
     hashType = parseHashType(algo);
     if (hashType == htUnknown)
         throw Error(format("unknown hash algorithm `%1%'") % algo);
-        
+
     hash = parseHash(hashType, this->hash);
 }
 
@@ -38,7 +38,7 @@ Path writeDerivation(StoreAPI & store,
        held during a garbage collection). */
     string suffix = name + drvExtension;
     string contents = unparseDerivation(drv);
-    return readOnlyMode
+    return settings.readOnlyMode
         ? computeStorePathForText(suffix, contents, references)
         : store.addTextToStore(suffix, contents, references);
 }
@@ -51,7 +51,7 @@ static Path parsePath(std::istream & str)
         throw Error(format("bad path `%1%' in derivation") % s);
     return s;
 }
-    
+
 
 static StringSet parseStrings(std::istream & str, bool arePaths)
 {
@@ -60,7 +60,7 @@ static StringSet parseStrings(std::istream & str, bool arePaths)
         res.insert(arePaths ? parsePath(str) : parseString(str));
     return res;
 }
-    
+
 
 Derivation parseDerivation(const string & s)
 {
@@ -106,7 +106,7 @@ Derivation parseDerivation(const string & s)
         expect(str, ")");
         drv.env[name] = value;
     }
-    
+
     expect(str, ")");
     return drv;
 }
@@ -165,7 +165,7 @@ string unparseDerivation(const Derivation & drv)
 
     s += "],";
     printStrings(s, drv.inputSrcs.begin(), drv.inputSrcs.end());
-    
+
     s += ','; printString(s, drv.platform);
     s += ','; printString(s, drv.builder);
     s += ','; printStrings(s, drv.args.begin(), drv.args.end());
@@ -178,9 +178,9 @@ string unparseDerivation(const Derivation & drv)
         s += ','; printString(s, i->second);
         s += ')';
     }
-    
+
     s += "])";
-    
+
     return s;
 }
 
@@ -190,7 +190,7 @@ bool isDerivation(const string & fileName)
     return hasSuffix(fileName, drvExtension);
 }
 
- 
+
 bool isFixedOutputDrv(const Derivation & drv)
 {
     return drv.outputs.size() == 1 &&
@@ -247,7 +247,7 @@ Hash hashDerivationModulo(StoreAPI & store, Derivation drv)
         inputs2[printHash(h)] = i->second;
     }
     drv.inputDrvs = inputs2;
-    
+
     return hashString(htSHA256, unparseDerivation(drv));
 }
 
diff --git a/src/libstore/gc.cc b/src/libstore/gc.cc
index 874efe4d32d9..1355702f8701 100644
--- a/src/libstore/gc.cc
+++ b/src/libstore/gc.cc
@@ -34,10 +34,10 @@ static const int defaultGcLevel = 1000;
 int LocalStore::openGCLock(LockType lockType)
 {
     Path fnGCLock = (format("%1%/%2%")
-        % nixStateDir % gcLockName).str();
-         
+        % settings.nixStateDir % gcLockName).str();
+
     debug(format("acquiring global GC lock `%1%'") % fnGCLock);
-    
+
     AutoCloseFD fdGCLock = open(fnGCLock.c_str(), O_RDWR | O_CREAT, 0600);
     if (fdGCLock == -1)
         throw SysError(format("opening global GC lock `%1%'") % fnGCLock);
@@ -51,7 +51,7 @@ int LocalStore::openGCLock(LockType lockType)
     /* !!! Restrict read permission on the GC root.  Otherwise any
        process that can open the file for reading can DoS the
        collector. */
-    
+
     return fdGCLock.borrow();
 }
 
@@ -85,7 +85,7 @@ void LocalStore::addIndirectRoot(const Path & path)
 {
     string hash = printHash32(hashString(htSHA1, path));
     Path realRoot = canonPath((format("%1%/%2%/auto/%3%")
-        % nixStateDir % gcRootsDir % hash).str());
+        % settings.nixStateDir % gcRootsDir % hash).str());
     createSymlink(realRoot, path);
 }
 
@@ -113,15 +113,15 @@ Path addPermRoot(StoreAPI & store, const Path & _storePath,
 
     else {
         if (!allowOutsideRootsDir) {
-            Path rootsDir = canonPath((format("%1%/%2%") % nixStateDir % gcRootsDir).str());
-    
+            Path rootsDir = canonPath((format("%1%/%2%") % settings.nixStateDir % gcRootsDir).str());
+
             if (string(gcRoot, 0, rootsDir.size() + 1) != rootsDir + "/")
                 throw Error(format(
                     "path `%1%' is not a valid garbage collector root; "
                     "it's not in the directory `%2%'")
                     % gcRoot % rootsDir);
         }
-            
+
         createSymlink(gcRoot, storePath);
     }
 
@@ -130,10 +130,10 @@ Path addPermRoot(StoreAPI & store, const Path & _storePath,
        Instead of reading all the roots, it would be more efficient to
        check if the root is in a directory in or linked from the
        gcroots directory. */
-    if (queryBoolSetting("gc-check-reachability", false)) {
+    if (settings.checkRootReachability) {
         Roots roots = store.findRoots();
         if (roots.find(gcRoot) == roots.end())
-            printMsg(lvlError, 
+            printMsg(lvlError,
                 format(
                     "warning: `%1%' is not in a directory where the garbage collector looks for roots; "
                     "therefore, `%2%' might be removed by the garbage collector")
@@ -144,7 +144,7 @@ Path addPermRoot(StoreAPI & store, const Path & _storePath,
        progress.  This prevents the set of permanent roots from
        increasing while a GC is in progress. */
     store.syncWithGC();
-    
+
     return gcRoot;
 }
 
@@ -160,23 +160,23 @@ void LocalStore::addTempRoot(const Path & path)
     if (fdTempRoots == -1) {
 
         while (1) {
-            Path dir = (format("%1%/%2%") % nixStateDir % tempRootsDir).str();
+            Path dir = (format("%1%/%2%") % settings.nixStateDir % tempRootsDir).str();
             createDirs(dir);
-            
+
             fnTempRoots = (format("%1%/%2%")
                 % dir % getpid()).str();
 
             AutoCloseFD fdGCLock = openGCLock(ltRead);
-            
+
             if (pathExists(fnTempRoots))
                 /* It *must* be stale, since there can be no two
                    processes with the same pid. */
                 unlink(fnTempRoots.c_str());
 
-	    fdTempRoots = openLockFile(fnTempRoots, true);
+            fdTempRoots = openLockFile(fnTempRoots, true);
 
             fdGCLock.close();
-      
+
             debug(format("acquiring read lock on `%1%'") % fnTempRoots);
             lockFile(fdTempRoots, ltRead, true);
 
@@ -186,7 +186,7 @@ void LocalStore::addTempRoot(const Path & path)
             if (fstat(fdTempRoots, &st) == -1)
                 throw SysError(format("statting `%1%'") % fnTempRoots);
             if (st.st_size == 0) break;
-            
+
             /* The garbage collector deleted this file before we could
                get a lock.  (It won't delete the file after we get a
                lock.)  Try again. */
@@ -218,7 +218,7 @@ void removeTempRoots()
 
 
 /* Automatically clean up the temporary roots file when we exit. */
-struct RemoveTempRoots 
+struct RemoveTempRoots
 {
     ~RemoveTempRoots()
     {
@@ -238,10 +238,10 @@ static void readTempRoots(PathSet & tempRoots, FDs & fds)
     /* Read the `temproots' directory for per-process temporary root
        files. */
     Strings tempRootFiles = readDirectory(
-        (format("%1%/%2%") % nixStateDir % tempRootsDir).str());
+        (format("%1%/%2%") % settings.nixStateDir % tempRootsDir).str());
 
     foreach (Strings::iterator, i, tempRootFiles) {
-        Path path = (format("%1%/%2%/%3%") % nixStateDir % tempRootsDir % *i).str();
+        Path path = (format("%1%/%2%/%3%") % settings.nixStateDir % tempRootsDir % *i).str();
 
         debug(format("reading temporary root file `%1%'") % path);
         FDPtr fd(new AutoCloseFD(open(path.c_str(), O_RDWR, 0666)));
@@ -295,7 +295,7 @@ static void findRoots(StoreAPI & store, const Path & path,
     bool recurseSymlinks, bool deleteStale, Roots & roots)
 {
     try {
-        
+
         struct stat st;
         if (lstat(path.c_str(), &st) == -1)
             throw SysError(format("statting `%1%'") % path);
@@ -315,7 +315,7 @@ static void findRoots(StoreAPI & store, const Path & path,
                 debug(format("found root `%1%' in `%2%'")
                     % target % path);
                 Path storePath = toStorePath(target);
-                if (store.isValidPath(storePath)) 
+                if (store.isValidPath(storePath))
                     roots[path] = storePath;
                 else
                     printMsg(lvlInfo, format("skipping invalid root from `%1%' to `%2%'")
@@ -350,7 +350,7 @@ static void findRoots(StoreAPI & store, const Path & path,
 static Roots findRoots(StoreAPI & store, bool deleteStale)
 {
     Roots roots;
-    Path rootsDir = canonPath((format("%1%/%2%") % nixStateDir % gcRootsDir).str());
+    Path rootsDir = canonPath((format("%1%/%2%") % settings.nixStateDir % gcRootsDir).str());
     findRoots(store, rootsDir, true, deleteStale, roots);
     return roots;
 }
@@ -365,16 +365,16 @@ Roots LocalStore::findRoots()
 static void addAdditionalRoots(StoreAPI & store, PathSet & roots)
 {
     Path rootFinder = getEnv("NIX_ROOT_FINDER",
-        nixLibexecDir + "/nix/find-runtime-roots.pl");
+        settings.nixLibexecDir + "/nix/find-runtime-roots.pl");
 
     if (rootFinder.empty()) return;
-    
+
     debug(format("executing `%1%' to find additional roots") % rootFinder);
 
     string result = runProgram(rootFinder);
 
     Strings paths = tokenizeString(result, "\n");
-    
+
     foreach (Strings::iterator, i, paths) {
         if (isInStore(*i)) {
             Path path = toStorePath(*i);
@@ -557,7 +557,7 @@ bool LocalStore::tryToDelete(GCState & state, const Path & path)
 
     } else
         printMsg(lvlTalkative, format("would delete `%1%'") % path);
-    
+
     state.deleted.insert(path);
     if (state.options.action != GCOptions::gcReturnLive)
         state.results.paths.insert(path);
@@ -605,10 +605,10 @@ void LocalStore::removeUnusedLinks()
 void LocalStore::collectGarbage(const GCOptions & options, GCResults & results)
 {
     GCState state(results);
-    state.options = options;    
-    
-    state.gcKeepOutputs = queryBoolSetting("gc-keep-outputs", false);
-    state.gcKeepDerivations = queryBoolSetting("gc-keep-derivations", true);
+    state.options = options;
+
+    state.gcKeepOutputs = settings.gcKeepOutputs;
+    state.gcKeepDerivations = settings.gcKeepDerivations;
 
     /* Using `--ignore-liveness' with `--delete' can have unintended
        consequences if `gc-keep-outputs' or `gc-keep-derivations' are
@@ -618,7 +618,7 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results)
         state.gcKeepOutputs = false;
         state.gcKeepDerivations = false;
     }
-    
+
     /* Acquire the global GC root.  This prevents
        a) New roots from being added.
        b) Processes from creating new temporary root files. */
@@ -659,18 +659,18 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results)
             if (!tryToDelete(state, *i))
                 throw Error(format("cannot delete path `%1%' since it is still alive") % *i);
         }
-        
+
     } else {
-        
+
         if (shouldDelete(state.options.action))
             printMsg(lvlError, format("deleting garbage..."));
         else
             printMsg(lvlError, format("determining live/dead paths..."));
-    
+
         try {
 
-            AutoCloseDir dir = opendir(nixStore.c_str());
-            if (!dir) throw SysError(format("opening directory `%1%'") % nixStore);
+            AutoCloseDir dir = opendir(settings.nixStore.c_str());
+            if (!dir) throw SysError(format("opening directory `%1%'") % settings.nixStore);
 
             /* Read the store and immediately delete all paths that
                aren't valid.  When using --max-freed etc., deleting
@@ -684,14 +684,14 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results)
                 checkInterrupt();
                 string name = dirent->d_name;
                 if (name == "." || name == "..") continue;
-                Path path = nixStore + "/" + name;
+                Path path = settings.nixStore + "/" + name;
                 if (isValidPath(path))
                     entries.push_back(path);
                 else
                     tryToDelete(state, path);
             }
 
-	    dir.close();
+            dir.close();
 
             /* Now delete the unreachable valid paths.  Randomise the
                order in which we delete entries to make the collector
diff --git a/src/libstore/globals.cc b/src/libstore/globals.cc
index a28e08427da5..7dc2e714bc9d 100644
--- a/src/libstore/globals.cc
+++ b/src/libstore/globals.cc
@@ -10,38 +10,63 @@
 namespace nix {
 
 
-string nixStore = "/UNINIT";
-string nixDataDir = "/UNINIT";
-string nixLogDir = "/UNINIT";
-string nixStateDir = "/UNINIT";
-string nixDBPath = "/UNINIT";
-string nixConfDir = "/UNINIT";
-string nixLibexecDir = "/UNINIT";
-string nixBinDir = "/UNINIT";
+Settings settings;
 
-bool keepFailed = false;
-bool keepGoing = false;
-bool tryFallback = false;
-Verbosity buildVerbosity = lvlError;
-unsigned int maxBuildJobs = 1;
-unsigned int buildCores = 1;
-bool readOnlyMode = false;
-string thisSystem = "unset";
-time_t maxSilentTime = 0;
-time_t buildTimeout = 0;
-Paths substituters;
-bool useBuildHook = true;
-bool printBuildTrace = false;
 
+Settings::Settings()
+{
+    keepFailed = false;
+    keepGoing = false;
+    tryFallback = false;
+    buildVerbosity = lvlError;
+    maxBuildJobs = 1;
+    buildCores = 1;
+    readOnlyMode = false;
+    thisSystem = SYSTEM;
+    maxSilentTime = 0;
+    buildTimeout = 0;
+    useBuildHook = true;
+    printBuildTrace = false;
+    reservedSize = 1024 * 1024;
+    fsyncMetadata = true;
+    useSQLiteWAL = true;
+    syncBeforeRegistering = false;
+    useSubstitutes = true;
+    useChroot = false;
+    dirsInChroot.insert("/dev");
+    dirsInChroot.insert("/dev/pts");
+    impersonateLinux26 = false;
+    keepLog = true;
+    compressLog = true;
+    cacheFailure = false;
+    pollInterval = 5;
+    checkRootReachability = false;
+    gcKeepOutputs = false;
+    gcKeepDerivations = true;
+    autoOptimiseStore = true;
+    envKeepDerivations = false;
+}
 
-static bool settingsRead = false;
-
-typedef std::map<string, Strings> Settings;
 
-static Settings settings;
+void Settings::processEnvironment()
+{
+    nixStore = canonPath(getEnv("NIX_STORE_DIR", getEnv("NIX_STORE", NIX_STORE_DIR)));
+    nixDataDir = canonPath(getEnv("NIX_DATA_DIR", NIX_DATA_DIR));
+    nixLogDir = canonPath(getEnv("NIX_LOG_DIR", NIX_LOG_DIR));
+    nixStateDir = canonPath(getEnv("NIX_STATE_DIR", NIX_STATE_DIR));
+    nixDBPath = getEnv("NIX_DB_DIR", nixStateDir + "/db");
+    nixConfDir = canonPath(getEnv("NIX_CONF_DIR", NIX_CONF_DIR));
+    nixLibexecDir = canonPath(getEnv("NIX_LIBEXEC_DIR", NIX_LIBEXEC_DIR));
+    nixBinDir = canonPath(getEnv("NIX_BIN_DIR", NIX_BIN_DIR));
 
-/* Overriden settings. */
-Settings settingsCmdline;
+    string subs = getEnv("NIX_SUBSTITUTERS", "default");
+    if (subs == "default") {
+        substituters.push_back(nixLibexecDir + "/nix/substituters/copy-from-other-stores.pl");
+        substituters.push_back(nixLibexecDir + "/nix/substituters/download-using-manifests.pl");
+        substituters.push_back(nixLibexecDir + "/nix/substituters/download-from-binary-cache.pl");
+    } else
+        substituters = tokenizeString(subs, ":");
+}
 
 
 string & at(Strings & ss, unsigned int n)
@@ -52,7 +77,7 @@ string & at(Strings & ss, unsigned int n)
 }
 
 
-static void readSettings()
+void Settings::loadConfFile()
 {
     Path settingsFile = (format("%1%/%2%") % nixConfDir % "nix.conf").str();
     if (!pathExists(settingsFile)) return;
@@ -80,104 +105,88 @@ static void readSettings()
 
         Strings::iterator i = tokens.begin();
         advance(i, 2);
-        settings[name] = Strings(i, tokens.end());
+        settings[name] = concatStringsSep(" ", Strings(i, tokens.end())); // FIXME: slow
     };
-
-    settings.insert(settingsCmdline.begin(), settingsCmdline.end());
-
-    settingsRead = true;
 }
 
 
-Strings querySetting(const string & name, const Strings & def)
+void Settings::set(const string & name, const string & value)
 {
-    if (!settingsRead) readSettings();
-    Settings::iterator i = settings.find(name);
-    return i == settings.end() ? def : i->second;
+    settings[name] = value;
 }
 
 
-string querySetting(const string & name, const string & def)
+void Settings::update()
 {
-    Strings defs;
-    defs.push_back(def);
-
-    Strings value = querySetting(name, defs);
-    if (value.size() != 1)
-        throw Error(format("configuration option `%1%' should not be a list") % name);
-
-    return value.front();
+    get(thisSystem, "system");
+    get(maxBuildJobs, "build-max-jobs");
+    get(buildCores, "build-cores");
+    get(maxSilentTime, "build-max-silent-time");
+    get(buildTimeout, "build-timeout");
+    get(reservedSize, "gc-reserved-space");
+    get(fsyncMetadata, "fsync-metadata");
+    get(useSQLiteWAL, "use-sqlite-wal");
+    get(syncBeforeRegistering, "sync-before-registering");
+    get(useSubstitutes, "build-use-substitutes");
+    get(buildUsersGroup, "build-users-group");
+    get(useChroot, "build-use-chroot");
+    get(dirsInChroot, "build-chroot-dirs");
+    get(impersonateLinux26, "build-impersonate-linux-26");
+    get(keepLog, "build-keep-log");
+    get(compressLog, "build-compress-log");
+    get(cacheFailure, "build-cache-failure");
+    get(pollInterval, "build-poll-interval");
+    get(checkRootReachability, "gc-check-reachability");
+    get(gcKeepOutputs, "gc-keep-outputs");
+    get(gcKeepDerivations, "gc-keep-derivations");
+    get(autoOptimiseStore, "auto-optimise-store");
+    get(envKeepDerivations, "env-keep-derivations");
 }
 
 
-bool queryBoolSetting(const string & name, bool def)
+void Settings::get(string & res, const string & name)
 {
-    string v = querySetting(name, def ? "true" : "false");
-    if (v == "true") return true;
-    else if (v == "false") return false;
-    else throw Error(format("configuration option `%1%' should be either `true' or `false', not `%2%'")
-        % name % v);
+    SettingsMap::iterator i = settings.find(name);
+    if (i == settings.end()) return;
+    res = i->second;
 }
 
 
-unsigned int queryIntSetting(const string & name, unsigned int def)
+void Settings::get(bool & res, const string & name)
 {
-    int n;
-    if (!string2Int(querySetting(name, int2String(def)), n) || n < 0)
-        throw Error(format("configuration setting `%1%' should have an integer value") % name);
-    return n;
-}
-
-
-void overrideSetting(const string & name, const Strings & value)
-{
-    if (settingsRead) settings[name] = value;
-    settingsCmdline[name] = value;
+    SettingsMap::iterator i = settings.find(name);
+    if (i == settings.end()) return;
+    if (i->second == "true") res = true;
+    else if (i->second == "false") res = false;
+    else throw Error(format("configuration option `%1%' should be either `true' or `false', not `%2%'")
+        % name % i->second);
 }
 
 
-void reloadSettings()
+void Settings::get(PathSet & res, const string & name)
 {
-    settingsRead = false;
-    settings.clear();
+    SettingsMap::iterator i = settings.find(name);
+    if (i == settings.end()) return;
+    res.clear();
+    Strings ss = tokenizeString(i->second);
+    res.insert(ss.begin(), ss.end());
 }
 
 
-void setDefaultsFromEnvironment()
+template<class N> void Settings::get(N & res, const string & name)
 {
-    /* Setup Nix paths. */
-    nixStore = canonPath(getEnv("NIX_STORE_DIR", getEnv("NIX_STORE", NIX_STORE_DIR)));
-    nixDataDir = canonPath(getEnv("NIX_DATA_DIR", NIX_DATA_DIR));
-    nixLogDir = canonPath(getEnv("NIX_LOG_DIR", NIX_LOG_DIR));
-    nixStateDir = canonPath(getEnv("NIX_STATE_DIR", NIX_STATE_DIR));
-    nixDBPath = getEnv("NIX_DB_DIR", nixStateDir + "/db");
-    nixConfDir = canonPath(getEnv("NIX_CONF_DIR", NIX_CONF_DIR));
-    nixLibexecDir = canonPath(getEnv("NIX_LIBEXEC_DIR", NIX_LIBEXEC_DIR));
-    nixBinDir = canonPath(getEnv("NIX_BIN_DIR", NIX_BIN_DIR));
-
-    string subs = getEnv("NIX_SUBSTITUTERS", "default");
-    if (subs == "default") {
-        substituters.push_back(nixLibexecDir + "/nix/substituters/copy-from-other-stores.pl");
-        substituters.push_back(nixLibexecDir + "/nix/substituters/download-using-manifests.pl");
-        substituters.push_back(nixLibexecDir + "/nix/substituters/download-from-binary-cache.pl");
-    } else
-        substituters = tokenizeString(subs, ":");
-
-    /* Get some settings from the configuration file. */
-    thisSystem = querySetting("system", SYSTEM);
-    maxBuildJobs = queryIntSetting("build-max-jobs", 1);
-    buildCores = queryIntSetting("build-cores", 1);
-    maxSilentTime = queryIntSetting("build-max-silent-time", 0);
-    buildTimeout = queryIntSetting("build-timeout", 0);
+    SettingsMap::iterator i = settings.find(name);
+    if (i == settings.end()) return;
+    if (!string2Int(i->second, res))
+        throw Error(format("configuration setting `%1%' should have an integer value") % name);
 }
 
 
-string packSettings()
+string Settings::pack()
 {
     string s;
-    if (!settingsRead) readSettings();
-    foreach (Settings::iterator, i, settings) {
-        s += i->first; s += '='; s += concatStringsSep(" ", i->second); s += '\n';
+    foreach (SettingsMap::iterator, i, settings) {
+        s += i->first; s += '='; s += i->second; s += '\n';
     }
     return s;
 }
diff --git a/src/libstore/globals.hh b/src/libstore/globals.hh
index 30acf59ef54f..5783d9bf37ba 100644
--- a/src/libstore/globals.hh
+++ b/src/libstore/globals.hh
@@ -2,120 +2,189 @@
 
 #include "types.hh"
 
+#include <map>
+
 
 namespace nix {
 
 
-/* Path names. */
+struct Settings {
+
+    Settings();
+
+    void processEnvironment();
+
+    void loadConfFile();
+
+    void set(const string & name, const string & value);
+
+    void update();
+
+    string pack();
+
+    /* The directory where we store sources and derived files. */
+    Path nixStore;
+
+    Path nixDataDir; /* !!! fix */
+
+    /* The directory where we log various operations. */
+    Path nixLogDir;
+
+    /* The directory where state is stored. */
+    Path nixStateDir;
+
+    /* The directory where we keep the SQLite database. */
+    Path nixDBPath;
+
+    /* The directory where configuration files are stored. */
+    Path nixConfDir;
+
+    /* The directory where internal helper programs are stored. */
+    Path nixLibexecDir;
+
+    /* The directory where the main programs are stored. */
+    Path nixBinDir;
+
+    /* Whether to keep temporary directories of failed builds. */
+    bool keepFailed;
+
+    /* Whether to keep building subgoals when a sibling (another
+       subgoal of the same goal) fails. */
+    bool keepGoing;
+
+    /* Whether, if we cannot realise the known closure corresponding
+       to a derivation, we should try to normalise the derivation
+       instead. */
+    bool tryFallback;
+
+    /* Verbosity level for build output. */
+    Verbosity buildVerbosity;
 
-/* nixStore is the directory where we generally store atomic and
-   derived files. */
-extern string nixStore;
+    /* Maximum number of parallel build jobs.  0 means unlimited. */
+    unsigned int maxBuildJobs;
 
-extern string nixDataDir; /* !!! fix */
+    /* Number of CPU cores to utilize in parallel within a build,
+       i.e. by passing this number to Make via '-j'. 0 means that the
+       number of actual CPU cores on the local host ought to be
+       auto-detected. */
+    unsigned int buildCores;
 
-/* nixLogDir is the directory where we log various operations. */ 
-extern string nixLogDir;
+    /* Read-only mode.  Don't copy stuff to the store, don't change
+       the database. */
+    bool readOnlyMode;
 
-/* nixStateDir is the directory where state is stored. */
-extern string nixStateDir;
+    /* The canonical system name, as returned by config.guess. */
+    string thisSystem;
 
-/* nixDBPath is the path name of our Berkeley DB environment. */
-extern string nixDBPath;
+    /* The maximum time in seconds that a builer can go without
+       producing any output on stdout/stderr before it is killed.  0
+       means infinity. */
+    time_t maxSilentTime;
 
-/* nixConfDir is the directory where configuration files are
-   stored. */
-extern string nixConfDir;
+    /* The maximum duration in seconds that a builder can run.  0
+       means infinity.  */
+    time_t buildTimeout;
 
-/* nixLibexecDir is the directory where internal helper programs are
-   stored. */
-extern string nixLibexecDir;
+    /* The substituters.  There are programs that can somehow realise
+       a store path without building, e.g., by downloading it or
+       copying it from a CD. */
+    Paths substituters;
 
-/* nixBinDir is the directory where the main programs are stored. */
-extern string nixBinDir;
+    /* Whether to use build hooks (for distributed builds).  Sometimes
+       users want to disable this from the command-line. */
+    bool useBuildHook;
 
+    /* Whether buildDerivations() should print out lines on stderr in
+       a fixed format to allow its progress to be monitored.  Each
+       line starts with a "@".  The following are defined:
 
-/* Misc. global flags. */
+       @ build-started <drvpath> <outpath> <system> <logfile>
+       @ build-failed <drvpath> <outpath> <exitcode> <error text>
+       @ build-succeeded <drvpath> <outpath>
+       @ substituter-started <outpath> <substituter>
+       @ substituter-failed <outpath> <exitcode> <error text>
+       @ substituter-succeeded <outpath>
 
-/* Whether to keep temporary directories of failed builds. */
-extern bool keepFailed;
+       Best combined with --no-build-output, otherwise stderr might
+       conceivably contain lines in this format printed by the
+       builders. */
+    bool printBuildTrace;
 
-/* Whether to keep building subgoals when a sibling (another subgoal
-   of the same goal) fails. */
-extern bool keepGoing;
+    /* Amount of reserved space for the garbage collector
+       (/nix/var/nix/db/reserved). */
+    off_t reservedSize;
 
-/* Whether, if we cannot realise the known closure corresponding to a
-   derivation, we should try to normalise the derivation instead. */
-extern bool tryFallback;
+    /* Whether SQLite should use fsync. */
+    bool fsyncMetadata;
 
-/* Verbosity level for build output. */
-extern Verbosity buildVerbosity;
+    /* Whether SQLite should use WAL mode. */
+    bool useSQLiteWAL;
 
-/* Maximum number of parallel build jobs.  0 means unlimited. */
-extern unsigned int maxBuildJobs;
+    /* Whether to call sync() before registering a path as valid. */
+    bool syncBeforeRegistering;
 
-/* Number of CPU cores to utilize in parallel within a build, i.e. by passing
-   this number to Make via '-j'. 0 means that the number of actual CPU cores on
-   the local host ought to be auto-detected. */
-extern unsigned int buildCores;
+    /* Whether to use substitutes. */
+    bool useSubstitutes;
 
-/* Read-only mode.  Don't copy stuff to the store, don't change the
-   database. */
-extern bool readOnlyMode;
+    /* The Unix group that contains the build users. */
+    string buildUsersGroup;
 
-/* The canonical system name, as returned by config.guess. */ 
-extern string thisSystem;
+    /* Whether to build in chroot. */
+    bool useChroot;
 
-/* The maximum time in seconds that a builer can go without producing
-   any output on stdout/stderr before it is killed.  0 means
-   infinity. */
-extern time_t maxSilentTime;
+    /* The directories from the host filesystem to be included in the
+       chroot. */
+    PathSet dirsInChroot;
 
-/* The maximum duration in seconds that a builder can run.  0 means
-   infinity.  */
-extern time_t buildTimeout;
+    /* Whether to impersonate a Linux 2.6 machine on newer kernels. */
+    bool impersonateLinux26;
 
-/* The substituters.  There are programs that can somehow realise a
-   store path without building, e.g., by downloading it or copying it
-   from a CD. */
-extern Paths substituters;
+    /* Whether to store build logs. */
+    bool keepLog;
 
-/* Whether to use build hooks (for distributed builds).  Sometimes
-   users want to disable this from the command-line. */
-extern bool useBuildHook;
+    /* Whether to compress logs. */
+    bool compressLog;
 
-/* Whether buildDerivations() should print out lines on stderr in a
-   fixed format to allow its progress to be monitored.  Each line
-   starts with a "@".  The following are defined:
+    /* Whether to cache build failures. */
+    bool cacheFailure;
 
-   @ build-started <drvpath> <outpath> <system> <logfile>
-   @ build-failed <drvpath> <outpath> <exitcode> <error text>
-   @ build-succeeded <drvpath> <outpath>
-   @ substituter-started <outpath> <substituter>
-   @ substituter-failed <outpath> <exitcode> <error text>
-   @ substituter-succeeded <outpath>
+    /* How often (in seconds) to poll for locks. */
+    unsigned int pollInterval;
 
-   Best combined with --no-build-output, otherwise stderr might
-   conceivably contain lines in this format printed by the builders.
-*/
-extern bool printBuildTrace;
+    /* Whether to check if new GC roots can in fact be found by the
+       garbage collector. */
+    bool checkRootReachability;
 
+    /* Whether the garbage collector should keep outputs of live
+       derivations. */
+    bool gcKeepOutputs;
 
-Strings querySetting(const string & name, const Strings & def);
+    /* Whether the garbage collector should keep derivers of live
+       paths. */
+    bool gcKeepDerivations;
 
-string querySetting(const string & name, const string & def);
+    /* Whether to automatically replace files with identical contents
+       with hard links. */
+    bool autoOptimiseStore;
 
-bool queryBoolSetting(const string & name, bool def);
+    /* Whether to add derivations as a dependency of user environments
+       (to prevent them from being GCed). */
+    bool envKeepDerivations;
 
-unsigned int queryIntSetting(const string & name, unsigned int def);
+private:
+    typedef std::map<string, string> SettingsMap;
 
-void overrideSetting(const string & name, const Strings & value);
+    SettingsMap settings;
 
-void reloadSettings();
+    void get(string & res, const string & name);
+    void get(bool & res, const string & name);
+    void get(PathSet & res, const string & name);
+    template<class N> void get(N & res, const string & name);
+};
 
-void setDefaultsFromEnvironment();
 
-string packSettings();
+// FIXME: don't use a global variable.
+extern Settings settings;
 
 
 }
diff --git a/src/libstore/local-store.cc b/src/libstore/local-store.cc
index aaa1abb56921..f20324d4e3d0 100644
--- a/src/libstore/local-store.cc
+++ b/src/libstore/local-store.cc
@@ -181,7 +181,7 @@ struct SQLiteTxn
 void checkStoreNotSymlink()
 {
     if (getEnv("NIX_IGNORE_SYMLINK_STORE") == "1") return;
-    Path path = nixStore;
+    Path path = settings.nixStore;
     struct stat st;
     while (path != "/") {
         if (lstat(path.c_str(), &st))
@@ -198,21 +198,21 @@ void checkStoreNotSymlink()
 
 LocalStore::LocalStore(bool reserveSpace)
 {
-    schemaPath = nixDBPath + "/schema";
+    schemaPath = settings.nixDBPath + "/schema";
 
-    if (readOnlyMode) {
+    if (settings.readOnlyMode) {
         openDB(false);
         return;
     }
 
     /* Create missing state directories if they don't already exist. */
-    createDirs(nixStore);
-    createDirs(linksDir = nixStore + "/.links");
-    Path profilesDir = nixStateDir + "/profiles";
-    createDirs(nixStateDir + "/profiles");
-    createDirs(nixStateDir + "/temproots");
-    createDirs(nixDBPath);
-    Path gcRootsDir = nixStateDir + "/gcroots";
+    createDirs(settings.nixStore);
+    createDirs(linksDir = settings.nixStore + "/.links");
+    Path profilesDir = settings.nixStateDir + "/profiles";
+    createDirs(settings.nixStateDir + "/profiles");
+    createDirs(settings.nixStateDir + "/temproots");
+    createDirs(settings.nixDBPath);
+    Path gcRootsDir = settings.nixStateDir + "/gcroots";
     if (!pathExists(gcRootsDir)) {
         createDirs(gcRootsDir);
         if (symlink(profilesDir.c_str(), (gcRootsDir + "/profiles").c_str()) == -1)
@@ -226,13 +226,12 @@ LocalStore::LocalStore(bool reserveSpace)
        needed, we reserve some dummy space that we can free just
        before doing a garbage collection. */
     try {
-        Path reservedPath = nixDBPath + "/reserved";
+        Path reservedPath = settings.nixDBPath + "/reserved";
         if (reserveSpace) {
-            int reservedSize = queryIntSetting("gc-reserved-space", 1024 * 1024);
             struct stat st;
             if (stat(reservedPath.c_str(), &st) == -1 ||
-                st.st_size != reservedSize)
-                writeFile(reservedPath, string(reservedSize, 'X'));
+                st.st_size != settings.reservedSize)
+                writeFile(reservedPath, string(settings.reservedSize, 'X'));
         }
         else
             deletePath(reservedPath);
@@ -242,11 +241,11 @@ LocalStore::LocalStore(bool reserveSpace)
     /* Acquire the big fat lock in shared mode to make sure that no
        schema upgrade is in progress. */
     try {
-        Path globalLockPath = nixDBPath + "/big-lock";
+        Path globalLockPath = settings.nixDBPath + "/big-lock";
         globalLock = openLockFile(globalLockPath.c_str(), true);
     } catch (SysError & e) {
         if (e.errNo != EACCES) throw;
-        readOnlyMode = true;
+        settings.readOnlyMode = true;
         openDB(false);
         return;
     }
@@ -325,7 +324,7 @@ int LocalStore::getSchema()
 void LocalStore::openDB(bool create)
 {
     /* Open the Nix database. */
-    if (sqlite3_open_v2((nixDBPath + "/db.sqlite").c_str(), &db.db,
+    if (sqlite3_open_v2((settings.nixDBPath + "/db.sqlite").c_str(), &db.db,
             SQLITE_OPEN_READWRITE | (create ? SQLITE_OPEN_CREATE : 0), 0) != SQLITE_OK)
         throw Error("cannot open SQLite database");
 
@@ -342,13 +341,13 @@ void LocalStore::openDB(bool create)
        should be safe enough.  If the user asks for it, don't sync at
        all.  This can cause database corruption if the system
        crashes. */
-    string syncMode = queryBoolSetting("fsync-metadata", true) ? "normal" : "off";
+    string syncMode = settings.fsyncMetadata ? "normal" : "off";
     if (sqlite3_exec(db, ("pragma synchronous = " + syncMode + ";").c_str(), 0, 0, 0) != SQLITE_OK)
         throwSQLiteError(db, "setting synchronous mode");
 
     /* Set the SQLite journal mode.  WAL mode is fastest, so it's the
        default. */
-    string mode = queryBoolSetting("use-sqlite-wal", true) ? "wal" : "truncate";
+    string mode = settings.useSQLiteWAL ? "wal" : "truncate";
     string prevMode;
     {
         SQLiteStmt stmt;
@@ -890,7 +889,7 @@ Path LocalStore::queryPathFromHashPart(const string & hashPart)
 
     SQLiteTxn txn(db);
 
-    Path prefix = nixStore + "/" + hashPart;
+    Path prefix = settings.nixStore + "/" + hashPart;
 
     SQLiteStmtUse use(stmtQueryPathFromHashPart);
     stmtQueryPathFromHashPart.bind(prefix);
@@ -933,7 +932,7 @@ void LocalStore::startSubstituter(const Path & substituter, RunningSubstituter &
 
             /* Pass configuration options (including those overriden
                with --option) to the substituter. */
-            setenv("_NIX_OPTIONS", packSettings().c_str(), 1);
+            setenv("_NIX_OPTIONS", settings.pack().c_str(), 1);
 
             fromPipe.readSide.close();
             toPipe.writeSide.close();
@@ -969,7 +968,7 @@ template<class T> T getIntLine(int fd)
 PathSet LocalStore::querySubstitutablePaths(const PathSet & paths)
 {
     PathSet res;
-    foreach (Paths::iterator, i, substituters) {
+    foreach (Paths::iterator, i, settings.substituters) {
         if (res.size() == paths.size()) break;
         RunningSubstituter & run(runningSubstituters[*i]);
         startSubstituter(*i, run);
@@ -1023,7 +1022,7 @@ void LocalStore::querySubstitutablePathInfos(const PathSet & paths,
     SubstitutablePathInfos & infos)
 {
     PathSet todo = paths;
-    foreach (Paths::iterator, i, substituters) {
+    foreach (Paths::iterator, i, settings.substituters) {
         if (todo.empty()) break;
         querySubstitutablePathInfos(*i, todo, infos);
     }
@@ -1046,11 +1045,10 @@ void LocalStore::registerValidPath(const ValidPathInfo & info)
 
 void LocalStore::registerValidPaths(const ValidPathInfos & infos)
 {
-    /* sqlite will fsync by default, but the new valid paths may not be fsync-ed.
+    /* SQLite will fsync by default, but the new valid paths may not be fsync-ed.
      * So some may want to fsync them before registering the validity, at the
      * expense of some speed of the path registering operation. */
-    if (queryBoolSetting("sync-before-registering", false))
-        sync();
+    if (settings.syncBeforeRegistering) sync();
 
     while (1) {
         try {
@@ -1294,7 +1292,7 @@ void LocalStore::exportPath(const Path & path, bool sign,
         Path hashFile = tmpDir + "/hash";
         writeFile(hashFile, printHash(hash));
 
-        Path secretKey = nixConfDir + "/signing-key.sec";
+        Path secretKey = settings.nixConfDir + "/signing-key.sec";
         checkSecrecy(secretKey);
 
         Strings args;
@@ -1340,7 +1338,7 @@ Path LocalStore::createTempDirInStore()
         /* There is a slight possibility that `tmpDir' gets deleted by
            the GC between createTempDir() and addTempRoot(), so repeat
            until `tmpDir' exists. */
-        tmpDir = createTempDir(nixStore);
+        tmpDir = createTempDir(settings.nixStore);
         addTempRoot(tmpDir);
     } while (!pathExists(tmpDir));
     return tmpDir;
@@ -1392,7 +1390,7 @@ Path LocalStore::importPath(bool requireSignature, Source & source)
             args.push_back("rsautl");
             args.push_back("-verify");
             args.push_back("-inkey");
-            args.push_back(nixConfDir + "/signing-key.pub");
+            args.push_back(settings.nixConfDir + "/signing-key.pub");
             args.push_back("-pubin");
             args.push_back("-in");
             args.push_back(sigFile);
@@ -1501,7 +1499,7 @@ void LocalStore::verifyStore(bool checkContents)
     /* Acquire the global GC lock to prevent a garbage collection. */
     AutoCloseFD fdGCLock = openGCLock(ltWrite);
 
-    Paths entries = readDirectory(nixStore);
+    Paths entries = readDirectory(settings.nixStore);
     PathSet store(entries.begin(), entries.end());
 
     /* Check whether all valid paths actually exist. */
@@ -1611,9 +1609,9 @@ void LocalStore::verifyPath(const Path & path, const PathSet & store,
 PathSet LocalStore::queryValidPathsOld()
 {
     PathSet paths;
-    Strings entries = readDirectory(nixDBPath + "/info");
+    Strings entries = readDirectory(settings.nixDBPath + "/info");
     foreach (Strings::iterator, i, entries)
-        if (i->at(0) != '.') paths.insert(nixStore + "/" + *i);
+        if (i->at(0) != '.') paths.insert(settings.nixStore + "/" + *i);
     return paths;
 }
 
@@ -1625,7 +1623,7 @@ ValidPathInfo LocalStore::queryPathInfoOld(const Path & path)
 
     /* Read the info file. */
     string baseName = baseNameOf(path);
-    Path infoFile = (format("%1%/info/%2%") % nixDBPath % baseName).str();
+    Path infoFile = (format("%1%/info/%2%") % settings.nixDBPath % baseName).str();
     if (!pathExists(infoFile))
         throw Error(format("path `%1%' is not valid") % path);
     string info = readFile(infoFile);
diff --git a/src/libstore/local-store.hh b/src/libstore/local-store.hh
index 4761658ed830..7cf9fc18d207 100644
--- a/src/libstore/local-store.hh
+++ b/src/libstore/local-store.hh
@@ -75,7 +75,7 @@ struct SQLiteStmt
     void bind64(long long value);
     void bind();
 };
-    
+
 
 class LocalStore : public StoreAPI
 {
@@ -84,7 +84,7 @@ private:
     RunningSubstituters runningSubstituters;
 
     Path linksDir;
-    
+
 public:
 
     /* Initialise the local store, upgrading the schema if
@@ -92,15 +92,15 @@ public:
     LocalStore(bool reserveSpace = true);
 
     ~LocalStore();
-    
+
     /* Implementations of abstract store API methods. */
-    
+
     bool isValidPath(const Path & path);
 
     PathSet queryValidPaths(const PathSet & paths);
-    
+
     PathSet queryAllValidPaths();
-    
+
     ValidPathInfo queryPathInfo(const Path & path);
 
     Hash queryPathHash(const Path & path);
@@ -120,17 +120,17 @@ public:
     PathSet queryDerivationOutputs(const Path & path);
 
     StringSet queryDerivationOutputNames(const Path & path);
-    
+
     Path queryPathFromHashPart(const string & hashPart);
-    
+
     PathSet querySubstitutablePaths(const PathSet & paths);
 
     void querySubstitutablePathInfos(const Path & substituter,
         PathSet & paths, SubstitutablePathInfos & infos);
-    
+
     void querySubstitutablePathInfos(const PathSet & paths,
         SubstitutablePathInfos & infos);
-    
+
     Path addToStore(const Path & srcPath,
         bool recursive = true, HashType hashAlgo = htSHA256,
         PathFilter & filter = defaultPathFilter);
@@ -149,7 +149,7 @@ public:
         Sink & sink);
 
     Paths importPaths(bool requireSignature, Source & source);
-    
+
     void buildPaths(const PathSet & paths);
 
     void ensurePath(const Path & path);
@@ -157,7 +157,7 @@ public:
     void addTempRoot(const Path & path);
 
     void addIndirectRoot(const Path & path);
-    
+
     void syncWithGC();
 
     Roots findRoots();
@@ -170,7 +170,7 @@ public:
 
     /* Optimise a single store path. */
     void optimisePath(const Path & path);
-    
+
     /* Check the integrity of the Nix store. */
     void verifyStore(bool checkContents);
 
@@ -229,18 +229,18 @@ private:
     unsigned long long queryValidPathId(const Path & path);
 
     unsigned long long addValidPath(const ValidPathInfo & info, bool checkOutputs = true);
-        
+
     void addReference(unsigned long long referrer, unsigned long long reference);
-    
+
     void appendReferrer(const Path & from, const Path & to, bool lock);
-    
+
     void rewriteReferrers(const Path & path, bool purge, PathSet referrers);
 
     void invalidatePath(const Path & path);
 
     /* Delete a path from the Nix store. */
     void invalidatePathChecked(const Path & path);
-    
+
     void verifyPath(const Path & path, const PathSet & store,
         PathSet & done, PathSet & validPaths);
 
@@ -253,14 +253,14 @@ private:
     struct GCState;
 
     void deleteGarbage(GCState & state, const Path & path);
-    
+
     bool tryToDelete(GCState & state, const Path & path);
-    
+
     bool isActiveTempFile(const GCState & state,
         const Path & path, const string & suffix);
-        
+
     int openGCLock(LockType lockType);
-    
+
     void removeUnusedLinks();
 
     void startSubstituter(const Path & substituter,
@@ -269,7 +269,7 @@ private:
     Path createTempDirInStore();
 
     Path importPath(bool requireSignature, Source & source);
-    
+
     void checkDerivationOutputs(const Path & drvPath, const Derivation & drv);
 
     void optimisePath_(OptimiseStats & stats, const Path & path);
@@ -290,9 +290,6 @@ void canonicalisePathMetaData(const Path & path, bool recurse);
 
 MakeError(PathInUse, Error);
 
-/* Whether we are in build users mode. */
-bool haveBuildUsers();
-
 /* Whether we are root. */
 bool amPrivileged();
 
@@ -305,5 +302,5 @@ void deletePathWrapped(const Path & path,
     unsigned long long & bytesFreed, unsigned long long & blocksFreed);
 
 void deletePathWrapped(const Path & path);
- 
+
 }
diff --git a/src/libstore/misc.cc b/src/libstore/misc.cc
index aa5f6ff727c9..3ce300e30678 100644
--- a/src/libstore/misc.cc
+++ b/src/libstore/misc.cc
@@ -52,10 +52,8 @@ void queryMissing(StoreAPI & store, const PathSet & targets,
     unsigned long long & downloadSize, unsigned long long & narSize)
 {
     downloadSize = narSize = 0;
-    
-    PathSet todo(targets.begin(), targets.end()), done;
 
-    bool useSubstitutes = queryBoolSetting("build-use-substitutes", true);
+    PathSet todo(targets.begin(), targets.end()), done;
 
     /* Getting substitute info has high latency when using the binary
        cache substituter.  Thus it's essential to do substitute
@@ -77,7 +75,7 @@ void queryMissing(StoreAPI & store, const PathSet & targets,
     */
 
     while (!todo.empty()) {
-              
+
         PathSet query, todoDrv, todoNonDrv;
 
         foreach (PathSet::iterator, i, todo) {
@@ -96,9 +94,9 @@ void queryMissing(StoreAPI & store, const PathSet & targets,
                 foreach (DerivationOutputs::iterator, j, drv.outputs)
                     if (!store.isValidPath(j->second.path)) invalid.insert(j->second.path);
                 if (invalid.empty()) continue;
-                
+
                 todoDrv.insert(*i);
-                if (useSubstitutes) query.insert(invalid.begin(), invalid.end());
+                if (settings.useSubstitutes) query.insert(invalid.begin(), invalid.end());
             }
 
             else {
@@ -109,7 +107,7 @@ void queryMissing(StoreAPI & store, const PathSet & targets,
         }
 
         todo.clear();
-        
+
         SubstitutablePathInfos infos;
         store.querySubstitutablePathInfos(query, infos);
 
@@ -118,7 +116,7 @@ void queryMissing(StoreAPI & store, const PathSet & targets,
             Derivation drv = derivationFromPath(store, *i);
 
             bool mustBuild = false;
-            if (useSubstitutes) {
+            if (settings.useSubstitutes) {
                 foreach (DerivationOutputs::iterator, j, drv.outputs)
                     if (!store.isValidPath(j->second.path) &&
                         infos.find(j->second.path) == infos.end())
@@ -135,7 +133,7 @@ void queryMissing(StoreAPI & store, const PathSet & targets,
                 foreach (DerivationOutputs::iterator, i, drv.outputs)
                     todoNonDrv.insert(i->second.path);
         }
-        
+
         foreach (PathSet::iterator, i, todoNonDrv) {
             done.insert(*i);
             SubstitutablePathInfos::iterator info = infos.find(*i);
@@ -150,22 +148,22 @@ void queryMissing(StoreAPI & store, const PathSet & targets,
     }
 }
 
- 
+
 static void dfsVisit(StoreAPI & store, const PathSet & paths,
     const Path & path, PathSet & visited, Paths & sorted,
     PathSet & parents)
 {
     if (parents.find(path) != parents.end())
         throw BuildError(format("cycle detected in the references of `%1%'") % path);
-    
+
     if (visited.find(path) != visited.end()) return;
     visited.insert(path);
     parents.insert(path);
-    
+
     PathSet references;
     if (store.isValidPath(path))
         store.queryReferences(path, references);
-    
+
     foreach (PathSet::iterator, i, references)
         /* Don't traverse into paths that don't exist.  That can
            happen due to substitutes for non-existent paths. */
diff --git a/src/libstore/optimise-store.cc b/src/libstore/optimise-store.cc
index 84a72604bba2..e08ee1784510 100644
--- a/src/libstore/optimise-store.cc
+++ b/src/libstore/optimise-store.cc
@@ -17,7 +17,7 @@ static void makeWritable(const Path & path)
 {
     struct stat st;
     if (lstat(path.c_str(), &st))
-	throw SysError(format("getting attributes of path `%1%'") % path);
+        throw SysError(format("getting attributes of path `%1%'") % path);
     if (S_ISDIR(st.st_mode) || S_ISREG(st.st_mode)) makeMutable(path);
     if (chmod(path.c_str(), st.st_mode | S_IWUSR) == -1)
         throw SysError(format("changing writability of `%1%'") % path);
@@ -53,15 +53,15 @@ void LocalStore::optimisePath_(OptimiseStats & stats, const Path & path)
 {
     struct stat st;
     if (lstat(path.c_str(), &st))
-	throw SysError(format("getting attributes of path `%1%'") % path);
+        throw SysError(format("getting attributes of path `%1%'") % path);
 
     if (S_ISDIR(st.st_mode)) {
         Strings names = readDirectory(path);
-	foreach (Strings::iterator, i, names)
-	    optimisePath_(stats, path + "/" + *i);
+        foreach (Strings::iterator, i, names)
+            optimisePath_(stats, path + "/" + *i);
         return;
     }
-    
+
     /* We can hard link regular files and maybe symlinks. */
     if (!S_ISREG(st.st_mode)
 #if CAN_LINK_SYMLINK
@@ -69,7 +69,7 @@ void LocalStore::optimisePath_(OptimiseStats & stats, const Path & path)
         && !S_ISLNK(st.st_mode)
 #endif
         ) return;
-        
+
     /* Sometimes SNAFUs can cause files in the Nix store to be
        modified, in particular when running programs as root under
        NixOS (example: $fontconfig/var/cache being modified).  Skip
@@ -110,25 +110,25 @@ void LocalStore::optimisePath_(OptimiseStats & stats, const Path & path)
        current file with a hard link to that file. */
     struct stat stLink;
     if (lstat(linkPath.c_str(), &stLink))
-	throw SysError(format("getting attributes of path `%1%'") % linkPath);
-    
+        throw SysError(format("getting attributes of path `%1%'") % linkPath);
+
     stats.sameContents++;
     if (st.st_ino == stLink.st_ino) {
         printMsg(lvlDebug, format("`%1%' is already linked to `%2%'") % path % linkPath);
         return;
     }
-        
+
     printMsg(lvlTalkative, format("linking `%1%' to `%2%'") % path % linkPath);
 
     Path tempLink = (format("%1%/.tmp-link-%2%-%3%")
-        % nixStore % getpid() % rand()).str();
+        % settings.nixStore % getpid() % rand()).str();
 
     /* Make the containing directory writable, but only if it's not
        the store itself (we don't want or need to mess with its
        permissions). */
     bool mustToggle = !isStorePath(path);
     if (mustToggle) makeWritable(dirOf(path));
-            
+
     /* When we're done, make the directory read-only again and reset
        its timestamp back to 0. */
     MakeReadOnly makeReadOnly(mustToggle ? dirOf(path) : "");
@@ -192,10 +192,8 @@ void LocalStore::optimiseStore(OptimiseStats & stats)
 
 void LocalStore::optimisePath(const Path & path)
 {
-    if (queryBoolSetting("auto-optimise-store", true)) {
-        OptimiseStats stats;
-        optimisePath_(stats, path);
-    }
+    OptimiseStats stats;
+    if (settings.autoOptimiseStore) optimisePath_(stats, path);
 }
 
 
diff --git a/src/libstore/remote-store.cc b/src/libstore/remote-store.cc
index 5910ffd53094..c67e53bfb87e 100644
--- a/src/libstore/remote-store.cc
+++ b/src/libstore/remote-store.cc
@@ -100,7 +100,7 @@ void RemoteStore::forkSlave()
     /* Start the worker. */
     Path worker = getEnv("NIX_WORKER");
     if (worker == "")
-        worker = nixBinDir + "/nix-worker";
+        worker = settings.nixBinDir + "/nix-worker";
 
     child = fork();
 
@@ -142,7 +142,7 @@ void RemoteStore::connectToDaemon()
     if (fdSocket == -1)
         throw SysError("cannot create Unix domain socket");
 
-    string socketPath = nixStateDir + DEFAULT_SOCKET_PATH;
+    string socketPath = settings.nixStateDir + DEFAULT_SOCKET_PATH;
 
     /* Urgh, sockaddr_un allows path names of only 108 characters.  So
        chdir to the socket directory so that we can pass a relative
@@ -184,23 +184,23 @@ RemoteStore::~RemoteStore()
 void RemoteStore::setOptions()
 {
     writeInt(wopSetOptions, to);
-    writeInt(keepFailed, to);
-    writeInt(keepGoing, to);
-    writeInt(tryFallback, to);
+    writeInt(settings.keepFailed, to);
+    writeInt(settings.keepGoing, to);
+    writeInt(settings.tryFallback, to);
     writeInt(verbosity, to);
-    writeInt(maxBuildJobs, to);
-    writeInt(maxSilentTime, to);
+    writeInt(settings.maxBuildJobs, to);
+    writeInt(settings.maxSilentTime, to);
     if (GET_PROTOCOL_MINOR(daemonVersion) >= 2)
-        writeInt(useBuildHook, to);
+        writeInt(settings.useBuildHook, to);
     if (GET_PROTOCOL_MINOR(daemonVersion) >= 4) {
-        writeInt(buildVerbosity, to);
+        writeInt(settings.buildVerbosity, to);
         writeInt(logType, to);
-        writeInt(printBuildTrace, to);
+        writeInt(settings.printBuildTrace, to);
     }
     if (GET_PROTOCOL_MINOR(daemonVersion) >= 6)
-        writeInt(buildCores, to);
+        writeInt(settings.buildCores, to);
     if (GET_PROTOCOL_MINOR(daemonVersion) >= 10)
-        writeInt(queryBoolSetting("build-use-substitutes", true), to);
+        writeInt(settings.useSubstitutes, to);
 
     processStderr();
 }
diff --git a/src/libstore/store-api.cc b/src/libstore/store-api.cc
index b64988268cdb..6f81a9aab072 100644
--- a/src/libstore/store-api.cc
+++ b/src/libstore/store-api.cc
@@ -19,16 +19,16 @@ GCOptions::GCOptions()
 bool isInStore(const Path & path)
 {
     return path[0] == '/'
-        && string(path, 0, nixStore.size()) == nixStore
-        && path.size() >= nixStore.size() + 2
-        && path[nixStore.size()] == '/';
+        && string(path, 0, settings.nixStore.size()) == settings.nixStore
+        && path.size() >= settings.nixStore.size() + 2
+        && path[settings.nixStore.size()] == '/';
 }
 
 
 bool isStorePath(const Path & path)
 {
     return isInStore(path)
-        && path.find('/', nixStore.size() + 1) == Path::npos;
+        && path.find('/', settings.nixStore.size() + 1) == Path::npos;
 }
 
 
@@ -43,7 +43,7 @@ Path toStorePath(const Path & path)
 {
     if (!isInStore(path))
         throw Error(format("path `%1%' is not in the Nix store") % path);
-    Path::size_type slash = path.find('/', nixStore.size() + 1);
+    Path::size_type slash = path.find('/', settings.nixStore.size() + 1);
     if (slash == Path::npos)
         return path;
     else
@@ -74,7 +74,7 @@ Path followLinksToStorePath(const Path & path)
 string storePathToName(const Path & path)
 {
     assertStorePath(path);
-    return string(path, nixStore.size() + 34);
+    return string(path, settings.nixStore.size() + 34);
 }
 
 
@@ -173,11 +173,11 @@ Path makeStorePath(const string & type,
 {
     /* e.g., "source:sha256:1abc...:/nix/store:foo.tar.gz" */
     string s = type + ":sha256:" + printHash(hash) + ":"
-        + nixStore + ":" + name;
+        + settings.nixStore + ":" + name;
 
     checkStoreName(name);
 
-    return nixStore + "/"
+    return settings.nixStore + "/"
         + printHash32(compressHash(hashString(htSHA256, s), 20))
         + "-" + name;
 }