cmWin32ProcessExecution.cxx 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933
  1. /*============================================================================
  2. CMake - Cross Platform Makefile Generator
  3. Copyright 2000-2009 Kitware, Inc., Insight Software Consortium
  4. Distributed under the OSI-approved BSD License (the "License");
  5. see accompanying file Copyright.txt for details.
  6. This software is distributed WITHOUT ANY WARRANTY; without even the
  7. implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  8. See the License for more information.
  9. ============================================================================*/
  10. #include "cmWin32ProcessExecution.h"
  11. #include "cmSystemTools.h"
  12. #include <malloc.h>
  13. #include <io.h>
  14. #include <fcntl.h>
  15. #include <sys/stat.h>
  16. #include <windows.h>
  17. #if defined(__BORLANDC__)
  18. # define STRICMP stricmp
  19. # define TO_INTPTR(x) ((long)(x))
  20. #else // Visual studio
  21. # if ( _MSC_VER >= 1300 )
  22. # include <stddef.h>
  23. # define TO_INTPTR(x) ((intptr_t)(x))
  24. # else // Visual Studio 6
  25. # define TO_INTPTR(x) ((long)(x))
  26. # endif // Visual studio .NET
  27. # define STRICMP _stricmp
  28. #endif // Borland
  29. #define POPEN_1 1
  30. #define POPEN_2 2
  31. #define POPEN_3 3
  32. #define POPEN_4 4
  33. #define cmMAX(x,y) (((x)<(y))?(y):(x))
  34. void DisplayErrorMessage()
  35. {
  36. LPVOID lpMsgBuf;
  37. FormatMessage(
  38. FORMAT_MESSAGE_ALLOCATE_BUFFER |
  39. FORMAT_MESSAGE_FROM_SYSTEM |
  40. FORMAT_MESSAGE_IGNORE_INSERTS,
  41. NULL,
  42. GetLastError(),
  43. MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
  44. (LPTSTR) &lpMsgBuf,
  45. 0,
  46. NULL
  47. );
  48. // Process any inserts in lpMsgBuf.
  49. // ...
  50. // Display the string.
  51. MessageBox( NULL, (LPCTSTR)lpMsgBuf, "Error", MB_OK | MB_ICONINFORMATION );
  52. // Free the buffer.
  53. LocalFree( lpMsgBuf );
  54. }
  55. // Code from a Borland web site with the following explaination :
  56. /* In this article, I will explain how to spawn a console application
  57. * and redirect its standard input/output using anonymous pipes. An
  58. * anonymous pipe is a pipe that goes only in one direction (read
  59. * pipe, write pipe, etc.). Maybe you are asking, "why would I ever
  60. * need to do this sort of thing?" One example would be a Windows
  61. * telnet server, where you spawn a shell and listen on a port and
  62. * send and receive data between the shell and the socket
  63. * client. (Windows does not really have a built-in remote
  64. * shell). First, we should talk about pipes. A pipe in Windows is
  65. * simply a method of communication, often between process. The SDK
  66. * defines a pipe as "a communication conduit with two ends;
  67. a process
  68. * with a handle to one end can communicate with a process having a
  69. * handle to the other end." In our case, we are using "anonymous"
  70. * pipes, one-way pipes that "transfer data between a parent process
  71. * and a child process or between two child processes of the same
  72. * parent process." It's easiest to imagine a pipe as its namesake. An
  73. * actual pipe running between processes that can carry data. We are
  74. * using anonymous pipes because the console app we are spawning is a
  75. * child process. We use the CreatePipe function which will create an
  76. * anonymous pipe and return a read handle and a write handle. We will
  77. * create two pipes, on for stdin and one for stdout. We will then
  78. * monitor the read end of the stdout pipe to check for display on our
  79. * child process. Every time there is something availabe for reading,
  80. * we will display it in our app. Consequently, we check for input in
  81. * our app and send it off to the write end of the stdin pipe. */
  82. inline bool IsWinNT()
  83. //check if we're running NT
  84. {
  85. OSVERSIONINFO osv;
  86. osv.dwOSVersionInfoSize = sizeof(osv);
  87. GetVersionEx(&osv);
  88. return (osv.dwPlatformId == VER_PLATFORM_WIN32_NT);
  89. }
  90. //---------------------------------------------------------------------------
  91. bool cmWin32ProcessExecution::BorlandRunCommand(
  92. const char* command, const char* dir,
  93. std::string& output, int& retVal, bool verbose, int /* timeout */,
  94. bool hideWindows)
  95. {
  96. //verbose = true;
  97. //std::cerr << std::endl
  98. // << "WindowsRunCommand(" << command << ")" << std::endl
  99. // << std::flush;
  100. const int BUFFER_SIZE = 4096;
  101. char buf[BUFFER_SIZE];
  102. //i/o buffer
  103. STARTUPINFO si;
  104. SECURITY_ATTRIBUTES sa;
  105. SECURITY_DESCRIPTOR sd;
  106. //security information for pipes
  107. PROCESS_INFORMATION pi;
  108. HANDLE newstdin,newstdout,read_stdout,write_stdin;
  109. //pipe handles
  110. if (IsWinNT())
  111. //initialize security descriptor (Windows NT)
  112. {
  113. InitializeSecurityDescriptor(&sd,SECURITY_DESCRIPTOR_REVISION);
  114. SetSecurityDescriptorDacl(&sd, true, NULL, false);
  115. sa.lpSecurityDescriptor = &sd;
  116. }
  117. else sa.lpSecurityDescriptor = NULL;
  118. sa.nLength = sizeof(SECURITY_ATTRIBUTES);
  119. sa.bInheritHandle = true;
  120. //allow inheritable handles
  121. if (!CreatePipe(&newstdin,&write_stdin,&sa,0))
  122. //create stdin pipe
  123. {
  124. return false;
  125. }
  126. if (!CreatePipe(&read_stdout,&newstdout,&sa,0))
  127. //create stdout pipe
  128. {
  129. CloseHandle(newstdin);
  130. CloseHandle(write_stdin);
  131. return false;
  132. }
  133. GetStartupInfo(&si);
  134. //set startupinfo for the spawned process
  135. /* The dwFlags member tells CreateProcess how to make the
  136. * process. STARTF_USESTDHANDLES validates the hStd*
  137. * members. STARTF_USESHOWWINDOW validates the wShowWindow
  138. * member. */
  139. si.cb = sizeof(STARTUPINFO);
  140. si.dwFlags = STARTF_USESTDHANDLES|STARTF_USESHOWWINDOW;
  141. si.hStdOutput = newstdout;
  142. si.hStdError = newstdout;
  143. si.wShowWindow = SW_SHOWDEFAULT;
  144. if(hideWindows)
  145. {
  146. si.wShowWindow = SW_HIDE;
  147. }
  148. //set the new handles for the child process si.hStdInput = newstdin;
  149. char* commandAndArgs = strcpy(new char[strlen(command)+1], command);
  150. if (!CreateProcess(NULL,commandAndArgs,NULL,NULL,TRUE,
  151. 0, // CREATE_NEW_CONSOLE,
  152. NULL,dir,&si,&pi))
  153. {
  154. std::cerr << "CreateProcess failed " << commandAndArgs << std::endl;
  155. CloseHandle(newstdin);
  156. CloseHandle(newstdout);
  157. CloseHandle(read_stdout);
  158. CloseHandle(write_stdin);
  159. delete [] commandAndArgs;
  160. return false;
  161. }
  162. delete [] commandAndArgs;
  163. unsigned long exit=0;
  164. //process exit code unsigned
  165. unsigned long bread;
  166. //bytes read unsigned
  167. unsigned long avail;
  168. //bytes available
  169. memset(buf, 0, sizeof(buf));
  170. for(;;)
  171. //main program loop
  172. {
  173. Sleep(10);
  174. //check to see if there is any data to read from stdout
  175. //std::cout << "Peek for data..." << std::endl;
  176. PeekNamedPipe(read_stdout,buf,1023,&bread,&avail,NULL);
  177. if (bread != 0)
  178. {
  179. memset(buf, 0, sizeof(buf));
  180. if (avail > 1023)
  181. {
  182. while (bread >= 1023)
  183. {
  184. //std::cout << "Read data..." << std::endl;
  185. ReadFile(read_stdout,buf,1023,&bread,NULL);
  186. //read the stdout pipe
  187. memset(buf, 0, sizeof(buf));
  188. output += buf;
  189. if (verbose)
  190. {
  191. cmSystemTools::Stdout(buf);
  192. }
  193. }
  194. }
  195. else
  196. {
  197. ReadFile(read_stdout,buf,1023,&bread,NULL);
  198. output += buf;
  199. if(verbose)
  200. {
  201. cmSystemTools::Stdout(buf);
  202. }
  203. }
  204. }
  205. //std::cout << "Check for process..." << std::endl;
  206. GetExitCodeProcess(pi.hProcess,&exit);
  207. //while the process is running
  208. if (exit != STILL_ACTIVE) break;
  209. }
  210. WaitForSingleObject(pi.hProcess, INFINITE);
  211. GetExitCodeProcess(pi.hProcess,&exit);
  212. CloseHandle(pi.hThread);
  213. CloseHandle(pi.hProcess);
  214. CloseHandle(newstdin);
  215. //clean stuff up
  216. CloseHandle(newstdout);
  217. CloseHandle(read_stdout);
  218. CloseHandle(write_stdin);
  219. retVal = exit;
  220. return true;
  221. }
  222. bool cmWin32ProcessExecution::StartProcess(
  223. const char* cmd, const char* path, bool verbose)
  224. {
  225. this->Initialize();
  226. this->Verbose = verbose;
  227. return this->PrivateOpen(cmd, path, _O_RDONLY | _O_TEXT, POPEN_3);
  228. }
  229. bool cmWin32ProcessExecution::Wait(int timeout)
  230. {
  231. return this->PrivateClose(timeout);
  232. }
  233. /*
  234. * Internal dictionary mapping popen* file pointers to process handles,
  235. * for use when retrieving the process exit code. See _PyPclose() below
  236. * for more information on this dictionary's use.
  237. */
  238. static void *_PyPopenProcs = NULL;
  239. static BOOL RealPopenCreateProcess(const char *cmdstring,
  240. const char *path,
  241. const char *szConsoleSpawn,
  242. HANDLE hStdin,
  243. HANDLE hStdout,
  244. HANDLE hStderr,
  245. HANDLE *hProcess,
  246. bool hideWindows,
  247. std::string& output)
  248. {
  249. PROCESS_INFORMATION piProcInfo;
  250. STARTUPINFO siStartInfo;
  251. char *s1=0,*s2=0, *s3 = " /c ";
  252. int i = GetEnvironmentVariable("COMSPEC",NULL,0);
  253. if (i)
  254. {
  255. char *comshell;
  256. s1 = (char *)malloc(i);
  257. int x = GetEnvironmentVariable("COMSPEC", s1, i);
  258. if (!x)
  259. {
  260. free(s1);
  261. return x;
  262. }
  263. /* Explicitly check if we are using COMMAND.COM. If we are
  264. * then use the w9xpopen hack.
  265. */
  266. comshell = s1 + x;
  267. while (comshell >= s1 && *comshell != '\\')
  268. --comshell;
  269. ++comshell;
  270. if (GetVersion() < 0x80000000 &&
  271. STRICMP(comshell, "command.com") != 0)
  272. {
  273. /* NT/2000 and not using command.com. */
  274. x = i + (int)strlen(s3) + (int)strlen(cmdstring) + 1;
  275. s2 = (char *)malloc(x);
  276. ZeroMemory(s2, x);
  277. //sprintf(s2, "%s%s%s", s1, s3, cmdstring);
  278. sprintf(s2, "%s", cmdstring);
  279. }
  280. else
  281. {
  282. /*
  283. * Oh gag, we're on Win9x or using COMMAND.COM. Use
  284. * the workaround listed in KB: Q150956
  285. */
  286. char modulepath[_MAX_PATH];
  287. struct stat statinfo;
  288. GetModuleFileName(NULL, modulepath, sizeof(modulepath));
  289. for (i = x = 0; modulepath[i]; i++)
  290. if (modulepath[i] == '\\')
  291. x = i+1;
  292. modulepath[x] = '\0';
  293. /* Create the full-name to w9xpopen, so we can test it exists */
  294. strncat(modulepath,
  295. szConsoleSpawn,
  296. (sizeof(modulepath)/sizeof(modulepath[0]))
  297. -strlen(modulepath));
  298. if (stat(modulepath, &statinfo) != 0)
  299. {
  300. /* Eeek - file-not-found - possibly an embedding
  301. situation - see if we can locate it in sys.prefix
  302. */
  303. strncpy(modulepath,
  304. ".",
  305. sizeof(modulepath)/sizeof(modulepath[0]));
  306. if (modulepath[strlen(modulepath)-1] != '\\')
  307. strcat(modulepath, "\\");
  308. strncat(modulepath,
  309. szConsoleSpawn,
  310. (sizeof(modulepath)/sizeof(modulepath[0]))
  311. -strlen(modulepath));
  312. /* No where else to look - raise an easily identifiable
  313. error, rather than leaving Windows to report
  314. "file not found" - as the user is probably blissfully
  315. unaware this shim EXE is used, and it will confuse them.
  316. (well, it confused me for a while ;-)
  317. */
  318. if (stat(modulepath, &statinfo) != 0)
  319. {
  320. std::cout
  321. << "Can not locate '" << modulepath
  322. << "' which is needed "
  323. "for popen to work with your shell "
  324. "or platform." << std::endl;
  325. free(s1);
  326. free(s2);
  327. return FALSE;
  328. }
  329. }
  330. x = i + (int)strlen(s3) + (int)strlen(cmdstring) + 1 +
  331. (int)strlen(modulepath) +
  332. (int)strlen(szConsoleSpawn) + 1;
  333. if(s2)
  334. {
  335. free(s2);
  336. }
  337. s2 = (char *)malloc(x);
  338. ZeroMemory(s2, x);
  339. sprintf(
  340. s2,
  341. "%s %s%s%s",
  342. modulepath,
  343. s1,
  344. s3,
  345. cmdstring);
  346. sprintf(
  347. s2,
  348. "%s %s",
  349. modulepath,
  350. cmdstring);
  351. }
  352. }
  353. /* Could be an else here to try cmd.exe / command.com in the path
  354. Now we'll just error out.. */
  355. else
  356. {
  357. std::cout << "Cannot locate a COMSPEC environment variable to "
  358. << "use as the shell" << std::endl;
  359. free(s2);
  360. free(s1);
  361. return FALSE;
  362. }
  363. ZeroMemory(&siStartInfo, sizeof(STARTUPINFO));
  364. siStartInfo.cb = sizeof(STARTUPINFO);
  365. siStartInfo.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
  366. siStartInfo.hStdInput = hStdin;
  367. siStartInfo.hStdOutput = hStdout;
  368. siStartInfo.hStdError = hStderr;
  369. siStartInfo.wShowWindow = SW_SHOWDEFAULT;
  370. if(hideWindows)
  371. {
  372. siStartInfo.wShowWindow = SW_HIDE;
  373. }
  374. //std::cout << "Create process: " << s2 << std::endl;
  375. if (CreateProcess(NULL,
  376. s2,
  377. NULL,
  378. NULL,
  379. TRUE,
  380. 0, //CREATE_NEW_CONSOLE,
  381. NULL,
  382. path,
  383. &siStartInfo,
  384. &piProcInfo) )
  385. {
  386. /* Close the handles now so anyone waiting is woken. */
  387. CloseHandle(piProcInfo.hThread);
  388. /* Return process handle */
  389. *hProcess = piProcInfo.hProcess;
  390. //std::cout << "Process created..." << std::endl;
  391. free(s2);
  392. free(s1);
  393. return TRUE;
  394. }
  395. output += "CreateProcessError: ";
  396. {
  397. /* Format the error message. */
  398. char message[1024];
  399. DWORD original = GetLastError();
  400. DWORD length = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
  401. FORMAT_MESSAGE_IGNORE_INSERTS, 0, original,
  402. MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
  403. message, 1023, 0);
  404. if(length < 1)
  405. {
  406. /* FormatMessage failed. Use a default message. */
  407. _snprintf(message, 1023,
  408. "Process execution failed with error 0x%X. "
  409. "FormatMessage failed with error 0x%X",
  410. original, GetLastError());
  411. }
  412. output += message;
  413. }
  414. output += "\n";
  415. output += "for command: ";
  416. output += s2;
  417. if(path)
  418. {
  419. output += "\nin dir: ";
  420. output += path;
  421. }
  422. output += "\n";
  423. free(s2);
  424. free(s1);
  425. return FALSE;
  426. }
  427. /* The following code is based off of KB: Q190351 */
  428. bool cmWin32ProcessExecution::PrivateOpen(const char *cmdstring,
  429. const char* path,
  430. int mode,
  431. int n)
  432. {
  433. HANDLE hProcess;
  434. SECURITY_ATTRIBUTES saAttr;
  435. BOOL fSuccess;
  436. int fd1, fd2, fd3;
  437. this->hChildStdinRd = 0;
  438. this->hChildStdinWr = 0;
  439. this->hChildStdoutRd = 0;
  440. this->hChildStdoutWr = 0;
  441. this->hChildStderrRd = 0;
  442. this->hChildStderrWr = 0;
  443. this->hChildStdinWrDup = 0;
  444. this->hChildStdoutRdDup = 0;
  445. this->hChildStderrRdDup = 0;
  446. saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
  447. saAttr.bInheritHandle = TRUE;
  448. saAttr.lpSecurityDescriptor = NULL;
  449. fd1 = 0;
  450. fd2 = 0;
  451. fd3 = 0;
  452. if (!CreatePipe(&this->hChildStdinRd, &this->hChildStdinWr, &saAttr, 0))
  453. {
  454. this->Output += "CreatePipeError\n";
  455. return false;
  456. }
  457. /* Create new output read handle and the input write handle. Set
  458. * the inheritance properties to FALSE. Otherwise, the child inherits
  459. * the these handles; resulting in non-closeable handles to the pipes
  460. * being created. */
  461. fSuccess = DuplicateHandle(GetCurrentProcess(), this->hChildStdinWr,
  462. GetCurrentProcess(), &this->hChildStdinWrDup, 0,
  463. FALSE,
  464. DUPLICATE_SAME_ACCESS);
  465. if (!fSuccess)
  466. {
  467. this->Output += "DuplicateHandleError\n";
  468. return false;
  469. }
  470. /* Close the inheritable version of ChildStdin
  471. that we're using. */
  472. CloseHandle(hChildStdinWr);
  473. if (!CreatePipe(&this->hChildStdoutRd, &this->hChildStdoutWr, &saAttr, 0))
  474. {
  475. this->Output += "CreatePipeError\n";
  476. return false;
  477. }
  478. fSuccess = DuplicateHandle(GetCurrentProcess(), this->hChildStdoutRd,
  479. GetCurrentProcess(), &this->hChildStdoutRdDup, 0,
  480. FALSE, DUPLICATE_SAME_ACCESS);
  481. if (!fSuccess)
  482. {
  483. this->Output += "DuplicateHandleError\n";
  484. return false;
  485. }
  486. /* Close the inheritable version of ChildStdout
  487. that we're using. */
  488. CloseHandle(hChildStdoutRd);
  489. if (n != POPEN_4)
  490. {
  491. if (!CreatePipe(&this->hChildStderrRd, &this->hChildStderrWr, &saAttr, 0))
  492. {
  493. this->Output += "CreatePipeError\n";
  494. return false;
  495. }
  496. fSuccess = DuplicateHandle(GetCurrentProcess(),
  497. this->hChildStderrRd,
  498. GetCurrentProcess(),
  499. &this->hChildStderrRdDup, 0,
  500. FALSE, DUPLICATE_SAME_ACCESS);
  501. if (!fSuccess)
  502. {
  503. this->Output += "DuplicateHandleError\n";
  504. return false;
  505. }
  506. /* Close the inheritable version of ChildStdErr that we're using. */
  507. CloseHandle(hChildStderrRd);
  508. }
  509. switch (n)
  510. {
  511. case POPEN_1:
  512. switch (mode & (_O_RDONLY | _O_TEXT | _O_BINARY | _O_WRONLY))
  513. {
  514. case _O_WRONLY | _O_TEXT:
  515. /* Case for writing to child Stdin in text mode. */
  516. fd1 = _open_osfhandle(TO_INTPTR(this->hChildStdinWrDup), mode);
  517. /* We don't care about these pipes anymore,
  518. so close them. */
  519. break;
  520. case _O_RDONLY | _O_TEXT:
  521. /* Case for reading from child Stdout in text mode. */
  522. fd1 = _open_osfhandle(TO_INTPTR(this->hChildStdoutRdDup), mode);
  523. /* We don't care about these pipes anymore,
  524. so close them. */
  525. break;
  526. case _O_RDONLY | _O_BINARY:
  527. /* Case for readinig from child Stdout in
  528. binary mode. */
  529. fd1 = _open_osfhandle(TO_INTPTR(this->hChildStdoutRdDup), mode);
  530. /* We don't care about these pipes anymore,
  531. so close them. */
  532. break;
  533. case _O_WRONLY | _O_BINARY:
  534. /* Case for writing to child Stdin in binary mode. */
  535. fd1 = _open_osfhandle(TO_INTPTR(this->hChildStdinWrDup), mode);
  536. /* We don't care about these pipes anymore,
  537. so close them. */
  538. break;
  539. }
  540. break;
  541. case POPEN_2:
  542. case POPEN_4:
  543. //if ( 1 )
  544. {
  545. fd1 = _open_osfhandle(TO_INTPTR(this->hChildStdinWrDup), mode);
  546. fd2 = _open_osfhandle(TO_INTPTR(this->hChildStdoutRdDup), mode);
  547. break;
  548. }
  549. case POPEN_3:
  550. //if ( 1)
  551. {
  552. fd1 = _open_osfhandle(TO_INTPTR(this->hChildStdinWrDup), mode);
  553. fd2 = _open_osfhandle(TO_INTPTR(this->hChildStdoutRdDup), mode);
  554. fd3 = _open_osfhandle(TO_INTPTR(this->hChildStderrRdDup), mode);
  555. break;
  556. }
  557. }
  558. if (n == POPEN_4)
  559. {
  560. if (!RealPopenCreateProcess(cmdstring,
  561. path,
  562. this->ConsoleSpawn.c_str(),
  563. this->hChildStdinRd,
  564. this->hChildStdoutWr,
  565. this->hChildStdoutWr,
  566. &hProcess, this->HideWindows,
  567. this->Output))
  568. {
  569. if(fd1 >= 0)
  570. {
  571. close(fd1);
  572. }
  573. if(fd2 >= 0)
  574. {
  575. close(fd2);
  576. }
  577. if(fd3 >= 0)
  578. {
  579. close(fd3);
  580. }
  581. return 0;
  582. }
  583. }
  584. else
  585. {
  586. if (!RealPopenCreateProcess(cmdstring,
  587. path,
  588. this->ConsoleSpawn.c_str(),
  589. this->hChildStdinRd,
  590. this->hChildStdoutWr,
  591. this->hChildStderrWr,
  592. &hProcess, this->HideWindows,
  593. this->Output))
  594. {
  595. if(fd1 >= 0)
  596. {
  597. close(fd1);
  598. }
  599. if(fd2 >= 0)
  600. {
  601. close(fd2);
  602. }
  603. if(fd3 >= 0)
  604. {
  605. close(fd3);
  606. }
  607. return 0;
  608. }
  609. }
  610. /*
  611. * Insert the files we've created into the process dictionary
  612. * all referencing the list with the process handle and the
  613. * initial number of files (see description below in _PyPclose).
  614. * Since if _PyPclose later tried to wait on a process when all
  615. * handles weren't closed, it could create a deadlock with the
  616. * child, we spend some energy here to try to ensure that we
  617. * either insert all file handles into the dictionary or none
  618. * at all. It's a little clumsy with the various popen modes
  619. * and variable number of files involved.
  620. */
  621. /* Child is launched. Close the parents copy of those pipe
  622. * handles that only the child should have open. You need to
  623. * make sure that no handles to the write end of the output pipe
  624. * are maintained in this process or else the pipe will not close
  625. * when the child process exits and the ReadFile will hang. */
  626. this->ProcessHandle = hProcess;
  627. if ( fd1 >= 0 )
  628. {
  629. this->pStdIn = fd1;
  630. }
  631. if ( fd2 >= 0 )
  632. {
  633. this->pStdOut = fd2;
  634. }
  635. if ( fd3 >= 0 )
  636. {
  637. this->pStdErr = fd3;
  638. }
  639. return true;
  640. }
  641. bool cmWin32ProcessExecution::CloseHandles()
  642. {
  643. if(this->pStdErr != -1 )
  644. {
  645. // this will close this as well: this->hChildStderrRdDup
  646. _close(this->pStdErr);
  647. this->pStdErr = -1;
  648. this->hChildStderrRdDup = 0;
  649. }
  650. if(this->pStdIn != -1 )
  651. {
  652. // this will close this as well: this->hChildStdinWrDup
  653. _close(this->pStdIn);
  654. this->pStdIn = -1;
  655. this->hChildStdinWrDup = 0;
  656. }
  657. if(this->pStdOut != -1 )
  658. {
  659. // this will close this as well: this->hChildStdoutRdDup
  660. _close(this->pStdOut);
  661. this->pStdOut = -1;
  662. this->hChildStdoutRdDup = 0;
  663. }
  664. bool ret = true;
  665. if (this->hChildStdinRd && !CloseHandle(this->hChildStdinRd))
  666. {
  667. ret = false;
  668. }
  669. this->hChildStdinRd = 0;
  670. // now close these two
  671. if (this->hChildStdoutWr && !CloseHandle(this->hChildStdoutWr))
  672. {
  673. ret = false;
  674. }
  675. this->hChildStdoutWr = 0;
  676. if (this->hChildStderrWr && !CloseHandle(this->hChildStderrWr))
  677. {
  678. ret = false;
  679. }
  680. this->hChildStderrWr = 0;
  681. return ret;
  682. }
  683. cmWin32ProcessExecution::~cmWin32ProcessExecution()
  684. {
  685. this->CloseHandles();
  686. }
  687. /*
  688. * Wrapper for fclose() to use for popen* files, so we can retrieve the
  689. * exit code for the child process and return as a result of the close.
  690. *
  691. * This function uses the _PyPopenProcs dictionary in order to map the
  692. * input file pointer to information about the process that was
  693. * originally created by the popen* call that created the file pointer.
  694. * The dictionary uses the file pointer as a key (with one entry
  695. * inserted for each file returned by the original popen* call) and a
  696. * single list object as the value for all files from a single call.
  697. * The list object contains the Win32 process handle at [0], and a file
  698. * count at [1], which is initialized to the total number of file
  699. * handles using that list.
  700. *
  701. * This function closes whichever handle it is passed, and decrements
  702. * the file count in the dictionary for the process handle pointed to
  703. * by this file. On the last close (when the file count reaches zero),
  704. * this function will wait for the child process and then return its
  705. * exit code as the result of the close() operation. This permits the
  706. * files to be closed in any order - it is always the close() of the
  707. * final handle that will return the exit code.
  708. */
  709. /* RED_FLAG 31-Aug-2000 Tim
  710. * This is always called (today!) between a pair of
  711. * Py_BEGIN_ALLOW_THREADS/ Py_END_ALLOW_THREADS
  712. * macros. So the thread running this has no valid thread state, as
  713. * far as Python is concerned. However, this calls some Python API
  714. * functions that cannot be called safely without a valid thread
  715. * state, in particular PyDict_GetItem.
  716. * As a temporary hack (although it may last for years ...), we
  717. * *rely* on not having a valid thread state in this function, in
  718. * order to create our own "from scratch".
  719. * This will deadlock if _PyPclose is ever called by a thread
  720. * holding the global lock.
  721. */
  722. bool cmWin32ProcessExecution::PrivateClose(int /* timeout */)
  723. {
  724. HANDLE hProcess = this->ProcessHandle;
  725. int result = -1;
  726. DWORD exit_code;
  727. std::string output = "";
  728. bool done = false;
  729. while(!done)
  730. {
  731. Sleep(10);
  732. bool have_some = false;
  733. struct _stat fsout;
  734. struct _stat fserr;
  735. int rout = _fstat(this->pStdOut, &fsout);
  736. int rerr = _fstat(this->pStdErr, &fserr);
  737. if ( rout && rerr )
  738. {
  739. break;
  740. }
  741. if (fserr.st_size > 0)
  742. {
  743. char buffer[1024];
  744. int len = read(this->pStdErr, buffer, 1023);
  745. buffer[len] = 0;
  746. if ( this->Verbose )
  747. {
  748. cmSystemTools::Stdout(buffer);
  749. }
  750. output += buffer;
  751. have_some = true;
  752. }
  753. if (fsout.st_size > 0)
  754. {
  755. char buffer[1024];
  756. int len = read(this->pStdOut, buffer, 1023);
  757. buffer[len] = 0;
  758. if ( this->Verbose )
  759. {
  760. cmSystemTools::Stdout(buffer);
  761. }
  762. output += buffer;
  763. have_some = true;
  764. }
  765. unsigned long exitCode;
  766. if ( ! have_some )
  767. {
  768. GetExitCodeProcess(hProcess,&exitCode);
  769. if (exitCode != STILL_ACTIVE)
  770. {
  771. break;
  772. }
  773. }
  774. }
  775. if (WaitForSingleObject(hProcess, INFINITE) != WAIT_FAILED &&
  776. GetExitCodeProcess(hProcess, &exit_code))
  777. {
  778. result = exit_code;
  779. }
  780. else
  781. {
  782. /* Indicate failure - this will cause the file object
  783. * to raise an I/O error and translate the last Win32
  784. * error code from errno. We do have a problem with
  785. * last errors that overlap the normal errno table,
  786. * but that's a consistent problem with the file object.
  787. */
  788. if (result != EOF)
  789. {
  790. /* If the error wasn't from the fclose(), then
  791. * set errno for the file object error handling.
  792. */
  793. errno = GetLastError();
  794. }
  795. result = -1;
  796. }
  797. /* Free up the native handle at this point */
  798. CloseHandle(hProcess);
  799. this->ExitValue = result;
  800. this->Output += output;
  801. bool ret = this->CloseHandles();
  802. if ( result < 0 || !ret)
  803. {
  804. return false;
  805. }
  806. return true;
  807. }
  808. int cmWin32ProcessExecution::Windows9xHack(const char* command)
  809. {
  810. BOOL bRet;
  811. STARTUPINFO si;
  812. PROCESS_INFORMATION pi;
  813. DWORD exit_code=0;
  814. if (!command)
  815. {
  816. cmSystemTools::Error("Windows9xHack: Command not specified");
  817. return 1;
  818. }
  819. /* Make child process use this app's standard files. */
  820. ZeroMemory(&si, sizeof si);
  821. si.cb = sizeof si;
  822. si.dwFlags = STARTF_USESTDHANDLES;
  823. si.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
  824. si.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
  825. si.hStdError = GetStdHandle(STD_ERROR_HANDLE);
  826. char * app = 0;
  827. char* cmd = new char[ strlen(command) + 1 ];
  828. strcpy(cmd, command);
  829. bRet = CreateProcess(
  830. app, cmd,
  831. 0, 0,
  832. TRUE, 0,
  833. 0, 0,
  834. &si, &pi
  835. );
  836. delete [] cmd;
  837. if (bRet)
  838. {
  839. if (WaitForSingleObject(pi.hProcess, INFINITE) != WAIT_FAILED)
  840. {
  841. GetExitCodeProcess(pi.hProcess, &exit_code);
  842. }
  843. CloseHandle(pi.hProcess);
  844. CloseHandle(pi.hThread);
  845. return exit_code;
  846. }
  847. return 1;
  848. }