string user;
uid_t uid;
gid_t gid;
+ std::vector<gid_t> supplementaryGIDs;
public:
UserLock();
string getUser() { return user; }
uid_t getUID() { return uid; }
uid_t getGID() { return gid; }
+ std::vector<gid_t> getSupplementaryGIDs() { return supplementaryGIDs; }
bool enabled() { return uid != 0; }
throw Error(format("the Nix user should not be a member of `%1%'")
% settings.buildUsersGroup);
+ /* Get the list of supplementary groups of this build user. This
+ is usually either empty or contains a group such as "kvm". */
+ supplementaryGIDs.resize(10);
+ int ngroups = supplementaryGIDs.size();
+ int err = getgrouplist(pw->pw_name, pw->pw_gid,
+ supplementaryGIDs.data(), &ngroups);
+ if (err == -1)
+ throw Error(format("failed to get list of supplementary groups for ‘%1%’") % pw->pw_name);
+
+ supplementaryGIDs.resize(ngroups);
+
return;
}
}
/* We are first going to try to create the invalid output paths
through substitutes. If that doesn't work, we'll build
them. */
- if (settings.useSubstitutes && !willBuildLocally(drv))
+ if (settings.useSubstitutes && substitutesAllowed(drv))
foreach (PathSet::iterator, i, invalidOutputs)
addWaitee(worker.makeSubstitutionGoal(*i, buildMode == bmRepair));
}
-PathSet outputPaths(const DerivationOutputs & outputs)
+static bool canBuildLocally(const string & platform)
{
- PathSet paths;
- foreach (DerivationOutputs::const_iterator, i, outputs)
- paths.insert(i->second.path);
- return paths;
+ return platform == settings.thisSystem
+#if __linux__
+ || (platform == "i686-linux" && settings.thisSystem == "x86_64-linux")
+#endif
+ ;
}
-static string get(const StringPairs & map, const string & key)
+static string get(const StringPairs & map, const string & key, const string & def = "")
{
StringPairs::const_iterator i = map.find(key);
- return i == map.end() ? (string) "" : i->second;
+ return i == map.end() ? def : i->second;
}
-static bool canBuildLocally(const string & platform)
+bool willBuildLocally(const Derivation & drv)
{
- return platform == settings.thisSystem
-#if __linux__
- || (platform == "i686-linux" && settings.thisSystem == "x86_64-linux")
-#endif
- ;
+ return get(drv.env, "preferLocalBuild") == "1" && canBuildLocally(drv.platform);
}
-bool willBuildLocally(const Derivation & drv)
+bool substitutesAllowed(const Derivation & drv)
{
- return get(drv.env, "preferLocalBuild") == "1" && canBuildLocally(drv.platform);
+ return get(drv.env, "allowSubstitutes", "1") == "1";
}
can't acquire the lock, then continue; hopefully some other
goal can start a build, and if not, the main loop will sleep a
few seconds and then retry this goal. */
- if (!outputLocks.lockPaths(outputPaths(drv.outputs), "", false)) {
+ if (!outputLocks.lockPaths(outputPaths(drv), "", false)) {
worker.waitForAWhile(shared_from_this());
return;
}
return;
}
- missingPaths = outputPaths(drv.outputs);
+ missingPaths = outputPaths(drv);
if (buildMode != bmCheck)
foreach (PathSet::iterator, i, validPaths) missingPaths.erase(*i);
Strings envStrs;
foreach (Environment::const_iterator, i, env)
envStrs.push_back(rewriteHashes(i->first + "=" + i->second, rewritesToTmp));
- auto envArr = stringsToCharPtrs(envStrs);
/* If we are running in `build-users' mode, then switch to the
user we allocated above. Make sure that we drop all root
setuid() when run as root sets the real, effective and
saved UIDs. */
if (buildUser.enabled()) {
- printMsg(lvlChatty, format("switching to user `%1%'") % buildUser.getUser());
-
- if (setgroups(0, 0) == -1)
- throw SysError("cannot clear the set of supplementary groups");
+ /* Preserve supplementary groups of the build user, to allow
+ admins to specify groups such as "kvm". */
+ if (setgroups(buildUser.getSupplementaryGIDs().size(),
+ buildUser.getSupplementaryGIDs().data()) == -1)
+ throw SysError("cannot set supplementary groups of build user");
if (setgid(buildUser.getGID()) == -1 ||
getgid() != buildUser.getGID() ||
args.push_back(builderBasename);
foreach (Strings::iterator, i, drv.args)
args.push_back(rewriteHashes(*i, rewritesToTmp));
- auto argArr = stringsToCharPtrs(args);
restoreSIGPIPE();
writeFull(STDERR_FILENO, "\n");
/* Execute the program. This should not return. */
- execve(drv.builder.c_str(), (char * *) &argArr[0], (char * *) &envArr[0]);
+ execve(drv.builder.c_str(), stringsToCharPtrs(args).data(), stringsToCharPtrs(envStrs).data());
throw SysError(format("executing `%1%'") % drv.builder);
args.push_back("--substitute");
args.push_back(storePath);
args.push_back(destPath);
- auto argArr = stringsToCharPtrs(args);
/* Fork the substitute program. */
pid = startProcess([&]() {
if (dup2(outPipe.writeSide, STDOUT_FILENO) == -1)
throw SysError("cannot dup output pipe into stdout");
- execv(sub.c_str(), (char * *) &argArr[0]);
+ execv(sub.c_str(), stringsToCharPtrs(args).data());
throw SysError(format("executing `%1%'") % sub);
});
bool gcKeepOutputs;
bool gcKeepDerivations;
unsigned long long bytesInvalidated;
+ bool moveToTrash = true;
Path trashDir;
bool shouldDelete;
GCState(GCResults & results_) : results(results_), bytesInvalidated(0) { }
not holding the global GC lock) we can delete the path without
being afraid that the path has become alive again. Otherwise
delete it right away. */
- if (S_ISDIR(st.st_mode)) {
+ if (state.moveToTrash && S_ISDIR(st.st_mode)) {
// Estimate the amount freed using the narSize field. FIXME:
// if the path was not valid, need to determine the actual
// size.
- state.bytesInvalidated += size;
- if (chmod(path.c_str(), st.st_mode | S_IWUSR) == -1)
- throw SysError(format("making `%1%' writable") % path);
- Path tmp = state.trashDir + "/" + baseNameOf(path);
- if (rename(path.c_str(), tmp.c_str()))
- throw SysError(format("unable to rename `%1%' to `%2%'") % path % tmp);
+ try {
+ if (chmod(path.c_str(), st.st_mode | S_IWUSR) == -1)
+ throw SysError(format("making `%1%' writable") % path);
+ Path tmp = state.trashDir + "/" + baseNameOf(path);
+ if (rename(path.c_str(), tmp.c_str()))
+ throw SysError(format("unable to rename `%1%' to `%2%'") % path % tmp);
+ state.bytesInvalidated += size;
+ } catch (SysError & e) {
+ if (e.errNo == ENOSPC) {
+ printMsg(lvlInfo, format("note: can't create move `%1%': %2%") % path % e.msg());
+ deleteGarbage(state, path);
+ }
+ }
} else
deleteGarbage(state, path);
if (state.shouldDelete) {
if (pathExists(state.trashDir)) deleteGarbage(state, state.trashDir);
- createDirs(state.trashDir);
+ try {
+ createDirs(state.trashDir);
+ } catch (SysError & e) {
+ if (e.errNo == ENOSPC) {
+ printMsg(lvlInfo, format("note: can't create trash directory: %1%") % e.msg());
+ state.moveToTrash = false;
+ }
+ }
}
/* Now either delete all garbage paths, or just the specified
struct stat st;
if (stat(reservedPath.c_str(), &st) == -1 ||
st.st_size != settings.reservedSize)
- writeFile(reservedPath, string(settings.reservedSize, 'X'));
+ {
+ AutoCloseFD fd = open(reservedPath.c_str(), O_WRONLY | O_CREAT, 0600);
+ int res = -1;
+#if HAVE_POSIX_FALLOCATE
+ res = posix_fallocate(fd, 0, settings.reservedSize);
+#endif
+ if (res == -1) {
+ writeFull(fd, string(settings.reservedSize, 'X'));
+ ftruncate(fd, settings.reservedSize);
+ }
+ }
}
else
deletePath(reservedPath);
if (n == 0) throw EndOfFile(format("substituter `%1%' died unexpectedly") % run.program);
err.append(buf, n);
string::size_type p;
- while (((p = err.find('\n')) != string::npos)
- || ((p = err.find('\r')) != string::npos)) {
- string thing(err, 0, p + 1);
- writeToStderr(run.program + ": " + thing);
+ while ((p = err.find('\n')) != string::npos) {
+ printMsg(lvlError, run.program + ": " + string(err, 0, p));
err = string(err, p + 1);
}
}