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.

child.cpp 11KB

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