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

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