cmWin32ProcessExecution.cxx 26 KB

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