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 16KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647
  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. #ifndef WIN32
  22. #include <unistd.h>
  23. #include <sys/types.h>
  24. #include <sys/wait.h>
  25. #include <signal.h>
  26. #include <cstring>
  27. #include <cerrno>
  28. #endif
  29. #include <iostream> // Temporary.
  30. #include <stdexcept>
  31. #include "child.hpp"
  32. namespace CodeDweller {
  33. Child::Child(std::vector<std::string> args, size_t bufSize) :
  34. readStreambuf(bufSize),
  35. writeStreambuf(bufSize),
  36. reader(&readStreambuf),
  37. writer(&writeStreambuf),
  38. cmdArgs(args) {
  39. init();
  40. }
  41. Child::Child(std::string childpath, size_t bufSize) :
  42. readStreambuf(bufSize),
  43. writeStreambuf(bufSize),
  44. reader(&readStreambuf),
  45. writer(&writeStreambuf),
  46. cmdline(childpath) {
  47. cmdArgs.push_back(childpath);
  48. init();
  49. }
  50. Child::~Child() {
  51. // Close handles.
  52. }
  53. void
  54. Child::init() {
  55. if (cmdArgs.empty()) {
  56. throw std::invalid_argument("A child executable must be specified.");
  57. }
  58. reader.exceptions(std::istream::failbit | std::istream::badbit);
  59. writer.exceptions(std::ostream::failbit | std::ostream::badbit);
  60. childStarted = false;
  61. childExited = false;
  62. exitCodeObtainedFlag = false;
  63. exitCode = 0;
  64. }
  65. void
  66. Child::run() {
  67. if (childStarted) {
  68. throw std::logic_error("Child process was active when "
  69. "run() was called");
  70. }
  71. #ifdef WIN32
  72. // Set the bInheritHandle flag so pipe handles are inherited.
  73. SECURITY_ATTRIBUTES securityAttributes;
  74. securityAttributes.nLength = sizeof(SECURITY_ATTRIBUTES);
  75. securityAttributes.bInheritHandle = true;
  76. securityAttributes.lpSecurityDescriptor = NULL;
  77. // Create a pipe for the child process's STDOUT.
  78. HANDLE childStdOutAtChild;
  79. HANDLE childStdOutAtParent;
  80. HANDLE childStdInAtChild;
  81. HANDLE childStdInAtParent;
  82. int bufferSize = 0;
  83. if (!CreatePipe(&childStdOutAtParent,
  84. &childStdOutAtChild,
  85. &securityAttributes,
  86. bufferSize)) {
  87. throw std::runtime_error("Error from CreatePipe for stdout: " +
  88. getErrorText());
  89. }
  90. // Ensure the read handle to the pipe for STDOUT is not inherited.
  91. int inheritFlag = 0;
  92. if (!SetHandleInformation(childStdOutAtParent,
  93. HANDLE_FLAG_INHERIT,
  94. inheritFlag) ) {
  95. throw std::runtime_error("Error from GetHandleInformation for stdout: " +
  96. getErrorText());
  97. }
  98. // Create a pipe for the child process's STDIN.
  99. if (! CreatePipe(&childStdInAtChild,
  100. &childStdInAtParent,
  101. &securityAttributes,
  102. bufferSize)) {
  103. throw std::runtime_error("Error from CreatePipe for stdin: " +
  104. getErrorText());
  105. }
  106. // Ensure the write handle to the pipe for STDIN is not inherited.
  107. if (!SetHandleInformation(childStdInAtParent,
  108. HANDLE_FLAG_INHERIT,
  109. inheritFlag)) {
  110. throw std::runtime_error("Error from GetHandleInformation for stdin: " +
  111. getErrorText());
  112. }
  113. // Set up members of the PROCESS_INFORMATION structure.
  114. PROCESS_INFORMATION processInfo;
  115. std::fill((char *) &processInfo,
  116. ((char *) &processInfo) + sizeof(PROCESS_INFORMATION),
  117. 0);
  118. // Set up members of the STARTUPINFO structure. This structure
  119. // specifies the STDIN and STDOUT handles for redirection.
  120. STARTUPINFO startInfo;
  121. std::fill((char *) &startInfo,
  122. ((char *) &startInfo) + sizeof(STARTUPINFO),
  123. 0);
  124. startInfo.cb = sizeof(STARTUPINFO);
  125. startInfo.hStdError = childStdOutAtChild;
  126. startInfo.hStdOutput = childStdOutAtChild;
  127. startInfo.hStdInput = childStdInAtChild;
  128. startInfo.dwFlags |= STARTF_USESTDHANDLES;
  129. // Assemble the command line.
  130. std::string cmdline;
  131. if (cmdArgs.size() == 1) {
  132. cmdline = cmdArgs[0];
  133. } else {
  134. // Append all but last command-line arguments.
  135. for (size_t i = 0; i < cmdArgs.size() - 1; i++) {
  136. cmdline += cmdArgs[i] + " ";
  137. }
  138. cmdline += cmdArgs.back(); // Append last command-line argument.
  139. }
  140. // Create the child process.
  141. bool status;
  142. status = CreateProcess(NULL,
  143. (char *) cmdline.c_str(),
  144. NULL, // process security attributes
  145. NULL, // primary thread security attributes
  146. true, // handles are inherited
  147. 0, // creation flags
  148. NULL, // use parent's environment
  149. NULL, // use parent's current directory
  150. &startInfo, // STARTUPINFO pointer
  151. &processInfo); // receives PROCESS_INFORMATION
  152. // If an error occurs, exit the application.
  153. if (!status ) {
  154. throw std::runtime_error("Error from CreateProcess with "
  155. "command line \"" + cmdline + "\": " +
  156. getErrorText());
  157. }
  158. // Provide the stream buffers with the handles for communicating
  159. // with the child process.
  160. readStreambuf.setInputHandle(childStdOutAtParent);
  161. writeStreambuf.setOutputHandle(childStdInAtParent);
  162. // Save the handles to the child process and its primary thread.
  163. childProcess = processInfo.hProcess;
  164. childThread = processInfo.hThread;
  165. // Close the child's end of the pipes.
  166. if (!CloseHandle(childStdOutAtChild)) {
  167. throw std::runtime_error("Error closing the child process "
  168. "stdout handle: " + getErrorText());
  169. }
  170. if (!CloseHandle(childStdInAtChild)) {
  171. throw std::runtime_error("Error closing the child process "
  172. "stdin handle: " + getErrorText());
  173. }
  174. #else
  175. // Create the pipes for the stdin and stdout.
  176. int childStdInPipe[2];
  177. int childStdOutPipe[2];
  178. if (pipe(childStdInPipe) != 0) {
  179. throw std::runtime_error("Error creating pipe for stdin: " +
  180. getErrorText());
  181. }
  182. if (pipe(childStdOutPipe) != 0) {
  183. close(childStdInPipe[0]);
  184. close(childStdInPipe[1]);
  185. throw std::runtime_error("Error creating pipe for stdout: " +
  186. getErrorText());
  187. }
  188. // Create the child process.
  189. childPid = fork();
  190. if (-1 == childPid) {
  191. for (int i = 0; i < 2; i++) {
  192. close(childStdInPipe[i]);
  193. close(childStdOutPipe[i]);
  194. }
  195. throw std::runtime_error("Error creating child process: " +
  196. getErrorText());
  197. }
  198. if (0 == childPid) {
  199. // The child executes this. Redirect stdin.
  200. if (dup2(childStdInPipe[0], STDIN_FILENO) == -1) {
  201. std::string errMsg;
  202. // Send message to parent.
  203. errMsg = "Error redirecting stdin in the child: " + getErrorText();
  204. write(childStdOutPipe[1], errMsg.data(), errMsg.size());
  205. exit(-1);
  206. }
  207. // Redirect stdout.
  208. if (dup2(childStdOutPipe[1], STDOUT_FILENO) == -1) {
  209. std::string errMsg;
  210. // Send message to parent.
  211. errMsg = "Error redirecting stdout in the child: " + getErrorText();
  212. write(childStdOutPipe[1], errMsg.data(), errMsg.size());
  213. exit(-1);
  214. }
  215. // Close pipes.
  216. if ( (close(childStdInPipe[0]) != 0) ||
  217. (close(childStdInPipe[1]) != 0) ||
  218. (close(childStdOutPipe[0]) != 0) ||
  219. (close(childStdOutPipe[1]) != 0) ) {
  220. std::string errMsg;
  221. // Send message to parent.
  222. errMsg = "Error closing the pipes in the child: " + getErrorText();
  223. write(STDOUT_FILENO, errMsg.data(), errMsg.size());
  224. exit(-1);
  225. }
  226. // Prepare the arguments.
  227. std::vector<const char *> execvArgv;
  228. for (auto &arg : cmdArgs) {
  229. execvArgv.push_back(arg.c_str());
  230. }
  231. execvArgv.push_back((char *) NULL);
  232. // Run the child process image.
  233. (void) execv(execvArgv[0], (char * const *) &(execvArgv[0]));
  234. // Error from exec.
  235. std::string errMsg;
  236. // Send message to parent.
  237. errMsg = "Error from exec: " + getErrorText();
  238. write(STDOUT_FILENO, errMsg.data(), errMsg.size());
  239. exit(-1);
  240. }
  241. // std::cout << "Child pid: " << childPid << std::endl; // DEBUG.
  242. // Provide the stream buffers with the file descriptors for
  243. // communicating with the child process.
  244. readStreambuf.setInputFileDescriptor(childStdOutPipe[0]);
  245. writeStreambuf.setOutputFileDescriptor(childStdInPipe[1]);
  246. // Close the child's end of the pipes.
  247. if ( (close(childStdInPipe[0]) != 0) ||
  248. (close(childStdOutPipe[1]) != 0) ) {
  249. std::string errMsg;
  250. throw std::runtime_error("Error closing child's end of pipes in "
  251. "the parent: " + getErrorText());
  252. }
  253. #endif
  254. childStarted = true;
  255. }
  256. void
  257. Child::terminate() {
  258. if (isDone()) {
  259. return;
  260. }
  261. #ifdef WIN32
  262. if (!TerminateProcess(childProcess, terminateExitCode)) {
  263. #else
  264. if (kill(childPid, SIGTERM) != 0) {
  265. #endif
  266. throw std::runtime_error("Error terminating the child process: " +
  267. getErrorText());
  268. }
  269. }
  270. bool
  271. Child::isDone() {
  272. if (childExited) {
  273. return true;
  274. }
  275. if (!childStarted) {
  276. throw std::logic_error("Child process was not started "
  277. "when isDone() was called");
  278. }
  279. int result;
  280. #ifdef WIN32
  281. if (!GetExitCodeProcess(childProcess, (LPDWORD) &result)) {
  282. throw std::runtime_error("Error checking status of child process: " +
  283. getErrorText());
  284. }
  285. if (STILL_ACTIVE == result) {
  286. return false;
  287. }
  288. // Child process has exited. Save the exit code.
  289. exitCode = result;
  290. exitCodeObtainedFlag = true;
  291. #else
  292. int status = 0;
  293. result = waitpid(childPid, &status, WNOHANG);
  294. // std::cout << "isDone(). waitpid(" << childPid << ",...) returned " << result << std::endl; // DEBUG
  295. if (-1 == result) {
  296. throw std::runtime_error("Error checking status of child process: " +
  297. getErrorText());
  298. } else if (0 == result) {
  299. // Child is still running.
  300. // std::cout << "isDone(). Child is still running..." << std::endl; // DEBUG.
  301. return false;
  302. }
  303. // std::cout << "isDone(). Child exited." << std::endl; // DEBUG.
  304. if (WIFEXITED(status)) {
  305. // Child exited normally.
  306. exitCode = WEXITSTATUS(status);
  307. exitCodeObtainedFlag = true;
  308. //std::cout << "isDone(). Child exited normally. Exit code: " << exitCode << std::endl; // DEBUG.
  309. }
  310. #endif
  311. childExited = true;
  312. return true;
  313. }
  314. int32_t
  315. Child::result() {
  316. if (exitCodeObtainedFlag) {
  317. return exitCode;
  318. }
  319. // Check whether the process is running, and get the exit code.
  320. if (!isDone()) {
  321. throw std::logic_error("Child process was still running"
  322. "when result() was called");
  323. }
  324. // Child process has exited.
  325. if (!exitCodeObtainedFlag) {
  326. // Exit code is not available.
  327. throw std::runtime_error("Child process has exited but the exit "
  328. "code is not available");
  329. }
  330. return exitCode;
  331. }
  332. std::string
  333. Child::getErrorText() {
  334. #ifdef WIN32
  335. LPVOID winMsgBuf;
  336. DWORD lastError = GetLastError();
  337. FormatMessage(
  338. FORMAT_MESSAGE_ALLOCATE_BUFFER |
  339. FORMAT_MESSAGE_FROM_SYSTEM |
  340. FORMAT_MESSAGE_IGNORE_INSERTS,
  341. NULL,
  342. lastError,
  343. MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
  344. (char *) &winMsgBuf,
  345. 0, NULL );
  346. std::string errMsg((char *) winMsgBuf);
  347. LocalFree(winMsgBuf);
  348. return errMsg;
  349. #else
  350. return strerror(errno);
  351. #endif
  352. }
  353. Child::ReadStreambuf::ReadStreambuf(std::size_t bufferSize) :
  354. #ifdef WIN32
  355. inputHandle(0),
  356. #else
  357. inputFileDescriptor(-1),
  358. #endif
  359. buffer(bufferSize + 1) {
  360. char *end = &(buffer.front()) + buffer.size();
  361. // Indicate to underflow that underflow has not been called.
  362. setg(end, end, end);
  363. }
  364. #ifdef WIN32
  365. void
  366. Child::ReadStreambuf::setInputHandle(HANDLE inHandle) {
  367. inputHandle = inHandle;
  368. }
  369. #else
  370. void
  371. Child::ReadStreambuf::setInputFileDescriptor(int inFd) {
  372. inputFileDescriptor = inFd;
  373. }
  374. #endif
  375. std::streambuf::int_type
  376. Child::ReadStreambuf::underflow() {
  377. // Check for empty buffer.
  378. if (gptr() < egptr()) {
  379. // Not empty.
  380. return traits_type::to_int_type(*gptr());
  381. }
  382. // Need to fill the buffer.
  383. char *base = &(buffer.front());
  384. char *start = base;
  385. // Check whether this is the first fill.
  386. if (eback() == base) {
  387. // Not the first fill. Copy one putback character.
  388. *(eback()) = *(egptr() - 1);
  389. start++;
  390. }
  391. // start points to the start of the buffer. Fill buffer.
  392. #ifdef WIN32
  393. DWORD nBytesRead;
  394. if (!ReadFile(inputHandle,
  395. start,
  396. buffer.size() - (start - base),
  397. &nBytesRead,
  398. NULL)) {
  399. return traits_type::eof();
  400. }
  401. #else
  402. size_t nBytesRead;
  403. nBytesRead = read(inputFileDescriptor,
  404. start,
  405. buffer.size() - (start - base));
  406. if (-1 == nBytesRead) {
  407. return traits_type::eof();
  408. }
  409. #endif
  410. // Check for EOF.
  411. if (0 == nBytesRead) {
  412. return traits_type::eof();
  413. }
  414. // Update buffer pointers.
  415. setg(base, start, start + nBytesRead);
  416. return traits_type::to_int_type(*gptr());
  417. }
  418. Child::WriteStreambuf::WriteStreambuf(std::size_t bufferSize) :
  419. #ifdef WIN32
  420. outputHandle(0),
  421. #else
  422. outputFileDescriptor(-1),
  423. #endif
  424. buffer(bufferSize + 1) {
  425. char *base = &(buffer.front());
  426. // Indicate to overflow that overflow has not been called.
  427. setp(base, base + buffer.size() - 1);
  428. }
  429. #ifdef WIN32
  430. void
  431. Child::WriteStreambuf::setOutputHandle(HANDLE outHandle) {
  432. outputHandle = outHandle;
  433. }
  434. #else
  435. void
  436. Child::WriteStreambuf::setOutputFileDescriptor(int outFd) {
  437. outputFileDescriptor = outFd;
  438. }
  439. #endif
  440. void
  441. Child::WriteStreambuf::flushBuffer() {
  442. // Write.
  443. std::ptrdiff_t nBytes = pptr() - pbase();
  444. #ifdef WIN32
  445. DWORD nBytesWritten;
  446. if (!WriteFile(outputHandle,
  447. pbase(),
  448. nBytes,
  449. &nBytesWritten,
  450. NULL)) {
  451. // Clear the output buffer.
  452. pbump(-nBytes);
  453. throw std::runtime_error("Error writing to child process: " +
  454. getErrorText());
  455. }
  456. #else
  457. size_t nBytesWritten;
  458. nBytesWritten = write(outputFileDescriptor, pbase(), nBytes);
  459. #endif
  460. // Clear the output buffer.
  461. pbump(-nBytes);
  462. if (nBytes != nBytesWritten) {
  463. throw std::runtime_error("Not all data was written to to child "
  464. "process: " + getErrorText());
  465. }
  466. return;
  467. }
  468. std::streambuf::int_type
  469. Child::WriteStreambuf::overflow(int_type ch) {
  470. // Check whether we're writing EOF.
  471. if (traits_type::eof() != ch) {
  472. // Not writing EOF.
  473. *(pptr()) = ch;
  474. pbump(1);
  475. // Write.
  476. flushBuffer();
  477. // Success.
  478. return ch;
  479. }
  480. return traits_type::eof();
  481. }
  482. int
  483. Child::WriteStreambuf::sync() {
  484. flushBuffer(); // Throws exception on failure.
  485. // Success.
  486. return 1;
  487. }
  488. }