Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

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