cmWin32ProcessExecution.cxx 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614
  1. /*=========================================================================
  2. Program: Insight Segmentation & Registration Toolkit
  3. Module: $RCSfile$
  4. Language: C++
  5. Date: $Date$
  6. Version: $Revision$
  7. Copyright (c) 2002 Insight Consortium. All rights reserved.
  8. See ITKCopyright.txt or http://www.itk.org/HTML/Copyright.htm 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 <stdio.h>
  19. #include <sys/stat.h>
  20. #include <windows.h>
  21. #define POPEN_1 1
  22. #define POPEN_2 2
  23. #define POPEN_3 3
  24. #define POPEN_4 4
  25. #define cmMAX(x,y) (((x)<(y))?(y):(x))
  26. #define win32_error(x,y) std::cout << "Win32_Error(" << x << ", " << y << ")" << std::endl, false
  27. bool cmWin32ProcessExecution::StartProcess(
  28. const char* cmd, const char* path, bool verbose)
  29. {
  30. this->Initialize();
  31. this->m_Verbose = verbose;
  32. return this->PrivateOpen(cmd, path, _O_RDONLY | _O_TEXT, POPEN_3);
  33. }
  34. bool cmWin32ProcessExecution::Wait(int timeout)
  35. {
  36. return this->PrivateClose(timeout);
  37. }
  38. /*
  39. * Internal dictionary mapping popen* file pointers to process handles,
  40. * for use when retrieving the process exit code. See _PyPclose() below
  41. * for more information on this dictionary's use.
  42. */
  43. static void *_PyPopenProcs = NULL;
  44. static BOOL RealPopenCreateProcess(const char *cmdstring,
  45. const char *path,
  46. const char *szConsoleSpawn,
  47. HANDLE hStdin,
  48. HANDLE hStdout,
  49. HANDLE hStderr,
  50. HANDLE *hProcess)
  51. {
  52. PROCESS_INFORMATION piProcInfo;
  53. STARTUPINFO siStartInfo;
  54. char *s1,*s2, *s3 = " /c ";
  55. int i;
  56. int x;
  57. if (i = GetEnvironmentVariable("COMSPEC",NULL,0))
  58. {
  59. char *comshell;
  60. s1 = (char *)_alloca(i);
  61. if (!(x = GetEnvironmentVariable("COMSPEC", s1, i)))
  62. return x;
  63. /* Explicitly check if we are using COMMAND.COM. If we are
  64. * then use the w9xpopen hack.
  65. */
  66. comshell = s1 + x;
  67. while (comshell >= s1 && *comshell != '\\')
  68. --comshell;
  69. ++comshell;
  70. if (GetVersion() < 0x80000000 &&
  71. _stricmp(comshell, "command.com") != 0)
  72. {
  73. /* NT/2000 and not using command.com. */
  74. x = i + (int)strlen(s3) + (int)strlen(cmdstring) + 1;
  75. s2 = (char *)_alloca(x);
  76. ZeroMemory(s2, x);
  77. sprintf(s2, "%s%s%s", s1, s3, cmdstring);
  78. //std::cout << "s1: " << s1 << " s2: " << s2 << " s3: " << s3
  79. // << std::endl;
  80. }
  81. else
  82. {
  83. /*
  84. * Oh gag, we're on Win9x or using COMMAND.COM. Use
  85. * the workaround listed in KB: Q150956
  86. */
  87. char modulepath[_MAX_PATH];
  88. struct stat statinfo;
  89. GetModuleFileName(NULL, modulepath, sizeof(modulepath));
  90. for (i = x = 0; modulepath[i]; i++)
  91. if (modulepath[i] == '\\')
  92. x = i+1;
  93. modulepath[x] = '\0';
  94. /* Create the full-name to w9xpopen, so we can test it exists */
  95. strncat(modulepath,
  96. szConsoleSpawn,
  97. (sizeof(modulepath)/sizeof(modulepath[0]))
  98. -strlen(modulepath));
  99. if (stat(modulepath, &statinfo) != 0)
  100. {
  101. /* Eeek - file-not-found - possibly an embedding
  102. situation - see if we can locate it in sys.prefix
  103. */
  104. strncpy(modulepath,
  105. ".",
  106. sizeof(modulepath)/sizeof(modulepath[0]));
  107. if (modulepath[strlen(modulepath)-1] != '\\')
  108. strcat(modulepath, "\\");
  109. strncat(modulepath,
  110. szConsoleSpawn,
  111. (sizeof(modulepath)/sizeof(modulepath[0]))
  112. -strlen(modulepath));
  113. /* No where else to look - raise an easily identifiable
  114. error, rather than leaving Windows to report
  115. "file not found" - as the user is probably blissfully
  116. unaware this shim EXE is used, and it will confuse them.
  117. (well, it confused me for a while ;-)
  118. */
  119. if (stat(modulepath, &statinfo) != 0)
  120. {
  121. std::cout
  122. << "Can not locate '" << szConsoleSpawn
  123. << "' which is needed "
  124. "for popen to work with your shell "
  125. "or platform." << std::endl;
  126. return FALSE;
  127. }
  128. }
  129. x = i + (int)strlen(s3) + (int)strlen(cmdstring) + 1 +
  130. (int)strlen(modulepath) +
  131. (int)strlen(szConsoleSpawn) + 1;
  132. s2 = (char *)_alloca(x);
  133. ZeroMemory(s2, x);
  134. sprintf(
  135. s2,
  136. "%s \"%s%s%s\"",
  137. modulepath,
  138. s1,
  139. s3,
  140. cmdstring);
  141. }
  142. }
  143. /* Could be an else here to try cmd.exe / command.com in the path
  144. Now we'll just error out.. */
  145. else
  146. {
  147. std::cout << "Cannot locate a COMSPEC environment variable to "
  148. << "use as the shell" << std::endl;
  149. return FALSE;
  150. }
  151. ZeroMemory(&siStartInfo, sizeof(STARTUPINFO));
  152. siStartInfo.cb = sizeof(STARTUPINFO);
  153. siStartInfo.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
  154. siStartInfo.hStdInput = hStdin;
  155. siStartInfo.hStdOutput = hStdout;
  156. siStartInfo.hStdError = hStderr;
  157. siStartInfo.wShowWindow = SW_HIDE;
  158. if (CreateProcess(NULL,
  159. s2,
  160. NULL,
  161. NULL,
  162. TRUE,
  163. CREATE_NEW_CONSOLE,
  164. NULL,
  165. path,
  166. &siStartInfo,
  167. &piProcInfo) )
  168. {
  169. /* Close the handles now so anyone waiting is woken. */
  170. CloseHandle(piProcInfo.hThread);
  171. /* Return process handle */
  172. *hProcess = piProcInfo.hProcess;
  173. return TRUE;
  174. }
  175. win32_error("CreateProcess", s2);
  176. return FALSE;
  177. }
  178. /* The following code is based off of KB: Q190351 */
  179. bool cmWin32ProcessExecution::PrivateOpen(const char *cmdstring,
  180. const char* path,
  181. int mode,
  182. int n)
  183. {
  184. HANDLE hChildStdinRd, hChildStdinWr, hChildStdoutRd, hChildStdoutWr,
  185. hChildStderrRd, hChildStderrWr, hChildStdinWrDup, hChildStdoutRdDup,
  186. hChildStderrRdDup, hProcess; /* hChildStdoutWrDup; */
  187. SECURITY_ATTRIBUTES saAttr;
  188. BOOL fSuccess;
  189. int fd1, fd2, fd3;
  190. FILE *f1, *f2, *f3;
  191. long file_count;
  192. saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
  193. saAttr.bInheritHandle = TRUE;
  194. saAttr.lpSecurityDescriptor = NULL;
  195. if (!CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0))
  196. return win32_error("CreatePipe", NULL);
  197. /* Create new output read handle and the input write handle. Set
  198. * the inheritance properties to FALSE. Otherwise, the child inherits
  199. * the these handles; resulting in non-closeable handles to the pipes
  200. * being created. */
  201. fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,
  202. GetCurrentProcess(), &hChildStdinWrDup, 0,
  203. FALSE,
  204. DUPLICATE_SAME_ACCESS);
  205. if (!fSuccess)
  206. return win32_error("DuplicateHandle", NULL);
  207. /* Close the inheritable version of ChildStdin
  208. that we're using. */
  209. CloseHandle(hChildStdinWr);
  210. if (!CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0))
  211. return win32_error("CreatePipe", NULL);
  212. fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,
  213. GetCurrentProcess(), &hChildStdoutRdDup, 0,
  214. FALSE, DUPLICATE_SAME_ACCESS);
  215. if (!fSuccess)
  216. return win32_error("DuplicateHandle", NULL);
  217. /* Close the inheritable version of ChildStdout
  218. that we're using. */
  219. CloseHandle(hChildStdoutRd);
  220. if (n != POPEN_4)
  221. {
  222. if (!CreatePipe(&hChildStderrRd, &hChildStderrWr, &saAttr, 0))
  223. return win32_error("CreatePipe", NULL);
  224. fSuccess = DuplicateHandle(GetCurrentProcess(),
  225. hChildStderrRd,
  226. GetCurrentProcess(),
  227. &hChildStderrRdDup, 0,
  228. FALSE, DUPLICATE_SAME_ACCESS);
  229. if (!fSuccess)
  230. return win32_error("DuplicateHandle", NULL);
  231. /* Close the inheritable version of ChildStdErr that we're using. */
  232. CloseHandle(hChildStderrRd);
  233. }
  234. switch (n)
  235. {
  236. case POPEN_1:
  237. switch (mode & (_O_RDONLY | _O_TEXT | _O_BINARY | _O_WRONLY))
  238. {
  239. case _O_WRONLY | _O_TEXT:
  240. /* Case for writing to child Stdin in text mode. */
  241. fd1 = _open_osfhandle((intptr_t)hChildStdinWrDup, mode);
  242. f1 = _fdopen(fd1, "w");
  243. /* We don't care about these pipes anymore,
  244. so close them. */
  245. CloseHandle(hChildStdoutRdDup);
  246. CloseHandle(hChildStderrRdDup);
  247. break;
  248. case _O_RDONLY | _O_TEXT:
  249. /* Case for reading from child Stdout in text mode. */
  250. fd1 = _open_osfhandle((intptr_t)hChildStdoutRdDup, mode);
  251. f1 = _fdopen(fd1, "r");
  252. /* We don't care about these pipes anymore,
  253. so close them. */
  254. CloseHandle(hChildStdinWrDup);
  255. CloseHandle(hChildStderrRdDup);
  256. break;
  257. case _O_RDONLY | _O_BINARY:
  258. /* Case for readinig from child Stdout in
  259. binary mode. */
  260. fd1 = _open_osfhandle((intptr_t)hChildStdoutRdDup, mode);
  261. f1 = _fdopen(fd1, "rb");
  262. /* We don't care about these pipes anymore,
  263. so close them. */
  264. CloseHandle(hChildStdinWrDup);
  265. CloseHandle(hChildStderrRdDup);
  266. break;
  267. case _O_WRONLY | _O_BINARY:
  268. /* Case for writing to child Stdin in binary mode. */
  269. fd1 = _open_osfhandle((intptr_t)hChildStdinWrDup, mode);
  270. f1 = _fdopen(fd1, "wb");
  271. /* We don't care about these pipes anymore,
  272. so close them. */
  273. CloseHandle(hChildStdoutRdDup);
  274. CloseHandle(hChildStderrRdDup);
  275. break;
  276. }
  277. file_count = 1;
  278. break;
  279. case POPEN_2:
  280. case POPEN_4:
  281. if ( 1 )
  282. {
  283. char *m1, *m2;
  284. if (mode && _O_TEXT)
  285. {
  286. m1 = "r";
  287. m2 = "w";
  288. }
  289. else
  290. {
  291. m1 = "rb";
  292. m2 = "wb";
  293. }
  294. fd1 = _open_osfhandle((intptr_t)hChildStdinWrDup, mode);
  295. f1 = _fdopen(fd1, m2);
  296. fd2 = _open_osfhandle((intptr_t)hChildStdoutRdDup, mode);
  297. f2 = _fdopen(fd2, m1);
  298. if (n != 4)
  299. CloseHandle(hChildStderrRdDup);
  300. file_count = 2;
  301. break;
  302. }
  303. case POPEN_3:
  304. if ( 1)
  305. {
  306. char *m1, *m2;
  307. if (mode && _O_TEXT)
  308. {
  309. m1 = "r";
  310. m2 = "w";
  311. }
  312. else
  313. {
  314. m1 = "rb";
  315. m2 = "wb";
  316. }
  317. fd1 = _open_osfhandle((intptr_t)hChildStdinWrDup, mode);
  318. f1 = _fdopen(fd1, m2);
  319. fd2 = _open_osfhandle((intptr_t)hChildStdoutRdDup, mode);
  320. f2 = _fdopen(fd2, m1);
  321. fd3 = _open_osfhandle((intptr_t)hChildStderrRdDup, mode);
  322. f3 = _fdopen(fd3, m1);
  323. file_count = 3;
  324. break;
  325. }
  326. }
  327. if (n == POPEN_4)
  328. {
  329. if (!RealPopenCreateProcess(cmdstring,
  330. path,
  331. this->m_ConsoleSpawn.c_str(),
  332. hChildStdinRd,
  333. hChildStdoutWr,
  334. hChildStdoutWr,
  335. &hProcess))
  336. return NULL;
  337. }
  338. else
  339. {
  340. if (!RealPopenCreateProcess(cmdstring,
  341. path,
  342. this->m_ConsoleSpawn.c_str(),
  343. hChildStdinRd,
  344. hChildStdoutWr,
  345. hChildStderrWr,
  346. &hProcess))
  347. return NULL;
  348. }
  349. /*
  350. * Insert the files we've created into the process dictionary
  351. * all referencing the list with the process handle and the
  352. * initial number of files (see description below in _PyPclose).
  353. * Since if _PyPclose later tried to wait on a process when all
  354. * handles weren't closed, it could create a deadlock with the
  355. * child, we spend some energy here to try to ensure that we
  356. * either insert all file handles into the dictionary or none
  357. * at all. It's a little clumsy with the various popen modes
  358. * and variable number of files involved.
  359. */
  360. /* Child is launched. Close the parents copy of those pipe
  361. * handles that only the child should have open. You need to
  362. * make sure that no handles to the write end of the output pipe
  363. * are maintained in this process or else the pipe will not close
  364. * when the child process exits and the ReadFile will hang. */
  365. if (!CloseHandle(hChildStdinRd))
  366. return win32_error("CloseHandle", NULL);
  367. if (!CloseHandle(hChildStdoutWr))
  368. return win32_error("CloseHandle", NULL);
  369. if ((n != 4) && (!CloseHandle(hChildStderrWr)))
  370. return win32_error("CloseHandle", NULL);
  371. this->m_ProcessHandle = hProcess;
  372. if ( f1 )
  373. {
  374. this->m_StdIn = f1;
  375. this->m_pStdIn = fd1;
  376. }
  377. if ( f2 )
  378. {
  379. this->m_StdOut = f2;
  380. this->m_pStdOut = fd2;
  381. }
  382. if ( f3 )
  383. {
  384. this->m_StdErr = f3;
  385. this->m_pStdErr = fd3;
  386. }
  387. return true;
  388. }
  389. /*
  390. * Wrapper for fclose() to use for popen* files, so we can retrieve the
  391. * exit code for the child process and return as a result of the close.
  392. *
  393. * This function uses the _PyPopenProcs dictionary in order to map the
  394. * input file pointer to information about the process that was
  395. * originally created by the popen* call that created the file pointer.
  396. * The dictionary uses the file pointer as a key (with one entry
  397. * inserted for each file returned by the original popen* call) and a
  398. * single list object as the value for all files from a single call.
  399. * The list object contains the Win32 process handle at [0], and a file
  400. * count at [1], which is initialized to the total number of file
  401. * handles using that list.
  402. *
  403. * This function closes whichever handle it is passed, and decrements
  404. * the file count in the dictionary for the process handle pointed to
  405. * by this file. On the last close (when the file count reaches zero),
  406. * this function will wait for the child process and then return its
  407. * exit code as the result of the close() operation. This permits the
  408. * files to be closed in any order - it is always the close() of the
  409. * final handle that will return the exit code.
  410. */
  411. /* RED_FLAG 31-Aug-2000 Tim
  412. * This is always called (today!) between a pair of
  413. * Py_BEGIN_ALLOW_THREADS/ Py_END_ALLOW_THREADS
  414. * macros. So the thread running this has no valid thread state, as
  415. * far as Python is concerned. However, this calls some Python API
  416. * functions that cannot be called safely without a valid thread
  417. * state, in particular PyDict_GetItem.
  418. * As a temporary hack (although it may last for years ...), we
  419. * *rely* on not having a valid thread state in this function, in
  420. * order to create our own "from scratch".
  421. * This will deadlock if _PyPclose is ever called by a thread
  422. * holding the global lock.
  423. */
  424. bool cmWin32ProcessExecution::PrivateClose(int timeout)
  425. {
  426. HANDLE hProcess = this->m_ProcessHandle;
  427. int result = -1;
  428. DWORD exit_code;
  429. std::string output = "";
  430. bool done = false;
  431. int stdoutloc = 0;
  432. int stderrloc = 0;
  433. while(!done)
  434. {
  435. Sleep(10);
  436. struct _stat fsout;
  437. struct _stat fserr;
  438. int rout = _fstat(this->m_pStdOut, &fsout);
  439. int rerr = _fstat(this->m_pStdErr, &fserr);
  440. if ( rout && rerr )
  441. {
  442. break;
  443. }
  444. if (fserr.st_size > 0)
  445. {
  446. int dist = fserr.st_size;
  447. char buffer[1023];
  448. int len = read(this->m_pStdErr, buffer, 1023);
  449. buffer[len] = 0;
  450. if ( this->m_Verbose )
  451. {
  452. std::cout << buffer << std::flush;
  453. }
  454. output += buffer;
  455. }
  456. if (fsout.st_size > 0)
  457. {
  458. int dist = fsout.st_size;
  459. char buffer[1023];
  460. int len = read(this->m_pStdOut, buffer, 1023);
  461. buffer[len] = 0;
  462. if ( this->m_Verbose )
  463. {
  464. std::cout << buffer << std::flush;
  465. }
  466. output += buffer;
  467. }
  468. unsigned long exitCode;
  469. GetExitCodeProcess(hProcess,&exitCode);
  470. if (exitCode != STILL_ACTIVE)
  471. {
  472. break;
  473. }
  474. }
  475. if (WaitForSingleObject(hProcess, INFINITE) != WAIT_FAILED &&
  476. GetExitCodeProcess(hProcess, &exit_code))
  477. {
  478. result = exit_code;
  479. }
  480. else
  481. {
  482. /* Indicate failure - this will cause the file object
  483. * to raise an I/O error and translate the last Win32
  484. * error code from errno. We do have a problem with
  485. * last errors that overlap the normal errno table,
  486. * but that's a consistent problem with the file object.
  487. */
  488. if (result != EOF)
  489. {
  490. /* If the error wasn't from the fclose(), then
  491. * set errno for the file object error handling.
  492. */
  493. errno = GetLastError();
  494. }
  495. result = -1;
  496. }
  497. /* Free up the native handle at this point */
  498. CloseHandle(hProcess);
  499. this->m_ExitValue = result;
  500. this->m_Output = output;
  501. if ( result < 0 )
  502. {
  503. return false;
  504. }
  505. return true;
  506. }
  507. int cmWin32ProcessExecution::Windows9xHack(const char* command)
  508. {
  509. BOOL bRet;
  510. STARTUPINFO si;
  511. PROCESS_INFORMATION pi;
  512. DWORD exit_code=0;
  513. if (!command)
  514. {
  515. cmSystemTools::Error("Windows9xHack: Command not specified");
  516. return 1;
  517. }
  518. /* Make child process use this app's standard files. */
  519. ZeroMemory(&si, sizeof si);
  520. si.cb = sizeof si;
  521. si.dwFlags = STARTF_USESTDHANDLES;
  522. si.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
  523. si.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
  524. si.hStdError = GetStdHandle(STD_ERROR_HANDLE);
  525. char* cmd = new char[ strlen(command) + 1 ];
  526. strcpy(cmd, command);
  527. bRet = CreateProcess(
  528. NULL, cmd,
  529. NULL, NULL,
  530. TRUE, 0,
  531. NULL, NULL,
  532. &si, &pi
  533. );
  534. delete [] cmd;
  535. if (bRet)
  536. {
  537. if (WaitForSingleObject(pi.hProcess, INFINITE) != WAIT_FAILED)
  538. {
  539. GetExitCodeProcess(pi.hProcess, &exit_code);
  540. }
  541. CloseHandle(pi.hProcess);
  542. CloseHandle(pi.hThread);
  543. return exit_code;
  544. }
  545. return 1;
  546. }