| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614 |
- /*=========================================================================
- Program: Insight Segmentation & Registration Toolkit
- Module: $RCSfile$
- Language: C++
- Date: $Date$
- Version: $Revision$
- Copyright (c) 2002 Insight Consortium. All rights reserved.
- See ITKCopyright.txt or http://www.itk.org/HTML/Copyright.htm for details.
- This software is distributed WITHOUT ANY WARRANTY; without even
- the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
- PURPOSE. See the above copyright notices for more information.
- =========================================================================*/
- #include "cmWin32ProcessExecution.h"
- #include "cmSystemTools.h"
- #include <malloc.h>
- #include <io.h>
- #include <fcntl.h>
- #include <stdio.h>
- #include <sys/stat.h>
- #include <windows.h>
- #define POPEN_1 1
- #define POPEN_2 2
- #define POPEN_3 3
- #define POPEN_4 4
- #define cmMAX(x,y) (((x)<(y))?(y):(x))
- #define win32_error(x,y) std::cout << "Win32_Error(" << x << ", " << y << ")" << std::endl, false
- bool cmWin32ProcessExecution::StartProcess(
- const char* cmd, const char* path, bool verbose)
- {
- this->Initialize();
- this->m_Verbose = verbose;
- return this->PrivateOpen(cmd, path, _O_RDONLY | _O_TEXT, POPEN_3);
- }
- bool cmWin32ProcessExecution::Wait(int timeout)
- {
- return this->PrivateClose(timeout);
- }
- /*
- * Internal dictionary mapping popen* file pointers to process handles,
- * for use when retrieving the process exit code. See _PyPclose() below
- * for more information on this dictionary's use.
- */
- static void *_PyPopenProcs = NULL;
- static BOOL RealPopenCreateProcess(const char *cmdstring,
- const char *path,
- const char *szConsoleSpawn,
- HANDLE hStdin,
- HANDLE hStdout,
- HANDLE hStderr,
- HANDLE *hProcess)
- {
- PROCESS_INFORMATION piProcInfo;
- STARTUPINFO siStartInfo;
- char *s1,*s2, *s3 = " /c ";
- int i;
- int x;
- if (i = GetEnvironmentVariable("COMSPEC",NULL,0))
- {
- char *comshell;
- s1 = (char *)_alloca(i);
- if (!(x = GetEnvironmentVariable("COMSPEC", s1, i)))
- return x;
- /* Explicitly check if we are using COMMAND.COM. If we are
- * then use the w9xpopen hack.
- */
- comshell = s1 + x;
- while (comshell >= s1 && *comshell != '\\')
- --comshell;
- ++comshell;
- if (GetVersion() < 0x80000000 &&
- _stricmp(comshell, "command.com") != 0)
- {
- /* NT/2000 and not using command.com. */
- x = i + (int)strlen(s3) + (int)strlen(cmdstring) + 1;
- s2 = (char *)_alloca(x);
- ZeroMemory(s2, x);
- sprintf(s2, "%s%s%s", s1, s3, cmdstring);
- //std::cout << "s1: " << s1 << " s2: " << s2 << " s3: " << s3
- // << std::endl;
- }
- else
- {
- /*
- * Oh gag, we're on Win9x or using COMMAND.COM. Use
- * the workaround listed in KB: Q150956
- */
- char modulepath[_MAX_PATH];
- struct stat statinfo;
- GetModuleFileName(NULL, modulepath, sizeof(modulepath));
- for (i = x = 0; modulepath[i]; i++)
- if (modulepath[i] == '\\')
- x = i+1;
- modulepath[x] = '\0';
- /* Create the full-name to w9xpopen, so we can test it exists */
- strncat(modulepath,
- szConsoleSpawn,
- (sizeof(modulepath)/sizeof(modulepath[0]))
- -strlen(modulepath));
- if (stat(modulepath, &statinfo) != 0)
- {
- /* Eeek - file-not-found - possibly an embedding
- situation - see if we can locate it in sys.prefix
- */
- strncpy(modulepath,
- ".",
- sizeof(modulepath)/sizeof(modulepath[0]));
- if (modulepath[strlen(modulepath)-1] != '\\')
- strcat(modulepath, "\\");
- strncat(modulepath,
- szConsoleSpawn,
- (sizeof(modulepath)/sizeof(modulepath[0]))
- -strlen(modulepath));
- /* No where else to look - raise an easily identifiable
- error, rather than leaving Windows to report
- "file not found" - as the user is probably blissfully
- unaware this shim EXE is used, and it will confuse them.
- (well, it confused me for a while ;-)
- */
- if (stat(modulepath, &statinfo) != 0)
- {
- std::cout
- << "Can not locate '" << szConsoleSpawn
- << "' which is needed "
- "for popen to work with your shell "
- "or platform." << std::endl;
- return FALSE;
- }
- }
- x = i + (int)strlen(s3) + (int)strlen(cmdstring) + 1 +
- (int)strlen(modulepath) +
- (int)strlen(szConsoleSpawn) + 1;
- s2 = (char *)_alloca(x);
- ZeroMemory(s2, x);
- sprintf(
- s2,
- "%s \"%s%s%s\"",
- modulepath,
- s1,
- s3,
- cmdstring);
- }
- }
- /* Could be an else here to try cmd.exe / command.com in the path
- Now we'll just error out.. */
- else
- {
- std::cout << "Cannot locate a COMSPEC environment variable to "
- << "use as the shell" << std::endl;
- return FALSE;
- }
-
- ZeroMemory(&siStartInfo, sizeof(STARTUPINFO));
- siStartInfo.cb = sizeof(STARTUPINFO);
- siStartInfo.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
- siStartInfo.hStdInput = hStdin;
- siStartInfo.hStdOutput = hStdout;
- siStartInfo.hStdError = hStderr;
- siStartInfo.wShowWindow = SW_HIDE;
- if (CreateProcess(NULL,
- s2,
- NULL,
- NULL,
- TRUE,
- CREATE_NEW_CONSOLE,
- NULL,
- path,
- &siStartInfo,
- &piProcInfo) )
- {
- /* Close the handles now so anyone waiting is woken. */
- CloseHandle(piProcInfo.hThread);
- /* Return process handle */
- *hProcess = piProcInfo.hProcess;
- return TRUE;
- }
- win32_error("CreateProcess", s2);
- return FALSE;
- }
- /* The following code is based off of KB: Q190351 */
- bool cmWin32ProcessExecution::PrivateOpen(const char *cmdstring,
- const char* path,
- int mode,
- int n)
- {
- HANDLE hChildStdinRd, hChildStdinWr, hChildStdoutRd, hChildStdoutWr,
- hChildStderrRd, hChildStderrWr, hChildStdinWrDup, hChildStdoutRdDup,
- hChildStderrRdDup, hProcess; /* hChildStdoutWrDup; */
- SECURITY_ATTRIBUTES saAttr;
- BOOL fSuccess;
- int fd1, fd2, fd3;
- FILE *f1, *f2, *f3;
- long file_count;
- saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
- saAttr.bInheritHandle = TRUE;
- saAttr.lpSecurityDescriptor = NULL;
- if (!CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0))
- return win32_error("CreatePipe", NULL);
- /* Create new output read handle and the input write handle. Set
- * the inheritance properties to FALSE. Otherwise, the child inherits
- * the these handles; resulting in non-closeable handles to the pipes
- * being created. */
- fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,
- GetCurrentProcess(), &hChildStdinWrDup, 0,
- FALSE,
- DUPLICATE_SAME_ACCESS);
- if (!fSuccess)
- return win32_error("DuplicateHandle", NULL);
- /* Close the inheritable version of ChildStdin
- that we're using. */
- CloseHandle(hChildStdinWr);
- if (!CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0))
- return win32_error("CreatePipe", NULL);
- fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,
- GetCurrentProcess(), &hChildStdoutRdDup, 0,
- FALSE, DUPLICATE_SAME_ACCESS);
- if (!fSuccess)
- return win32_error("DuplicateHandle", NULL);
- /* Close the inheritable version of ChildStdout
- that we're using. */
- CloseHandle(hChildStdoutRd);
- if (n != POPEN_4)
- {
- if (!CreatePipe(&hChildStderrRd, &hChildStderrWr, &saAttr, 0))
- return win32_error("CreatePipe", NULL);
- fSuccess = DuplicateHandle(GetCurrentProcess(),
- hChildStderrRd,
- GetCurrentProcess(),
- &hChildStderrRdDup, 0,
- FALSE, DUPLICATE_SAME_ACCESS);
- if (!fSuccess)
- return win32_error("DuplicateHandle", NULL);
- /* Close the inheritable version of ChildStdErr that we're using. */
- CloseHandle(hChildStderrRd);
- }
-
- switch (n)
- {
- case POPEN_1:
- switch (mode & (_O_RDONLY | _O_TEXT | _O_BINARY | _O_WRONLY))
- {
- case _O_WRONLY | _O_TEXT:
- /* Case for writing to child Stdin in text mode. */
- fd1 = _open_osfhandle((intptr_t)hChildStdinWrDup, mode);
- f1 = _fdopen(fd1, "w");
- /* We don't care about these pipes anymore,
- so close them. */
- CloseHandle(hChildStdoutRdDup);
- CloseHandle(hChildStderrRdDup);
- break;
- case _O_RDONLY | _O_TEXT:
- /* Case for reading from child Stdout in text mode. */
- fd1 = _open_osfhandle((intptr_t)hChildStdoutRdDup, mode);
- f1 = _fdopen(fd1, "r");
- /* We don't care about these pipes anymore,
- so close them. */
- CloseHandle(hChildStdinWrDup);
- CloseHandle(hChildStderrRdDup);
- break;
- case _O_RDONLY | _O_BINARY:
- /* Case for readinig from child Stdout in
- binary mode. */
- fd1 = _open_osfhandle((intptr_t)hChildStdoutRdDup, mode);
- f1 = _fdopen(fd1, "rb");
- /* We don't care about these pipes anymore,
- so close them. */
- CloseHandle(hChildStdinWrDup);
- CloseHandle(hChildStderrRdDup);
- break;
- case _O_WRONLY | _O_BINARY:
- /* Case for writing to child Stdin in binary mode. */
- fd1 = _open_osfhandle((intptr_t)hChildStdinWrDup, mode);
- f1 = _fdopen(fd1, "wb");
- /* We don't care about these pipes anymore,
- so close them. */
- CloseHandle(hChildStdoutRdDup);
- CloseHandle(hChildStderrRdDup);
- break;
- }
- file_count = 1;
- break;
-
- case POPEN_2:
- case POPEN_4:
- if ( 1 )
- {
- char *m1, *m2;
-
- if (mode && _O_TEXT)
- {
- m1 = "r";
- m2 = "w";
- }
- else
- {
- m1 = "rb";
- m2 = "wb";
- }
- fd1 = _open_osfhandle((intptr_t)hChildStdinWrDup, mode);
- f1 = _fdopen(fd1, m2);
- fd2 = _open_osfhandle((intptr_t)hChildStdoutRdDup, mode);
- f2 = _fdopen(fd2, m1);
- if (n != 4)
- CloseHandle(hChildStderrRdDup);
- file_count = 2;
- break;
- }
-
- case POPEN_3:
- if ( 1)
- {
- char *m1, *m2;
-
- if (mode && _O_TEXT)
- {
- m1 = "r";
- m2 = "w";
- }
- else
- {
- m1 = "rb";
- m2 = "wb";
- }
- fd1 = _open_osfhandle((intptr_t)hChildStdinWrDup, mode);
- f1 = _fdopen(fd1, m2);
- fd2 = _open_osfhandle((intptr_t)hChildStdoutRdDup, mode);
- f2 = _fdopen(fd2, m1);
- fd3 = _open_osfhandle((intptr_t)hChildStderrRdDup, mode);
- f3 = _fdopen(fd3, m1);
- file_count = 3;
- break;
- }
- }
- if (n == POPEN_4)
- {
- if (!RealPopenCreateProcess(cmdstring,
- path,
- this->m_ConsoleSpawn.c_str(),
- hChildStdinRd,
- hChildStdoutWr,
- hChildStdoutWr,
- &hProcess))
- return NULL;
- }
- else
- {
- if (!RealPopenCreateProcess(cmdstring,
- path,
- this->m_ConsoleSpawn.c_str(),
- hChildStdinRd,
- hChildStdoutWr,
- hChildStderrWr,
- &hProcess))
- return NULL;
- }
- /*
- * Insert the files we've created into the process dictionary
- * all referencing the list with the process handle and the
- * initial number of files (see description below in _PyPclose).
- * Since if _PyPclose later tried to wait on a process when all
- * handles weren't closed, it could create a deadlock with the
- * child, we spend some energy here to try to ensure that we
- * either insert all file handles into the dictionary or none
- * at all. It's a little clumsy with the various popen modes
- * and variable number of files involved.
- */
- /* Child is launched. Close the parents copy of those pipe
- * handles that only the child should have open. You need to
- * make sure that no handles to the write end of the output pipe
- * are maintained in this process or else the pipe will not close
- * when the child process exits and the ReadFile will hang. */
- if (!CloseHandle(hChildStdinRd))
- return win32_error("CloseHandle", NULL);
-
- if (!CloseHandle(hChildStdoutWr))
- return win32_error("CloseHandle", NULL);
-
- if ((n != 4) && (!CloseHandle(hChildStderrWr)))
- return win32_error("CloseHandle", NULL);
-
- this->m_ProcessHandle = hProcess;
- if ( f1 )
- {
- this->m_StdIn = f1;
- this->m_pStdIn = fd1;
- }
- if ( f2 )
- {
- this->m_StdOut = f2;
- this->m_pStdOut = fd2;
- }
- if ( f3 )
- {
- this->m_StdErr = f3;
- this->m_pStdErr = fd3;
- }
- return true;
- }
- /*
- * Wrapper for fclose() to use for popen* files, so we can retrieve the
- * exit code for the child process and return as a result of the close.
- *
- * This function uses the _PyPopenProcs dictionary in order to map the
- * input file pointer to information about the process that was
- * originally created by the popen* call that created the file pointer.
- * The dictionary uses the file pointer as a key (with one entry
- * inserted for each file returned by the original popen* call) and a
- * single list object as the value for all files from a single call.
- * The list object contains the Win32 process handle at [0], and a file
- * count at [1], which is initialized to the total number of file
- * handles using that list.
- *
- * This function closes whichever handle it is passed, and decrements
- * the file count in the dictionary for the process handle pointed to
- * by this file. On the last close (when the file count reaches zero),
- * this function will wait for the child process and then return its
- * exit code as the result of the close() operation. This permits the
- * files to be closed in any order - it is always the close() of the
- * final handle that will return the exit code.
- */
- /* RED_FLAG 31-Aug-2000 Tim
- * This is always called (today!) between a pair of
- * Py_BEGIN_ALLOW_THREADS/ Py_END_ALLOW_THREADS
- * macros. So the thread running this has no valid thread state, as
- * far as Python is concerned. However, this calls some Python API
- * functions that cannot be called safely without a valid thread
- * state, in particular PyDict_GetItem.
- * As a temporary hack (although it may last for years ...), we
- * *rely* on not having a valid thread state in this function, in
- * order to create our own "from scratch".
- * This will deadlock if _PyPclose is ever called by a thread
- * holding the global lock.
- */
- bool cmWin32ProcessExecution::PrivateClose(int timeout)
- {
- HANDLE hProcess = this->m_ProcessHandle;
- int result = -1;
- DWORD exit_code;
- std::string output = "";
- bool done = false;
- int stdoutloc = 0;
- int stderrloc = 0;
- while(!done)
- {
- Sleep(10);
- struct _stat fsout;
- struct _stat fserr;
- int rout = _fstat(this->m_pStdOut, &fsout);
- int rerr = _fstat(this->m_pStdErr, &fserr);
- if ( rout && rerr )
- {
- break;
- }
- if (fserr.st_size > 0)
- {
- int dist = fserr.st_size;
- char buffer[1023];
- int len = read(this->m_pStdErr, buffer, 1023);
- buffer[len] = 0;
- if ( this->m_Verbose )
- {
- std::cout << buffer << std::flush;
- }
- output += buffer;
- }
- if (fsout.st_size > 0)
- {
- int dist = fsout.st_size;
- char buffer[1023];
- int len = read(this->m_pStdOut, buffer, 1023);
- buffer[len] = 0;
- if ( this->m_Verbose )
- {
- std::cout << buffer << std::flush;
- }
- output += buffer;
- }
- unsigned long exitCode;
- GetExitCodeProcess(hProcess,&exitCode);
- if (exitCode != STILL_ACTIVE)
- {
- break;
- }
- }
-
- if (WaitForSingleObject(hProcess, INFINITE) != WAIT_FAILED &&
- GetExitCodeProcess(hProcess, &exit_code))
- {
- result = exit_code;
- }
- else
- {
- /* Indicate failure - this will cause the file object
- * to raise an I/O error and translate the last Win32
- * error code from errno. We do have a problem with
- * last errors that overlap the normal errno table,
- * but that's a consistent problem with the file object.
- */
- if (result != EOF)
- {
- /* If the error wasn't from the fclose(), then
- * set errno for the file object error handling.
- */
- errno = GetLastError();
- }
- result = -1;
- }
-
- /* Free up the native handle at this point */
- CloseHandle(hProcess);
- this->m_ExitValue = result;
- this->m_Output = output;
- if ( result < 0 )
- {
- return false;
- }
- return true;
- }
- int cmWin32ProcessExecution::Windows9xHack(const char* command)
- {
- BOOL bRet;
- STARTUPINFO si;
- PROCESS_INFORMATION pi;
- DWORD exit_code=0;
- if (!command)
- {
- cmSystemTools::Error("Windows9xHack: Command not specified");
- return 1;
- }
- /* Make child process use this app's standard files. */
- ZeroMemory(&si, sizeof si);
- si.cb = sizeof si;
- si.dwFlags = STARTF_USESTDHANDLES;
- si.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
- si.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
- si.hStdError = GetStdHandle(STD_ERROR_HANDLE);
- char* cmd = new char[ strlen(command) + 1 ];
- strcpy(cmd, command);
- bRet = CreateProcess(
- NULL, cmd,
- NULL, NULL,
- TRUE, 0,
- NULL, NULL,
- &si, &pi
- );
- delete [] cmd;
- if (bRet)
- {
- if (WaitForSingleObject(pi.hProcess, INFINITE) != WAIT_FAILED)
- {
- GetExitCodeProcess(pi.hProcess, &exit_code);
- }
- CloseHandle(pi.hProcess);
- CloseHandle(pi.hThread);
- return exit_code;
- }
- return 1;
- }
|