|
|
|
|
|
|
|
|
// Place, Suite 330, Boston, MA 02111-1307 USA
|
|
|
// Place, Suite 330, Boston, MA 02111-1307 USA
|
|
|
//==============================================================================
|
|
|
//==============================================================================
|
|
|
|
|
|
|
|
|
|
|
|
#ifndef WIN32
|
|
|
|
|
|
#include <unistd.h>
|
|
|
|
|
|
#include <sys/types.h>
|
|
|
|
|
|
#include <sys/wait.h>
|
|
|
|
|
|
#include <signal.h>
|
|
|
|
|
|
|
|
|
|
|
|
#include <cstring>
|
|
|
|
|
|
#include <cerrno>
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
#include <iostream> // Temporary.
|
|
|
#include <iostream> // Temporary.
|
|
|
#include <stdexcept>
|
|
|
#include <stdexcept>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
readStreambuf(bufSize),
|
|
|
readStreambuf(bufSize),
|
|
|
writeStreambuf(bufSize),
|
|
|
writeStreambuf(bufSize),
|
|
|
reader(&readStreambuf),
|
|
|
reader(&readStreambuf),
|
|
|
writer(&writeStreambuf) {
|
|
|
|
|
|
|
|
|
writer(&writeStreambuf),
|
|
|
|
|
|
cmdArgs(args) {
|
|
|
|
|
|
|
|
|
init();
|
|
|
init();
|
|
|
|
|
|
|
|
|
if (args.size() == 0) {
|
|
|
|
|
|
//
|
|
|
|
|
|
} else if (args.size() == 1) {
|
|
|
|
|
|
cmdline = args[0];
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Append all but last command-line arguments.
|
|
|
|
|
|
for (size_t i = 0; i < args.size() - 1; i++) {
|
|
|
|
|
|
cmdline += args[i] + " ";
|
|
|
|
|
|
}
|
|
|
|
|
|
cmdline += args.back(); // Append last command-line argument.
|
|
|
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
Child::Child(std::string childpath, size_t bufSize) :
|
|
|
Child::Child(std::string childpath, size_t bufSize) :
|
|
|
|
|
|
|
|
|
reader(&readStreambuf),
|
|
|
reader(&readStreambuf),
|
|
|
writer(&writeStreambuf),
|
|
|
writer(&writeStreambuf),
|
|
|
cmdline(childpath) {
|
|
|
cmdline(childpath) {
|
|
|
|
|
|
cmdArgs.push_back(childpath);
|
|
|
init();
|
|
|
init();
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
void
|
|
|
Child::init() {
|
|
|
Child::init() {
|
|
|
|
|
|
|
|
|
|
|
|
if (cmdArgs.empty()) {
|
|
|
|
|
|
throw std::invalid_argument("A child executable must be specified.");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
reader.exceptions(std::istream::failbit | std::istream::badbit);
|
|
|
reader.exceptions(std::istream::failbit | std::istream::badbit);
|
|
|
writer.exceptions(std::ostream::failbit | std::ostream::badbit);
|
|
|
writer.exceptions(std::ostream::failbit | std::ostream::badbit);
|
|
|
childStarted = false;
|
|
|
childStarted = false;
|
|
|
|
|
|
childExited = false;
|
|
|
exitCodeObtainedFlag = false;
|
|
|
exitCodeObtainedFlag = false;
|
|
|
exitCode = 0;
|
|
|
exitCode = 0;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
"run() was called");
|
|
|
"run() was called");
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef WIN32
|
|
|
// Set the bInheritHandle flag so pipe handles are inherited.
|
|
|
// Set the bInheritHandle flag so pipe handles are inherited.
|
|
|
SECURITY_ATTRIBUTES securityAttributes;
|
|
|
SECURITY_ATTRIBUTES securityAttributes;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
startInfo.hStdInput = childStdInAtChild;
|
|
|
startInfo.hStdInput = childStdInAtChild;
|
|
|
startInfo.dwFlags |= STARTF_USESTDHANDLES;
|
|
|
startInfo.dwFlags |= STARTF_USESTDHANDLES;
|
|
|
|
|
|
|
|
|
|
|
|
// Assemble the command line.
|
|
|
|
|
|
std::string cmdline;
|
|
|
|
|
|
|
|
|
|
|
|
if (cmdArgs.size() == 1) {
|
|
|
|
|
|
cmdline = cmdArgs[0];
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
|
|
|
|
// Append all but last command-line arguments.
|
|
|
|
|
|
for (size_t i = 0; i < cmdArgs.size() - 1; i++) {
|
|
|
|
|
|
cmdline += cmdArgs[i] + " ";
|
|
|
|
|
|
}
|
|
|
|
|
|
cmdline += cmdArgs.back(); // Append last command-line argument.
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
// Create the child process.
|
|
|
// Create the child process.
|
|
|
bool status;
|
|
|
bool status;
|
|
|
|
|
|
|
|
|
status = CreateProcess(NULL,
|
|
|
status = CreateProcess(NULL,
|
|
|
(char *) cmdline.c_str(), // command line
|
|
|
|
|
|
NULL, // process security attributes
|
|
|
|
|
|
NULL, // primary thread security attributes
|
|
|
|
|
|
true, // handles are inherited
|
|
|
|
|
|
0, // creation flags
|
|
|
|
|
|
NULL, // use parent's environment
|
|
|
|
|
|
NULL, // use parent's current directory
|
|
|
|
|
|
&startInfo, // STARTUPINFO pointer
|
|
|
|
|
|
&processInfo); // receives PROCESS_INFORMATION
|
|
|
|
|
|
|
|
|
(char *) cmdline.c_str(),
|
|
|
|
|
|
NULL, // process security attributes
|
|
|
|
|
|
NULL, // primary thread security attributes
|
|
|
|
|
|
true, // handles are inherited
|
|
|
|
|
|
0, // creation flags
|
|
|
|
|
|
NULL, // use parent's environment
|
|
|
|
|
|
NULL, // use parent's current directory
|
|
|
|
|
|
&startInfo, // STARTUPINFO pointer
|
|
|
|
|
|
&processInfo); // receives PROCESS_INFORMATION
|
|
|
|
|
|
|
|
|
// If an error occurs, exit the application.
|
|
|
// If an error occurs, exit the application.
|
|
|
if (!status ) {
|
|
|
if (!status ) {
|
|
|
|
|
|
|
|
|
childProcess = processInfo.hProcess;
|
|
|
childProcess = processInfo.hProcess;
|
|
|
childThread = processInfo.hThread;
|
|
|
childThread = processInfo.hThread;
|
|
|
|
|
|
|
|
|
childStarted = true;
|
|
|
|
|
|
|
|
|
|
|
|
// Close the child's end of the pipes.
|
|
|
// Close the child's end of the pipes.
|
|
|
if (!CloseHandle(childStdOutAtChild)) {
|
|
|
if (!CloseHandle(childStdOutAtChild)) {
|
|
|
throw std::runtime_error("Error closing the child process stdout handle: " +
|
|
|
|
|
|
getErrorText());
|
|
|
|
|
|
|
|
|
throw std::runtime_error("Error closing the child process "
|
|
|
|
|
|
"stdout handle: " + getErrorText());
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
if (!CloseHandle(childStdInAtChild)) {
|
|
|
if (!CloseHandle(childStdInAtChild)) {
|
|
|
throw std::runtime_error("Error closing the child process stdin handle: " +
|
|
|
|
|
|
|
|
|
throw std::runtime_error("Error closing the child process "
|
|
|
|
|
|
"stdin handle: " + getErrorText());
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#else
|
|
|
|
|
|
|
|
|
|
|
|
// Create the pipes for the stdin and stdout.
|
|
|
|
|
|
int childStdInPipe[2];
|
|
|
|
|
|
int childStdOutPipe[2];
|
|
|
|
|
|
|
|
|
|
|
|
if (pipe(childStdInPipe) != 0) {
|
|
|
|
|
|
throw std::runtime_error("Error creating pipe for stdin: " +
|
|
|
|
|
|
getErrorText());
|
|
|
|
|
|
}
|
|
|
|
|
|
if (pipe(childStdOutPipe) != 0) {
|
|
|
|
|
|
close(childStdInPipe[0]);
|
|
|
|
|
|
close(childStdInPipe[1]);
|
|
|
|
|
|
throw std::runtime_error("Error creating pipe for stdout: " +
|
|
|
|
|
|
getErrorText());
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Create the child process.
|
|
|
|
|
|
childPid = fork();
|
|
|
|
|
|
if (-1 == childPid) {
|
|
|
|
|
|
for (int i = 0; i < 2; i++) {
|
|
|
|
|
|
close(childStdInPipe[i]);
|
|
|
|
|
|
close(childStdOutPipe[i]);
|
|
|
|
|
|
}
|
|
|
|
|
|
throw std::runtime_error("Error creating child process: " +
|
|
|
getErrorText());
|
|
|
getErrorText());
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (0 == childPid) {
|
|
|
|
|
|
|
|
|
|
|
|
// The child executes this. Redirect stdin.
|
|
|
|
|
|
if (dup2(childStdInPipe[0], STDIN_FILENO) == -1) {
|
|
|
|
|
|
std::string errMsg;
|
|
|
|
|
|
|
|
|
|
|
|
// Send message to parent.
|
|
|
|
|
|
errMsg = "Error redirecting stdin in the child: " + getErrorText();
|
|
|
|
|
|
write(childStdOutPipe[1], errMsg.data(), errMsg.size());
|
|
|
|
|
|
exit(-1);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Redirect stdout.
|
|
|
|
|
|
if (dup2(childStdOutPipe[1], STDOUT_FILENO) == -1) {
|
|
|
|
|
|
std::string errMsg;
|
|
|
|
|
|
|
|
|
|
|
|
// Send message to parent.
|
|
|
|
|
|
errMsg = "Error redirecting stdout in the child: " + getErrorText();
|
|
|
|
|
|
write(childStdOutPipe[1], errMsg.data(), errMsg.size());
|
|
|
|
|
|
exit(-1);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Close pipes.
|
|
|
|
|
|
if ( (close(childStdInPipe[0]) != 0) ||
|
|
|
|
|
|
(close(childStdInPipe[1]) != 0) ||
|
|
|
|
|
|
(close(childStdOutPipe[0]) != 0) ||
|
|
|
|
|
|
(close(childStdOutPipe[1]) != 0) ) {
|
|
|
|
|
|
std::string errMsg;
|
|
|
|
|
|
|
|
|
|
|
|
// Send message to parent.
|
|
|
|
|
|
errMsg = "Error closing the pipes in the child: " + getErrorText();
|
|
|
|
|
|
write(STDOUT_FILENO, errMsg.data(), errMsg.size());
|
|
|
|
|
|
exit(-1);
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Prepare the arguments.
|
|
|
|
|
|
std::vector<const char *> execvArgv;
|
|
|
|
|
|
|
|
|
|
|
|
for (auto &arg : cmdArgs) {
|
|
|
|
|
|
execvArgv.push_back(arg.c_str());
|
|
|
|
|
|
}
|
|
|
|
|
|
execvArgv.push_back((char *) NULL);
|
|
|
|
|
|
|
|
|
|
|
|
// Run the child process image.
|
|
|
|
|
|
(void) execv(execvArgv[0], (char * const *) &(execvArgv[0]));
|
|
|
|
|
|
|
|
|
|
|
|
// Error from exec.
|
|
|
|
|
|
std::string errMsg;
|
|
|
|
|
|
|
|
|
|
|
|
// Send message to parent.
|
|
|
|
|
|
errMsg = "Error from exec: " + getErrorText();
|
|
|
|
|
|
write(STDOUT_FILENO, errMsg.data(), errMsg.size());
|
|
|
|
|
|
exit(-1);
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// std::cout << "Child pid: " << childPid << std::endl; // DEBUG.
|
|
|
|
|
|
|
|
|
|
|
|
// Provide the stream buffers with the file descriptors for
|
|
|
|
|
|
// communicating with the child process.
|
|
|
|
|
|
readStreambuf.setInputFileDescriptor(childStdOutPipe[0]);
|
|
|
|
|
|
writeStreambuf.setOutputFileDescriptor(childStdInPipe[1]);
|
|
|
|
|
|
|
|
|
|
|
|
// Close the child's end of the pipes.
|
|
|
|
|
|
if ( (close(childStdInPipe[0]) != 0) ||
|
|
|
|
|
|
(close(childStdOutPipe[1]) != 0) ) {
|
|
|
|
|
|
std::string errMsg;
|
|
|
|
|
|
|
|
|
|
|
|
throw std::runtime_error("Error closing child's end of pipes in "
|
|
|
|
|
|
"the parent: " + getErrorText());
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
childStarted = true;
|
|
|
|
|
|
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef WIN32
|
|
|
if (!TerminateProcess(childProcess, terminateExitCode)) {
|
|
|
if (!TerminateProcess(childProcess, terminateExitCode)) {
|
|
|
throw std::runtime_error("Error terminating the child process: " +
|
|
|
|
|
|
getErrorText());
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
#else
|
|
|
|
|
|
if (kill(childPid, SIGTERM) != 0) {
|
|
|
|
|
|
#endif
|
|
|
|
|
|
throw std::runtime_error("Error terminating the child process: " +
|
|
|
|
|
|
getErrorText());
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
|
Child::isDone() {
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
|
Child::isDone() {
|
|
|
|
|
|
|
|
|
if (exitCodeObtainedFlag) {
|
|
|
|
|
|
|
|
|
if (childExited) {
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
if (!childStarted) {
|
|
|
if (!childStarted) {
|
|
|
throw std::logic_error("Child process was not started "
|
|
|
|
|
|
"when isDone() was called");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
throw std::logic_error("Child process was not started "
|
|
|
|
|
|
"when isDone() was called");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
int result;
|
|
|
int result;
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef WIN32
|
|
|
if (!GetExitCodeProcess(childProcess, (LPDWORD) &result)) {
|
|
|
if (!GetExitCodeProcess(childProcess, (LPDWORD) &result)) {
|
|
|
throw std::runtime_error("Error checking status of child process: " +
|
|
|
|
|
|
getErrorText());
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
throw std::runtime_error("Error checking status of child process: " +
|
|
|
|
|
|
getErrorText());
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
if (STILL_ACTIVE == result) {
|
|
|
if (STILL_ACTIVE == result) {
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
// Child process has exited. Save the exit code.
|
|
|
// Child process has exited. Save the exit code.
|
|
|
exitCode = result;
|
|
|
exitCode = result;
|
|
|
exitCodeObtainedFlag = true;
|
|
|
exitCodeObtainedFlag = true;
|
|
|
|
|
|
|
|
|
|
|
|
#else
|
|
|
|
|
|
int status = 0;
|
|
|
|
|
|
|
|
|
|
|
|
result = waitpid(childPid, &status, WNOHANG);
|
|
|
|
|
|
// std::cout << "isDone(). waitpid(" << childPid << ",...) returned " << result << std::endl; // DEBUG
|
|
|
|
|
|
if (-1 == result) {
|
|
|
|
|
|
throw std::runtime_error("Error checking status of child process: " +
|
|
|
|
|
|
getErrorText());
|
|
|
|
|
|
} else if (0 == result) {
|
|
|
|
|
|
|
|
|
|
|
|
// Child is still running.
|
|
|
|
|
|
// std::cout << "isDone(). Child is still running..." << std::endl; // DEBUG.
|
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// std::cout << "isDone(). Child exited." << std::endl; // DEBUG.
|
|
|
|
|
|
if (WIFEXITED(status)) {
|
|
|
|
|
|
|
|
|
|
|
|
// Child exited normally.
|
|
|
|
|
|
exitCode = WEXITSTATUS(status);
|
|
|
|
|
|
exitCodeObtainedFlag = true;
|
|
|
|
|
|
//std::cout << "isDone(). Child exited normally. Exit code: " << exitCode << std::endl; // DEBUG.
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
childExited = true;
|
|
|
|
|
|
|
|
|
return true;
|
|
|
return true;
|
|
|
|
|
|
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
int32_t
|
|
|
|
|
|
Child::result() {
|
|
|
|
|
|
|
|
|
int32_t
|
|
|
|
|
|
Child::result() {
|
|
|
|
|
|
|
|
|
if (exitCodeObtainedFlag) {
|
|
|
if (exitCodeObtainedFlag) {
|
|
|
|
|
|
|
|
|
return exitCode;
|
|
|
|
|
|
|
|
|
return exitCode;
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
if (!childStarted) {
|
|
|
|
|
|
throw std::logic_error("Child process was not started "
|
|
|
|
|
|
"when result() was called");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
// Check whether the process is running, and get the exit code.
|
|
|
|
|
|
if (!isDone()) {
|
|
|
|
|
|
throw std::logic_error("Child process was still running"
|
|
|
|
|
|
"when result() was called");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
int result;
|
|
|
|
|
|
|
|
|
// Child process has exited.
|
|
|
|
|
|
if (!exitCodeObtainedFlag) {
|
|
|
|
|
|
|
|
|
if (!GetExitCodeProcess(childProcess, (LPDWORD) &result)) {
|
|
|
|
|
|
throw std::runtime_error("Error getting child process exit code: " +
|
|
|
|
|
|
getErrorText());
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
// Exit code is not available.
|
|
|
|
|
|
throw std::runtime_error("Child process has exited but the exit "
|
|
|
|
|
|
"code is not available");
|
|
|
|
|
|
|
|
|
// Error if the process has not exited.
|
|
|
|
|
|
if (STILL_ACTIVE == result) {
|
|
|
|
|
|
throw std::logic_error("Child process was active when "
|
|
|
|
|
|
"result() was called");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
// Child process has exited. Save the exit code.
|
|
|
|
|
|
exitCode = result;
|
|
|
|
|
|
exitCodeObtainedFlag = true;
|
|
|
|
|
|
|
|
|
return exitCode;
|
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
std::string
|
|
|
|
|
|
Child::getErrorText() {
|
|
|
|
|
|
|
|
|
std::string
|
|
|
|
|
|
Child::getErrorText() {
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef WIN32
|
|
|
LPVOID winMsgBuf;
|
|
|
LPVOID winMsgBuf;
|
|
|
DWORD lastError = GetLastError();
|
|
|
DWORD lastError = GetLastError();
|
|
|
|
|
|
|
|
|
FormatMessage(
|
|
|
FormatMessage(
|
|
|
FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
|
|
|
|
|
FORMAT_MESSAGE_FROM_SYSTEM |
|
|
|
|
|
|
FORMAT_MESSAGE_IGNORE_INSERTS,
|
|
|
|
|
|
NULL,
|
|
|
|
|
|
lastError,
|
|
|
|
|
|
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
|
|
|
|
|
(char *) &winMsgBuf,
|
|
|
|
|
|
0, NULL );
|
|
|
|
|
|
|
|
|
FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
|
|
|
|
|
FORMAT_MESSAGE_FROM_SYSTEM |
|
|
|
|
|
|
FORMAT_MESSAGE_IGNORE_INSERTS,
|
|
|
|
|
|
NULL,
|
|
|
|
|
|
lastError,
|
|
|
|
|
|
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
|
|
|
|
|
(char *) &winMsgBuf,
|
|
|
|
|
|
0, NULL );
|
|
|
|
|
|
|
|
|
std::string errMsg((char *) winMsgBuf);
|
|
|
std::string errMsg((char *) winMsgBuf);
|
|
|
|
|
|
|
|
|
LocalFree(winMsgBuf);
|
|
|
LocalFree(winMsgBuf);
|
|
|
return errMsg;
|
|
|
return errMsg;
|
|
|
|
|
|
#else
|
|
|
|
|
|
return strerror(errno);
|
|
|
|
|
|
#endif
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
Child::ReadStreambuf::ReadStreambuf(std::size_t bufferSize) :
|
|
|
|
|
|
inputHandle(0),
|
|
|
|
|
|
buffer(bufferSize + 1) {
|
|
|
|
|
|
|
|
|
Child::ReadStreambuf::ReadStreambuf(std::size_t bufferSize) :
|
|
|
|
|
|
#ifdef WIN32
|
|
|
|
|
|
inputHandle(0),
|
|
|
|
|
|
#else
|
|
|
|
|
|
inputFileDescriptor(-1),
|
|
|
|
|
|
#endif
|
|
|
|
|
|
buffer(bufferSize + 1) {
|
|
|
|
|
|
|
|
|
char *end = &(buffer.front()) + buffer.size();
|
|
|
char *end = &(buffer.front()) + buffer.size();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
|
Child::ReadStreambuf::setInputHandle(HANDLE inHandle) {
|
|
|
|
|
|
|
|
|
#ifdef WIN32
|
|
|
|
|
|
void
|
|
|
|
|
|
Child::ReadStreambuf::setInputHandle(HANDLE inHandle) {
|
|
|
|
|
|
|
|
|
inputHandle = inHandle;
|
|
|
inputHandle = inHandle;
|
|
|
|
|
|
|
|
|
}
|
|
|
}
|
|
|
|
|
|
#else
|
|
|
|
|
|
void
|
|
|
|
|
|
Child::ReadStreambuf::setInputFileDescriptor(int inFd) {
|
|
|
|
|
|
|
|
|
|
|
|
inputFileDescriptor = inFd;
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
std::streambuf::int_type
|
|
|
|
|
|
Child::ReadStreambuf::underflow() {
|
|
|
|
|
|
|
|
|
std::streambuf::int_type
|
|
|
|
|
|
Child::ReadStreambuf::underflow() {
|
|
|
|
|
|
|
|
|
// Check for empty buffer.
|
|
|
// Check for empty buffer.
|
|
|
if (gptr() < egptr()) {
|
|
|
if (gptr() < egptr()) {
|
|
|
|
|
|
|
|
|
// Not empty.
|
|
|
|
|
|
return traits_type::to_int_type(*gptr());
|
|
|
|
|
|
|
|
|
// Not empty.
|
|
|
|
|
|
return traits_type::to_int_type(*gptr());
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
// Need to fill the buffer.
|
|
|
// Need to fill the buffer.
|
|
|
char *base = &(buffer.front());
|
|
|
char *base = &(buffer.front());
|
|
|
|
|
|
|
|
|
// Check whether this is the first fill.
|
|
|
// Check whether this is the first fill.
|
|
|
if (eback() == base) {
|
|
|
if (eback() == base) {
|
|
|
|
|
|
|
|
|
// Not the first fill. Copy one putback character.
|
|
|
|
|
|
*(eback()) = *(egptr() - 1);
|
|
|
|
|
|
start++;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
// Not the first fill. Copy one putback character.
|
|
|
|
|
|
*(eback()) = *(egptr() - 1);
|
|
|
|
|
|
start++;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
// start points to the start of the buffer. Fill buffer.
|
|
|
// start points to the start of the buffer. Fill buffer.
|
|
|
|
|
|
#ifdef WIN32
|
|
|
DWORD nBytesRead;
|
|
|
DWORD nBytesRead;
|
|
|
|
|
|
|
|
|
if (!ReadFile(inputHandle,
|
|
|
if (!ReadFile(inputHandle,
|
|
|
start,
|
|
|
|
|
|
buffer.size() - (start - base),
|
|
|
|
|
|
&nBytesRead,
|
|
|
|
|
|
NULL)) {
|
|
|
|
|
|
return traits_type::eof();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
start,
|
|
|
|
|
|
buffer.size() - (start - base),
|
|
|
|
|
|
&nBytesRead,
|
|
|
|
|
|
NULL)) {
|
|
|
|
|
|
return traits_type::eof();
|
|
|
|
|
|
}
|
|
|
|
|
|
#else
|
|
|
|
|
|
size_t nBytesRead;
|
|
|
|
|
|
|
|
|
|
|
|
nBytesRead = read(inputFileDescriptor,
|
|
|
|
|
|
start,
|
|
|
|
|
|
buffer.size() - (start - base));
|
|
|
|
|
|
if (-1 == nBytesRead) {
|
|
|
|
|
|
return traits_type::eof();
|
|
|
|
|
|
}
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
// Check for EOF.
|
|
|
// Check for EOF.
|
|
|
if (0 == nBytesRead) {
|
|
|
if (0 == nBytesRead) {
|
|
|
return traits_type::eof();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
return traits_type::eof();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
// Update buffer pointers.
|
|
|
// Update buffer pointers.
|
|
|
setg(base, start, start + nBytesRead);
|
|
|
setg(base, start, start + nBytesRead);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
Child::WriteStreambuf::WriteStreambuf(std::size_t bufferSize) :
|
|
|
|
|
|
outputHandle(0),
|
|
|
|
|
|
buffer(bufferSize + 1) {
|
|
|
|
|
|
|
|
|
Child::WriteStreambuf::WriteStreambuf(std::size_t bufferSize) :
|
|
|
|
|
|
#ifdef WIN32
|
|
|
|
|
|
outputHandle(0),
|
|
|
|
|
|
#else
|
|
|
|
|
|
outputFileDescriptor(-1),
|
|
|
|
|
|
#endif
|
|
|
|
|
|
buffer(bufferSize + 1) {
|
|
|
|
|
|
|
|
|
char *base = &(buffer.front());
|
|
|
char *base = &(buffer.front());
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
|
Child::WriteStreambuf::setOutputHandle(HANDLE outHandle) {
|
|
|
|
|
|
|
|
|
#ifdef WIN32
|
|
|
|
|
|
void
|
|
|
|
|
|
Child::WriteStreambuf::setOutputHandle(HANDLE outHandle) {
|
|
|
|
|
|
|
|
|
outputHandle = outHandle;
|
|
|
outputHandle = outHandle;
|
|
|
|
|
|
|
|
|
}
|
|
|
}
|
|
|
|
|
|
#else
|
|
|
|
|
|
void
|
|
|
|
|
|
Child::WriteStreambuf::setOutputFileDescriptor(int outFd) {
|
|
|
|
|
|
|
|
|
|
|
|
outputFileDescriptor = outFd;
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
|
Child::WriteStreambuf::flushBuffer() {
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
|
Child::WriteStreambuf::flushBuffer() {
|
|
|
|
|
|
|
|
|
// Write.
|
|
|
// Write.
|
|
|
std::ptrdiff_t nBytes = pptr() - pbase();
|
|
|
std::ptrdiff_t nBytes = pptr() - pbase();
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef WIN32
|
|
|
DWORD nBytesWritten;
|
|
|
DWORD nBytesWritten;
|
|
|
|
|
|
|
|
|
if (!WriteFile(outputHandle,
|
|
|
if (!WriteFile(outputHandle,
|
|
|
pbase(),
|
|
|
|
|
|
nBytes,
|
|
|
|
|
|
&nBytesWritten,
|
|
|
|
|
|
NULL)) {
|
|
|
|
|
|
pbump(epptr() - pptr()); // Indicate failure.
|
|
|
|
|
|
throw std::runtime_error("Error writing to child process: " +
|
|
|
|
|
|
getErrorText());
|
|
|
|
|
|
}
|
|
|
|
|
|
if (nBytes != nBytesWritten) {
|
|
|
|
|
|
pbump(epptr() - pptr()); // Indicate failure.
|
|
|
|
|
|
throw std::runtime_error("Not all data was written to to child process: " +
|
|
|
|
|
|
getErrorText());
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
pbase(),
|
|
|
|
|
|
nBytes,
|
|
|
|
|
|
&nBytesWritten,
|
|
|
|
|
|
NULL)) {
|
|
|
|
|
|
// Clear the output buffer.
|
|
|
|
|
|
pbump(-nBytes);
|
|
|
|
|
|
throw std::runtime_error("Error writing to child process: " +
|
|
|
|
|
|
getErrorText());
|
|
|
|
|
|
}
|
|
|
|
|
|
#else
|
|
|
|
|
|
size_t nBytesWritten;
|
|
|
|
|
|
|
|
|
|
|
|
nBytesWritten = write(outputFileDescriptor, pbase(), nBytes);
|
|
|
|
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
// Clear the output buffer.
|
|
|
pbump(-nBytes);
|
|
|
pbump(-nBytes);
|
|
|
|
|
|
|
|
|
|
|
|
if (nBytes != nBytesWritten) {
|
|
|
|
|
|
throw std::runtime_error("Not all data was written to to child "
|
|
|
|
|
|
"process: " + getErrorText());
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
return;
|
|
|
return;
|
|
|
|
|
|
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
std::streambuf::int_type
|
|
|
|
|
|
Child::WriteStreambuf::overflow(int_type ch) {
|
|
|
|
|
|
|
|
|
std::streambuf::int_type
|
|
|
|
|
|
Child::WriteStreambuf::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) {
|
|
|
|
|
|
|
|
|
// Not writing EOF.
|
|
|
|
|
|
*(pptr()) = ch;
|
|
|
|
|
|
pbump(1);
|
|
|
|
|
|
|
|
|
// Not writing EOF.
|
|
|
|
|
|
*(pptr()) = ch;
|
|
|
|
|
|
pbump(1);
|
|
|
|
|
|
|
|
|
// Write.
|
|
|
|
|
|
flushBuffer();
|
|
|
|
|
|
|
|
|
// Write.
|
|
|
|
|
|
flushBuffer();
|
|
|
|
|
|
|
|
|
// Success.
|
|
|
|
|
|
return ch;
|
|
|
|
|
|
|
|
|
// Success.
|
|
|
|
|
|
return ch;
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
return traits_type::eof();
|
|
|
return traits_type::eof();
|
|
|
|
|
|
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
|
Child::WriteStreambuf::sync() {
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
|
Child::WriteStreambuf::sync() {
|
|
|
|
|
|
|
|
|
flushBuffer(); // Throws exception on failure.
|
|
|
flushBuffer(); // Throws exception on failure.
|
|
|
|
|
|
|