Process stderr from substituters while doing have/info queries
This commit is contained in:
parent
c5f9d0d080
commit
5959c591a0
4 changed files with 59 additions and 9 deletions
|
@ -1007,10 +1007,6 @@ void LocalStore::startSubstituter(const Path & substituter, RunningSubstituter &
|
||||||
fromPipe.create();
|
fromPipe.create();
|
||||||
errorPipe.create();
|
errorPipe.create();
|
||||||
|
|
||||||
/* Hack: prevent substituters that write too much to stderr from
|
|
||||||
deadlocking our read() from stdout. */
|
|
||||||
fcntl(errorPipe.writeSide, F_SETFL, O_NONBLOCK);
|
|
||||||
|
|
||||||
setSubstituterEnv();
|
setSubstituterEnv();
|
||||||
|
|
||||||
run.pid = maybeVfork();
|
run.pid = maybeVfork();
|
||||||
|
@ -1038,20 +1034,65 @@ void LocalStore::startSubstituter(const Path & substituter, RunningSubstituter &
|
||||||
|
|
||||||
/* Parent. */
|
/* Parent. */
|
||||||
|
|
||||||
|
run.program = baseNameOf(substituter);
|
||||||
run.to = toPipe.writeSide.borrow();
|
run.to = toPipe.writeSide.borrow();
|
||||||
run.from = run.fromBuf.fd = fromPipe.readSide.borrow();
|
run.from = run.fromBuf.fd = fromPipe.readSide.borrow();
|
||||||
run.error = errorPipe.readSide.borrow();
|
run.error = errorPipe.readSide.borrow();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Read a line from the substituter's stdout, while also processing
|
||||||
|
its stderr. */
|
||||||
string LocalStore::getLineFromSubstituter(RunningSubstituter & run)
|
string LocalStore::getLineFromSubstituter(RunningSubstituter & run)
|
||||||
{
|
{
|
||||||
string s;
|
string res, err;
|
||||||
|
|
||||||
|
/* We might have stdout data left over from the last time. */
|
||||||
|
if (run.fromBuf.hasData()) goto haveData;
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
unsigned char c;
|
fd_set fds;
|
||||||
run.fromBuf(&c, 1);
|
FD_ZERO(&fds);
|
||||||
if (c == '\n') return s;
|
FD_SET(run.from, &fds);
|
||||||
s += c;
|
FD_SET(run.error, &fds);
|
||||||
|
|
||||||
|
/* Wait for data to appear on the substituter's stdout or
|
||||||
|
stderr. */
|
||||||
|
if (select(run.from > run.error ? run.from + 1 : run.error + 1, &fds, 0, 0, 0) == -1) {
|
||||||
|
if (errno == EINTR) continue;
|
||||||
|
throw SysError("waiting for input from the substituter");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Completely drain stderr before dealing with stdout. */
|
||||||
|
if (FD_ISSET(run.error, &fds)) {
|
||||||
|
char buf[4096];
|
||||||
|
ssize_t n = read(run.error, (unsigned char *) buf, sizeof(buf));
|
||||||
|
if (n == -1) {
|
||||||
|
if (errno == EINTR) continue;
|
||||||
|
throw SysError("reading from substituter's stderr");
|
||||||
|
}
|
||||||
|
if (n == 0) throw Error(format("substituter `%1%' died unexpectedly") % run.program);
|
||||||
|
err.append(buf, n);
|
||||||
|
string::size_type p;
|
||||||
|
while ((p = err.find('\n')) != string::npos) {
|
||||||
|
printMsg(lvlError, run.program + ": " + string(err, 0, p));
|
||||||
|
err = string(err, p + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Read from stdout until we get a newline or the buffer is empty. */
|
||||||
|
else if (run.fromBuf.hasData() || FD_ISSET(run.from, &fds)) {
|
||||||
|
haveData:
|
||||||
|
do {
|
||||||
|
unsigned char c;
|
||||||
|
run.fromBuf(&c, 1);
|
||||||
|
if (c == '\n') {
|
||||||
|
if (!err.empty()) printMsg(lvlError, run.program + ": " + err);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
res += c;
|
||||||
|
} while (run.fromBuf.hasData());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -44,6 +44,7 @@ struct OptimiseStats
|
||||||
|
|
||||||
struct RunningSubstituter
|
struct RunningSubstituter
|
||||||
{
|
{
|
||||||
|
Path program;
|
||||||
Pid pid;
|
Pid pid;
|
||||||
AutoCloseFD to, from, error;
|
AutoCloseFD to, from, error;
|
||||||
FdSource fromBuf;
|
FdSource fromBuf;
|
||||||
|
|
|
@ -90,6 +90,12 @@ size_t BufferedSource::read(unsigned char * data, size_t len)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool BufferedSource::hasData()
|
||||||
|
{
|
||||||
|
return bufPosOut < bufPosIn;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
size_t FdSource::readUnbuffered(unsigned char * data, size_t len)
|
size_t FdSource::readUnbuffered(unsigned char * data, size_t len)
|
||||||
{
|
{
|
||||||
ssize_t n;
|
ssize_t n;
|
||||||
|
|
|
@ -63,6 +63,8 @@ struct BufferedSource : Source
|
||||||
|
|
||||||
/* Underlying read call, to be overriden. */
|
/* Underlying read call, to be overriden. */
|
||||||
virtual size_t readUnbuffered(unsigned char * data, size_t len) = 0;
|
virtual size_t readUnbuffered(unsigned char * data, size_t len) = 0;
|
||||||
|
|
||||||
|
bool hasData();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue