forked from lix-project/lix
* Beginning of secure multi-user Nix stores. If Nix is started as
root (or setuid root), then builds will be performed under one of the users listed in the `build-users' configuration variables. This is to make it impossible to influence build results externally, allowing locally built derivations to be shared safely between users (see ASE-2005 paper). To do: only one builder should be active per build user.
This commit is contained in:
parent
15ff877438
commit
32282abcea
src
|
@ -334,6 +334,11 @@ void switchToNixUser()
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* !!! for setuid operation, we should: 1) wipe the environment;
|
||||||
|
2) verify file descriptors 0, 1, 2; 3) etc.
|
||||||
|
See: http://www.daemon-systems.org/man/setuid.7.html
|
||||||
|
*/
|
||||||
|
|
||||||
haveSwitched = true;
|
haveSwitched = true;
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
#include <map>
|
#include <map>
|
||||||
|
#include <sstream>
|
||||||
#include <boost/shared_ptr.hpp>
|
#include <boost/shared_ptr.hpp>
|
||||||
#include <boost/weak_ptr.hpp>
|
#include <boost/weak_ptr.hpp>
|
||||||
#include <boost/enable_shared_from_this.hpp>
|
#include <boost/enable_shared_from_this.hpp>
|
||||||
|
@ -9,6 +10,9 @@
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
|
||||||
|
#include <pwd.h>
|
||||||
|
#include <grp.h>
|
||||||
|
|
||||||
#include "build.hh"
|
#include "build.hh"
|
||||||
#include "references.hh"
|
#include "references.hh"
|
||||||
#include "pathlocks.hh"
|
#include "pathlocks.hh"
|
||||||
|
@ -22,6 +26,9 @@
|
||||||
static string pathNullDevice = "/dev/null";
|
static string pathNullDevice = "/dev/null";
|
||||||
|
|
||||||
|
|
||||||
|
static const uid_t rootUserId = 0;
|
||||||
|
|
||||||
|
|
||||||
/* Forward definition. */
|
/* Forward definition. */
|
||||||
class Worker;
|
class Worker;
|
||||||
|
|
||||||
|
@ -84,7 +91,12 @@ public:
|
||||||
|
|
||||||
virtual void waiteeDone(GoalPtr waitee, bool success);
|
virtual void waiteeDone(GoalPtr waitee, bool success);
|
||||||
|
|
||||||
virtual void writeLog(int fd, const unsigned char * buf, size_t count)
|
virtual void handleChildOutput(int fd, const string & data)
|
||||||
|
{
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void handleEOF(int fd)
|
||||||
{
|
{
|
||||||
abort();
|
abort();
|
||||||
}
|
}
|
||||||
|
@ -100,18 +112,19 @@ public:
|
||||||
{
|
{
|
||||||
return exitCode;
|
return exitCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void amDone(bool success = true);
|
void amDone(bool success = true);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/* A mapping used to remember for each child process to what goal it
|
/* A mapping used to remember for each child process to what goal it
|
||||||
belongs, and a file descriptor for receiving log data. */
|
belongs, and file descriptors for receiving log data and output
|
||||||
|
path creation commands. */
|
||||||
struct Child
|
struct Child
|
||||||
{
|
{
|
||||||
WeakGoalPtr goal;
|
WeakGoalPtr goal;
|
||||||
int fdOutput;
|
set<int> fds;
|
||||||
bool inBuildSlot;
|
bool inBuildSlot;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -166,8 +179,8 @@ public:
|
||||||
bool canBuildMore();
|
bool canBuildMore();
|
||||||
|
|
||||||
/* Registers / unregisters a running child process. */
|
/* Registers / unregisters a running child process. */
|
||||||
void childStarted(GoalPtr goal, pid_t pid, int fdOutput,
|
void childStarted(GoalPtr goal, pid_t pid,
|
||||||
bool inBuildSlot);
|
const set<int> & fds, bool inBuildSlot);
|
||||||
void childTerminated(pid_t pid, bool wakeSleepers = true);
|
void childTerminated(pid_t pid, bool wakeSleepers = true);
|
||||||
|
|
||||||
/* Add a goal to the set of goals waiting for a build slot. */
|
/* Add a goal to the set of goals waiting for a build slot. */
|
||||||
|
@ -299,7 +312,6 @@ const char * * strings2CharPtrs(const Strings & ss)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
|
||||||
|
@ -324,6 +336,9 @@ private:
|
||||||
/* Referenceable paths (i.e., input and output paths). */
|
/* Referenceable paths (i.e., input and output paths). */
|
||||||
PathSet allPaths;
|
PathSet allPaths;
|
||||||
|
|
||||||
|
/* User selected for running the builder. */
|
||||||
|
uid_t buildUser;
|
||||||
|
|
||||||
/* The process ID of the builder. */
|
/* The process ID of the builder. */
|
||||||
Pid pid;
|
Pid pid;
|
||||||
|
|
||||||
|
@ -333,6 +348,13 @@ private:
|
||||||
/* File descriptor for the log file. */
|
/* File descriptor for the log file. */
|
||||||
AutoCloseFD fdLogFile;
|
AutoCloseFD fdLogFile;
|
||||||
|
|
||||||
|
/* File descriptor for the output creation fifo. */
|
||||||
|
AutoCloseFD fdCreateOutput;
|
||||||
|
AutoCloseFD fdOutputCreated;
|
||||||
|
|
||||||
|
Path pathCreateOutput;
|
||||||
|
Path pathOutputCreated;
|
||||||
|
|
||||||
/* Pipe for the builder's standard output/error. */
|
/* Pipe for the builder's standard output/error. */
|
||||||
Pipe logPipe;
|
Pipe logPipe;
|
||||||
|
|
||||||
|
@ -396,7 +418,8 @@ private:
|
||||||
void deleteTmpDir(bool force);
|
void deleteTmpDir(bool force);
|
||||||
|
|
||||||
/* Callback used by the worker to write to the log. */
|
/* Callback used by the worker to write to the log. */
|
||||||
void writeLog(int fd, const unsigned char * buf, size_t count);
|
void handleChildOutput(int fd, const string & data);
|
||||||
|
void handleEOF(int fd);
|
||||||
|
|
||||||
/* Return the set of (in)valid paths. */
|
/* Return the set of (in)valid paths. */
|
||||||
PathSet checkPathValidity(bool returnValid);
|
PathSet checkPathValidity(bool returnValid);
|
||||||
|
@ -408,6 +431,7 @@ DerivationGoal::DerivationGoal(const Path & drvPath, Worker & worker)
|
||||||
{
|
{
|
||||||
this->drvPath = drvPath;
|
this->drvPath = drvPath;
|
||||||
state = &DerivationGoal::init;
|
state = &DerivationGoal::init;
|
||||||
|
buildUser = 0;
|
||||||
name = (format("building of `%1%'") % drvPath).str();
|
name = (format("building of `%1%'") % drvPath).str();
|
||||||
trace("created");
|
trace("created");
|
||||||
}
|
}
|
||||||
|
@ -607,7 +631,8 @@ void DerivationGoal::buildDone()
|
||||||
to have terminated. In fact, the builder could also have
|
to have terminated. In fact, the builder could also have
|
||||||
simply have closed its end of the pipe --- just don't do that
|
simply have closed its end of the pipe --- just don't do that
|
||||||
:-) */
|
:-) */
|
||||||
/* !!! this could block! */
|
/* !!! this could block! security problem! solution: kill the
|
||||||
|
child */
|
||||||
pid_t savedPid = pid;
|
pid_t savedPid = pid;
|
||||||
int status = pid.wait(true);
|
int status = pid.wait(true);
|
||||||
|
|
||||||
|
@ -757,7 +782,7 @@ DerivationGoal::HookReply DerivationGoal::tryBuildHook()
|
||||||
/* parent */
|
/* parent */
|
||||||
logPipe.writeSide.close();
|
logPipe.writeSide.close();
|
||||||
worker.childStarted(shared_from_this(),
|
worker.childStarted(shared_from_this(),
|
||||||
pid, logPipe.readSide, false);
|
pid, singleton<set<int> >(logPipe.readSide), false);
|
||||||
|
|
||||||
fromHook.writeSide.close();
|
fromHook.writeSide.close();
|
||||||
toHook.readSide.close();
|
toHook.readSide.close();
|
||||||
|
@ -953,6 +978,29 @@ bool DerivationGoal::prepareBuild()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static uid_t allocBuildUser()
|
||||||
|
{
|
||||||
|
Strings buildUsers = querySetting("build-users", Strings());
|
||||||
|
|
||||||
|
if (buildUsers.empty())
|
||||||
|
throw Error(
|
||||||
|
"cannot build as `root'; you must define "
|
||||||
|
"one or more users for building in `build-users' "
|
||||||
|
"in the Nix configuration file");
|
||||||
|
|
||||||
|
for (Strings::iterator i = buildUsers.begin(); i != buildUsers.end(); ++i) {
|
||||||
|
printMsg(lvlError, format("trying user `%1%'") % *i);
|
||||||
|
|
||||||
|
struct passwd * pw = getpwnam(i->c_str());
|
||||||
|
if (!pw)
|
||||||
|
throw Error(format("the user `%1%' listed in `build-users' does not exist") % *i);
|
||||||
|
|
||||||
|
return pw->pw_uid;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void DerivationGoal::startBuilder()
|
void DerivationGoal::startBuilder()
|
||||||
{
|
{
|
||||||
startNest(nest, lvlInfo,
|
startNest(nest, lvlInfo,
|
||||||
|
@ -1026,6 +1074,39 @@ void DerivationGoal::startBuilder()
|
||||||
if (i->second.hash != "")
|
if (i->second.hash != "")
|
||||||
env["NIX_OUTPUT_CHECKED"] = "1";
|
env["NIX_OUTPUT_CHECKED"] = "1";
|
||||||
|
|
||||||
|
|
||||||
|
/* Create the FIFOs through which the child can tell this process
|
||||||
|
to create the output path. */
|
||||||
|
pathCreateOutput = tmpDir + "/.create-output.fifo";
|
||||||
|
pathOutputCreated = tmpDir + "/.output-created.fifo";
|
||||||
|
|
||||||
|
if (mkfifo(pathCreateOutput.c_str(), 0622) == -1 ||
|
||||||
|
chmod(pathCreateOutput.c_str(), 0622) == -1)
|
||||||
|
throw SysError(format("cannot create FIFO `%1%'") % pathCreateOutput);
|
||||||
|
|
||||||
|
if (mkfifo(pathOutputCreated.c_str(), 0700) == -1 ||
|
||||||
|
chmod(pathOutputCreated.c_str(), 0622) == -1)
|
||||||
|
throw SysError(format("cannot create FIFO `%1%'") % pathOutputCreated);
|
||||||
|
|
||||||
|
fdCreateOutput = open(pathCreateOutput.c_str(), O_RDONLY | O_NONBLOCK);
|
||||||
|
if (fdCreateOutput == -1)
|
||||||
|
throw SysError(format("cannot open FIFO `%1%'") % pathCreateOutput);
|
||||||
|
|
||||||
|
|
||||||
|
/* If we are running as root, and the `build-allow-root' setting
|
||||||
|
is `false', then we have to build as one of the users listed in
|
||||||
|
`build-users'. */
|
||||||
|
if (!queryBoolSetting("build-allow-root", true) &&
|
||||||
|
getuid() == rootUserId)
|
||||||
|
{
|
||||||
|
buildUser = allocBuildUser();
|
||||||
|
|
||||||
|
/* Change ownership of the temporary build directory. !!! gid */
|
||||||
|
if (chown(tmpDir.c_str(), buildUser, (gid_t) -1) == -1)
|
||||||
|
throw SysError(format("cannot change ownership of `%1%'") % tmpDir);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Run the builder. */
|
/* Run the builder. */
|
||||||
printMsg(lvlChatty, format("executing builder `%1%'") %
|
printMsg(lvlChatty, format("executing builder `%1%'") %
|
||||||
drv.builder);
|
drv.builder);
|
||||||
|
@ -1064,6 +1145,25 @@ void DerivationGoal::startBuilder()
|
||||||
envStrs.push_back(i->first + "=" + i->second);
|
envStrs.push_back(i->first + "=" + i->second);
|
||||||
const char * * envArr = strings2CharPtrs(envStrs);
|
const char * * envArr = strings2CharPtrs(envStrs);
|
||||||
|
|
||||||
|
/* If we are running as root and `build-allow-root' is
|
||||||
|
`false', then switch to the user we allocated above.
|
||||||
|
Make sure that we drop all root privileges. Note that
|
||||||
|
initChild() above has closed all file descriptors
|
||||||
|
except std*, so that's safe. Also note that setuid()
|
||||||
|
when run as root sets the real, effective and saved
|
||||||
|
UIDs. */
|
||||||
|
if (buildUser != 0) {
|
||||||
|
printMsg(lvlError, format("switching to uid `%1%'") % buildUser);
|
||||||
|
|
||||||
|
/* !!! setgid also */
|
||||||
|
if (setgroups(0, 0) == -1)
|
||||||
|
throw SysError("cannot clear the set of supplementary groups");
|
||||||
|
setuid(buildUser);
|
||||||
|
assert(getuid() == buildUser);
|
||||||
|
assert(geteuid() == buildUser);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
/* Execute the program. This should not return. */
|
/* Execute the program. This should not return. */
|
||||||
execve(drv.builder.c_str(),
|
execve(drv.builder.c_str(),
|
||||||
(char * *) argArr, (char * *) envArr);
|
(char * *) argArr, (char * *) envArr);
|
||||||
|
@ -1077,11 +1177,14 @@ void DerivationGoal::startBuilder()
|
||||||
_exit(1);
|
_exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* parent */
|
/* parent */
|
||||||
pid.setSeparatePG(true);
|
pid.setSeparatePG(true);
|
||||||
logPipe.writeSide.close();
|
logPipe.writeSide.close();
|
||||||
worker.childStarted(shared_from_this(),
|
set<int> fds;
|
||||||
pid, logPipe.readSide, true);
|
fds.insert(logPipe.readSide);
|
||||||
|
fds.insert(fdCreateOutput);
|
||||||
|
worker.childStarted(shared_from_this(), pid, fds, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1270,11 +1373,60 @@ void DerivationGoal::deleteTmpDir(bool force)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void DerivationGoal::writeLog(int fd,
|
void DerivationGoal::handleChildOutput(int fd, const string & data)
|
||||||
const unsigned char * buf, size_t count)
|
|
||||||
{
|
{
|
||||||
assert(fd == logPipe.readSide);
|
if (fd == logPipe.readSide) {
|
||||||
writeFull(fdLogFile, buf, count);
|
if (verbosity >= buildVerbosity)
|
||||||
|
writeFull(STDERR_FILENO, (unsigned char *) data.c_str(), data.size());
|
||||||
|
writeFull(fdLogFile, (unsigned char *) data.c_str(), data.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (fd == fdCreateOutput) {
|
||||||
|
|
||||||
|
/* The child sent us a command to create the output path. */
|
||||||
|
debug(format("got output creation command `%1%'") % data);
|
||||||
|
|
||||||
|
istringstream str(data);
|
||||||
|
string id, type;
|
||||||
|
str >> id >> type;
|
||||||
|
|
||||||
|
if (id != "out") throw Error("not supported!"); /* !!! */
|
||||||
|
|
||||||
|
Path path;
|
||||||
|
for (DerivationOutputs::const_iterator i = drv.outputs.begin();
|
||||||
|
i != drv.outputs.end(); ++i)
|
||||||
|
if (i->first == "out") path = i->second.path.c_str();
|
||||||
|
|
||||||
|
if (path.empty()) throw Error(format("unknown output ID `%1%'") % id);
|
||||||
|
|
||||||
|
if (type == "d") {
|
||||||
|
if (mkdir(path.c_str(), 0700) == -1)
|
||||||
|
throw SysError(format("creating directory `%1%'") % path);
|
||||||
|
} else if (type == "f") {
|
||||||
|
AutoCloseFD fd = open(path.c_str(), O_CREAT | O_EXCL | O_RDONLY, 0600);
|
||||||
|
if (fd == -1)
|
||||||
|
throw SysError(format("creating file `%1%'") % path);
|
||||||
|
} else
|
||||||
|
throw Error(format("bad output creation command `%1%'") % type);
|
||||||
|
|
||||||
|
if (chown(path.c_str(), buildUser, (gid_t) -1) == -1) /* !!! */
|
||||||
|
throw SysError(format("cannot change ownership of `%1%'") % path);
|
||||||
|
|
||||||
|
/* The builder must open this FIFO for writing. This will
|
||||||
|
block until we have opened it for reading. Here we do
|
||||||
|
so, causing the builder to unblock and proceed. */
|
||||||
|
fdOutputCreated = open(pathOutputCreated.c_str(), O_RDONLY | O_NONBLOCK);
|
||||||
|
if (fdOutputCreated == -1)
|
||||||
|
throw SysError(format("cannot open FIFO `%1%'") % pathOutputCreated);
|
||||||
|
}
|
||||||
|
|
||||||
|
else abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void DerivationGoal::handleEOF(int fd)
|
||||||
|
{
|
||||||
|
if (fd == logPipe.readSide) worker.wakeUp(shared_from_this());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1337,7 +1489,8 @@ public:
|
||||||
void finished();
|
void finished();
|
||||||
|
|
||||||
/* Callback used by the worker to write to the log. */
|
/* Callback used by the worker to write to the log. */
|
||||||
void writeLog(int fd, const unsigned char * buf, size_t count);
|
void handleChildOutput(int fd, const string & data);
|
||||||
|
void handleEOF(int fd);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -1502,7 +1655,7 @@ void SubstitutionGoal::tryToRun()
|
||||||
pid.setSeparatePG(true);
|
pid.setSeparatePG(true);
|
||||||
logPipe.writeSide.close();
|
logPipe.writeSide.close();
|
||||||
worker.childStarted(shared_from_this(),
|
worker.childStarted(shared_from_this(),
|
||||||
pid, logPipe.readSide, true);
|
pid, singleton<set<int> >(logPipe.readSide), true);
|
||||||
|
|
||||||
state = &SubstitutionGoal::finished;
|
state = &SubstitutionGoal::finished;
|
||||||
}
|
}
|
||||||
|
@ -1569,15 +1722,22 @@ void SubstitutionGoal::finished()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void SubstitutionGoal::writeLog(int fd,
|
void SubstitutionGoal::handleChildOutput(int fd, const string & data)
|
||||||
const unsigned char * buf, size_t count)
|
|
||||||
{
|
{
|
||||||
assert(fd == logPipe.readSide);
|
assert(fd == logPipe.readSide);
|
||||||
|
if (verbosity >= buildVerbosity)
|
||||||
|
writeFull(STDERR_FILENO, (unsigned char *) data.c_str(), data.size());
|
||||||
/* Don't write substitution output to a log file for now. We
|
/* Don't write substitution output to a log file for now. We
|
||||||
probably should, though. */
|
probably should, though. */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void SubstitutionGoal::handleEOF(int fd)
|
||||||
|
{
|
||||||
|
if (fd == logPipe.readSide) worker.wakeUp(shared_from_this());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
@ -1674,11 +1834,11 @@ bool Worker::canBuildMore()
|
||||||
|
|
||||||
|
|
||||||
void Worker::childStarted(GoalPtr goal,
|
void Worker::childStarted(GoalPtr goal,
|
||||||
pid_t pid, int fdOutput, bool inBuildSlot)
|
pid_t pid, const set<int> & fds, bool inBuildSlot)
|
||||||
{
|
{
|
||||||
Child child;
|
Child child;
|
||||||
child.goal = goal;
|
child.goal = goal;
|
||||||
child.fdOutput = fdOutput;
|
child.fds = fds;
|
||||||
child.inBuildSlot = inBuildSlot;
|
child.inBuildSlot = inBuildSlot;
|
||||||
children[pid] = child;
|
children[pid] = child;
|
||||||
if (inBuildSlot) nrChildren++;
|
if (inBuildSlot) nrChildren++;
|
||||||
|
@ -1772,9 +1932,11 @@ void Worker::waitForInput()
|
||||||
{
|
{
|
||||||
printMsg(lvlVomit, "waiting for children");
|
printMsg(lvlVomit, "waiting for children");
|
||||||
|
|
||||||
/* Process log output from the children. We also use this to
|
/* Process output from the file descriptors attached to the
|
||||||
detect child termination: if we get EOF on the logger pipe of a
|
children, namely log output and output path creation commands.
|
||||||
build, we assume that the builder has terminated. */
|
We also use this to detect child termination: if we get EOF on
|
||||||
|
the logger pipe of a build, we assume that the builder has
|
||||||
|
terminated. */
|
||||||
|
|
||||||
/* Use select() to wait for the input side of any logger pipe to
|
/* Use select() to wait for the input side of any logger pipe to
|
||||||
become `available'. Note that `available' (i.e., non-blocking)
|
become `available'. Note that `available' (i.e., non-blocking)
|
||||||
|
@ -1785,9 +1947,12 @@ void Worker::waitForInput()
|
||||||
for (Children::iterator i = children.begin();
|
for (Children::iterator i = children.begin();
|
||||||
i != children.end(); ++i)
|
i != children.end(); ++i)
|
||||||
{
|
{
|
||||||
int fd = i->second.fdOutput;
|
for (set<int>::iterator j = i->second.fds.begin();
|
||||||
FD_SET(fd, &fds);
|
j != i->second.fds.end(); ++j)
|
||||||
if (fd >= fdMax) fdMax = fd + 1;
|
{
|
||||||
|
FD_SET(*j, &fds);
|
||||||
|
if (*j >= fdMax) fdMax = *j + 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (select(fdMax, &fds, 0, 0, 0) == -1) {
|
if (select(fdMax, &fds, 0, 0, 0) == -1) {
|
||||||
|
@ -1802,29 +1967,33 @@ void Worker::waitForInput()
|
||||||
checkInterrupt();
|
checkInterrupt();
|
||||||
GoalPtr goal = i->second.goal.lock();
|
GoalPtr goal = i->second.goal.lock();
|
||||||
assert(goal);
|
assert(goal);
|
||||||
int fd = i->second.fdOutput;
|
set<int> fds2(i->second.fds);
|
||||||
if (FD_ISSET(fd, &fds)) {
|
for (set<int>::iterator j = fds2.begin(); j != fds2.end(); ++j)
|
||||||
unsigned char buffer[4096];
|
{
|
||||||
ssize_t rd = read(fd, buffer, sizeof(buffer));
|
if (FD_ISSET(*j, &fds)) {
|
||||||
if (rd == -1) {
|
unsigned char buffer[4096];
|
||||||
if (errno != EINTR)
|
ssize_t rd = read(*j, buffer, sizeof(buffer));
|
||||||
throw SysError(format("reading from %1%")
|
if (rd == -1) {
|
||||||
% goal->getName());
|
if (errno != EINTR)
|
||||||
} else if (rd == 0) {
|
throw SysError(format("reading from %1%")
|
||||||
debug(format("%1%: got EOF") % goal->getName());
|
% goal->getName());
|
||||||
wakeUp(goal);
|
} else if (rd == 0) {
|
||||||
} else {
|
debug(format("%1%: got EOF") % goal->getName());
|
||||||
printMsg(lvlVomit, format("%1%: read %2% bytes")
|
goal->handleEOF(*j);
|
||||||
% goal->getName() % rd);
|
i->second.fds.erase(*j);
|
||||||
goal->writeLog(fd, buffer, (size_t) rd);
|
} else {
|
||||||
if (verbosity >= buildVerbosity)
|
printMsg(lvlVomit, format("%1%: read %2% bytes")
|
||||||
writeFull(STDERR_FILENO, buffer, rd);
|
% goal->getName() % rd);
|
||||||
|
string data((char *) buffer, rd);
|
||||||
|
goal->handleChildOutput(*j, data);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -17,8 +17,6 @@ bool tryFallback = false;
|
||||||
Verbosity buildVerbosity = lvlInfo;
|
Verbosity buildVerbosity = lvlInfo;
|
||||||
unsigned int maxBuildJobs = 1;
|
unsigned int maxBuildJobs = 1;
|
||||||
bool readOnlyMode = false;
|
bool readOnlyMode = false;
|
||||||
bool buildAllowRoot = true;
|
|
||||||
list<string> buildUsers;
|
|
||||||
|
|
||||||
|
|
||||||
static bool settingsRead = false;
|
static bool settingsRead = false;
|
||||||
|
@ -79,8 +77,6 @@ Strings querySetting(const string & name, const Strings & def)
|
||||||
|
|
||||||
bool queryBoolSetting(const string & name, bool def)
|
bool queryBoolSetting(const string & name, bool def)
|
||||||
{
|
{
|
||||||
debug("X");
|
|
||||||
|
|
||||||
Strings defs;
|
Strings defs;
|
||||||
if (def) defs.push_back("true"); else defs.push_back("false");
|
if (def) defs.push_back("true"); else defs.push_back("false");
|
||||||
|
|
||||||
|
|
|
@ -53,15 +53,6 @@ extern unsigned int maxBuildJobs;
|
||||||
database. */
|
database. */
|
||||||
extern bool readOnlyMode;
|
extern bool readOnlyMode;
|
||||||
|
|
||||||
/* Whether to allow builds by root. Corresponds to the
|
|
||||||
`build-allow-root' configuration option. */
|
|
||||||
extern bool buildAllowRoot;
|
|
||||||
|
|
||||||
/* The list of users under which root-initiated builds can be
|
|
||||||
performed. Correspons to the `build-users' configuration
|
|
||||||
option. */
|
|
||||||
extern list<string> buildUsers;
|
|
||||||
|
|
||||||
|
|
||||||
Strings querySetting(const string & name, const Strings & def);
|
Strings querySetting(const string & name, const Strings & def);
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue