|
|
|
|
|
|
|
|
|
|
|
|
|
|
#include <stdexcept>
|
|
|
#include <stdexcept>
|
|
|
|
|
|
|
|
|
#include "child.hpp"
|
|
|
|
|
|
|
|
|
#include "CodeDweller/child.hpp"
|
|
|
|
|
|
|
|
|
namespace CodeDweller {
|
|
|
namespace CodeDweller {
|
|
|
|
|
|
|
|
|
Child::Child(std::vector<std::string> args, size_t bufSize) :
|
|
|
Child::Child(std::vector<std::string> args, size_t bufSize) :
|
|
|
readStreambuf(bufSize),
|
|
|
|
|
|
writeStreambuf(bufSize),
|
|
|
|
|
|
reader(&readStreambuf),
|
|
|
|
|
|
writer(&writeStreambuf),
|
|
|
|
|
|
|
|
|
childStreambuf(bufSize),
|
|
|
|
|
|
childStream(&childStreambuf),
|
|
|
cmdArgs(args) {
|
|
|
cmdArgs(args) {
|
|
|
|
|
|
|
|
|
init();
|
|
|
init();
|
|
|
|
|
|
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
Child::Child(std::string childpath, size_t bufSize) :
|
|
|
Child::Child(std::string childpath, size_t bufSize) :
|
|
|
readStreambuf(bufSize),
|
|
|
|
|
|
writeStreambuf(bufSize),
|
|
|
|
|
|
reader(&readStreambuf),
|
|
|
|
|
|
writer(&writeStreambuf),
|
|
|
|
|
|
|
|
|
childStreambuf(bufSize),
|
|
|
|
|
|
childStream(&childStreambuf),
|
|
|
cmdline(childpath) {
|
|
|
cmdline(childpath) {
|
|
|
cmdArgs.push_back(childpath);
|
|
|
cmdArgs.push_back(childpath);
|
|
|
init();
|
|
|
init();
|
|
|
|
|
|
|
|
|
size_t
|
|
|
size_t
|
|
|
Child::numBytesAvailable() const {
|
|
|
Child::numBytesAvailable() const {
|
|
|
|
|
|
|
|
|
return readStreambuf.numBytesAvailable();
|
|
|
|
|
|
|
|
|
return childStreambuf.numBytesAvailable();
|
|
|
|
|
|
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Provide the stream buffers with the handles for communicating
|
|
|
// Provide the stream buffers with the handles for communicating
|
|
|
// with the child process.
|
|
|
// with the child process.
|
|
|
readStreambuf.setInputHandle(childStdOutAtParent);
|
|
|
|
|
|
writeStreambuf.setOutputHandle(childStdInAtParent);
|
|
|
|
|
|
|
|
|
childStreambuf.setInputHandle(childStdOutAtParent);
|
|
|
|
|
|
childStreambuf.setOutputHandle(childStdInAtParent);
|
|
|
|
|
|
|
|
|
// Save the handles to the child process and its primary thread.
|
|
|
// Save the handles to the child process and its primary thread.
|
|
|
childProcess = processInfo.hProcess;
|
|
|
childProcess = processInfo.hProcess;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Provide the stream buffers with the file descriptors for
|
|
|
// Provide the stream buffers with the file descriptors for
|
|
|
// communicating with the child process.
|
|
|
// communicating with the child process.
|
|
|
readStreambuf.setInputFileDescriptor(childStdOutPipe[0]);
|
|
|
|
|
|
writeStreambuf.setOutputFileDescriptor(childStdInPipe[1]);
|
|
|
|
|
|
|
|
|
childStreambuf.setInputFileDescriptor(childStdOutPipe[0]);
|
|
|
|
|
|
childStreambuf.setOutputFileDescriptor(childStdInPipe[1]);
|
|
|
|
|
|
|
|
|
// Close the child's end of the pipes.
|
|
|
// Close the child's end of the pipes.
|
|
|
if ( (close(childStdInPipe[0]) != 0) ||
|
|
|
if ( (close(childStdInPipe[0]) != 0) ||
|
|
|
|
|
|
|
|
|
int status = 0;
|
|
|
int status = 0;
|
|
|
|
|
|
|
|
|
result = waitpid(childPid, &status, WNOHANG);
|
|
|
result = waitpid(childPid, &status, WNOHANG);
|
|
|
// std::cout << "isDone(). waitpid(" << childPid << ",...) returned " << result << std::endl; // DEBUG
|
|
|
|
|
|
if (-1 == result) {
|
|
|
if (-1 == result) {
|
|
|
throw std::runtime_error("Error checking status of child process: " +
|
|
|
throw std::runtime_error("Error checking status of child process: " +
|
|
|
getErrorText());
|
|
|
getErrorText());
|
|
|
} else if (0 == result) {
|
|
|
} else if (0 == result) {
|
|
|
|
|
|
|
|
|
// Child is still running.
|
|
|
// Child is still running.
|
|
|
// std::cout << "isDone(). Child is still running..." << std::endl; // DEBUG.
|
|
|
|
|
|
return false;
|
|
|
return false;
|
|
|
|
|
|
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
// std::cout << "isDone(). Child exited." << std::endl; // DEBUG.
|
|
|
|
|
|
if (WIFEXITED(status)) {
|
|
|
if (WIFEXITED(status)) {
|
|
|
|
|
|
|
|
|
// Child exited normally.
|
|
|
// Child exited normally.
|
|
|
exitCode = WEXITSTATUS(status);
|
|
|
exitCode = WEXITSTATUS(status);
|
|
|
exitCodeObtainedFlag = true;
|
|
|
exitCodeObtainedFlag = true;
|
|
|
//std::cout << "isDone(). Child exited normally. Exit code: " << exitCode << std::endl; // DEBUG.
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#endif
|
|
|
#endif
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
Child::ReadStreambuf::ReadStreambuf(std::size_t bufferSize) :
|
|
|
|
|
|
|
|
|
Child::ChildStreambuf::ChildStreambuf(std::size_t bufferSize) :
|
|
|
#ifdef _WIN32
|
|
|
#ifdef _WIN32
|
|
|
inputHandle(0),
|
|
|
inputHandle(0),
|
|
|
|
|
|
outputHandle(0),
|
|
|
#else
|
|
|
#else
|
|
|
inputFileDescriptor(-1),
|
|
|
inputFileDescriptor(-1),
|
|
|
|
|
|
outputFileDescriptor(-1),
|
|
|
#endif
|
|
|
#endif
|
|
|
buffer(bufferSize + 1) {
|
|
|
|
|
|
|
|
|
readBuffer(bufferSize + 1),
|
|
|
|
|
|
writeBuffer(bufferSize + 1) {
|
|
|
|
|
|
|
|
|
char *end = &(buffer.front()) + buffer.size();
|
|
|
|
|
|
|
|
|
// Read buffer initialization.
|
|
|
|
|
|
char *end = &(readBuffer.front()) + readBuffer.size();
|
|
|
|
|
|
|
|
|
// Indicate to underflow that underflow has not been called.
|
|
|
// Indicate to underflow that underflow has not been called.
|
|
|
setg(end, end, end);
|
|
|
setg(end, end, end);
|
|
|
|
|
|
|
|
|
|
|
|
// Write buffer initialization.
|
|
|
|
|
|
char *base = &(writeBuffer.front());
|
|
|
|
|
|
|
|
|
|
|
|
// Indicate to overflow that overflow has not been called.
|
|
|
|
|
|
setp(base, base + writeBuffer.size() - 1);
|
|
|
|
|
|
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
#ifdef _WIN32
|
|
|
#ifdef _WIN32
|
|
|
void
|
|
|
void
|
|
|
Child::ReadStreambuf::setInputHandle(HANDLE inHandle) {
|
|
|
|
|
|
|
|
|
Child::ChildStreambuf::setInputHandle(HANDLE inHandle) {
|
|
|
|
|
|
|
|
|
inputHandle = inHandle;
|
|
|
inputHandle = inHandle;
|
|
|
|
|
|
|
|
|
}
|
|
|
}
|
|
|
#else
|
|
|
#else
|
|
|
void
|
|
|
void
|
|
|
Child::ReadStreambuf::setInputFileDescriptor(int inFd) {
|
|
|
|
|
|
|
|
|
Child::ChildStreambuf::setInputFileDescriptor(int inFd) {
|
|
|
|
|
|
|
|
|
inputFileDescriptor = inFd;
|
|
|
inputFileDescriptor = inFd;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#endif
|
|
|
#endif
|
|
|
|
|
|
|
|
|
size_t
|
|
|
size_t
|
|
|
Child::ReadStreambuf::numBytesAvailable() const {
|
|
|
|
|
|
|
|
|
Child::ChildStreambuf::numBytesAvailable() const {
|
|
|
|
|
|
|
|
|
size_t nBytesAvailable = egptr() - gptr();
|
|
|
size_t nBytesAvailable = egptr() - gptr();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
std::streambuf::int_type
|
|
|
std::streambuf::int_type
|
|
|
Child::ReadStreambuf::underflow() {
|
|
|
|
|
|
|
|
|
Child::ChildStreambuf::underflow() {
|
|
|
|
|
|
|
|
|
// Check for empty buffer.
|
|
|
// Check for empty buffer.
|
|
|
if (gptr() < egptr()) {
|
|
|
if (gptr() < egptr()) {
|
|
|
|
|
|
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
// Need to fill the buffer.
|
|
|
// Need to fill the buffer.
|
|
|
char *base = &(buffer.front());
|
|
|
|
|
|
|
|
|
char *base = &(readBuffer.front());
|
|
|
char *start = base;
|
|
|
char *start = base;
|
|
|
|
|
|
|
|
|
// Check whether this is the first fill.
|
|
|
// Check whether this is the first fill.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (!ReadFile(inputHandle,
|
|
|
if (!ReadFile(inputHandle,
|
|
|
start,
|
|
|
start,
|
|
|
buffer.size() - (start - base),
|
|
|
|
|
|
|
|
|
readBuffer.size() - (start - base),
|
|
|
&nBytesRead,
|
|
|
&nBytesRead,
|
|
|
NULL)) {
|
|
|
NULL)) {
|
|
|
return traits_type::eof();
|
|
|
return traits_type::eof();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
nBytesRead = read(inputFileDescriptor,
|
|
|
nBytesRead = read(inputFileDescriptor,
|
|
|
start,
|
|
|
start,
|
|
|
buffer.size() - (start - base));
|
|
|
|
|
|
|
|
|
readBuffer.size() - (start - base));
|
|
|
if (-1 == nBytesRead) {
|
|
|
if (-1 == nBytesRead) {
|
|
|
return traits_type::eof();
|
|
|
return traits_type::eof();
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
Child::WriteStreambuf::WriteStreambuf(std::size_t bufferSize) :
|
|
|
|
|
|
#ifdef _WIN32
|
|
|
|
|
|
outputHandle(0),
|
|
|
|
|
|
#else
|
|
|
|
|
|
outputFileDescriptor(-1),
|
|
|
|
|
|
#endif
|
|
|
|
|
|
buffer(bufferSize + 1) {
|
|
|
|
|
|
|
|
|
|
|
|
char *base = &(buffer.front());
|
|
|
|
|
|
|
|
|
|
|
|
// Indicate to overflow that overflow has not been called.
|
|
|
|
|
|
setp(base, base + buffer.size() - 1);
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef _WIN32
|
|
|
#ifdef _WIN32
|
|
|
void
|
|
|
void
|
|
|
Child::WriteStreambuf::setOutputHandle(HANDLE outHandle) {
|
|
|
|
|
|
|
|
|
Child::ChildStreambuf::setOutputHandle(HANDLE outHandle) {
|
|
|
|
|
|
|
|
|
outputHandle = outHandle;
|
|
|
outputHandle = outHandle;
|
|
|
|
|
|
|
|
|
}
|
|
|
}
|
|
|
#else
|
|
|
#else
|
|
|
void
|
|
|
void
|
|
|
Child::WriteStreambuf::setOutputFileDescriptor(int outFd) {
|
|
|
|
|
|
|
|
|
Child::ChildStreambuf::setOutputFileDescriptor(int outFd) {
|
|
|
|
|
|
|
|
|
outputFileDescriptor = outFd;
|
|
|
outputFileDescriptor = outFd;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#endif
|
|
|
#endif
|
|
|
|
|
|
|
|
|
void
|
|
|
void
|
|
|
Child::WriteStreambuf::flushBuffer() {
|
|
|
|
|
|
|
|
|
Child::ChildStreambuf::flushBuffer() {
|
|
|
|
|
|
|
|
|
// Write.
|
|
|
// Write.
|
|
|
std::ptrdiff_t nBytes = pptr() - pbase();
|
|
|
std::ptrdiff_t nBytes = pptr() - pbase();
|
|
|
|
|
|
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
std::streambuf::int_type
|
|
|
std::streambuf::int_type
|
|
|
Child::WriteStreambuf::overflow(int_type ch) {
|
|
|
|
|
|
|
|
|
Child::ChildStreambuf::overflow(int_type ch) {
|
|
|
|
|
|
|
|
|
// Check whether we're writing EOF.
|
|
|
// Check whether we're writing EOF.
|
|
|
if (traits_type::eof() != ch) {
|
|
|
if (traits_type::eof() != ch) {
|
|
|
|
|
|
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
int
|
|
|
int
|
|
|
Child::WriteStreambuf::sync() {
|
|
|
|
|
|
|
|
|
Child::ChildStreambuf::sync() {
|
|
|
|
|
|
|
|
|
flushBuffer(); // Throws exception on failure.
|
|
|
flushBuffer(); // Throws exception on failure.
|
|
|
|
|
|
|