about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--perl/lib/Nix/Store.xs2
-rw-r--r--src/libexpr/eval.cc4
-rw-r--r--src/libexpr/primops.cc6
-rw-r--r--src/libmain/shared.cc47
-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
-rw-r--r--src/nix-env/nix-env.cc20
-rw-r--r--src/nix-instantiate/nix-instantiate.cc4
-rw-r--r--src/nix-store/nix-store.cc70
-rw-r--r--src/nix-worker/nix-worker.cc32
19 files changed, 566 insertions, 522 deletions
diff --git a/perl/lib/Nix/Store.xs b/perl/lib/Nix/Store.xs
index 76de674e6d..00311aa8f3 100644
--- a/perl/lib/Nix/Store.xs
+++ b/perl/lib/Nix/Store.xs
@@ -19,7 +19,7 @@ void doInit()
 {
     if (!store) {
         try {
-            setDefaultsFromEnvironment();
+            settings.processEnvironment();
             store = openStore();
         } catch (Error & e) {
             croak(e.what());
diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc
index cf7c62ad20..74f7560fe0 100644
--- a/src/libexpr/eval.cc
+++ b/src/libexpr/eval.cc
@@ -179,7 +179,7 @@ EvalState::EvalState()
     searchPathInsertionPoint = searchPath.end();
     Strings paths = tokenizeString(getEnv("NIX_PATH", ""), ":");
     foreach (Strings::iterator, i, paths) addToSearchPath(*i);
-    addToSearchPath("nix=" + nixDataDir + "/nix/corepkgs");
+    addToSearchPath("nix=" + settings.nixDataDir + "/nix/corepkgs");
     searchPathInsertionPoint = searchPath.begin();
 
     createBaseEnv();
@@ -1058,7 +1058,7 @@ string EvalState::coerceToString(Value & v, PathSet & context,
         if (srcToStore[path] != "")
             dstPath = srcToStore[path];
         else {
-            dstPath = readOnlyMode
+            dstPath = settings.readOnlyMode
                 ? computeStorePathForPath(path).first
                 : store->addToStore(path);
             srcToStore[path] = dstPath;
diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc
index 5c011c43e3..7258c4cc0f 100644
--- a/src/libexpr/primops.cc
+++ b/src/libexpr/primops.cc
@@ -623,7 +623,7 @@ static void prim_toFile(EvalState & state, Value * * args, Value & v)
         refs.insert(path);
     }
     
-    Path storePath = readOnlyMode
+    Path storePath = settings.readOnlyMode
         ? computeStorePathForText(name, contents, refs)
         : store->addTextToStore(name, contents, refs);
 
@@ -687,7 +687,7 @@ static void prim_filterSource(EvalState & state, Value * * args, Value & v)
 
     FilterFromExpr filter(state, *args[0]);
 
-    Path dstPath = readOnlyMode
+    Path dstPath = settings.readOnlyMode
         ? computeStorePathForPath(path, true, htSHA256, filter).first
         : store->addToStore(path, true, htSHA256, filter);
 
@@ -1079,7 +1079,7 @@ void EvalState::createBaseEnv()
     mkInt(v, time(0));
     addConstant("__currentTime", v);
 
-    mkString(v, thisSystem.c_str());
+    mkString(v, settings.thisSystem.c_str());
     addConstant("__currentSystem", v);
 
     // Miscellaneous
diff --git a/src/libmain/shared.cc b/src/libmain/shared.cc
index 2118e43918..9c62e320f6 100644
--- a/src/libmain/shared.cc
+++ b/src/libmain/shared.cc
@@ -74,7 +74,7 @@ void printMissing(StoreAPI & store, const PathSet & paths)
 
     if (!unknown.empty()) {
         printMsg(lvlInfo, format("don't know how to build these paths%1%:")
-            % (readOnlyMode ? " (may be caused by read-only store access)" : ""));
+            % (settings.readOnlyMode ? " (may be caused by read-only store access)" : ""));
         foreach (PathSet::iterator, i, unknown)
             printMsg(lvlInfo, format("  %1%") % *i);
     }
@@ -93,11 +93,20 @@ static void setLogType(string lt)
 static bool showTrace = false;
 
 
+string getArg(const string & opt,
+    Strings::iterator & i, const Strings::iterator & end)
+{
+    ++i;
+    if (i == end) throw UsageError(format("`%1%' requires an argument") % opt);
+    return *i;
+}
+
 /* Initialize and reorder arguments, then call the actual argument
    processor. */
 static void initAndRun(int argc, char * * argv)
 {
-    setDefaultsFromEnvironment();
+    settings.processEnvironment();
+    settings.loadConfFile();
 
     /* Catch SIGINT. */
     struct sigaction act;
@@ -156,20 +165,19 @@ static void initAndRun(int argc, char * * argv)
     remaining.clear();
 
     /* Process default options. */
-    int verbosityDelta = 0;
+    int verbosityDelta = lvlInfo;
     for (Strings::iterator i = args.begin(); i != args.end(); ++i) {
         string arg = *i;
         if (arg == "--verbose" || arg == "-v") verbosityDelta++;
         else if (arg == "--quiet") verbosityDelta--;
         else if (arg == "--log-type") {
-            ++i;
-            if (i == args.end()) throw UsageError("`--log-type' requires an argument");
-            setLogType(*i);
+            string s = getArg(arg, i, args.end());
+            setLogType(s);
         }
         else if (arg == "--no-build-output" || arg == "-Q")
-            buildVerbosity = lvlVomit;
+            settings.buildVerbosity = lvlVomit;
         else if (arg == "--print-build-trace")
-            printBuildTrace = true;
+            settings.printBuildTrace = true;
         else if (arg == "--help") {
             printHelp();
             return;
@@ -179,23 +187,23 @@ static void initAndRun(int argc, char * * argv)
             return;
         }
         else if (arg == "--keep-failed" || arg == "-K")
-            keepFailed = true;
+            settings.keepFailed = true;
         else if (arg == "--keep-going" || arg == "-k")
-            keepGoing = true;
+            settings.keepGoing = true;
         else if (arg == "--fallback")
-            tryFallback = true;
+            settings.tryFallback = true;
         else if (arg == "--max-jobs" || arg == "-j")
-            maxBuildJobs = getIntArg<unsigned int>(arg, i, args.end());
+            settings.set("build-max-jobs", getArg(arg, i, args.end()));
         else if (arg == "--cores")
-            buildCores = getIntArg<unsigned int>(arg, i, args.end());
+            settings.set("build-cores", getArg(arg, i, args.end()));
         else if (arg == "--readonly-mode")
-            readOnlyMode = true;
+            settings.readOnlyMode = true;
         else if (arg == "--max-silent-time")
-            maxSilentTime = getIntArg<unsigned int>(arg, i, args.end());
+            settings.set("build-max-silent-time", getArg(arg, i, args.end()));
         else if (arg == "--timeout")
-            buildTimeout = getIntArg<unsigned int>(arg, i, args.end());
+            settings.set("build-timeout", getArg(arg, i, args.end()));
         else if (arg == "--no-build-hook")
-            useBuildHook = false;
+            settings.useBuildHook = false;
         else if (arg == "--show-trace")
             showTrace = true;
         else if (arg == "--option") {
@@ -203,14 +211,15 @@ static void initAndRun(int argc, char * * argv)
             string name = *i;
             ++i; if (i == args.end()) throw UsageError("`--option' requires two arguments");
             string value = *i;
-            overrideSetting(name, tokenizeString(value));
+            settings.set(name, value);
         }
         else remaining.push_back(arg);
     }
 
-    verbosityDelta += queryIntSetting("verbosity", lvlInfo);
     verbosity = (Verbosity) (verbosityDelta < 0 ? 0 : verbosityDelta);
 
+    settings.update();
+
     run(remaining);
 
     /* Close the Nix database. */
diff --git a/src/libstore/build.cc b/src/libstore/build.cc
index 4a2bc5218b..0972d6e193 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 97343d57d4..73047c7538 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 874efe4d32..1355702f87 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 a28e08427d..7dc2e714bc 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 30acf59ef5..5783d9bf37 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 aaa1abb569..f20324d4e3 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 4761658ed8..7cf9fc18d2 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 aa5f6ff727..3ce300e306 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 84a72604bb..e08ee17845 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 5910ffd530..c67e53bfb8 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 b64988268c..6f81a9aab0 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;
 }
diff --git a/src/nix-env/nix-env.cc b/src/nix-env/nix-env.cc
index f06f23dad5..475bdd3669 100644
--- a/src/nix-env/nix-env.cc
+++ b/src/nix-env/nix-env.cc
@@ -55,7 +55,6 @@ struct Globals
     EvalState state;
     bool dryRun;
     bool preserveInstalled;
-    bool keepDerivations;
     string forceName;
     bool prebuiltOnly;
 };
@@ -266,8 +265,8 @@ static DrvInfos filterBySelector(EvalState & state, const DrvInfos & allElems,
                 
                 if (k != newest.end()) {
                     d = j->first.system == k->second.first.system ? 0 :
-                        j->first.system == thisSystem ? 1 :
-                        k->second.first.system == thisSystem ? -1 : 0;
+                        j->first.system == settings.thisSystem ? 1 :
+                        k->second.first.system == settings.thisSystem ? -1 : 0;
                     if (d == 0)
                         d = comparePriorities(state, j->first, k->second.first);
                     if (d == 0)
@@ -498,7 +497,7 @@ static void installDerivations(Globals & globals,
         if (globals.dryRun) return;
 
         if (createUserEnv(globals.state, allElems,
-                profile, globals.keepDerivations, lockToken)) break;
+                profile, settings.envKeepDerivations, lockToken)) break;
     }
 }
 
@@ -605,7 +604,7 @@ static void upgradeDerivations(Globals & globals,
         if (globals.dryRun) return;
 
         if (createUserEnv(globals.state, newElems,
-                globals.profile, globals.keepDerivations, lockToken)) break;
+                globals.profile, settings.envKeepDerivations, lockToken)) break;
     }
 }
 
@@ -672,7 +671,7 @@ static void opSetFlag(Globals & globals,
 
         /* Write the new user environment. */
         if (createUserEnv(globals.state, installedElems,
-                globals.profile, globals.keepDerivations, lockToken)) break;
+                globals.profile, settings.envKeepDerivations, lockToken)) break;
     }
 }
 
@@ -740,7 +739,7 @@ static void uninstallDerivations(Globals & globals, Strings & selectors,
         if (globals.dryRun) return;
 
         if (createUserEnv(globals.state, newElems,
-                profile, globals.keepDerivations, lockToken)) break;
+                profile, settings.envKeepDerivations, lockToken)) break;
     }
 }
 
@@ -869,7 +868,7 @@ static void opQuery(Globals & globals,
 
     enum { sInstalled, sAvailable } source = sInstalled;
 
-    readOnlyMode = true; /* makes evaluation a bit faster */
+    settings.readOnlyMode = true; /* makes evaluation a bit faster */
 
     for (Strings::iterator i = args.begin(); i != args.end(); ) {
         string arg = *i++;
@@ -1262,9 +1261,6 @@ void run(Strings args)
     globals.preserveInstalled = false;
     globals.prebuiltOnly = false;
 
-    globals.keepDerivations =
-        queryBoolSetting("env-keep-derivations", false);
-    
     for (Strings::iterator i = args.begin(); i != args.end(); ) {
         string arg = *i++;
 
@@ -1331,7 +1327,7 @@ void run(Strings args)
         Path profileLink = getHomeDir() + "/.nix-profile";
         globals.profile = pathExists(profileLink)
             ? absPath(readLink(profileLink), dirOf(profileLink))
-            : canonPath(nixStateDir + "/profiles/default");
+            : canonPath(settings.nixStateDir + "/profiles/default");
     }
     
     store = openStore();
diff --git a/src/nix-instantiate/nix-instantiate.cc b/src/nix-instantiate/nix-instantiate.cc
index d86c9fc845..270b4ddc2e 100644
--- a/src/nix-instantiate/nix-instantiate.cc
+++ b/src/nix-instantiate/nix-instantiate.cc
@@ -94,11 +94,11 @@ void run(Strings args)
         if (arg == "-")
             readStdin = true;
         else if (arg == "--eval-only") {
-            readOnlyMode = true;
+            settings.readOnlyMode = true;
             evalOnly = true;
         }
         else if (arg == "--parse-only") {
-            readOnlyMode = true;
+            settings.readOnlyMode = true;
             parseOnly = evalOnly = true;
         }
         else if (arg == "--find-file")
diff --git a/src/nix-store/nix-store.cc b/src/nix-store/nix-store.cc
index 941301d2e7..5ada797136 100644
--- a/src/nix-store/nix-store.cc
+++ b/src/nix-store/nix-store.cc
@@ -47,7 +47,7 @@ LocalStore & ensureLocalStore()
 
 
 static Path useDeriver(Path path)
-{       
+{
     if (!isDerivation(path)) {
         path = store->queryDeriver(path);
         if (path == "")
@@ -89,18 +89,18 @@ static PathSet realisePath(const Path & path)
 static void opRealise(Strings opFlags, Strings opArgs)
 {
     bool dryRun = false;
-    
+
     foreach (Strings::iterator, i, opFlags)
         if (*i == "--dry-run") dryRun = true;
         else throw UsageError(format("unknown flag `%1%'") % *i);
 
     foreach (Strings::iterator, i, opArgs)
         *i = followLinksToStorePath(*i);
-            
+
     printMissing(*store, PathSet(opArgs.begin(), opArgs.end()));
-    
+
     if (dryRun) return;
-    
+
     /* Build all paths at the same time to exploit parallelism. */
     PathSet paths(opArgs.begin(), opArgs.end());
     store->buildPaths(paths);
@@ -128,7 +128,7 @@ static void opAdd(Strings opFlags, Strings opArgs)
 static void opAddFixed(Strings opFlags, Strings opArgs)
 {
     bool recursive = false;
-    
+
     for (Strings::iterator i = opFlags.begin();
          i != opFlags.end(); ++i)
         if (*i == "--recursive") recursive = true;
@@ -136,7 +136,7 @@ static void opAddFixed(Strings opFlags, Strings opArgs)
 
     if (opArgs.empty())
         throw UsageError("first argument must be hash algorithm");
-    
+
     HashType hashAlgo = parseHashType(opArgs.front());
     opArgs.pop_front();
 
@@ -149,7 +149,7 @@ static void opAddFixed(Strings opFlags, Strings opArgs)
 static void opPrintFixedPath(Strings opFlags, Strings opArgs)
 {
     bool recursive = false;
-    
+
     for (Strings::iterator i = opFlags.begin();
          i != opFlags.end(); ++i)
         if (*i == "--recursive") recursive = true;
@@ -157,7 +157,7 @@ static void opPrintFixedPath(Strings opFlags, Strings opArgs)
 
     if (opArgs.size() != 3)
         throw UsageError(format("`--print-fixed-path' requires three arguments"));
-    
+
     Strings::iterator i = opArgs.begin();
     HashType hashAlgo = parseHashType(*i++);
     string hash = *i++;
@@ -205,12 +205,12 @@ static void printTree(const Path & path,
 
     PathSet references;
     store->queryReferences(path, references);
-    
-#if 0     
+
+#if 0
     for (PathSet::iterator i = drv.inputSrcs.begin();
          i != drv.inputSrcs.end(); ++i)
         cout << format("%1%%2%\n") % (tailPad + treeConn) % *i;
-#endif    
+#endif
 
     /* Topologically sort under the relation A < B iff A \in
        closure(B).  That is, if derivation A is an (possibly indirect)
@@ -266,7 +266,7 @@ static void opQuery(Strings opFlags, Strings opArgs)
         else throw UsageError(format("unknown flag `%1%'") % *i);
 
     switch (query) {
-        
+
         case qOutputs: {
             foreach (Strings::iterator, i, opArgs) {
                 *i = followLinksToStorePath(*i);
@@ -293,7 +293,7 @@ static void opQuery(Strings opFlags, Strings opArgs)
                 }
             }
             Paths sorted = topoSortPaths(*store, paths);
-            for (Paths::reverse_iterator i = sorted.rbegin(); 
+            for (Paths::reverse_iterator i = sorted.rbegin();
                  i != sorted.rend(); ++i)
                 cout << format("%s\n") % *i;
             break;
@@ -328,7 +328,7 @@ static void opQuery(Strings opFlags, Strings opArgs)
                     if (query == qHash) {
                         assert(info.hash.type == htSHA256);
                         cout << format("sha256:%1%\n") % printHash32(info.hash);
-                    } else if (query == qSize) 
+                    } else if (query == qSize)
                         cout << format("%1%\n") % info.narSize;
                 }
             }
@@ -340,7 +340,7 @@ static void opQuery(Strings opFlags, Strings opArgs)
                 printTree(followLinksToStorePath(*i), "", "", done);
             break;
         }
-            
+
         case qGraph: {
             PathSet roots;
             foreach (Strings::iterator, i, opArgs) {
@@ -366,7 +366,7 @@ static void opQuery(Strings opFlags, Strings opArgs)
                 cout << format("%1%\n") % followLinksToStorePath(*i);
             break;
         }
-            
+
         case qRoots: {
             PathSet referrers;
             foreach (Strings::iterator, i, opArgs) {
@@ -380,7 +380,7 @@ static void opQuery(Strings opFlags, Strings opArgs)
                     cout << format("%1%\n") % i->first;
             break;
         }
-            
+
         default:
             abort();
     }
@@ -426,9 +426,9 @@ static void opReadLog(Strings opFlags, Strings opArgs)
 
     foreach (Strings::iterator, i, opArgs) {
         Path path = useDeriver(followLinksToStorePath(*i));
-        
+
         Path logPath = (format("%1%/%2%/%3%") %
-            nixLogDir % drvsLogDir % baseNameOf(path)).str();
+            settings.nixLogDir % drvsLogDir % baseNameOf(path)).str();
         Path logBz2Path = logPath + ".bz2";
 
         if (pathExists(logPath)) {
@@ -454,7 +454,7 @@ static void opReadLog(Strings opFlags, Strings opArgs)
             } while (err != BZ_STREAM_END);
             BZ2_bzReadClose(&err, bz);
         }
-        
+
         else throw Error(format("build log of derivation `%1%' is not available") % path);
     }
 }
@@ -474,7 +474,7 @@ static void opDumpDB(Strings opFlags, Strings opArgs)
 static void registerValidity(bool reregister, bool hashGiven, bool canonicalise)
 {
     ValidPathInfos infos;
-    
+
     while (1) {
         ValidPathInfo info = decodeValidPathInfo(cin, hashGiven);
         if (info.path == "") break;
@@ -508,7 +508,7 @@ static void opRegisterValidity(Strings opFlags, Strings opArgs)
 {
     bool reregister = false; // !!! maybe this should be the default
     bool hashGiven = false;
-        
+
     for (Strings::iterator i = opFlags.begin();
          i != opFlags.end(); ++i)
         if (*i == "--reregister") reregister = true;
@@ -524,7 +524,7 @@ static void opRegisterValidity(Strings opFlags, Strings opArgs)
 static void opCheckValidity(Strings opFlags, Strings opArgs)
 {
     bool printInvalid = false;
-    
+
     for (Strings::iterator i = opFlags.begin();
          i != opFlags.end(); ++i)
         if (*i == "--print-invalid") printInvalid = true;
@@ -551,13 +551,13 @@ static string showBytes(unsigned long long bytes, unsigned long long blocks)
 }
 
 
-struct PrintFreed 
+struct PrintFreed
 {
     bool show;
     const GCResults & results;
     PrintFreed(bool show, const GCResults & results)
         : show(show), results(results) { }
-    ~PrintFreed() 
+    ~PrintFreed()
     {
         if (show)
             cout << format("%1% store paths deleted, %2% freed\n")
@@ -572,9 +572,9 @@ static void opGC(Strings opFlags, Strings opArgs)
     bool printRoots = false;
     GCOptions options;
     options.action = GCOptions::gcDeleteDead;
-    
+
     GCResults results;
-    
+
     /* Do what? */
     foreach (Strings::iterator, i, opFlags)
         if (*i == "--print-roots") printRoots = true;
@@ -613,14 +613,14 @@ static void opDelete(Strings opFlags, Strings opArgs)
 {
     GCOptions options;
     options.action = GCOptions::gcDeleteSpecific;
-    
+
     foreach (Strings::iterator, i, opFlags)
         if (*i == "--ignore-liveness") options.ignoreLiveness = true;
         else throw UsageError(format("unknown flag `%1%'") % *i);
 
     foreach (Strings::iterator, i, opArgs)
         options.pathsToDelete.insert(followLinksToStorePath(*i));
-    
+
     GCResults results;
     PrintFreed freed(true, results);
     store->collectGarbage(options, results);
@@ -671,9 +671,9 @@ static void opImport(Strings opFlags, Strings opArgs)
     foreach (Strings::iterator, i, opFlags)
         if (*i == "--require-signature") requireSignature = true;
         else throw UsageError(format("unknown flag `%1%'") % *i);
-    
+
     if (!opArgs.empty()) throw UsageError("no arguments expected");
-    
+
     FdSource source(STDIN_FILENO);
     Paths paths = store->importPaths(requireSignature, source);
 
@@ -700,12 +700,12 @@ static void opVerify(Strings opFlags, Strings opArgs)
         throw UsageError("no arguments expected");
 
     bool checkContents = false;
-    
+
     for (Strings::iterator i = opFlags.begin();
          i != opFlags.end(); ++i)
         if (*i == "--check-contents") checkContents = true;
         else throw UsageError(format("unknown flag `%1%'") % *i);
-    
+
     ensureLocalStore().verifyStore(checkContents);
 }
 
@@ -844,7 +844,7 @@ void run(Strings args)
         }
         else if (arg == "--indirect")
             indirectRoot = true;
-        else if (arg[0] == '-') {            
+        else if (arg[0] == '-') {
             opFlags.push_back(arg);
             if (arg == "--max-freed" || arg == "--max-links" || arg == "--max-atime") { /* !!! hack */
                 if (i != args.end()) opFlags.push_back(*i++);
diff --git a/src/nix-worker/nix-worker.cc b/src/nix-worker/nix-worker.cc
index 09800c1608..84ad689048 100644
--- a/src/nix-worker/nix-worker.cc
+++ b/src/nix-worker/nix-worker.cc
@@ -527,27 +527,23 @@ static void performOp(unsigned int clientVersion,
     }
 
     case wopSetOptions: {
-        keepFailed = readInt(from) != 0;
-        keepGoing = readInt(from) != 0;
-        tryFallback = readInt(from) != 0;
+        settings.keepFailed = readInt(from) != 0;
+        settings.keepGoing = readInt(from) != 0;
+        settings.tryFallback = readInt(from) != 0;
         verbosity = (Verbosity) readInt(from);
-        maxBuildJobs = readInt(from);
-        maxSilentTime = readInt(from);
+        settings.maxBuildJobs = readInt(from);
+        settings.maxSilentTime = readInt(from);
         if (GET_PROTOCOL_MINOR(clientVersion) >= 2)
-            useBuildHook = readInt(from) != 0;
+            settings.useBuildHook = readInt(from) != 0;
         if (GET_PROTOCOL_MINOR(clientVersion) >= 4) {
-            buildVerbosity = (Verbosity) readInt(from);
+            settings.buildVerbosity = (Verbosity) readInt(from);
             logType = (LogType) readInt(from);
-            printBuildTrace = readInt(from) != 0;
+            settings.printBuildTrace = readInt(from) != 0;
         }
         if (GET_PROTOCOL_MINOR(clientVersion) >= 6)
-            buildCores = readInt(from);
-        if (GET_PROTOCOL_MINOR(clientVersion) >= 10) {
-            int x = readInt(from);
-            Strings ss;
-            ss.push_back(x == 0 ? "false" : "true");
-            overrideSetting("build-use-substitutes", ss);
-        }
+            settings.buildCores = readInt(from);
+        if (GET_PROTOCOL_MINOR(clientVersion) >= 10)
+            settings.useSubstitutes = readInt(from) != 0;
         startWork();
         stopWork();
         break;
@@ -768,7 +764,7 @@ static void daemonLoop()
         if (fdSocket == -1)
             throw SysError("cannot create Unix domain socket");
 
-        string socketPath = nixStateDir + DEFAULT_SOCKET_PATH;
+        string socketPath = settings.nixStateDir + DEFAULT_SOCKET_PATH;
 
         createDirs(dirOf(socketPath));
 
@@ -867,10 +863,6 @@ static void daemonLoop()
                         strncpy(argvSaved[1], processName.c_str(), strlen(argvSaved[1]));
                     }
 
-                    /* Since the daemon can be long-running, the
-                       settings may have changed.  So force a reload. */
-                    reloadSettings();
-
                     /* Handle the connection. */
                     from.fd = remote;
                     to.fd = remote;