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

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