Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[2.3-maintenance] Wait for build users when none are available #9261

Merged
merged 3 commits into from
Nov 2, 2023
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 36 additions & 26 deletions src/libstore/build.cc
Original file line number Diff line number Diff line change
Expand Up @@ -524,17 +524,24 @@ class UserLock
uid_t getGID() { assert(gid); return gid; }
std::vector<gid_t> getSupplementaryGIDs() { return supplementaryGIDs; }

bool findFreeUser();

bool enabled() { return uid != 0; }

};


Sync<PathSet> UserLock::lockedPaths_;


UserLock::UserLock()
{
assert(settings.buildUsersGroup != "");
createDirs(settings.nixStateDir + "/userpool");
/* Mark that user is not enabled by default */
uid = 0;
}

bool UserLock::findFreeUser() {

/* Get the members of the build-users-group. */
struct group * gr = getgrnam(settings.buildUsersGroup.get().c_str());
Expand Down Expand Up @@ -564,7 +571,6 @@ UserLock::UserLock()
throw Error(format("the user '%1%' in the group '%2%' does not exist")
% i % settings.buildUsersGroup);

createDirs(settings.nixStateDir + "/userpool");

fnUserLock = (format("%1%/userpool/%2%") % settings.nixStateDir % pw->pw_uid).str();

Expand Down Expand Up @@ -605,28 +611,24 @@ UserLock::UserLock()
supplementaryGIDs.resize(ngroups);
#endif

return;
return true;
}

} catch (...) {
lockedPaths_.lock()->erase(fnUserLock);
return false;
}
}

throw Error(format("all build users are currently in use; "
"consider creating additional users and adding them to the '%1%' group")
% settings.buildUsersGroup);
return false;
}


UserLock::~UserLock()
{
auto lockedPaths(lockedPaths_.lock());
assert(lockedPaths->count(fnUserLock));
lockedPaths->erase(fnUserLock);
}


void UserLock::kill()
{
killUser(uid);
Expand Down Expand Up @@ -1415,6 +1417,30 @@ void DerivationGoal::tryToBuild()
{
trace("trying to build");

/* If `build-users-group' is not empty, then we have to build as
one of the members of that group. */
if (settings.buildUsersGroup != "" && getuid() == 0) {
#if defined(__linux__) || defined(__APPLE__)
if (!buildUser) buildUser = std::make_unique<UserLock>();

if (!buildUser->enabled()) {
if (!buildUser->findFreeUser()) {
debug("waiting for build users");
worker.waitForAWhile(shared_from_this());
return;
}

/* Make sure that no other processes are executing under this
uid. */
buildUser->kill();
}
#else
/* Don't know how to block the creation of setuid/setgid
binaries on this platform. */
throw Error("build users are not supported on this platform for security reasons");
#endif
}

/* Obtain locks on all output paths. The locks are automatically
released when we exit this function or Nix crashes. If we
can't acquire the lock, then continue; hopefully some other
Expand Down Expand Up @@ -1931,22 +1957,6 @@ void DerivationGoal::startBuilder()
#endif
}

/* If `build-users-group' is not empty, then we have to build as
one of the members of that group. */
if (settings.buildUsersGroup != "" && getuid() == 0) {
#if defined(__linux__) || defined(__APPLE__)
buildUser = std::make_unique<UserLock>();

/* Make sure that no other processes are executing under this
uid. */
buildUser->kill();
#else
/* Don't know how to block the creation of setuid/setgid
binaries on this platform. */
throw Error("build users are not supported on this platform for security reasons");
#endif
}

/* Create a temporary directory where the build will take
place. */
auto drvName = storePathToName(drvPath);
Expand Down Expand Up @@ -4465,7 +4475,7 @@ void Worker::waitForInput()
if (!waitingForAWhile.empty()) {
useTimeout = true;
if (lastWokenUp == steady_time_point::min())
printError("waiting for locks or build slots...");
printError("waiting for locks, build slots or build users...");
if (lastWokenUp == steady_time_point::min() || lastWokenUp > before) lastWokenUp = before;
timeout.tv_sec = std::max(1L,
(long) std::chrono::duration_cast<std::chrono::seconds>(
Expand Down
Loading