child.cpp 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433
  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, size_t bufSize) :
  26. readStreambuf(bufSize),
  27. writeStreambuf(bufSize),
  28. reader(&readStreambuf),
  29. writer(&writeStreambuf) {
  30. init();
  31. if (args.size() == 0) {
  32. //
  33. } else if (args.size() == 1) {
  34. cmdline = args[0];
  35. return;
  36. }
  37. // Append all but last command-line arguments.
  38. for (size_t i = 0; i < args.size() - 1; i++) {
  39. cmdline += args[i] + " ";
  40. }
  41. cmdline += args.back(); // Append last command-line argument.
  42. }
  43. Child::Child(std::string childpath, size_t bufSize) :
  44. readStreambuf(bufSize),
  45. writeStreambuf(bufSize),
  46. reader(&readStreambuf),
  47. writer(&writeStreambuf),
  48. cmdline(childpath) {
  49. init();
  50. }
  51. Child::~Child() {
  52. // Close handles.
  53. }
  54. void
  55. Child::init() {
  56. childStarted = false;
  57. exitCodeObtainedFlag = false;
  58. exitCode = 0;
  59. }
  60. void
  61. Child::run() {
  62. if (childStarted) {
  63. throw std::logic_error("Child process was active when "
  64. "run() was called");
  65. }
  66. // Set the bInheritHandle flag so pipe handles are inherited.
  67. SECURITY_ATTRIBUTES securityAttributes;
  68. securityAttributes.nLength = sizeof(SECURITY_ATTRIBUTES);
  69. securityAttributes.bInheritHandle = true;
  70. securityAttributes.lpSecurityDescriptor = NULL;
  71. // Create a pipe for the child process's STDOUT.
  72. HANDLE childStdOutAtChild;
  73. HANDLE childStdOutAtParent;
  74. HANDLE childStdInAtChild;
  75. HANDLE childStdInAtParent;
  76. int bufferSize = 0;
  77. if (!CreatePipe(&childStdOutAtParent,
  78. &childStdOutAtChild,
  79. &securityAttributes,
  80. bufferSize)) {
  81. throw std::runtime_error("Error from CreatePipe for stdout: " +
  82. getErrorText());
  83. }
  84. // Ensure the read handle to the pipe for STDOUT is not inherited.
  85. int inheritFlag = 0;
  86. if (!SetHandleInformation(childStdOutAtParent,
  87. HANDLE_FLAG_INHERIT,
  88. inheritFlag) ) {
  89. throw std::runtime_error("Error from GetHandleInformation for stdout: " +
  90. getErrorText());
  91. }
  92. // Create a pipe for the child process's STDIN.
  93. if (! CreatePipe(&childStdInAtChild,
  94. &childStdInAtParent,
  95. &securityAttributes,
  96. bufferSize)) {
  97. throw std::runtime_error("Error from CreatePipe for stdin: " +
  98. getErrorText());
  99. }
  100. // Ensure the write handle to the pipe for STDIN is not inherited.
  101. if (!SetHandleInformation(childStdInAtParent,
  102. HANDLE_FLAG_INHERIT,
  103. inheritFlag)) {
  104. throw std::runtime_error("Error from GetHandleInformation for stdin: " +
  105. getErrorText());
  106. }
  107. // Set up members of the PROCESS_INFORMATION structure.
  108. PROCESS_INFORMATION processInfo;
  109. std::fill((char *) &processInfo,
  110. ((char *) &processInfo) + sizeof(PROCESS_INFORMATION),
  111. 0);
  112. // Set up members of the STARTUPINFO structure. This structure
  113. // specifies the STDIN and STDOUT handles for redirection.
  114. STARTUPINFO startInfo;
  115. std::fill((char *) &startInfo,
  116. ((char *) &startInfo) + sizeof(STARTUPINFO),
  117. 0);
  118. startInfo.cb = sizeof(STARTUPINFO);
  119. startInfo.hStdError = childStdOutAtChild;
  120. startInfo.hStdOutput = childStdOutAtChild;
  121. startInfo.hStdInput = childStdInAtChild;
  122. startInfo.dwFlags |= STARTF_USESTDHANDLES;
  123. // Create the child process.
  124. bool status;
  125. status = CreateProcess(NULL,
  126. (char *) cmdline.c_str(), // command line
  127. NULL, // process security attributes
  128. NULL, // primary thread security attributes
  129. true, // handles are inherited
  130. 0, // creation flags
  131. NULL, // use parent's environment
  132. NULL, // use parent's current directory
  133. &startInfo, // STARTUPINFO pointer
  134. &processInfo); // receives PROCESS_INFORMATION
  135. // If an error occurs, exit the application.
  136. if (!status ) {
  137. throw std::runtime_error("Error from CreateProcess with "
  138. "command line \"" + cmdline + "\": " +
  139. getErrorText());
  140. }
  141. // Provide the stream buffers with the handles for communicating
  142. // with the child process.
  143. readStreambuf.setInputHandle(childStdOutAtParent);
  144. writeStreambuf.setOutputHandle(childStdInAtParent);
  145. // Save the handles to the child process and its primary thread.
  146. childProcess = processInfo.hProcess;
  147. childThread = processInfo.hThread;
  148. childStarted = true;
  149. // Close the child's end of the pipes.
  150. if (!CloseHandle(childStdOutAtChild)) {
  151. throw std::runtime_error("Error closing the child process stdout handle: " +
  152. getErrorText());
  153. }
  154. if (!CloseHandle(childStdInAtChild)) {
  155. throw std::runtime_error("Error closing the child process stdin handle: " +
  156. getErrorText());
  157. }
  158. }
  159. void
  160. Child::terminate() {
  161. if (isDone()) {
  162. return;
  163. }
  164. if (!TerminateProcess(childProcess, terminateExitCode)) {
  165. throw std::runtime_error("Error terminating the child process: " +
  166. getErrorText());
  167. }
  168. }
  169. bool
  170. Child::isDone() {
  171. if (exitCodeObtainedFlag) {
  172. return true;
  173. }
  174. if (!childStarted) {
  175. throw std::logic_error("Child process was not started "
  176. "when isDone() was called");
  177. }
  178. int result;
  179. if (!GetExitCodeProcess(childProcess, (LPDWORD) &result)) {
  180. throw std::runtime_error("Error checking status of child process: " +
  181. getErrorText());
  182. }
  183. if (STILL_ACTIVE == result) {
  184. return false;
  185. }
  186. // Child process has exited. Save the exit code.
  187. exitCode = result;
  188. exitCodeObtainedFlag = true;
  189. return true;
  190. }
  191. int32_t
  192. Child::result() {
  193. if (exitCodeObtainedFlag) {
  194. return exitCode;
  195. }
  196. if (!childStarted) {
  197. throw std::logic_error("Child process was not started "
  198. "when result() was called");
  199. }
  200. int result;
  201. if (!GetExitCodeProcess(childProcess, (LPDWORD) &result)) {
  202. throw std::runtime_error("Error getting child process exit code: " +
  203. getErrorText());
  204. }
  205. // Error if the process has not exited.
  206. if (STILL_ACTIVE == result) {
  207. throw std::logic_error("Child process was active when "
  208. "result() was called");
  209. }
  210. // Child process has exited. Save the exit code.
  211. exitCode = result;
  212. exitCodeObtainedFlag = true;
  213. return result;
  214. }
  215. std::string
  216. Child::getErrorText() {
  217. LPVOID winMsgBuf;
  218. DWORD lastError = GetLastError();
  219. FormatMessage(
  220. FORMAT_MESSAGE_ALLOCATE_BUFFER |
  221. FORMAT_MESSAGE_FROM_SYSTEM |
  222. FORMAT_MESSAGE_IGNORE_INSERTS,
  223. NULL,
  224. lastError,
  225. MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
  226. (char *) &winMsgBuf,
  227. 0, NULL );
  228. std::string errMsg((char *) winMsgBuf);
  229. LocalFree(winMsgBuf);
  230. return errMsg;
  231. }
  232. Child::ReadStreambuf::ReadStreambuf(std::size_t bufferSize) :
  233. inputHandle(0),
  234. buffer(bufferSize + 1) {
  235. char *end = &(buffer.front()) + buffer.size();
  236. // Indicate to underflow that underflow has not been called.
  237. setg(end, end, end);
  238. }
  239. void
  240. Child::ReadStreambuf::setInputHandle(HANDLE inHandle) {
  241. inputHandle = inHandle;
  242. }
  243. std::streambuf::int_type
  244. Child::ReadStreambuf::underflow() {
  245. // Check for empty buffer.
  246. if (gptr() < egptr()) {
  247. // Not empty.
  248. return traits_type::to_int_type(*gptr());
  249. }
  250. // Need to fill the buffer.
  251. char *base = &(buffer.front());
  252. char *start = base;
  253. // Check whether this is the first fill.
  254. if (eback() == base) {
  255. // Not the first fill. Copy putback characters.
  256. }
  257. // start points to the start of the buffer. Fill buffer.
  258. DWORD nBytesRead;
  259. if (!ReadFile(inputHandle,
  260. start,
  261. buffer.size() - (start - base),
  262. &nBytesRead,
  263. NULL)) {
  264. return traits_type::eof();
  265. }
  266. // Check for EOF.
  267. if (0 == nBytesRead) {
  268. return traits_type::eof();
  269. }
  270. // Update buffer pointers.
  271. setg(base, start, start + nBytesRead);
  272. return traits_type::to_int_type(*gptr());
  273. }
  274. Child::WriteStreambuf::WriteStreambuf(std::size_t bufferSize) :
  275. outputHandle(0),
  276. buffer(bufferSize + 1) {
  277. char *base = &(buffer.front());
  278. // Indicate to overflow that overflow has not been called.
  279. setp(base, base + buffer.size() - 1);
  280. }
  281. void
  282. Child::WriteStreambuf::setOutputHandle(HANDLE outHandle) {
  283. outputHandle = outHandle;
  284. }
  285. void
  286. Child::WriteStreambuf::flushBuffer() {
  287. // Write.
  288. std::ptrdiff_t nBytes = pptr() - pbase();
  289. DWORD nBytesWritten;
  290. if (!WriteFile(outputHandle,
  291. pbase(),
  292. nBytes,
  293. &nBytesWritten,
  294. NULL)) {
  295. throw std::runtime_error("Error writing to child process: " +
  296. getErrorText());
  297. }
  298. if (nBytes != nBytesWritten) {
  299. throw std::runtime_error("Not all data was written to to child process: " +
  300. getErrorText());
  301. }
  302. pbump(-nBytes);
  303. return;
  304. }
  305. std::streambuf::int_type
  306. Child::WriteStreambuf::overflow(int_type ch) {
  307. // Check whether we're writing EOF.
  308. if (traits_type::eof() != ch) {
  309. // Not writing EOF.
  310. *(pptr()) = ch;
  311. pbump(1);
  312. }
  313. // Write.
  314. flushBuffer();
  315. // Success.
  316. return traits_type::not_eof('a');
  317. }
  318. int
  319. Child::WriteStreambuf::sync() {
  320. flushBuffer();
  321. return 1; // Success.
  322. }
  323. }