// child.cpp // Copyright (C) 2014 MicroNeil Research Corporation. // // This program is part of the MicroNeil Research Open Library Project. For // more information go to http://www.microneil.com/OpenLibrary/index.html // // This program is free software; you can redistribute it and/or modify it // under the terms of the GNU General Public License as published by the // Free Software Foundation; either version 2 of the License, or (at your // option) any later version. // // This program is distributed in the hope that it will be useful, but WITHOUT // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or // FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for // more details. // // You should have received a copy of the GNU General Public License along with // this program; if not, write to the Free Software Foundation, Inc., 59 Temple // Place, Suite 330, Boston, MA 02111-1307 USA //============================================================================== #include // Temporary. #include #include "child.hpp" namespace CodeDweller { Child::Child(std::vector args) { 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) : cmdline(childpath) { init(); } Child::~Child() { } void Child::init() { exitCodeObtainedFlag = false; exitCode = 0; } std::istream * Child::reader() { return new std::istream(std::cin.rdbuf()); } std::ostream * Child::writer() { return new std::ostream(std::cout.rdbuf()); } void Child::run() { // Set the bInheritHandle flag so pipe handles are inherited. SECURITY_ATTRIBUTES securityAttributes; securityAttributes.nLength = sizeof(SECURITY_ATTRIBUTES); securityAttributes.bInheritHandle = true; securityAttributes.lpSecurityDescriptor = NULL; // Create a pipe for the child process's STDOUT. HANDLE childStdOutAtChild; HANDLE childStdInAtChild; int bufferSize = 0; if (!CreatePipe(&childStdOutAtParent, &childStdOutAtChild, &securityAttributes, bufferSize)) { throw std::runtime_error("Error from CreatePipe for stdout: " + getErrorText()); } // Ensure the read handle to the pipe for STDOUT is not inherited. int inheritFlag = 0; if (!SetHandleInformation(childStdOutAtParent, HANDLE_FLAG_INHERIT, inheritFlag) ) { throw std::runtime_error("Error from GetHandleInformation for stdout: " + getErrorText()); } // Create a pipe for the child process's STDIN. if (! CreatePipe(&childStdInAtChild, &childStdInAtParent, &securityAttributes, bufferSize)) { throw std::runtime_error("Error from CreatePipe for stdin: " + getErrorText()); } // Ensure the write handle to the pipe for STDIN is not inherited. if (!SetHandleInformation(childStdInAtParent, HANDLE_FLAG_INHERIT, inheritFlag)) { throw std::runtime_error("Error from GetHandleInformation for stdin: " + getErrorText()); } // Set up members of the PROCESS_INFORMATION structure. PROCESS_INFORMATION processInfo; std::fill((char *) &processInfo, ((char *) &processInfo) + sizeof(PROCESS_INFORMATION), 0); // Set up members of the STARTUPINFO structure. This structure // specifies the STDIN and STDOUT handles for redirection. STARTUPINFO startInfo; std::fill((char *) &startInfo, ((char *) &startInfo) + sizeof(STARTUPINFO), 0); startInfo.cb = sizeof(STARTUPINFO); startInfo.hStdError = childStdOutAtChild; startInfo.hStdOutput = childStdOutAtChild; startInfo.hStdInput = childStdInAtChild; startInfo.dwFlags |= STARTF_USESTDHANDLES; // Create the child process. bool createSuccess; createSuccess = 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 // If an error occurs, exit the application. if (!createSuccess ) { throw std::runtime_error("Error from CreateProcess with " "command line \"" + cmdline + "\": " + getErrorText()); } // Save the handles to the child process and its primary thread. childProcess = processInfo.hProcess; childThread = processInfo.hThread; // Close the child's end of the pipes. CloseHandle(childStdOutAtChild); CloseHandle(childStdInAtChild); } void Child::terminate() { if (!TerminateProcess(childProcess, terminateExitCode)) { throw std::runtime_error("Error terminating the child process: " + getErrorText()); } } int32_t Child::result() { int result; if (!GetExitCodeProcess(childProcess, (LPDWORD) &result)) { throw std::runtime_error("Error getting child process exit code: " + getErrorText()); } return result; } std::string Child::getErrorText() { LPVOID winMsgBuf; DWORD lastError = GetLastError(); FormatMessage( 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); LocalFree(winMsgBuf); return errMsg; } }