* Some hackery to propagate the worker's stderr and exceptions to the
client.
This commit is contained in:
parent
714fa24cfb
commit
7951c3c546
7 changed files with 209 additions and 107 deletions
|
@ -872,7 +872,7 @@ static void drain(int fd)
|
||||||
if (errno != EINTR)
|
if (errno != EINTR)
|
||||||
throw SysError("draining");
|
throw SysError("draining");
|
||||||
} else if (rd == 0) break;
|
} else if (rd == 0) break;
|
||||||
else writeFull(STDERR_FILENO, buffer, rd);
|
else writeToStderr(buffer, rd);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1610,7 +1610,7 @@ void DerivationGoal::handleChildOutput(int fd, const string & data)
|
||||||
{
|
{
|
||||||
if (fd == logPipe.readSide) {
|
if (fd == logPipe.readSide) {
|
||||||
if (verbosity >= buildVerbosity)
|
if (verbosity >= buildVerbosity)
|
||||||
writeFull(STDERR_FILENO, (unsigned char *) data.c_str(), data.size());
|
writeToStderr((unsigned char *) data.c_str(), data.size());
|
||||||
writeFull(fdLogFile, (unsigned char *) data.c_str(), data.size());
|
writeFull(fdLogFile, (unsigned char *) data.c_str(), data.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1923,7 +1923,7 @@ void SubstitutionGoal::handleChildOutput(int fd, const string & data)
|
||||||
{
|
{
|
||||||
assert(fd == logPipe.readSide);
|
assert(fd == logPipe.readSide);
|
||||||
if (verbosity >= buildVerbosity)
|
if (verbosity >= buildVerbosity)
|
||||||
writeFull(STDERR_FILENO, (unsigned char *) data.c_str(), data.size());
|
writeToStderr((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. */
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,10 @@
|
||||||
#include "worker-protocol.hh"
|
#include "worker-protocol.hh"
|
||||||
#include "archive.hh"
|
#include "archive.hh"
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
|
@ -38,8 +42,14 @@ RemoteStore::RemoteStore()
|
||||||
if (dup2(toChild.readSide, STDIN_FILENO) == -1)
|
if (dup2(toChild.readSide, STDIN_FILENO) == -1)
|
||||||
throw SysError("dupping read side");
|
throw SysError("dupping read side");
|
||||||
|
|
||||||
|
int fdDebug = open("/tmp/worker-log", O_WRONLY | O_CREAT | O_TRUNC, 0644);
|
||||||
|
assert(fdDebug != -1);
|
||||||
|
if (dup2(fdDebug, STDERR_FILENO) == -1)
|
||||||
|
throw SysError("dupping stderr");
|
||||||
|
close(fdDebug);
|
||||||
|
|
||||||
execlp(worker.c_str(), worker.c_str(),
|
execlp(worker.c_str(), worker.c_str(),
|
||||||
"--slave", NULL);
|
"-vvv", "--slave", NULL);
|
||||||
|
|
||||||
throw SysError(format("executing `%1%'") % worker);
|
throw SysError(format("executing `%1%'") % worker);
|
||||||
|
|
||||||
|
@ -66,9 +76,13 @@ RemoteStore::RemoteStore()
|
||||||
|
|
||||||
RemoteStore::~RemoteStore()
|
RemoteStore::~RemoteStore()
|
||||||
{
|
{
|
||||||
writeInt(wopQuit, to);
|
try {
|
||||||
readInt(from);
|
fromChild.readSide.close();
|
||||||
child.wait(true);
|
toChild.writeSide.close();
|
||||||
|
child.wait(true);
|
||||||
|
} catch (Error & e) {
|
||||||
|
printMsg(lvlError, format("error (ignored): %1%") % e.msg());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -158,6 +172,7 @@ void RemoteStore::buildDerivations(const PathSet & drvPaths)
|
||||||
{
|
{
|
||||||
writeInt(wopBuildDerivations, to);
|
writeInt(wopBuildDerivations, to);
|
||||||
writeStringSet(drvPaths, to);
|
writeStringSet(drvPaths, to);
|
||||||
|
processStderr();
|
||||||
readInt(from);
|
readInt(from);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -185,4 +200,18 @@ void RemoteStore::syncWithGC()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void RemoteStore::processStderr()
|
||||||
|
{
|
||||||
|
unsigned int msg;
|
||||||
|
while ((msg = readInt(from)) == STDERR_NEXT) {
|
||||||
|
string s = readString(from);
|
||||||
|
writeToStderr((unsigned char *) s.c_str(), s.size());
|
||||||
|
}
|
||||||
|
if (msg == STDERR_ERROR)
|
||||||
|
throw Error(readString(from));
|
||||||
|
else if (msg != STDERR_LAST)
|
||||||
|
throw Error("protocol error processing standard error");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,6 +57,8 @@ private:
|
||||||
FdSink to;
|
FdSink to;
|
||||||
FdSource from;
|
FdSource from;
|
||||||
Pid child;
|
Pid child;
|
||||||
|
|
||||||
|
void processStderr();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -23,4 +23,9 @@ typedef enum {
|
||||||
} WorkerOp;
|
} WorkerOp;
|
||||||
|
|
||||||
|
|
||||||
|
#define STDERR_NEXT 0x6f6c6d67
|
||||||
|
#define STDERR_LAST 0x616c7473
|
||||||
|
#define STDERR_ERROR 0x63787470
|
||||||
|
|
||||||
|
|
||||||
#endif /* !__WORKER_PROTOCOL_H */
|
#endif /* !__WORKER_PROTOCOL_H */
|
||||||
|
|
|
@ -437,7 +437,7 @@ void printMsg_(Verbosity level, const format & f)
|
||||||
else if (logType == ltEscapes && level != lvlInfo)
|
else if (logType == ltEscapes && level != lvlInfo)
|
||||||
prefix = "\033[" + escVerbosity(level) + "s";
|
prefix = "\033[" + escVerbosity(level) + "s";
|
||||||
string s = (format("%1%%2%\n") % prefix % f.str()).str();
|
string s = (format("%1%%2%\n") % prefix % f.str()).str();
|
||||||
writeFull(STDERR_FILENO, (const unsigned char *) s.c_str(), s.size());
|
writeToStderr((const unsigned char *) s.c_str(), s.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -450,6 +450,15 @@ void warnOnce(bool & haveWarned, const format & f)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void defaultWriteToStderr(const unsigned char * buf, size_t count)
|
||||||
|
{
|
||||||
|
writeFull(STDERR_FILENO, buf, count);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void (*writeToStderr) (const unsigned char * buf, size_t count) = defaultWriteToStderr;
|
||||||
|
|
||||||
|
|
||||||
void readFull(int fd, unsigned char * buf, size_t count)
|
void readFull(int fd, unsigned char * buf, size_t count)
|
||||||
{
|
{
|
||||||
while (count) {
|
while (count) {
|
||||||
|
|
|
@ -131,6 +131,8 @@ void printMsg_(Verbosity level, const format & f);
|
||||||
|
|
||||||
void warnOnce(bool & haveWarned, const format & f);
|
void warnOnce(bool & haveWarned, const format & f);
|
||||||
|
|
||||||
|
extern void (*writeToStderr) (const unsigned char * buf, size_t count);
|
||||||
|
|
||||||
|
|
||||||
/* Wrappers arount read()/write() that read/write exactly the
|
/* Wrappers arount read()/write() that read/write exactly the
|
||||||
requested number of bytes. */
|
requested number of bytes. */
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
using namespace nix;
|
using namespace nix;
|
||||||
|
|
||||||
|
|
||||||
Path readStorePath(Source & from)
|
static Path readStorePath(Source & from)
|
||||||
{
|
{
|
||||||
Path path = readString(from);
|
Path path = readString(from);
|
||||||
assertStorePath(path);
|
assertStorePath(path);
|
||||||
|
@ -18,7 +18,7 @@ Path readStorePath(Source & from)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
PathSet readStorePaths(Source & from)
|
static PathSet readStorePaths(Source & from)
|
||||||
{
|
{
|
||||||
PathSet paths = readStringSet(from);
|
PathSet paths = readStringSet(from);
|
||||||
for (PathSet::iterator i = paths.begin(); i != paths.end(); ++i)
|
for (PathSet::iterator i = paths.begin(); i != paths.end(); ++i)
|
||||||
|
@ -27,7 +27,148 @@ PathSet readStorePaths(Source & from)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void processConnection(Source & from, Sink & to)
|
static Sink * _to; /* !!! should make writeToStderr an object */
|
||||||
|
bool canSendStderr;
|
||||||
|
|
||||||
|
|
||||||
|
static void tunnelStderr(const unsigned char * buf, size_t count)
|
||||||
|
{
|
||||||
|
writeFull(STDERR_FILENO, buf, count);
|
||||||
|
if (canSendStderr) {
|
||||||
|
try {
|
||||||
|
writeInt(STDERR_NEXT, *_to);
|
||||||
|
writeString(string((char *) buf, count), *_to);
|
||||||
|
} catch (...) {
|
||||||
|
/* Write failed; that means that the other side is
|
||||||
|
gone. */
|
||||||
|
canSendStderr = false;
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* startWork() means that we're starting an operation for which we
|
||||||
|
want to send out stderr to the client. */
|
||||||
|
static void startWork()
|
||||||
|
{
|
||||||
|
canSendStderr = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* stopWork() means that we're done; stop sending stderr to the
|
||||||
|
client. */
|
||||||
|
static void stopWork()
|
||||||
|
{
|
||||||
|
canSendStderr = false;
|
||||||
|
writeInt(STDERR_LAST, *_to);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void performOp(Source & from, Sink & to, unsigned int op)
|
||||||
|
{
|
||||||
|
switch (op) {
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
case wopQuit: {
|
||||||
|
/* Close the database. */
|
||||||
|
store.reset((StoreAPI *) 0);
|
||||||
|
writeInt(1, to);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
case wopIsValidPath: {
|
||||||
|
Path path = readStorePath(from);
|
||||||
|
writeInt(store->isValidPath(path), to);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case wopHasSubstitutes: {
|
||||||
|
Path path = readStorePath(from);
|
||||||
|
writeInt(store->hasSubstitutes(path), to);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case wopQueryPathHash: {
|
||||||
|
Path path = readStorePath(from);
|
||||||
|
writeString(printHash(store->queryPathHash(path)), to);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case wopQueryReferences:
|
||||||
|
case wopQueryReferrers: {
|
||||||
|
Path path = readStorePath(from);
|
||||||
|
PathSet paths;
|
||||||
|
if (op == wopQueryReferences)
|
||||||
|
store->queryReferences(path, paths);
|
||||||
|
else
|
||||||
|
store->queryReferrers(path, paths);
|
||||||
|
writeStringSet(paths, to);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case wopAddToStore: {
|
||||||
|
/* !!! uberquick hack */
|
||||||
|
string baseName = readString(from);
|
||||||
|
bool fixed = readInt(from) == 1;
|
||||||
|
bool recursive = readInt(from) == 1;
|
||||||
|
string hashAlgo = readString(from);
|
||||||
|
|
||||||
|
Path tmp = createTempDir();
|
||||||
|
Path tmp2 = tmp + "/" + baseName;
|
||||||
|
restorePath(tmp2, from);
|
||||||
|
|
||||||
|
writeString(store->addToStore(tmp2, fixed, recursive, hashAlgo), to);
|
||||||
|
|
||||||
|
deletePath(tmp);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case wopAddTextToStore: {
|
||||||
|
string suffix = readString(from);
|
||||||
|
string s = readString(from);
|
||||||
|
PathSet refs = readStorePaths(from);
|
||||||
|
writeString(store->addTextToStore(suffix, s, refs), to);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case wopBuildDerivations: {
|
||||||
|
PathSet drvs = readStorePaths(from);
|
||||||
|
startWork();
|
||||||
|
store->buildDerivations(drvs);
|
||||||
|
stopWork();
|
||||||
|
writeInt(1, to);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case wopEnsurePath: {
|
||||||
|
Path path = readStorePath(from);
|
||||||
|
store->ensurePath(path);
|
||||||
|
writeInt(1, to);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case wopAddTempRoot: {
|
||||||
|
Path path = readStorePath(from);
|
||||||
|
store->addTempRoot(path);
|
||||||
|
writeInt(1, to);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case wopSyncWithGC: {
|
||||||
|
store->syncWithGC();
|
||||||
|
writeInt(1, to);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw Error(format("invalid operation %1%") % op);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void processConnection(Source & from, Sink & to)
|
||||||
{
|
{
|
||||||
store = boost::shared_ptr<StoreAPI>(new LocalStore(true));
|
store = boost::shared_ptr<StoreAPI>(new LocalStore(true));
|
||||||
|
|
||||||
|
@ -38,110 +179,24 @@ void processConnection(Source & from, Sink & to)
|
||||||
|
|
||||||
debug("greeting exchanged");
|
debug("greeting exchanged");
|
||||||
|
|
||||||
|
_to = &to;
|
||||||
|
canSendStderr = false;
|
||||||
|
writeToStderr = tunnelStderr;
|
||||||
|
|
||||||
bool quit = false;
|
bool quit = false;
|
||||||
|
|
||||||
unsigned int opCount = 0;
|
unsigned int opCount = 0;
|
||||||
|
|
||||||
do {
|
do {
|
||||||
|
|
||||||
WorkerOp op = (WorkerOp) readInt(from);
|
WorkerOp op = (WorkerOp) readInt(from);
|
||||||
|
|
||||||
opCount++;
|
opCount++;
|
||||||
|
|
||||||
switch (op) {
|
try {
|
||||||
|
performOp(from, to, op);
|
||||||
case wopQuit: {
|
} catch (Error & e) {
|
||||||
/* Close the database. */
|
writeInt(STDERR_ERROR, *_to);
|
||||||
store.reset((StoreAPI *) 0);
|
writeString(e.msg(), to);
|
||||||
writeInt(1, to);
|
|
||||||
quit = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case wopIsValidPath: {
|
|
||||||
Path path = readStorePath(from);
|
|
||||||
writeInt(store->isValidPath(path), to);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case wopHasSubstitutes: {
|
|
||||||
Path path = readStorePath(from);
|
|
||||||
writeInt(store->hasSubstitutes(path), to);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case wopQueryPathHash: {
|
|
||||||
Path path = readStorePath(from);
|
|
||||||
writeString(printHash(store->queryPathHash(path)), to);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case wopQueryReferences:
|
|
||||||
case wopQueryReferrers: {
|
|
||||||
Path path = readStorePath(from);
|
|
||||||
PathSet paths;
|
|
||||||
if (op == wopQueryReferences)
|
|
||||||
store->queryReferences(path, paths);
|
|
||||||
else
|
|
||||||
store->queryReferrers(path, paths);
|
|
||||||
writeStringSet(paths, to);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case wopAddToStore: {
|
|
||||||
/* !!! uberquick hack */
|
|
||||||
string baseName = readString(from);
|
|
||||||
bool fixed = readInt(from) == 1;
|
|
||||||
bool recursive = readInt(from) == 1;
|
|
||||||
string hashAlgo = readString(from);
|
|
||||||
|
|
||||||
Path tmp = createTempDir();
|
|
||||||
Path tmp2 = tmp + "/" + baseName;
|
|
||||||
restorePath(tmp2, from);
|
|
||||||
|
|
||||||
writeString(store->addToStore(tmp2, fixed, recursive, hashAlgo), to);
|
|
||||||
|
|
||||||
deletePath(tmp);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case wopAddTextToStore: {
|
|
||||||
string suffix = readString(from);
|
|
||||||
string s = readString(from);
|
|
||||||
PathSet refs = readStorePaths(from);
|
|
||||||
writeString(store->addTextToStore(suffix, s, refs), to);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case wopBuildDerivations: {
|
|
||||||
PathSet drvs = readStorePaths(from);
|
|
||||||
store->buildDerivations(drvs);
|
|
||||||
writeInt(1, to);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case wopEnsurePath: {
|
|
||||||
Path path = readStorePath(from);
|
|
||||||
store->ensurePath(path);
|
|
||||||
writeInt(1, to);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case wopAddTempRoot: {
|
|
||||||
Path path = readStorePath(from);
|
|
||||||
store->addTempRoot(path);
|
|
||||||
writeInt(1, to);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case wopSyncWithGC: {
|
|
||||||
store->syncWithGC();
|
|
||||||
writeInt(1, to);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
throw Error(format("invalid operation %1%") % op);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} while (!quit);
|
} while (!quit);
|
||||||
|
|
Loading…
Reference in a new issue