aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/libstore/local-store.cc59
-rw-r--r--src/libstore/local-store.hh1
-rw-r--r--src/libutil/serialise.cc6
-rw-r--r--src/libutil/serialise.hh2
4 files changed, 59 insertions, 9 deletions
diff --git a/src/libstore/local-store.cc b/src/libstore/local-store.cc
index cfb853205..056247b7e 100644
--- a/src/libstore/local-store.cc
+++ b/src/libstore/local-store.cc
@@ -1007,10 +1007,6 @@ void LocalStore::startSubstituter(const Path & substituter, RunningSubstituter &
fromPipe.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();
run.pid = maybeVfork();
@@ -1038,20 +1034,65 @@ void LocalStore::startSubstituter(const Path & substituter, RunningSubstituter &
/* Parent. */
+ run.program = baseNameOf(substituter);
run.to = toPipe.writeSide.borrow();
run.from = run.fromBuf.fd = fromPipe.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 s;
+ string res, err;
+
+ /* We might have stdout data left over from the last time. */
+ if (run.fromBuf.hasData()) goto haveData;
+
while (1) {
- unsigned char c;
- run.fromBuf(&c, 1);
- if (c == '\n') return s;
- s += c;
+ fd_set fds;
+ FD_ZERO(&fds);
+ FD_SET(run.from, &fds);
+ 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());
+ }
}
}
diff --git a/src/libstore/local-store.hh b/src/libstore/local-store.hh
index 424515667..b1fde99cf 100644
--- a/src/libstore/local-store.hh
+++ b/src/libstore/local-store.hh
@@ -44,6 +44,7 @@ struct OptimiseStats
struct RunningSubstituter
{
+ Path program;
Pid pid;
AutoCloseFD to, from, error;
FdSource fromBuf;
diff --git a/src/libutil/serialise.cc b/src/libutil/serialise.cc
index 9270806a7..1f817a869 100644
--- a/src/libutil/serialise.cc
+++ b/src/libutil/serialise.cc
@@ -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)
{
ssize_t n;
diff --git a/src/libutil/serialise.hh b/src/libutil/serialise.hh
index 42dd27117..e1c4514db 100644
--- a/src/libutil/serialise.hh
+++ b/src/libutil/serialise.hh
@@ -63,6 +63,8 @@ struct BufferedSource : Source
/* Underlying read call, to be overriden. */
virtual size_t readUnbuffered(unsigned char * data, size_t len) = 0;
+
+ bool hasData();
};