| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235 |
- /*=========================================================================
- Program: KWSys - Kitware System Library
- Module: $RCSfile$
- Language: C++
- Date: $Date$
- Version: $Revision$
- Copyright (c) 2002 Kitware, Inc., Insight Consortium. All rights reserved.
- See http://www.cmake.org/HTML/Copyright.html 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.
- =========================================================================*/
- #define KWSYS_IN_PROCESS_C
- #include <Process.h>
- /*
- Implementation for Windows
-
- On windows, a thread is created to wait for data on each pipe. The
- threads are synchronized with the main thread to simulate the use of
- a UNIX-style select system call.
-
- On Windows9x platforms, a small WIN32 console application is spawned
- in-between the calling process and the actual child to be executed.
- This is to work-around a problem with connecting pipes from WIN16
- console applications to WIN32 applications.
-
- For more information, please check Microsoft Knowledge Base Articles
- Q190351 and Q150956.
- */
- #include <windows.h> /* Windows API */
- #include <string.h> /* strlen, strdup */
- #include <stdio.h> /* sprintf */
- #include <process.h> /* _getpid */
- #include <io.h> /* _unlink */
- /* The number of pipes for the child's output. The standard stdout
- and stderr pipes are the first two. One more pipe is used on Win9x
- for the forwarding executable to use in reporting problems. */
- #define CMPE_PIPE_COUNT 3
- #define CMPE_PIPE_STDOUT 0
- #define CMPE_PIPE_STDERR 1
- #define CMPE_PIPE_ERROR 2
- /* The maximum amount to read from a pipe at a time. */
- #define CMPE_PIPE_BUFFER_SIZE 1024
- #define kwsysEncodedWriteArrayProcessFwd kwsys(EncodedWriteArrayProcessFwd)
- typedef LARGE_INTEGER kwsysProcessTime;
- /*--------------------------------------------------------------------------*/
- typedef struct kwsysProcessPipeData_s kwsysProcessPipeData;
- static DWORD WINAPI kwsysProcessPipeThread(LPVOID ptd);
- static void kwsysProcessPipeThreadReadPipe(kwsysProcess* cp, kwsysProcessPipeData* td);
- static void kwsysProcessCleanupHandle(PHANDLE h);
- static void kwsysProcessCleanup(kwsysProcess* cp, int error);
- static int kwsysProcessGetTimeoutTime(kwsysProcess* cp, double* userTimeout,
- kwsysProcessTime* timeoutTime);
- static int kwsysProcessGetTimeoutLeft(kwsysProcess* cp, kwsysProcessTime* timeoutTime,
- kwsysProcessTime* timeoutLength);
- static kwsysProcessTime kwsysProcessTimeGetCurrent();
- static DWORD kwsysProcessTimeToDWORD(kwsysProcessTime t);
- static double kwsysProcessTimeToDouble(kwsysProcessTime t);
- static kwsysProcessTime kwsysProcessTimeFromDouble(double d);
- static int kwsysProcessTimeLess(kwsysProcessTime in1, kwsysProcessTime in2);
- static kwsysProcessTime kwsysProcessTimeAdd(kwsysProcessTime in1, kwsysProcessTime in2);
- static kwsysProcessTime kwsysProcessTimeSubtract(kwsysProcessTime in1, kwsysProcessTime in2);
- extern int kwsysEncodedWriteArrayProcessFwd9x(const char* fname);
- /*--------------------------------------------------------------------------*/
- /* A structure containing data for each pipe's thread. */
- struct kwsysProcessPipeData_s
- {
- /* ------------- Data managed per instance of kwsysProcess ------------- */
-
- /* Handle for the thread for this pipe. */
- HANDLE Thread;
-
- /* Semaphore indicating a process and pipe are available. */
- HANDLE Ready;
-
- /* Semaphore indicating when this thread's buffer is empty. */
- HANDLE Empty;
-
- /* Semaphore indicating a pipe thread has reset for another process. */
- HANDLE Reset;
-
- /* Index of this pipe. */
- int Index;
- /* The kwsysProcess instance owning this pipe. */
- kwsysProcess* Process;
-
- /* ------------- Data managed per call to Execute ------------- */
-
- /* Buffer for data read in this pipe's thread. */
- char DataBuffer[CMPE_PIPE_BUFFER_SIZE];
-
- /* The length of the data stored in the buffer. */
- DWORD DataLength;
-
- /* Whether the pipe has been closed. */
- int Closed;
-
- /* Handle for the read end of this pipe. */
- HANDLE Read;
-
- /* Handle for the write end of this pipe. */
- HANDLE Write;
- };
- /*--------------------------------------------------------------------------*/
- /* Structure containing data used to implement the child's execution. */
- struct kwsysProcess_s
- {
- /* ------------- Data managed per instance of kwsysProcess ------------- */
-
- /* The status of the process. */
- int State;
-
- /* The command line to execute. */
- char* Command;
-
- /* On Win9x platforms, the path to the forwarding executable. */
- char* Win9x;
-
- /* On Win9x platforms, the kill event for the forwarding executable. */
- HANDLE Win9xKillEvent;
-
- /* Mutex to protect the shared index used by threads to report data. */
- HANDLE SharedIndexMutex;
-
- /* Semaphore used by threads to signal data ready. */
- HANDLE Full;
-
- /* The number of pipes needed to implement the child's execution.
- This is 3 on Win9x and 2 otherwise. */
- int PipeCount;
-
- /* Whether we are currently deleting this kwsysProcess instance. */
- int Deleting;
-
- /* Data specific to each pipe and its thread. */
- kwsysProcessPipeData Pipe[CMPE_PIPE_COUNT];
-
- /* ------------- Data managed per call to Execute ------------- */
-
- /* The process exit code, if any. */
- int ExitCode;
-
- /* Index of last pipe to report data, if any. */
- int CurrentIndex;
-
- /* Index shared by threads to report data. */
- int SharedIndex;
-
- /* The timeout length. */
- double Timeout;
-
- /* Time at which the child started. */
- kwsysProcessTime StartTime;
-
- /* Time at which the child will timeout. Negative for no timeout. */
- kwsysProcessTime TimeoutTime;
-
- /* Flag for whether the process was killed. */
- int Killed;
-
- /* Flag for whether the timeout expired. */
- int TimeoutExpired;
-
- /* Flag for whether the process has terminated. */
- int Terminated;
-
- /* The number of pipes still open during execution and while waiting
- for pipes to close after process termination. */
- int PipesLeft;
-
- /* Buffer for error messages (possibly from Win9x child). */
- char ErrorMessage[CMPE_PIPE_BUFFER_SIZE+1];
- int ErrorMessageLength;
-
- /* The actual command line that will be used to create the process. */
- char* RealCommand;
- /* Windows process information data. */
- PROCESS_INFORMATION ProcessInformation;
- };
- /*--------------------------------------------------------------------------*/
- kwsysProcess* kwsysProcess_New()
- {
- int i;
- /* Process control structure. */
- kwsysProcess* cp;
- /* Path to Win9x forwarding executable. */
- char* win9x = 0;
- /* Windows version number data. */
- OSVERSIONINFO osv;
-
- /* Allocate a process control structure. */
- cp = (kwsysProcess*)malloc(sizeof(kwsysProcess));
- ZeroMemory(cp, sizeof(*cp));
-
- /* Set initial status. */
- cp->State = kwsysProcess_Starting;
-
- /* Choose a method of running the child based on version of
- windows. */
- ZeroMemory(&osv, sizeof(osv));
- osv.dwOSVersionInfoSize = sizeof(osv);
- GetVersionEx(&osv);
- if(osv.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS)
- {
- /* This is Win9x. We need the console forwarding executable to
- work-around a Windows 9x bug. */
- char fwdName[_MAX_FNAME+1] = "";
- char tempDir[_MAX_PATH+1] = "";
-
- /* We will try putting the executable in the system temp
- directory. */
- DWORD length = GetEnvironmentVariable("TEMP", tempDir, _MAX_PATH);
-
- /* Construct the executable name from the process id and kwsysProcess
- instance. This should be unique. */
- sprintf(fwdName, "cmw9xfwd_%u_%p.exe", _getpid(), cp);
-
- /* If the environment variable "TEMP" gave us a directory, use it. */
- if(length > 0 && length <= _MAX_PATH)
- {
- /* Make sure there is no trailing slash. */
- size_t tdlen = strlen(tempDir);
- if(tempDir[tdlen-1] == '/' || tempDir[tdlen-1] == '\\')
- {
- tempDir[tdlen-1] = 0;
- --tdlen;
- }
-
- /* Allocate a buffer to hold the forwarding executable path. */
- win9x = (char*)malloc(tdlen + strlen(fwdName) + 2);
- if(!win9x)
- {
- kwsysProcess_Delete(cp);
- return 0;
- }
-
- /* Construct the full path to the forwarding executable. */
- sprintf(win9x, "%s/%s", tempDir, fwdName);
- }
-
- /* If we found a place to put the forwarding executable, try to
- write it. */
- if(win9x)
- {
- if(!kwsysEncodedWriteArrayProcessFwd9x(win9x))
- {
- /* Failed to create forwarding executable. Give up. */
- free(win9x);
- kwsysProcess_Delete(cp);
- return 0;
- }
- }
- else
- {
- /* Failed to find a place to put forwarding executable. */
- kwsysProcess_Delete(cp);
- return 0;
- }
- }
-
- /* We need the extra error pipe on Win9x. */
- cp->Win9x = win9x;
- cp->PipeCount = cp->Win9x? 3:2;
-
- /* Initially no thread owns the mutex. Initialize semaphore to 1. */
- if(!(cp->SharedIndexMutex = CreateSemaphore(0, 1, 1, 0)))
- {
- kwsysProcess_Delete(cp);
- return 0;
- }
-
- /* Initially no data are available. Initialize semaphore to 0. */
- if(!(cp->Full = CreateSemaphore(0, 0, 1, 0)))
- {
- kwsysProcess_Delete(cp);
- return 0;
- }
- if(cp->Win9x)
- {
- /* Create an event to tell the forwarding executable to kill the
- child. */
- SECURITY_ATTRIBUTES sa;
- ZeroMemory(&sa, sizeof(sa));
- sa.nLength = sizeof(sa);
- sa.bInheritHandle = TRUE;
- if(!(cp->Win9xKillEvent = CreateEvent(&sa, TRUE, 0, 0)))
- {
- kwsysProcess_Delete(cp);
- return 0;
- }
- }
-
- /* Create the thread to read each pipe. */
- for(i=0; i < cp->PipeCount; ++i)
- {
- DWORD dummy=0;
-
- /* Assign the thread its index. */
- cp->Pipe[i].Index = i;
-
- /* Give the thread a pointer back to the kwsysProcess instance. */
- cp->Pipe[i].Process = cp;
-
- /* The pipe is not yet ready to read. Initialize semaphore to 0. */
- if(!(cp->Pipe[i].Ready = CreateSemaphore(0, 0, 1, 0)))
- {
- kwsysProcess_Delete(cp);
- return 0;
- }
-
- /* The pipe is not yet reset. Initialize semaphore to 0. */
- if(!(cp->Pipe[i].Reset = CreateSemaphore(0, 0, 1, 0)))
- {
- kwsysProcess_Delete(cp);
- return 0;
- }
-
- /* The thread's buffer is initially empty. Initialize semaphore to 1. */
- if(!(cp->Pipe[i].Empty = CreateSemaphore(0, 1, 1, 0)))
- {
- kwsysProcess_Delete(cp);
- return 0;
- }
-
- /* Create the thread. It will block immediately. */
- if(!(cp->Pipe[i].Thread = CreateThread(0, 0, kwsysProcessPipeThread,
- &cp->Pipe[i], 0, &dummy)))
- {
- kwsysProcess_Delete(cp);
- return 0;
- }
- }
-
- return cp;
- }
- /*--------------------------------------------------------------------------*/
- void kwsysProcess_Delete(kwsysProcess* cp)
- {
- int i;
- /* If the process is executing, wait for it to finish. */
- if(cp->State == kwsysProcess_Executing)
- {
- kwsysProcess_WaitForExit(cp, 0);
- }
-
- /* We are deleting the kwsysProcess instance. */
- cp->Deleting = 1;
-
- /* Terminate each of the threads. */
- for(i=0; i < cp->PipeCount; ++i)
- {
- if(cp->Pipe[i].Thread)
- {
- /* Signal the thread we are ready for it. It will terminate
- immediately since Deleting is set. */
- ReleaseSemaphore(cp->Pipe[i].Ready, 1, 0);
-
- /* Wait for the thread to exit. */
- WaitForSingleObject(cp->Pipe[i].Thread, INFINITE);
-
- /* Close the handle to the thread. */
- kwsysProcessCleanupHandle(&cp->Pipe[i].Thread);
- }
-
- /* Cleanup the pipe's semaphores. */
- kwsysProcessCleanupHandle(&cp->Pipe[i].Ready);
- kwsysProcessCleanupHandle(&cp->Pipe[i].Empty);
- }
-
- /* Close the shared semaphores. */
- kwsysProcessCleanupHandle(&cp->SharedIndexMutex);
- kwsysProcessCleanupHandle(&cp->Full);
-
- /* Close the Win9x kill event handle. */
- if(cp->Win9x)
- {
- kwsysProcessCleanupHandle(&cp->Win9xKillEvent);
- }
-
- /* Free memory. */
- kwsysProcess_SetCommand(cp, 0);
- if(cp->Win9x)
- {
- _unlink(cp->Win9x);
- free(cp->Win9x);
- }
- free(cp);
- }
- /*--------------------------------------------------------------------------*/
- void kwsysProcess_SetCommand(kwsysProcess* cp, char const* const* command)
- {
- if(cp->Command)
- {
- free(cp->Command);
- cp->Command = 0;
- }
- if(command)
- {
- /* We need to construct a single string representing the command
- and its arguments. We will surround each argument with
- double-quotes so it can contain spaces. We need to escape
- double-quotes and all backslashes before them. We also need to
- escape backslashes at the end of an argument because they come
- before the closing double-quote for the argument. */
- char* cmd;
- char const* const* arg;
- int length = 0;
- /* First determine the length of the final string. */
- for(arg = command; *arg; ++arg)
- {
- /* Keep track of how many backslashes have been encountered in a
- row in this argument. */
- int backslashes = 0;
- const char* c;
- /* Add the length of the argument, plus 3 for the double quotes
- and space separating the arguments. */
- length += strlen(*arg) + 3;
-
- /* Scan the string to find characters that need escaping. */
- for(c=*arg; *c; ++c)
- {
- if(*c == '\\')
- {
- /* Found a backslash. It may need to be escaped later. */
- ++backslashes;
- }
- else if(*c == '"')
- {
- /* Found a double-quote. We need to escape it and all
- immediately preceding backslashes. */
- length += backslashes + 1;
- backslashes = 0;
- }
- else
- {
- /* Found another character. This eliminates the possibility
- that any immediately preceding backslashes will be
- escaped. */
- backslashes = 0;
- }
- }
-
- /* We need to escape all ending backslashes. */
- length += backslashes;
- }
-
- /* Allocate enough space for the command. We do not need an extra
- byte for the terminating null because we allocated a space for
- the first argument that we will not use. */
- cp->Command = (char*)malloc(length);
-
- /* Construct the command line in the allocated buffer. */
- cmd = cp->Command;
- for(arg = command; *arg; ++arg)
- {
- /* Keep track of how many backslashes have been encountered in a
- row in an argument. */
- int backslashes = 0;
- const char* c;
-
- /* Add the separating space if this is not the first argument. */
- if(arg != command)
- {
- *cmd++ = ' ';
- }
- /* Add the opening double-quote for this argument. */
- *cmd++ = '"';
- /* Add the characters of the argument, possibly escaping them. */
- for(c=*arg; *c; ++c)
- {
- if(*c == '\\')
- {
- /* Found a backslash. It may need to be escaped later. */
- ++backslashes;
- *cmd++ = '\\';
- }
- else if(*c == '"')
- {
- /* Add enough backslashes to escape any that preceded the
- double-quote. */
- while(backslashes > 0)
- {
- --backslashes;
- *cmd++ = '\\';
- }
-
- /* Add the backslash to escape the double-quote. */
- *cmd++ = '\\';
-
- /* Add the double-quote itself. */
- *cmd++ = '"';
- }
- else
- {
- /* We encountered a normal character. This eliminates any
- escaping needed for preceding backslashes. Add the
- character. */
- backslashes = 0;
- *cmd++ = *c;
- }
- }
-
- /* Add enough backslashes to escape any trailing ones. */
- while(backslashes > 0)
- {
- --backslashes;
- *cmd++ = '\\';
- }
- /* Add the opening double-quote for this argument. */
- *cmd++ = '"';
- }
-
- /* Add the terminating null character to the command line. */
- *cmd++ = 0;
- }
- }
- /*--------------------------------------------------------------------------*/
- void kwsysProcess_SetTimeout(kwsysProcess* cp, double timeout)
- {
- cp->Timeout = timeout;
- }
- /*--------------------------------------------------------------------------*/
- int kwsysProcess_GetState(kwsysProcess* cp)
- {
- return cp->State;
- }
- /*--------------------------------------------------------------------------*/
- int kwsysProcess_GetExitCode(kwsysProcess* cp)
- {
- return cp->ExitCode;
- }
- /*--------------------------------------------------------------------------*/
- const char* kwsysProcess_GetErrorString(kwsysProcess* cp)
- {
- return cp->ErrorMessage;
- }
- /*--------------------------------------------------------------------------*/
- void kwsysProcess_Execute(kwsysProcess* cp)
- {
- int i=0;
- /* Windows child startup control data. */
- STARTUPINFO si;
-
- /* Do not execute a second time. */
- if(cp->State == kwsysProcess_Executing)
- {
- return;
- }
-
- /* Initialize startup info data. */
- ZeroMemory(&si, sizeof(si));
- si.cb = sizeof(si);
-
- /* Reset internal status flags. */
- cp->TimeoutExpired = 0;
- cp->Terminated = 0;
- cp->Killed = 0;
-
- /* Reset error data. */
- cp->ErrorMessage[0] = 0;
- cp->ErrorMessageLength = 0;
-
- /* Reset the Win9x kill event. */
- if(cp->Win9x)
- {
- if(!ResetEvent(cp->Win9xKillEvent))
- {
- kwsysProcessCleanup(cp, 1);
- return;
- }
- }
-
- /* Create a pipe for each child output. */
- for(i=0; i < cp->PipeCount; ++i)
- {
- HANDLE writeEnd;
- DWORD dummy=0;
-
- /* The pipe is not closed. */
- cp->Pipe[i].Closed = 0;
-
- /* Create the pipe. Neither end is directly inherited. */
- if(!CreatePipe(&cp->Pipe[i].Read, &writeEnd, 0, 0))
- {
- kwsysProcessCleanup(cp, 1);
- return;
- }
-
- /* Create an inherited duplicate of the write end. This also closes
- the non-inherited version. */
- if(!DuplicateHandle(GetCurrentProcess(), writeEnd,
- GetCurrentProcess(), &cp->Pipe[i].Write,
- 0, TRUE, (DUPLICATE_CLOSE_SOURCE |
- DUPLICATE_SAME_ACCESS)))
- {
- kwsysProcessCleanup(cp, 1);
- CloseHandle(writeEnd);
- return;
- }
- }
-
- /* Construct the real command line. */
- if(cp->Win9x)
- {
- /* Windows 9x */
-
- /* The forwarding executable is given a handle to the error pipe
- and a handle to the kill event. */
- cp->RealCommand = malloc(strlen(cp->Win9x)+strlen(cp->Command)+100);
- sprintf(cp->RealCommand, "%s %d %d %s", cp->Win9x,
- cp->Pipe[CMPE_PIPE_ERROR].Write,
- cp->Win9xKillEvent, cp->Command);
- }
- else
- {
- /* Not Windows 9x */
- cp->RealCommand = strdup(cp->Command);
- }
- /* Connect the child's output pipes to the threads. */
- si.dwFlags = STARTF_USESTDHANDLES;
- si.hStdOutput = cp->Pipe[CMPE_PIPE_STDOUT].Write;
- si.hStdError = cp->Pipe[CMPE_PIPE_STDERR].Write;
-
- /* Hide the forwarding executable console on Windows 9x. */
- si.dwFlags |= STARTF_USESHOWWINDOW;
- if(cp->Win9x)
- {
- si.wShowWindow = SW_HIDE;
- }
- else
- {
- si.wShowWindow = SW_SHOWDEFAULT;
- }
-
- /* The timeout period starts now. */
- cp->StartTime = kwsysProcessTimeGetCurrent();
- cp->TimeoutTime = kwsysProcessTimeFromDouble(-1);
-
- /* CREATE THE CHILD PROCESS */
- if(!CreateProcess(0, cp->RealCommand, 0, 0, TRUE,
- cp->Win9x? CREATE_NEW_CONSOLE:DETACHED_PROCESS, 0,
- 0, &si, &cp->ProcessInformation))
- {
- kwsysProcessCleanup(cp, 1);
- return;
- }
-
- /* ---- It is no longer safe to call kwsysProcessCleanup. ----- */
- /* Tell the pipe threads that a process has started. */
- for(i=0; i < cp->PipeCount; ++i)
- {
- ReleaseSemaphore(cp->Pipe[i].Ready, 1, 0);
- }
-
- /* We don't care about the child's main thread. */
- kwsysProcessCleanupHandle(&cp->ProcessInformation.hThread);
-
- /* No pipe has reported data. */
- cp->CurrentIndex = CMPE_PIPE_COUNT;
- cp->PipesLeft = cp->PipeCount;
-
- /* The process has now started. */
- cp->State = kwsysProcess_Executing;
- }
- /*--------------------------------------------------------------------------*/
- int kwsysProcess_WaitForData(kwsysProcess* cp, int pipes, char** data, int* length,
- double* userTimeout)
- {
- kwsysProcessTime userStartTime;
- kwsysProcessTime timeoutLength;
- kwsysProcessTime timeoutTime;
- DWORD timeout;
- int user;
- int done = 0;
- int expired = 0;
- int pipeId = 0;
- DWORD w;
- HANDLE events[2];
-
- /* Make sure we are executing a process. */
- if(cp->State != kwsysProcess_Executing || cp->Killed || cp->TimeoutExpired)
- {
- return 0;
- }
-
- /* We will wait for data until the process termiantes or data are
- available. */
- events[0] = cp->Full;
- events[1] = cp->ProcessInformation.hProcess;
-
- /* Record the time at which user timeout period starts. */
- if(userTimeout)
- {
- userStartTime = kwsysProcessTimeGetCurrent();
- }
-
- /* Calculate the time at which a timeout will expire, and whether it
- is the user or process timeout. */
- user = kwsysProcessGetTimeoutTime(cp, userTimeout, &timeoutTime);
-
- /* Loop until we have a reason to return. */
- while(!done && cp->PipesLeft > 0)
- {
- /* If we previously got data from a thread, let it know we are
- done with the data. */
- if(cp->CurrentIndex < CMPE_PIPE_COUNT)
- {
- ReleaseSemaphore(cp->Pipe[cp->CurrentIndex].Empty, 1, 0);
- cp->CurrentIndex = CMPE_PIPE_COUNT;
- }
-
- /* Setup a timeout if required. */
- if(kwsysProcessGetTimeoutLeft(cp, &timeoutTime, &timeoutLength))
- {
- /* Timeout has already expired. */
- expired = 1;
- done = 1;
- break;
- }
- if(timeoutTime.QuadPart < 0)
- {
- timeout = INFINITE;
- }
- else
- {
- timeout = kwsysProcessTimeToDWORD(timeoutLength);
- }
-
- /* Wait for a pipe's thread to signal or the application to
- terminate. */
- w = WaitForMultipleObjects(cp->Terminated?1:2, events, 0, timeout);
- if(w == WAIT_TIMEOUT)
- {
- /* Timeout has expired. */
- expired = 1;
- done = 1;
- }
- else if(w == WAIT_OBJECT_0)
- {
- /* Save the index of the reporting thread and release the mutex.
- The thread will block until we signal its Empty mutex. */
- cp->CurrentIndex = cp->SharedIndex;
- ReleaseSemaphore(cp->SharedIndexMutex, 1, 0);
-
- /* Data are available or a pipe closed. */
- if(cp->Pipe[cp->CurrentIndex].Closed)
- {
- /* The pipe closed. */
- --cp->PipesLeft;
- }
- else if(cp->CurrentIndex == CMPE_PIPE_ERROR)
- {
- /* This is data on the special error reporting pipe for Win9x.
- Append it to the error buffer. */
- int length = cp->Pipe[cp->CurrentIndex].DataLength;
- if(length > CMPE_PIPE_BUFFER_SIZE - cp->ErrorMessageLength)
- {
- length = CMPE_PIPE_BUFFER_SIZE - cp->ErrorMessageLength;
- }
- if(length > 0)
- {
- memcpy(cp->ErrorMessage+cp->ErrorMessageLength,
- cp->Pipe[cp->CurrentIndex].DataBuffer, length);
- cp->ErrorMessageLength += length;
- }
- else
- {
- cp->ErrorMessage[cp->ErrorMessageLength] = 0;
- }
- }
- else if(pipes & (1 << cp->CurrentIndex))
- {
- /* Caller wants this data. Report it. */
- *data = cp->Pipe[cp->CurrentIndex].DataBuffer;
- *length = cp->Pipe[cp->CurrentIndex].DataLength;
- pipeId = (1 << cp->CurrentIndex);
- done = 1;
- }
- else
- {
- /* Caller does not care about this pipe. Ignore the data. */
- }
- }
- else
- {
- int i;
- /* Process has terminated. */
- cp->Terminated = 1;
-
- /* Close our copies of the pipe write handles so the pipe
- threads can detect end-of-data. */
- for(i=0; i < cp->PipeCount; ++i)
- {
- kwsysProcessCleanupHandle(&cp->Pipe[i].Write);
- }
- }
- }
-
- /* Update the user timeout. */
- if(userTimeout)
- {
- kwsysProcessTime userEndTime = kwsysProcessTimeGetCurrent();
- kwsysProcessTime difference = kwsysProcessTimeSubtract(userEndTime,
- userStartTime);
- double d = kwsysProcessTimeToDouble(difference);
- *userTimeout -= d;
- if(*userTimeout < 0)
- {
- *userTimeout = 0;
- }
- }
-
- /* Check what happened. */
- if(pipeId)
- {
- /* Data are ready on a pipe. */
- return pipeId;
- }
- else if(expired)
- {
- /* A timeout has expired. */
- if(user)
- {
- /* The user timeout has expired. It has no time left. */
- return kwsysProcess_Timeout;
- }
- else
- {
- /* The process timeout has expired. Kill the child now. */
- kwsysProcess_Kill(cp);
- cp->TimeoutExpired = 1;
- cp->Killed = 0;
- return 0;
- }
- }
- else
- {
- /* The process has terminated and no more data are available. */
- return 0;
- }
- }
- /*--------------------------------------------------------------------------*/
- int kwsysProcess_WaitForExit(kwsysProcess* cp, double* userTimeout)
- {
- int i;
- int pipe = 0;
- int pipesLeft = cp->PipeCount;
-
- /* Buffer for child's return value. */
- int childReturnValue = 0;
-
- /* Make sure we are executing a process. */
- if(cp->State != kwsysProcess_Executing)
- {
- return 1;
- }
-
- /* Wait for the process to terminate. Ignore all data. */
- while((pipe = kwsysProcess_WaitForData(cp, 0, 0, 0, userTimeout)) > 0)
- {
- if(pipe == kwsysProcess_Timeout)
- {
- /* The user timeout has expired. */
- return 0;
- }
- }
- /* When the last pipe closes in WaitForData, the loop terminates
- without releaseing the pipe's thread. Release it now. */
- if(cp->CurrentIndex < CMPE_PIPE_COUNT)
- {
- ReleaseSemaphore(cp->Pipe[cp->CurrentIndex].Empty, 1, 0);
- cp->CurrentIndex = CMPE_PIPE_COUNT;
- }
- /* Wait for all pipe threads to reset. */
- for(i=0; i < cp->PipeCount; ++i)
- {
- WaitForSingleObject(cp->Pipe[i].Reset, INFINITE);
- }
-
- /* ---- It is now safe again to call kwsysProcessCleanup. ----- */
- /* Close all the pipes. */
- kwsysProcessCleanup(cp, 0);
-
- /* We are done reading all data. Wait for the child to terminate.
- This will only block if we killed the child and are waiting for
- it to cleanup. */
- WaitForSingleObject(cp->ProcessInformation.hProcess, INFINITE);
-
- /* Determine the outcome. */
- if(cp->Killed)
- {
- /* We killed the child. */
- cp->State = kwsysProcess_Killed;
- }
- else if(cp->ErrorMessageLength)
- {
- /* Failed to run the process. */
- cp->State = kwsysProcess_Error;
- }
- else if(cp->TimeoutExpired)
- {
- /* The timeout expired. */
- cp->State = kwsysProcess_Expired;
- }
- else if(GetExitCodeProcess(cp->ProcessInformation.hProcess,
- &childReturnValue))
- {
- /* The child exited. */
- cp->State = kwsysProcess_Exited;
- cp->ExitCode = childReturnValue;
- }
- else
- {
- /* Error getting the child return code. */
- strcpy(cp->ErrorMessage, "Error getting child return code.");
- cp->State = kwsysProcess_Error;
- }
-
- /* The child process is terminated. */
- CloseHandle(cp->ProcessInformation.hProcess);
-
- return 1;
- }
- /*--------------------------------------------------------------------------*/
- void kwsysProcess_Kill(kwsysProcess* cp)
- {
- int i;
-
- /* Make sure we are executing a process. */
- if(cp->State != kwsysProcess_Executing || cp->TimeoutExpired || cp->Killed ||
- cp->Terminated)
- {
- return;
- }
-
- /* If we are killing a process that just reported data, release
- the pipe's thread. */
- if(cp->CurrentIndex < CMPE_PIPE_COUNT)
- {
- ReleaseSemaphore(cp->Pipe[cp->CurrentIndex].Empty, 1, 0);
- cp->CurrentIndex = CMPE_PIPE_COUNT;
- }
-
- /* Wake up all the pipe threads with dummy data. */
- for(i=0; i < cp->PipeCount; ++i)
- {
- DWORD dummy;
- WriteFile(cp->Pipe[i].Write, "", 1, &dummy, 0);
- }
-
- /* Tell pipe threads to reset until we run another process. */
- while(cp->PipesLeft > 0)
- {
- WaitForSingleObject(cp->Full, INFINITE);
- cp->CurrentIndex = cp->SharedIndex;
- ReleaseSemaphore(cp->SharedIndexMutex, 1, 0);
- cp->Pipe[cp->CurrentIndex].Closed = 1;
- ReleaseSemaphore(cp->Pipe[cp->CurrentIndex].Empty, 1, 0);
- --cp->PipesLeft;
- }
-
- /* Kill the child. */
- cp->Killed = 1;
- if(cp->Win9x)
- {
- /* Windows 9x. Tell the forwarding executable to kill the child. */
- SetEvent(cp->Win9xKillEvent);
- }
- else
- {
- /* Not Windows 9x. Just terminate the child. */
- TerminateProcess(cp->ProcessInformation.hProcess, -1);
- }
- }
- /*--------------------------------------------------------------------------*/
- /*
- Function executed for each pipe's thread. Argument is a pointer to
- the kwsysProcessPipeData instance for this thread.
- */
- DWORD WINAPI kwsysProcessPipeThread(LPVOID ptd)
- {
- kwsysProcessPipeData* td = (kwsysProcessPipeData*)ptd;
- kwsysProcess* cp = td->Process;
-
- /* Wait for a process to be ready. */
- while((WaitForSingleObject(td->Ready, INFINITE), !cp->Deleting))
- {
- /* Read output from the process for this thread's pipe. */
- kwsysProcessPipeThreadReadPipe(cp, td);
-
- /* We were signalled to exit with our buffer empty. Reset the
- mutex for a new process. */
- ReleaseSemaphore(td->Empty, 1, 0);
-
- /* Signal the main thread we have reset for a new process. */
- ReleaseSemaphore(td->Reset, 1, 0);
- }
- return 0;
- }
- /*--------------------------------------------------------------------------*/
- /*
- Function called in each pipe's thread to handle data for one
- execution of a subprocess.
- */
- void kwsysProcessPipeThreadReadPipe(kwsysProcess* cp, kwsysProcessPipeData* td)
- {
- /* Wait for space in the thread's buffer. */
- while((WaitForSingleObject(td->Empty, INFINITE), !td->Closed))
- {
- /* Read data from the pipe. This may block until data are available. */
- if(!ReadFile(td->Read, td->DataBuffer, CMPE_PIPE_BUFFER_SIZE,
- &td->DataLength, 0))
- {
- if(GetLastError() != ERROR_BROKEN_PIPE)
- {
- /* UNEXPECTED failure to read the pipe. */
- }
-
- /* The pipe closed. There are no more data to read. */
- td->Closed = 1;
- }
-
- /* Wait for our turn to be handled by the main thread. */
- WaitForSingleObject(cp->SharedIndexMutex, INFINITE);
-
- /* Tell the main thread we have something to report. */
- cp->SharedIndex = td->Index;
- ReleaseSemaphore(cp->Full, 1, 0);
- }
- }
- /*--------------------------------------------------------------------------*/
- /* Close the given handle if it is open. Reset its value to 0. */
- void kwsysProcessCleanupHandle(PHANDLE h)
- {
- if(h && *h)
- {
- CloseHandle(*h);
- *h = 0;
- }
- }
- /*--------------------------------------------------------------------------*/
- /* Close all handles created by kwsysProcess_Execute. */
- void kwsysProcessCleanup(kwsysProcess* cp, int error)
- {
- int i;
-
- /* If this is an error case, report the error. */
- if(error)
- {
- FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
- 0, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
- cp->ErrorMessage, CMPE_PIPE_BUFFER_SIZE, 0);
- cp->State = kwsysProcess_Error;
- }
-
- /* Free memory. */
- if(cp->RealCommand)
- {
- free(cp->RealCommand);
- cp->RealCommand = 0;
- }
- /* Close each pipe. */
- for(i=0; i < cp->PipeCount; ++i)
- {
- kwsysProcessCleanupHandle(&cp->Pipe[i].Write);
- kwsysProcessCleanupHandle(&cp->Pipe[i].Read);
- }
- }
- /*--------------------------------------------------------------------------*/
- /* Get the time at which either the process or user timeout will
- expire. Returns 1 if the user timeout is first, and 0 otherwise. */
- int kwsysProcessGetTimeoutTime(kwsysProcess* cp, double* userTimeout,
- kwsysProcessTime* timeoutTime)
- {
- /* The first time this is called, we need to calculate the time at
- which the child will timeout. */
- if(cp->Timeout && cp->TimeoutTime.QuadPart < 0)
- {
- kwsysProcessTime length = kwsysProcessTimeFromDouble(cp->Timeout);
- cp->TimeoutTime = kwsysProcessTimeAdd(cp->StartTime, length);
- }
-
- /* Start with process timeout. */
- *timeoutTime = cp->TimeoutTime;
-
- /* Check if the user timeout is earlier. */
- if(userTimeout)
- {
- kwsysProcessTime currentTime = kwsysProcessTimeGetCurrent();
- kwsysProcessTime userTimeoutLength = kwsysProcessTimeFromDouble(*userTimeout);
- kwsysProcessTime userTimeoutTime = kwsysProcessTimeAdd(currentTime,
- userTimeoutLength);
- if(kwsysProcessTimeLess(userTimeoutTime, *timeoutTime))
- {
- *timeoutTime = userTimeoutTime;
- return 1;
- }
- }
- return 0;
- }
- /*--------------------------------------------------------------------------*/
- /* Get the length of time before the given timeout time arrives.
- Returns 1 if the time has already arrived, and 0 otherwise. */
- int kwsysProcessGetTimeoutLeft(kwsysProcess* cp, kwsysProcessTime* timeoutTime,
- kwsysProcessTime* timeoutLength)
- {
- if(timeoutTime->QuadPart < 0)
- {
- /* No timeout time has been requested. */
- return 0;
- }
- else
- {
- /* Calculate the remaining time. */
- kwsysProcessTime currentTime = kwsysProcessTimeGetCurrent();
- *timeoutLength = kwsysProcessTimeSubtract(*timeoutTime, currentTime);
- if(timeoutLength->QuadPart < 0)
- {
- /* Timeout has already expired. */
- return 1;
- }
- else
- {
- /* There is some time left. */
- return 0;
- }
- }
- }
- /*--------------------------------------------------------------------------*/
- kwsysProcessTime kwsysProcessTimeGetCurrent()
- {
- kwsysProcessTime current;
- FILETIME ft;
- GetSystemTimeAsFileTime(&ft);
- current.LowPart = ft.dwLowDateTime;
- current.HighPart = ft.dwHighDateTime;
- return current;
- }
- /*--------------------------------------------------------------------------*/
- DWORD kwsysProcessTimeToDWORD(kwsysProcessTime t)
- {
- return (DWORD)(t.QuadPart * 0.0001);
- }
- /*--------------------------------------------------------------------------*/
- double kwsysProcessTimeToDouble(kwsysProcessTime t)
- {
- return t.QuadPart * 0.0000001;
- }
- /*--------------------------------------------------------------------------*/
- kwsysProcessTime kwsysProcessTimeFromDouble(double d)
- {
- kwsysProcessTime t;
- t.QuadPart = (LONGLONG)(d*10000000);
- return t;
- }
- /*--------------------------------------------------------------------------*/
- int kwsysProcessTimeLess(kwsysProcessTime in1, kwsysProcessTime in2)
- {
- return in1.QuadPart < in2.QuadPart;
- }
- /*--------------------------------------------------------------------------*/
- kwsysProcessTime kwsysProcessTimeAdd(kwsysProcessTime in1, kwsysProcessTime in2)
- {
- kwsysProcessTime out;
- out.QuadPart = in1.QuadPart + in2.QuadPart;
- return out;
- }
- /*--------------------------------------------------------------------------*/
- kwsysProcessTime kwsysProcessTimeSubtract(kwsysProcessTime in1, kwsysProcessTime in2)
- {
- kwsysProcessTime out;
- out.QuadPart = in1.QuadPart - in2.QuadPart;
- return out;
- }
|