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

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