You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217
  1. // child.cpp
  2. // Copyright (C) 2014 MicroNeil Research Corporation.
  3. //
  4. // This program is part of the MicroNeil Research Open Library Project. For
  5. // more information go to http://www.microneil.com/OpenLibrary/index.html
  6. //
  7. // This program is free software; you can redistribute it and/or modify it
  8. // under the terms of the GNU General Public License as published by the
  9. // Free Software Foundation; either version 2 of the License, or (at your
  10. // option) any later version.
  11. //
  12. // This program is distributed in the hope that it will be useful, but WITHOUT
  13. // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  14. // FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
  15. // more details.
  16. //
  17. // You should have received a copy of the GNU General Public License along with
  18. // this program; if not, write to the Free Software Foundation, Inc., 59 Temple
  19. // Place, Suite 330, Boston, MA 02111-1307 USA
  20. //==============================================================================
  21. #include <iostream> // Temporary.
  22. #include <stdexcept>
  23. #include "child.hpp"
  24. namespace CodeDweller {
  25. Child::Child(std::vector<std::string> args) {
  26. init();
  27. if (args.size() == 0) {
  28. //
  29. } else if (args.size() == 1) {
  30. cmdline = args[0];
  31. return;
  32. }
  33. // Append all but last command-line arguments.
  34. for (size_t i = 0; i < args.size() - 1; i++) {
  35. cmdline += args[i] + " ";
  36. }
  37. cmdline += args.back(); // Append last command-line argument.
  38. }
  39. Child::Child(std::string childpath) :
  40. cmdline(childpath) {
  41. init();
  42. }
  43. Child::~Child() {
  44. }
  45. void
  46. Child::init() {
  47. exitCodeObtainedFlag = false;
  48. exitCode = 0;
  49. }
  50. std::istream *
  51. Child::reader() {
  52. return new std::istream(std::cin.rdbuf());
  53. }
  54. std::ostream *
  55. Child::writer() {
  56. return new std::ostream(std::cout.rdbuf());
  57. }
  58. void
  59. Child::run() {
  60. // Set the bInheritHandle flag so pipe handles are inherited.
  61. SECURITY_ATTRIBUTES securityAttributes;
  62. securityAttributes.nLength = sizeof(SECURITY_ATTRIBUTES);
  63. securityAttributes.bInheritHandle = true;
  64. securityAttributes.lpSecurityDescriptor = NULL;
  65. // Create a pipe for the child process's STDOUT.
  66. HANDLE childStdOutAtChild;
  67. HANDLE childStdInAtChild;
  68. int bufferSize = 0;
  69. if (!CreatePipe(&childStdOutAtParent,
  70. &childStdOutAtChild,
  71. &securityAttributes,
  72. bufferSize)) {
  73. throw std::runtime_error("Error from CreatePipe for stdout: " +
  74. getErrorText());
  75. }
  76. // Ensure the read handle to the pipe for STDOUT is not inherited.
  77. int inheritFlag = 0;
  78. if (!SetHandleInformation(childStdOutAtParent,
  79. HANDLE_FLAG_INHERIT,
  80. inheritFlag) ) {
  81. throw std::runtime_error("Error from GetHandleInformation for stdout: " +
  82. getErrorText());
  83. }
  84. // Create a pipe for the child process's STDIN.
  85. if (! CreatePipe(&childStdInAtChild,
  86. &childStdInAtParent,
  87. &securityAttributes,
  88. bufferSize)) {
  89. throw std::runtime_error("Error from CreatePipe for stdin: " +
  90. getErrorText());
  91. }
  92. // Ensure the write handle to the pipe for STDIN is not inherited.
  93. if (!SetHandleInformation(childStdInAtParent,
  94. HANDLE_FLAG_INHERIT,
  95. inheritFlag)) {
  96. throw std::runtime_error("Error from GetHandleInformation for stdin: " +
  97. getErrorText());
  98. }
  99. // Set up members of the PROCESS_INFORMATION structure.
  100. PROCESS_INFORMATION processInfo;
  101. std::fill((char *) &processInfo,
  102. ((char *) &processInfo) + sizeof(PROCESS_INFORMATION),
  103. 0);
  104. // Set up members of the STARTUPINFO structure. This structure
  105. // specifies the STDIN and STDOUT handles for redirection.
  106. STARTUPINFO startInfo;
  107. std::fill((char *) &startInfo,
  108. ((char *) &startInfo) + sizeof(STARTUPINFO),
  109. 0);
  110. startInfo.cb = sizeof(STARTUPINFO);
  111. startInfo.hStdError = childStdOutAtChild;
  112. startInfo.hStdOutput = childStdOutAtChild;
  113. startInfo.hStdInput = childStdInAtChild;
  114. startInfo.dwFlags |= STARTF_USESTDHANDLES;
  115. // Create the child process.
  116. bool createSuccess;
  117. createSuccess = CreateProcess(NULL,
  118. (char *) cmdline.c_str(), // command line
  119. NULL, // process security attributes
  120. NULL, // primary thread security attributes
  121. true, // handles are inherited
  122. 0, // creation flags
  123. NULL, // use parent's environment
  124. NULL, // use parent's current directory
  125. &startInfo, // STARTUPINFO pointer
  126. &processInfo); // receives PROCESS_INFORMATION
  127. // If an error occurs, exit the application.
  128. if (!createSuccess ) {
  129. throw std::runtime_error("Error from CreateProcess with "
  130. "command line \"" + cmdline + "\": " +
  131. getErrorText());
  132. }
  133. // Save the handles to the child process and its primary thread.
  134. childProcess = processInfo.hProcess;
  135. childThread = processInfo.hThread;
  136. // Close the child's end of the pipes.
  137. CloseHandle(childStdOutAtChild);
  138. CloseHandle(childStdInAtChild);
  139. }
  140. void
  141. Child::terminate() {
  142. if (!TerminateProcess(childProcess, terminateExitCode)) {
  143. throw std::runtime_error("Error terminating the child process: " +
  144. getErrorText());
  145. }
  146. }
  147. int32_t
  148. Child::result() {
  149. int result;
  150. if (!GetExitCodeProcess(childProcess, (LPDWORD) &result)) {
  151. throw std::runtime_error("Error getting child process exit code: " +
  152. getErrorText());
  153. }
  154. return result;
  155. }
  156. std::string
  157. Child::getErrorText() {
  158. LPVOID winMsgBuf;
  159. DWORD lastError = GetLastError();
  160. FormatMessage(
  161. FORMAT_MESSAGE_ALLOCATE_BUFFER |
  162. FORMAT_MESSAGE_FROM_SYSTEM |
  163. FORMAT_MESSAGE_IGNORE_INSERTS,
  164. NULL,
  165. lastError,
  166. MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
  167. (char *) &winMsgBuf,
  168. 0, NULL );
  169. std::string errMsg((char *) winMsgBuf);
  170. LocalFree(winMsgBuf);
  171. return errMsg;
  172. }
  173. }