Merge branch 'readonly-store'
This commit is contained in:
		
						commit
						e464b0247d
					
				
					 14 changed files with 105 additions and 134 deletions
				
			
		|  | @ -10,7 +10,7 @@ namespace nix { | |||
| void findAlongAttrPath(EvalState & state, const string & attrPath, | ||||
|     Bindings & autoArgs, Expr * e, Value & v) | ||||
| { | ||||
|     Strings tokens = tokenizeString(attrPath, "."); | ||||
|     Strings tokens = tokenizeString<Strings>(attrPath, "."); | ||||
| 
 | ||||
|     Error attrError = | ||||
|         Error(format("attribute selection path `%1%' does not match expression") % attrPath); | ||||
|  |  | |||
|  | @ -179,7 +179,7 @@ EvalState::EvalState() | |||
| 
 | ||||
|     /* Initialise the Nix expression search path. */ | ||||
|     searchPathInsertionPoint = searchPath.end(); | ||||
|     Strings paths = tokenizeString(getEnv("NIX_PATH", ""), ":"); | ||||
|     Strings paths = tokenizeString<Strings>(getEnv("NIX_PATH", ""), ":"); | ||||
|     foreach (Strings::iterator, i, paths) addToSearchPath(*i); | ||||
|     addToSearchPath("nix=" + settings.nixDataDir + "/nix/corepkgs"); | ||||
|     searchPathInsertionPoint = searchPath.begin(); | ||||
|  |  | |||
|  | @ -363,7 +363,7 @@ static void prim_derivationStrict(EvalState & state, Value * * args, Value & v) | |||
|                     else throw EvalError(format("invalid value `%1%' for `outputHashMode' attribute") % s); | ||||
|                 } | ||||
|                 else if (key == "outputs") { | ||||
|                     Strings tmp = tokenizeString(s); | ||||
|                     Strings tmp = tokenizeString<Strings>(s); | ||||
|                     outputs.clear(); | ||||
|                     foreach (Strings::iterator, j, tmp) { | ||||
|                         if (outputs.find(*j) != outputs.end()) | ||||
|  |  | |||
|  | @ -1355,11 +1355,6 @@ void DerivationGoal::buildDone() | |||
|         /* Delete the chroot (if we were using one). */ | ||||
|         autoDelChroot.reset(); /* this runs the destructor */ | ||||
| 
 | ||||
|         /* Deleting the chroot will have caused the immutable bits on
 | ||||
|            hard-linked inputs to be cleared.  So set them again. */ | ||||
|         foreach (PathSet::iterator, i, regularInputPaths) | ||||
|             makeImmutable(*i); | ||||
| 
 | ||||
|         /* Delete redirected outputs (when doing hash rewriting). */ | ||||
|         foreach (PathSet::iterator, i, redirectedOutputs) | ||||
|             deletePath(*i); | ||||
|  | @ -1435,7 +1430,7 @@ HookReply DerivationGoal::tryBuildHook() | |||
|     /* Tell the hook about system features (beyond the system type)
 | ||||
|        required from the build machine.  (The hook could parse the | ||||
|        drv file itself, but this is easier.) */ | ||||
|     Strings features = tokenizeString(drv.env["requiredSystemFeatures"]); | ||||
|     Strings features = tokenizeString<Strings>(drv.env["requiredSystemFeatures"]); | ||||
|     foreach (Strings::iterator, i, features) checkStoreName(*i); /* !!! abuse */ | ||||
| 
 | ||||
|     /* Send the request to the hook. */ | ||||
|  | @ -1594,7 +1589,7 @@ void DerivationGoal::startBuilder() | |||
|        fixed-output derivations is by definition pure (since we | ||||
|        already know the cryptographic hash of the output). */ | ||||
|     if (fixedOutput) { | ||||
|         Strings varNames = tokenizeString(drv.env["impureEnvVars"]); | ||||
|         Strings varNames = tokenizeString<Strings>(drv.env["impureEnvVars"]); | ||||
|         foreach (Strings::iterator, i, varNames) env[*i] = getEnv(*i); | ||||
|     } | ||||
| 
 | ||||
|  | @ -1606,7 +1601,7 @@ void DerivationGoal::startBuilder() | |||
|        by `nix-store --register-validity'.  However, the deriver | ||||
|        fields are left empty. */ | ||||
|     string s = drv.env["exportReferencesGraph"]; | ||||
|     Strings ss = tokenizeString(s); | ||||
|     Strings ss = tokenizeString<Strings>(s); | ||||
|     if (ss.size() % 2 != 0) | ||||
|         throw BuildError(format("odd number of tokens in `exportReferencesGraph': `%1%'") % s); | ||||
|     for (Strings::iterator i = ss.begin(); i != ss.end(); ) { | ||||
|  | @ -1766,11 +1761,8 @@ void DerivationGoal::startBuilder() | |||
|                     /* Hard-linking fails if we exceed the maximum
 | ||||
|                        link count on a file (e.g. 32000 of ext3), | ||||
|                        which is quite possible after a `nix-store | ||||
|                        --optimise'.  It can also fail if another | ||||
|                        process called makeImmutable() on *i after we | ||||
|                        did makeMutable().  In those cases, make a copy | ||||
|                        instead. */ | ||||
|                     if (errno != EMLINK && errno != EPERM) | ||||
|                        --optimise'. */ | ||||
|                     if (errno != EMLINK) | ||||
|                         throw SysError(format("linking `%1%' to `%2%'") % p % *i); | ||||
|                     StringSink sink; | ||||
|                     dumpPath(*i, sink); | ||||
|  | @ -1778,7 +1770,6 @@ void DerivationGoal::startBuilder() | |||
|                     restorePath(p, source); | ||||
|                 } | ||||
| 
 | ||||
|                 makeImmutable(*i); | ||||
|                 regularInputPaths.insert(*i); | ||||
|             } | ||||
|         } | ||||
|  | @ -1911,14 +1902,11 @@ void DerivationGoal::initChild() | |||
|                outside of the namespace.  Making a subtree private is | ||||
|                local to the namespace, though, so setting MS_PRIVATE | ||||
|                does not affect the outside world. */ | ||||
|             Strings mounts = tokenizeString(readFile("/proc/self/mountinfo", true), "\n"); | ||||
|             Strings mounts = tokenizeString<Strings>(readFile("/proc/self/mountinfo", true), "\n"); | ||||
|             foreach (Strings::iterator, i, mounts) { | ||||
|                 Strings fields = tokenizeString(*i, " "); | ||||
|                 assert(fields.size() >= 5); | ||||
|                 Strings::iterator j = fields.begin(); | ||||
|                 std::advance(j, 4); | ||||
|                 if (mount(0, j->c_str(), 0, MS_PRIVATE, 0) == -1) | ||||
|                     throw SysError(format("unable to make filesystem `%1%' private") % *j); | ||||
|                 vector<string> fields = tokenizeString<vector<string> >(*i, " "); | ||||
|                 if (mount(0, fields.at(4).c_str(), 0, MS_PRIVATE, 0) == -1) | ||||
|                     throw SysError(format("unable to make filesystem `%1%' private") % fields.at(4)); | ||||
|             } | ||||
| 
 | ||||
|             /* Bind-mount all the directories from the "host"
 | ||||
|  | @ -2053,7 +2041,7 @@ void DerivationGoal::initChild() | |||
| PathSet parseReferenceSpecifiers(const Derivation & drv, string attr) | ||||
| { | ||||
|     PathSet result; | ||||
|     Paths paths = tokenizeString(attr); | ||||
|     Paths paths = tokenizeString<Paths>(attr); | ||||
|     foreach (Strings::iterator, i, paths) { | ||||
|         if (isStorePath(*i)) | ||||
|             result.insert(*i); | ||||
|  |  | |||
|  | @ -373,7 +373,7 @@ static void addAdditionalRoots(StoreAPI & store, PathSet & roots) | |||
| 
 | ||||
|     string result = runProgram(rootFinder); | ||||
| 
 | ||||
|     Strings paths = tokenizeString(result, "\n"); | ||||
|     Strings paths = tokenizeString<Strings>(result, "\n"); | ||||
| 
 | ||||
|     foreach (Strings::iterator, i, paths) { | ||||
|         if (isInStore(*i)) { | ||||
|  |  | |||
|  | @ -65,15 +65,7 @@ void Settings::processEnvironment() | |||
|         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) | ||||
| { | ||||
|     Strings::iterator i = ss.begin(); | ||||
|     advance(i, n); | ||||
|     return *i; | ||||
|         substituters = tokenizeString<Strings>(subs, ":"); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
|  | @ -95,15 +87,15 @@ void Settings::loadConfFile() | |||
|         if (hash != string::npos) | ||||
|             line = string(line, 0, hash); | ||||
| 
 | ||||
|         Strings tokens = tokenizeString(line); | ||||
|         vector<string> tokens = tokenizeString<vector<string> >(line); | ||||
|         if (tokens.empty()) continue; | ||||
| 
 | ||||
|         if (tokens.size() < 2 || at(tokens, 1) != "=") | ||||
|         if (tokens.size() < 2 || tokens[1] != "=") | ||||
|             throw Error(format("illegal configuration line `%1%' in `%2%'") % line % settingsFile); | ||||
| 
 | ||||
|         string name = at(tokens, 0); | ||||
|         string name = tokens[0]; | ||||
| 
 | ||||
|         Strings::iterator i = tokens.begin(); | ||||
|         vector<string>::iterator i = tokens.begin(); | ||||
|         advance(i, 2); | ||||
|         settings[name] = concatStringsSep(" ", Strings(i, tokens.end())); // FIXME: slow
 | ||||
|     }; | ||||
|  | @ -170,7 +162,7 @@ void Settings::get(PathSet & res, const string & name) | |||
|     SettingsMap::iterator i = settings.find(name); | ||||
|     if (i == settings.end()) return; | ||||
|     res.clear(); | ||||
|     Strings ss = tokenizeString(i->second); | ||||
|     Strings ss = tokenizeString<Strings>(i->second); | ||||
|     res.insert(ss.begin(), ss.end()); | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -20,6 +20,11 @@ | |||
| #include <stdio.h> | ||||
| #include <time.h> | ||||
| 
 | ||||
| #if HAVE_UNSHARE | ||||
| #include <sched.h> | ||||
| #include <sys/mount.h> | ||||
| #endif | ||||
| 
 | ||||
| #include <sqlite3.h> | ||||
| 
 | ||||
| 
 | ||||
|  | @ -292,6 +297,8 @@ LocalStore::LocalStore(bool reserveSpace) | |||
|     } | ||||
| 
 | ||||
|     else openDB(false); | ||||
| 
 | ||||
|     makeStoreWritable(); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
|  | @ -411,6 +418,38 @@ void LocalStore::openDB(bool create) | |||
| } | ||||
| 
 | ||||
| 
 | ||||
| /* To improve purity, users may want to make the Nix store a read-only
 | ||||
|    bind mount.  So make the Nix store writable for this process. */ | ||||
| void LocalStore::makeStoreWritable() | ||||
| { | ||||
| #if HAVE_UNSHARE | ||||
|     if (getuid() != 0) return; | ||||
| 
 | ||||
|     if (!pathExists("/proc/self/mountinfo")) return; | ||||
| 
 | ||||
|     /* Check if /nix/store is a read-only bind mount. */ | ||||
|     bool found = false; | ||||
|     Strings mounts = tokenizeString<Strings>(readFile("/proc/self/mountinfo", true), "\n"); | ||||
|     foreach (Strings::iterator, i, mounts) { | ||||
|         vector<string> fields = tokenizeString<vector<string> >(*i, " "); | ||||
|         if (fields.at(3) == "/" || fields.at(4) != settings.nixStore) continue; | ||||
|         Strings options = tokenizeString<Strings>(fields.at(5), ","); | ||||
|         if (std::find(options.begin(), options.end(), "ro") == options.end()) continue; | ||||
|         found = true; | ||||
|         break; | ||||
|     } | ||||
| 
 | ||||
|     if (!found) return; | ||||
| 
 | ||||
|     if (unshare(CLONE_NEWNS) == -1) | ||||
|         throw SysError("setting up a private mount namespace"); | ||||
| 
 | ||||
|     if (mount(0, settings.nixStore.c_str(), 0, MS_REMOUNT | MS_BIND, 0) == -1) | ||||
|         throw SysError(format("remounting %1% writable") % settings.nixStore); | ||||
| #endif | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| const time_t mtimeStore = 1; /* 1 second into the epoch */ | ||||
| 
 | ||||
| 
 | ||||
|  | @ -478,8 +517,6 @@ void canonicalisePathMetaData(const Path & path, bool recurse) | |||
|         foreach (Strings::iterator, i, names) | ||||
|             canonicalisePathMetaData(path + "/" + *i, true); | ||||
|     } | ||||
| 
 | ||||
|     makeImmutable(path); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
|  | @ -1435,7 +1472,7 @@ Path LocalStore::importPath(bool requireSignature, Source & source) | |||
|         /* Lock the output path.  But don't lock if we're being called
 | ||||
|            from a build hook (whose parent process already acquired a | ||||
|            lock on this path). */ | ||||
|         Strings locksHeld = tokenizeString(getEnv("NIX_HELD_LOCKS")); | ||||
|         Strings locksHeld = tokenizeString<Strings>(getEnv("NIX_HELD_LOCKS")); | ||||
|         if (find(locksHeld.begin(), locksHeld.end(), dstPath) == locksHeld.end()) | ||||
|             outputLock.lockPaths(singleton<PathSet, Path>(dstPath)); | ||||
| 
 | ||||
|  | @ -1645,7 +1682,7 @@ ValidPathInfo LocalStore::queryPathInfoOld(const Path & path) | |||
|     string info = readFile(infoFile); | ||||
| 
 | ||||
|     /* Parse it. */ | ||||
|     Strings lines = tokenizeString(info, "\n"); | ||||
|     Strings lines = tokenizeString<Strings>(info, "\n"); | ||||
| 
 | ||||
|     foreach (Strings::iterator, i, lines) { | ||||
|         string::size_type p = i->find(':'); | ||||
|  | @ -1654,7 +1691,7 @@ ValidPathInfo LocalStore::queryPathInfoOld(const Path & path) | |||
|         string name(*i, 0, p); | ||||
|         string value(*i, p + 2); | ||||
|         if (name == "References") { | ||||
|             Strings refs = tokenizeString(value, " "); | ||||
|             Strings refs = tokenizeString<Strings>(value, " "); | ||||
|             res.references = PathSet(refs.begin(), refs.end()); | ||||
|         } else if (name == "Deriver") { | ||||
|             res.deriver = value; | ||||
|  |  | |||
|  | @ -228,6 +228,8 @@ private: | |||
| 
 | ||||
|     void openDB(bool create); | ||||
| 
 | ||||
|     void makeStoreWritable(); | ||||
| 
 | ||||
|     unsigned long long queryValidPathId(const Path & path); | ||||
| 
 | ||||
|     unsigned long long addValidPath(const ValidPathInfo & info, bool checkOutputs = true); | ||||
|  |  | |||
|  | @ -33,8 +33,7 @@ struct MakeReadOnly | |||
|     ~MakeReadOnly() | ||||
|     { | ||||
|         try { | ||||
|             /* This will make the path read-only (and restore the
 | ||||
|                immutable bit on platforms that support it). */ | ||||
|             /* This will make the path read-only. */ | ||||
|             if (path != "") canonicalisePathMetaData(path, false); | ||||
|         } catch (...) { | ||||
|             ignoreException(); | ||||
|  | @ -43,14 +42,6 @@ struct MakeReadOnly | |||
| }; | ||||
| 
 | ||||
| 
 | ||||
| struct MakeImmutable | ||||
| { | ||||
|     Path path; | ||||
|     MakeImmutable(const Path & path) : path(path) { } | ||||
|     ~MakeImmutable() { makeImmutable(path); } | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| void LocalStore::optimisePath_(OptimiseStats & stats, const Path & path) | ||||
| { | ||||
|     checkInterrupt(); | ||||
|  | @ -101,7 +92,6 @@ void LocalStore::optimisePath_(OptimiseStats & stats, const Path & path) | |||
|     if (!pathExists(linkPath)) { | ||||
|         /* Nope, create a hard link in the links directory. */ | ||||
|         makeMutable(path); | ||||
|         MakeImmutable mk1(path); | ||||
|         if (link(path.c_str(), linkPath.c_str()) == 0) return; | ||||
|         if (errno != EEXIST) | ||||
|             throw SysError(format("cannot link `%1%' to `%2%'") % linkPath % path); | ||||
|  | @ -134,57 +124,41 @@ void LocalStore::optimisePath_(OptimiseStats & stats, const Path & path) | |||
|     MakeReadOnly makeReadOnly(mustToggle ? dirOf(path) : ""); | ||||
| 
 | ||||
|     /* If ‘linkPath’ is immutable, we can't create hard links to it,
 | ||||
|        so make it mutable first (and make it immutable again when | ||||
|        we're done).  We also have to make ‘path’ mutable, otherwise | ||||
|        rename() will fail to delete it. */ | ||||
|        so make it mutable first.  We also have to make ‘path’ mutable, | ||||
|        otherwise rename() will fail to delete it. */ | ||||
|     makeMutable(path); | ||||
|     MakeImmutable mk2(path); | ||||
|     makeMutable(linkPath); | ||||
| 
 | ||||
|     /* Another process might be doing the same thing (creating a new
 | ||||
|        link to ‘linkPath’) and make ‘linkPath’ immutable before we're | ||||
|        done.  In that case, just retry. */ | ||||
|     unsigned int retries = 1024; | ||||
|     while (--retries > 0) { | ||||
|         makeMutable(linkPath); | ||||
|         MakeImmutable mk1(linkPath); | ||||
|     Path tempLink = (format("%1%/.tmp-link-%2%-%3%") | ||||
|         % settings.nixStore % getpid() % rand()).str(); | ||||
| 
 | ||||
|         Path tempLink = (format("%1%/.tmp-link-%2%-%3%") | ||||
|             % settings.nixStore % getpid() % rand()).str(); | ||||
| 
 | ||||
|         if (link(linkPath.c_str(), tempLink.c_str()) == -1) { | ||||
|             if (errno == EMLINK) { | ||||
|                 /* Too many links to the same file (>= 32000 on most
 | ||||
|                    file systems).  This is likely to happen with empty | ||||
|                    files.  Just shrug and ignore. */ | ||||
|                 if (st.st_size) | ||||
|                     printMsg(lvlInfo, format("`%1%' has maximum number of links") % linkPath); | ||||
|                 return; | ||||
|             } | ||||
|             if (errno == EPERM) continue; | ||||
|             throw SysError(format("cannot link `%1%' to `%2%'") % tempLink % linkPath); | ||||
|     if (link(linkPath.c_str(), tempLink.c_str()) == -1) { | ||||
|         if (errno == EMLINK) { | ||||
|             /* Too many links to the same file (>= 32000 on most file
 | ||||
|                systems).  This is likely to happen with empty files. | ||||
|                Just shrug and ignore. */ | ||||
|             if (st.st_size) | ||||
|                 printMsg(lvlInfo, format("`%1%' has maximum number of links") % linkPath); | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         /* Atomically replace the old file with the new hard link. */ | ||||
|         if (rename(tempLink.c_str(), path.c_str()) == -1) { | ||||
|             if (unlink(tempLink.c_str()) == -1) | ||||
|                 printMsg(lvlError, format("unable to unlink `%1%'") % tempLink); | ||||
|             if (errno == EMLINK) { | ||||
|                 /* Some filesystems generate too many links on the
 | ||||
|                    rename, rather than on the original link. | ||||
|                    (Probably it temporarily increases the st_nlink | ||||
|                    field before decreasing it again.) */ | ||||
|                 if (st.st_size) | ||||
|                     printMsg(lvlInfo, format("`%1%' has maximum number of links") % linkPath); | ||||
|                 return; | ||||
|             } | ||||
|             if (errno == EPERM) continue; | ||||
|             throw SysError(format("cannot rename `%1%' to `%2%'") % tempLink % path); | ||||
|         } | ||||
| 
 | ||||
|         break; | ||||
|         throw SysError(format("cannot link `%1%' to `%2%'") % tempLink % linkPath); | ||||
|     } | ||||
| 
 | ||||
|     if (retries == 0) throw Error(format("cannot link `%1%' to `%2%'") % path % linkPath); | ||||
|     /* Atomically replace the old file with the new hard link. */ | ||||
|     if (rename(tempLink.c_str(), path.c_str()) == -1) { | ||||
|         if (unlink(tempLink.c_str()) == -1) | ||||
|             printMsg(lvlError, format("unable to unlink `%1%'") % tempLink); | ||||
|         if (errno == EMLINK) { | ||||
|             /* Some filesystems generate too many links on the rename,
 | ||||
|                rather than on the original link.  (Probably it | ||||
|                temporarily increases the st_nlink field before | ||||
|                decreasing it again.) */ | ||||
|             if (st.st_size) | ||||
|                 printMsg(lvlInfo, format("`%1%' has maximum number of links") % linkPath); | ||||
|             return; | ||||
|         } | ||||
|         throw SysError(format("cannot rename `%1%' to `%2%'") % tempLink % path); | ||||
|     } | ||||
| 
 | ||||
|     stats.filesLinked++; | ||||
|     stats.bytesFreed += st.st_size; | ||||
|  |  | |||
|  | @ -16,7 +16,7 @@ | |||
| namespace nix { | ||||
| 
 | ||||
| 
 | ||||
| void changeMutable(const Path & path, bool mut) | ||||
| void makeMutable(const Path & path) | ||||
| { | ||||
| #if defined(FS_IOC_SETFLAGS) && defined(FS_IOC_GETFLAGS) && defined(FS_IMMUTABLE_FL) | ||||
| 
 | ||||
|  | @ -38,30 +38,12 @@ void changeMutable(const Path & path, bool mut) | |||
|     /* Silently ignore errors getting/setting the immutable flag so
 | ||||
|        that we work correctly on filesystems that don't support it. */ | ||||
|     if (ioctl(fd, FS_IOC_GETFLAGS, &flags)) return; | ||||
| 
 | ||||
|     old = flags; | ||||
|      | ||||
|     if (mut) flags &= ~FS_IMMUTABLE_FL; | ||||
|     else flags |= FS_IMMUTABLE_FL; | ||||
| 
 | ||||
|     flags &= ~FS_IMMUTABLE_FL; | ||||
|     if (old == flags) return; | ||||
| 
 | ||||
|     if (ioctl(fd, FS_IOC_SETFLAGS, &flags)) return; | ||||
|      | ||||
| #endif | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| void makeImmutable(const Path & path) | ||||
| { | ||||
|     changeMutable(path, false); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| void makeMutable(const Path & path) | ||||
| { | ||||
|     changeMutable(path, true); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| } | ||||
|  |  | |||
|  | @ -4,12 +4,6 @@ | |||
| 
 | ||||
| namespace nix { | ||||
| 
 | ||||
| /* Make the given path immutable, i.e., prevent it from being modified
 | ||||
|    in any way, even by root.  This is a no-op on platforms that do not | ||||
|    support this, or if the calling user is not privileged.  On Linux, | ||||
|    this is implemented by doing the equivalent of ‘chattr +i path’. */ | ||||
| void makeImmutable(const Path & path); | ||||
| 
 | ||||
| /* Make the given path mutable. */ | ||||
| void makeMutable(const Path & path); | ||||
| 
 | ||||
|  |  | |||
|  | @ -984,9 +984,9 @@ void _interrupted() | |||
| //////////////////////////////////////////////////////////////////////
 | ||||
| 
 | ||||
| 
 | ||||
| Strings tokenizeString(const string & s, const string & separators) | ||||
| template<class C> C tokenizeString(const string & s, const string & separators) | ||||
| { | ||||
|     Strings result; | ||||
|     C result; | ||||
|     string::size_type pos = s.find_first_not_of(separators, 0); | ||||
|     while (pos != string::npos) { | ||||
|         string::size_type end = s.find_first_of(separators, pos + 1); | ||||
|  | @ -998,6 +998,9 @@ Strings tokenizeString(const string & s, const string & separators) | |||
|     return result; | ||||
| } | ||||
| 
 | ||||
| template Strings tokenizeString(const string & s, const string & separators); | ||||
| template vector<string> tokenizeString(const string & s, const string & separators); | ||||
| 
 | ||||
| 
 | ||||
| string concatStringsSep(const string & sep, const Strings & ss) | ||||
| { | ||||
|  |  | |||
|  | @ -286,7 +286,7 @@ MakeError(Interrupted, BaseError) | |||
| 
 | ||||
| 
 | ||||
| /* String tokenizer. */ | ||||
| Strings tokenizeString(const string & s, const string & separators = " \t\n\r"); | ||||
| template<class C> C tokenizeString(const string & s, const string & separators = " \t\n\r"); | ||||
| 
 | ||||
| 
 | ||||
| /* Concatenate the given strings with a separator between the
 | ||||
|  |  | |||
|  | @ -193,16 +193,15 @@ static void run(int argc, char * * argv) | |||
|     if (st.st_mode & (S_IWGRP | S_IWOTH)) | ||||
|         throw Error(format("`%1%' should not be group or world-writable") % configFile); | ||||
| 
 | ||||
|     Strings tokens = tokenizeString(readFile(fdConfig)); | ||||
|     vector<string> tokens = tokenizeString<vector<string> >(readFile(fdConfig)); | ||||
| 
 | ||||
|     fdConfig.close(); | ||||
| 
 | ||||
|     if (tokens.size() != 2) | ||||
|         throw Error(format("parse error in `%1%'") % configFile); | ||||
| 
 | ||||
|     Strings::iterator i = tokens.begin(); | ||||
|     string nixUser = *i++; | ||||
|     string buildUsersGroup = *i++; | ||||
|     string nixUser = tokens[0]; | ||||
|     string buildUsersGroup = tokens[1]; | ||||
| 
 | ||||
| 
 | ||||
|     /* Check that the caller (real uid) is the one allowed to call
 | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue