| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017 |
- /*============================================================================
- KWSys - Kitware System Library
- Copyright 2000-2009 Kitware, Inc., Insight Software Consortium
- Distributed under the OSI-approved BSD License (the "License");
- see accompanying file Copyright.txt for details.
- This software is distributed WITHOUT ANY WARRANTY; without even the
- implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- See the License for more information.
- ============================================================================*/
- #include "kwsysPrivate.h"
- #include KWSYS_HEADER(Process.h)
- #include KWSYS_HEADER(Encoding.h)
- /* Work-around CMake dependency scanning limitation. This must
- duplicate the above list of headers. */
- #if 0
- # include "Process.h.in"
- # include "Encoding_c.h.in"
- #endif
- /*
- 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.
- */
- #ifdef _MSC_VER
- #pragma warning (push, 1)
- #endif
- #include <windows.h> /* Windows API */
- #if defined(_MSC_VER) && _MSC_VER >= 1800
- # define KWSYS_WINDOWS_DEPRECATED_GetVersionEx
- #endif
- #include <string.h> /* strlen, strdup */
- #include <stdio.h> /* sprintf */
- #include <io.h> /* _unlink */
- #ifdef __WATCOMC__
- #define _unlink unlink
- #endif
- #ifndef _MAX_FNAME
- #define _MAX_FNAME 4096
- #endif
- #ifndef _MAX_PATH
- #define _MAX_PATH 4096
- #endif
- #ifdef _MSC_VER
- #pragma warning (pop)
- #pragma warning (disable: 4514)
- #pragma warning (disable: 4706)
- #endif
- #if defined(__BORLANDC__)
- # pragma warn -8004 /* assigned a value that is never used */
- # pragma warn -8060 /* Assignment inside if() condition. */
- #endif
- /* There are pipes for the process pipeline's stdout and stderr. */
- #define KWSYSPE_PIPE_COUNT 2
- #define KWSYSPE_PIPE_STDOUT 0
- #define KWSYSPE_PIPE_STDERR 1
- /* The maximum amount to read from a pipe at a time. */
- #define KWSYSPE_PIPE_BUFFER_SIZE 1024
- /* Debug output macro. */
- #if 0
- # define KWSYSPE_DEBUG(x) \
- ( \
- (void*)cp == (void*)0x00226DE0? \
- ( \
- fprintf(stderr, "%d/%p/%d ", (int)GetCurrentProcessId(), cp, __LINE__), \
- fprintf x, \
- fflush(stderr), \
- 1 \
- ) : (1) \
- )
- #else
- # define KWSYSPE_DEBUG(x) (void)1
- #endif
- typedef LARGE_INTEGER kwsysProcessTime;
- typedef struct kwsysProcessCreateInformation_s
- {
- /* Windows child startup control data. */
- STARTUPINFOW StartupInfo;
- /* Original handles before making inherited duplicates. */
- HANDLE hStdInput;
- HANDLE hStdOutput;
- HANDLE hStdError;
- } kwsysProcessCreateInformation;
- /*--------------------------------------------------------------------------*/
- typedef struct kwsysProcessPipeData_s kwsysProcessPipeData;
- static DWORD WINAPI kwsysProcessPipeThreadRead(LPVOID ptd);
- static void kwsysProcessPipeThreadReadPipe(kwsysProcess* cp,
- kwsysProcessPipeData* td);
- static DWORD WINAPI kwsysProcessPipeThreadWake(LPVOID ptd);
- static void kwsysProcessPipeThreadWakePipe(kwsysProcess* cp,
- kwsysProcessPipeData* td);
- static int kwsysProcessInitialize(kwsysProcess* cp);
- static DWORD kwsysProcessCreate(kwsysProcess* cp, int index,
- kwsysProcessCreateInformation* si);
- static void kwsysProcessDestroy(kwsysProcess* cp, int event);
- static DWORD kwsysProcessSetupOutputPipeFile(PHANDLE handle,
- const char* name);
- static void kwsysProcessSetupSharedPipe(DWORD nStdHandle, PHANDLE handle);
- static void kwsysProcessSetupPipeNative(HANDLE native, PHANDLE handle);
- static void kwsysProcessCleanupHandle(PHANDLE h);
- static void kwsysProcessCleanup(kwsysProcess* cp, DWORD error);
- static void kwsysProcessCleanErrorMessage(kwsysProcess* cp);
- static int kwsysProcessGetTimeoutTime(kwsysProcess* cp, double* userTimeout,
- kwsysProcessTime* timeoutTime);
- static int kwsysProcessGetTimeoutLeft(kwsysProcessTime* timeoutTime,
- double* userTimeout,
- kwsysProcessTime* timeoutLength);
- static kwsysProcessTime kwsysProcessTimeGetCurrent(void);
- 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);
- static void kwsysProcessSetExitException(kwsysProcess* cp, int code);
- static void kwsysProcessKillTree(int pid);
- static void kwsysProcessDisablePipeThreads(kwsysProcess* cp);
- static int kwsysProcessesInitialize(void);
- static int kwsysTryEnterCreateProcessSection(void);
- static void kwsysLeaveCreateProcessSection(void);
- static int kwsysProcessesAdd(HANDLE hProcess, DWORD dwProcessId,
- int newProcessGroup);
- static void kwsysProcessesRemove(HANDLE hProcess);
- static BOOL WINAPI kwsysCtrlHandler(DWORD dwCtrlType);
- /*--------------------------------------------------------------------------*/
- /* A structure containing synchronization data for each thread. */
- typedef struct kwsysProcessPipeSync_s kwsysProcessPipeSync;
- struct kwsysProcessPipeSync_s
- {
- /* Handle to the thread. */
- HANDLE Thread;
- /* Semaphore indicating to the thread that a process has started. */
- HANDLE Ready;
- /* Semaphore indicating to the thread that it should begin work. */
- HANDLE Go;
- /* Semaphore indicating thread has reset for another process. */
- HANDLE Reset;
- };
- /*--------------------------------------------------------------------------*/
- /* A structure containing data for each pipe's threads. */
- struct kwsysProcessPipeData_s
- {
- /* ------------- Data managed per instance of kwsysProcess ------------- */
- /* Synchronization data for reading thread. */
- kwsysProcessPipeSync Reader;
- /* Synchronization data for waking thread. */
- kwsysProcessPipeSync Waker;
- /* 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[KWSYSPE_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 structure. */
- int State;
- /* The command lines to execute. */
- wchar_t** Commands;
- int NumberOfCommands;
- /* The exit code of each command. */
- DWORD* CommandExitCodes;
- /* The working directory for the child process. */
- wchar_t* WorkingDirectory;
- /* Whether to create the child as a detached process. */
- int OptionDetach;
- /* Whether the child was created as a detached process. */
- int Detached;
- /* Whether to hide the child process's window. */
- int HideWindow;
- /* Whether to treat command lines as verbatim. */
- int Verbatim;
- /* Whether to merge stdout/stderr of the child. */
- int MergeOutput;
- /* Whether to create the process in a new process group. */
- int CreateProcessGroup;
- /* Mutex to protect the shared index used by threads to report data. */
- HANDLE SharedIndexMutex;
- /* Semaphore used by threads to signal data ready. */
- HANDLE Full;
- /* Whether we are currently deleting this kwsysProcess instance. */
- int Deleting;
- /* Data specific to each pipe and its thread. */
- kwsysProcessPipeData Pipe[KWSYSPE_PIPE_COUNT];
- /* Name of files to which stdin and stdout pipes are attached. */
- char* PipeFileSTDIN;
- char* PipeFileSTDOUT;
- char* PipeFileSTDERR;
- /* Whether each pipe is shared with the parent process. */
- int PipeSharedSTDIN;
- int PipeSharedSTDOUT;
- int PipeSharedSTDERR;
- /* Native pipes provided by the user. */
- HANDLE PipeNativeSTDIN[2];
- HANDLE PipeNativeSTDOUT[2];
- HANDLE PipeNativeSTDERR[2];
- /* ------------- Data managed per call to Execute ------------- */
- /* The exceptional behavior that terminated the process, if any. */
- int ExitException;
- /* The process exit code. */
- DWORD ExitCode;
- /* The process return code, if any. */
- int ExitValue;
- /* 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. */
- char ErrorMessage[KWSYSPE_PIPE_BUFFER_SIZE+1];
- /* Description for the ExitException. */
- char ExitExceptionString[KWSYSPE_PIPE_BUFFER_SIZE+1];
- /* Windows process information data. */
- PROCESS_INFORMATION* ProcessInformation;
- /* Data and process termination events for which to wait. */
- PHANDLE ProcessEvents;
- int ProcessEventsLength;
- /* Real working directory of our own process. */
- DWORD RealWorkingDirectoryLength;
- wchar_t* RealWorkingDirectory;
- /* Own handles for the child's ends of the pipes in the parent process.
- Used temporarily during process creation. */
- HANDLE PipeChildStd[3];
- };
- /*--------------------------------------------------------------------------*/
- kwsysProcess* kwsysProcess_New(void)
- {
- int i;
- /* Process control structure. */
- kwsysProcess* cp;
- /* Windows version number data. */
- OSVERSIONINFO osv;
- /* Initialize list of processes before we get any farther. It's especially
- important that the console Ctrl handler be added BEFORE starting the
- first process. This prevents the risk of an orphaned process being
- started by the main thread while the default Ctrl handler is in
- progress. */
- if(!kwsysProcessesInitialize())
- {
- return 0;
- }
- /* Allocate a process control structure. */
- cp = (kwsysProcess*)malloc(sizeof(kwsysProcess));
- if(!cp)
- {
- /* Could not allocate memory for the control structure. */
- return 0;
- }
- ZeroMemory(cp, sizeof(*cp));
- /* Share stdin with the parent process by default. */
- cp->PipeSharedSTDIN = 1;
- /* Set initial status. */
- cp->State = kwsysProcess_State_Starting;
- /* Choose a method of running the child based on version of
- windows. */
- ZeroMemory(&osv, sizeof(osv));
- osv.dwOSVersionInfoSize = sizeof(osv);
- #ifdef KWSYS_WINDOWS_DEPRECATED_GetVersionEx
- # pragma warning (push)
- # ifdef __INTEL_COMPILER
- # pragma warning (disable:1478)
- # else
- # pragma warning (disable:4996)
- # endif
- #endif
- GetVersionEx(&osv);
- #ifdef KWSYS_WINDOWS_DEPRECATED_GetVersionEx
- # pragma warning (pop)
- #endif
- if(osv.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS)
- {
- /* Win9x no longer supported. */
- kwsysProcess_Delete(cp);
- return 0;
- }
- /* 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;
- }
- /* Create the thread to read each pipe. */
- for(i=0; i < KWSYSPE_PIPE_COUNT; ++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;
- /* No process is yet running. Initialize semaphore to 0. */
- if(!(cp->Pipe[i].Reader.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].Reader.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].Reader.Go = CreateSemaphore(0, 1, 1, 0)))
- {
- kwsysProcess_Delete(cp);
- return 0;
- }
- /* Create the reading thread. It will block immediately. The
- thread will not make deeply nested calls, so we need only a
- small stack. */
- if(!(cp->Pipe[i].Reader.Thread = CreateThread(0, 1024,
- kwsysProcessPipeThreadRead,
- &cp->Pipe[i], 0, &dummy)))
- {
- kwsysProcess_Delete(cp);
- return 0;
- }
- /* No process is yet running. Initialize semaphore to 0. */
- if(!(cp->Pipe[i].Waker.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].Waker.Reset = CreateSemaphore(0, 0, 1, 0)))
- {
- kwsysProcess_Delete(cp);
- return 0;
- }
- /* The waker should not wake immediately. Initialize semaphore to 0. */
- if(!(cp->Pipe[i].Waker.Go = CreateSemaphore(0, 0, 1, 0)))
- {
- kwsysProcess_Delete(cp);
- return 0;
- }
- /* Create the waking thread. It will block immediately. The
- thread will not make deeply nested calls, so we need only a
- small stack. */
- if(!(cp->Pipe[i].Waker.Thread = CreateThread(0, 1024,
- kwsysProcessPipeThreadWake,
- &cp->Pipe[i], 0, &dummy)))
- {
- kwsysProcess_Delete(cp);
- return 0;
- }
- }
- for(i=0; i < 3; ++i)
- {
- cp->PipeChildStd[i] = INVALID_HANDLE_VALUE;
- }
- return cp;
- }
- /*--------------------------------------------------------------------------*/
- void kwsysProcess_Delete(kwsysProcess* cp)
- {
- int i;
- /* Make sure we have an instance. */
- if(!cp)
- {
- return;
- }
- /* If the process is executing, wait for it to finish. */
- if(cp->State == kwsysProcess_State_Executing)
- {
- if(cp->Detached)
- {
- kwsysProcess_Disown(cp);
- }
- else
- {
- kwsysProcess_WaitForExit(cp, 0);
- }
- }
- /* We are deleting the kwsysProcess instance. */
- cp->Deleting = 1;
- /* Terminate each of the threads. */
- for(i=0; i < KWSYSPE_PIPE_COUNT; ++i)
- {
- /* Terminate this reading thread. */
- if(cp->Pipe[i].Reader.Thread)
- {
- /* Signal the thread we are ready for it. It will terminate
- immediately since Deleting is set. */
- ReleaseSemaphore(cp->Pipe[i].Reader.Ready, 1, 0);
- /* Wait for the thread to exit. */
- WaitForSingleObject(cp->Pipe[i].Reader.Thread, INFINITE);
- /* Close the handle to the thread. */
- kwsysProcessCleanupHandle(&cp->Pipe[i].Reader.Thread);
- }
- /* Terminate this waking thread. */
- if(cp->Pipe[i].Waker.Thread)
- {
- /* Signal the thread we are ready for it. It will terminate
- immediately since Deleting is set. */
- ReleaseSemaphore(cp->Pipe[i].Waker.Ready, 1, 0);
- /* Wait for the thread to exit. */
- WaitForSingleObject(cp->Pipe[i].Waker.Thread, INFINITE);
- /* Close the handle to the thread. */
- kwsysProcessCleanupHandle(&cp->Pipe[i].Waker.Thread);
- }
- /* Cleanup the pipe's semaphores. */
- kwsysProcessCleanupHandle(&cp->Pipe[i].Reader.Ready);
- kwsysProcessCleanupHandle(&cp->Pipe[i].Reader.Go);
- kwsysProcessCleanupHandle(&cp->Pipe[i].Reader.Reset);
- kwsysProcessCleanupHandle(&cp->Pipe[i].Waker.Ready);
- kwsysProcessCleanupHandle(&cp->Pipe[i].Waker.Go);
- kwsysProcessCleanupHandle(&cp->Pipe[i].Waker.Reset);
- }
- /* Close the shared semaphores. */
- kwsysProcessCleanupHandle(&cp->SharedIndexMutex);
- kwsysProcessCleanupHandle(&cp->Full);
- /* Free memory. */
- kwsysProcess_SetCommand(cp, 0);
- kwsysProcess_SetWorkingDirectory(cp, 0);
- kwsysProcess_SetPipeFile(cp, kwsysProcess_Pipe_STDIN, 0);
- kwsysProcess_SetPipeFile(cp, kwsysProcess_Pipe_STDOUT, 0);
- kwsysProcess_SetPipeFile(cp, kwsysProcess_Pipe_STDERR, 0);
- if(cp->CommandExitCodes)
- {
- free(cp->CommandExitCodes);
- }
- free(cp);
- }
- /*--------------------------------------------------------------------------*/
- int kwsysProcess_SetCommand(kwsysProcess* cp, char const* const* command)
- {
- int i;
- if(!cp)
- {
- return 0;
- }
- for(i=0; i < cp->NumberOfCommands; ++i)
- {
- free(cp->Commands[i]);
- }
- cp->NumberOfCommands = 0;
- if(cp->Commands)
- {
- free(cp->Commands);
- cp->Commands = 0;
- }
- if(command)
- {
- return kwsysProcess_AddCommand(cp, command);
- }
- return 1;
- }
- /*--------------------------------------------------------------------------*/
- int kwsysProcess_AddCommand(kwsysProcess* cp, char const* const* command)
- {
- int newNumberOfCommands;
- wchar_t** newCommands;
- /* Make sure we have a command to add. */
- if(!cp || !command || !*command)
- {
- return 0;
- }
- /* Allocate a new array for command pointers. */
- newNumberOfCommands = cp->NumberOfCommands + 1;
- if(!(newCommands = (wchar_t**)malloc(sizeof(wchar_t*) * newNumberOfCommands)))
- {
- /* Out of memory. */
- return 0;
- }
- /* Copy any existing commands into the new array. */
- {
- int i;
- for(i=0; i < cp->NumberOfCommands; ++i)
- {
- newCommands[i] = cp->Commands[i];
- }
- }
- if (cp->Verbatim)
- {
- /* Copy the verbatim command line into the buffer. */
- newCommands[cp->NumberOfCommands] = kwsysEncoding_DupToWide(*command);
- }
- else
- {
- /* Encode the arguments so CommandLineToArgvW can decode
- them from the command line string in the child. */
- char buffer[32768]; /* CreateProcess max command-line length. */
- char* end = buffer + sizeof(buffer);
- char* out = buffer;
- char const* const* a;
- for (a = command; *a; ++a)
- {
- int quote = !**a; /* Quote the empty string. */
- int slashes = 0;
- char const* c;
- if (a != command && out != end) { *out++ = ' '; }
- for (c = *a; !quote && *c; ++c)
- { quote = (*c == ' ' || *c == '\t'); }
- if (quote && out != end) { *out++ = '"'; }
- for (c = *a; *c; ++c)
- {
- if (*c == '\\')
- {
- ++slashes;
- }
- else
- {
- if (*c == '"')
- {
- // Add n+1 backslashes to total 2n+1 before internal '"'.
- while(slashes-- >= 0 && out != end) { *out++ = '\\'; }
- }
- slashes = 0;
- }
- if (out != end) { *out++ = *c; }
- }
- if (quote)
- {
- // Add n backslashes to total 2n before ending '"'.
- while (slashes-- > 0 && out != end) { *out++ = '\\'; }
- if (out != end) { *out++ = '"'; }
- }
- }
- if(out != end)
- {
- *out = '\0';
- newCommands[cp->NumberOfCommands] = kwsysEncoding_DupToWide(buffer);
- }
- else
- {
- newCommands[cp->NumberOfCommands] = 0;
- }
- }
- if (!newCommands[cp->NumberOfCommands])
- {
- /* Out of memory or command line too long. */
- free(newCommands);
- return 0;
- }
- /* Save the new array of commands. */
- free(cp->Commands);
- cp->Commands = newCommands;
- cp->NumberOfCommands = newNumberOfCommands;
- return 1;
- }
- /*--------------------------------------------------------------------------*/
- void kwsysProcess_SetTimeout(kwsysProcess* cp, double timeout)
- {
- if(!cp)
- {
- return;
- }
- cp->Timeout = timeout;
- if(cp->Timeout < 0)
- {
- cp->Timeout = 0;
- }
- }
- /*--------------------------------------------------------------------------*/
- int kwsysProcess_SetWorkingDirectory(kwsysProcess* cp, const char* dir)
- {
- if(!cp)
- {
- return 0;
- }
- if(cp->WorkingDirectory)
- {
- free(cp->WorkingDirectory);
- cp->WorkingDirectory = 0;
- }
- if(dir && dir[0])
- {
- wchar_t* wdir = kwsysEncoding_DupToWide(dir);
- /* We must convert the working directory to a full path. */
- DWORD length = GetFullPathNameW(wdir, 0, 0, 0);
- if(length > 0)
- {
- wchar_t* work_dir = malloc(length*sizeof(wchar_t));
- if(!work_dir)
- {
- free(wdir);
- return 0;
- }
- if(!GetFullPathNameW(wdir, length, work_dir, 0))
- {
- free(work_dir);
- free(wdir);
- return 0;
- }
- cp->WorkingDirectory = work_dir;
- }
- free(wdir);
- }
- return 1;
- }
- /*--------------------------------------------------------------------------*/
- int kwsysProcess_SetPipeFile(kwsysProcess* cp, int pipe, const char* file)
- {
- char** pfile;
- if(!cp)
- {
- return 0;
- }
- switch(pipe)
- {
- case kwsysProcess_Pipe_STDIN: pfile = &cp->PipeFileSTDIN; break;
- case kwsysProcess_Pipe_STDOUT: pfile = &cp->PipeFileSTDOUT; break;
- case kwsysProcess_Pipe_STDERR: pfile = &cp->PipeFileSTDERR; break;
- default: return 0;
- }
- if(*pfile)
- {
- free(*pfile);
- *pfile = 0;
- }
- if(file)
- {
- *pfile = (char*)malloc(strlen(file)+1);
- if(!*pfile)
- {
- return 0;
- }
- strcpy(*pfile, file);
- }
- /* If we are redirecting the pipe, do not share it or use a native
- pipe. */
- if(*pfile)
- {
- kwsysProcess_SetPipeNative(cp, pipe, 0);
- kwsysProcess_SetPipeShared(cp, pipe, 0);
- }
- return 1;
- }
- /*--------------------------------------------------------------------------*/
- void kwsysProcess_SetPipeShared(kwsysProcess* cp, int pipe, int shared)
- {
- if(!cp)
- {
- return;
- }
- switch(pipe)
- {
- case kwsysProcess_Pipe_STDIN: cp->PipeSharedSTDIN = shared?1:0; break;
- case kwsysProcess_Pipe_STDOUT: cp->PipeSharedSTDOUT = shared?1:0; break;
- case kwsysProcess_Pipe_STDERR: cp->PipeSharedSTDERR = shared?1:0; break;
- default: return;
- }
- /* If we are sharing the pipe, do not redirect it to a file or use a
- native pipe. */
- if(shared)
- {
- kwsysProcess_SetPipeFile(cp, pipe, 0);
- kwsysProcess_SetPipeNative(cp, pipe, 0);
- }
- }
- /*--------------------------------------------------------------------------*/
- void kwsysProcess_SetPipeNative(kwsysProcess* cp, int pipe, HANDLE p[2])
- {
- HANDLE* pPipeNative = 0;
- if(!cp)
- {
- return;
- }
- switch(pipe)
- {
- case kwsysProcess_Pipe_STDIN: pPipeNative = cp->PipeNativeSTDIN; break;
- case kwsysProcess_Pipe_STDOUT: pPipeNative = cp->PipeNativeSTDOUT; break;
- case kwsysProcess_Pipe_STDERR: pPipeNative = cp->PipeNativeSTDERR; break;
- default: return;
- }
- /* Copy the native pipe handles provided. */
- if(p)
- {
- pPipeNative[0] = p[0];
- pPipeNative[1] = p[1];
- }
- else
- {
- pPipeNative[0] = 0;
- pPipeNative[1] = 0;
- }
- /* If we are using a native pipe, do not share it or redirect it to
- a file. */
- if(p)
- {
- kwsysProcess_SetPipeFile(cp, pipe, 0);
- kwsysProcess_SetPipeShared(cp, pipe, 0);
- }
- }
- /*--------------------------------------------------------------------------*/
- int kwsysProcess_GetOption(kwsysProcess* cp, int optionId)
- {
- if(!cp)
- {
- return 0;
- }
- switch(optionId)
- {
- case kwsysProcess_Option_Detach: return cp->OptionDetach;
- case kwsysProcess_Option_HideWindow: return cp->HideWindow;
- case kwsysProcess_Option_MergeOutput: return cp->MergeOutput;
- case kwsysProcess_Option_Verbatim: return cp->Verbatim;
- case kwsysProcess_Option_CreateProcessGroup:
- return cp->CreateProcessGroup;
- default: return 0;
- }
- }
- /*--------------------------------------------------------------------------*/
- void kwsysProcess_SetOption(kwsysProcess* cp, int optionId, int value)
- {
- if(!cp)
- {
- return;
- }
- switch(optionId)
- {
- case kwsysProcess_Option_Detach: cp->OptionDetach = value; break;
- case kwsysProcess_Option_HideWindow: cp->HideWindow = value; break;
- case kwsysProcess_Option_MergeOutput: cp->MergeOutput = value; break;
- case kwsysProcess_Option_Verbatim: cp->Verbatim = value; break;
- case kwsysProcess_Option_CreateProcessGroup:
- cp->CreateProcessGroup = value; break;
- default: break;
- }
- }
- /*--------------------------------------------------------------------------*/
- int kwsysProcess_GetState(kwsysProcess* cp)
- {
- return cp? cp->State : kwsysProcess_State_Error;
- }
- /*--------------------------------------------------------------------------*/
- int kwsysProcess_GetExitException(kwsysProcess* cp)
- {
- return cp? cp->ExitException : kwsysProcess_Exception_Other;
- }
- /*--------------------------------------------------------------------------*/
- int kwsysProcess_GetExitValue(kwsysProcess* cp)
- {
- return cp? cp->ExitValue : -1;
- }
- /*--------------------------------------------------------------------------*/
- int kwsysProcess_GetExitCode(kwsysProcess* cp)
- {
- return cp? cp->ExitCode : 0;
- }
- /*--------------------------------------------------------------------------*/
- const char* kwsysProcess_GetErrorString(kwsysProcess* cp)
- {
- if(!cp)
- {
- return "Process management structure could not be allocated";
- }
- else if(cp->State == kwsysProcess_State_Error)
- {
- return cp->ErrorMessage;
- }
- return "Success";
- }
- /*--------------------------------------------------------------------------*/
- const char* kwsysProcess_GetExceptionString(kwsysProcess* cp)
- {
- if(!cp)
- {
- return "GetExceptionString called with NULL process management structure";
- }
- else if(cp->State == kwsysProcess_State_Exception)
- {
- return cp->ExitExceptionString;
- }
- return "No exception";
- }
- /*--------------------------------------------------------------------------*/
- void kwsysProcess_Execute(kwsysProcess* cp)
- {
- int i;
- /* Do not execute a second time. */
- if(!cp || cp->State == kwsysProcess_State_Executing)
- {
- return;
- }
- /* Make sure we have something to run. */
- if(cp->NumberOfCommands < 1)
- {
- strcpy(cp->ErrorMessage, "No command");
- cp->State = kwsysProcess_State_Error;
- return;
- }
- /* Initialize the control structure for a new process. */
- if(!kwsysProcessInitialize(cp))
- {
- strcpy(cp->ErrorMessage, "Out of memory");
- cp->State = kwsysProcess_State_Error;
- return;
- }
- /* Save the real working directory of this process and change to
- the working directory for the child processes. This is needed
- to make pipe file paths evaluate correctly. */
- if(cp->WorkingDirectory)
- {
- if(!GetCurrentDirectoryW(cp->RealWorkingDirectoryLength,
- cp->RealWorkingDirectory))
- {
- kwsysProcessCleanup(cp, GetLastError());
- return;
- }
- SetCurrentDirectoryW(cp->WorkingDirectory);
- }
- /* Setup the stdin pipe for the first process. */
- if(cp->PipeFileSTDIN)
- {
- /* Create a handle to read a file for stdin. */
- wchar_t* wstdin = kwsysEncoding_DupToWide(cp->PipeFileSTDIN);
- DWORD error;
- cp->PipeChildStd[0] =
- CreateFileW(wstdin, GENERIC_READ|GENERIC_WRITE,
- FILE_SHARE_READ|FILE_SHARE_WRITE,
- 0, OPEN_EXISTING, 0, 0);
- error = GetLastError(); /* Check now in case free changes this. */
- free(wstdin);
- if(cp->PipeChildStd[0] == INVALID_HANDLE_VALUE)
- {
- kwsysProcessCleanup(cp, error);
- return;
- }
- }
- else if(cp->PipeSharedSTDIN)
- {
- /* Share this process's stdin with the child. */
- kwsysProcessSetupSharedPipe(STD_INPUT_HANDLE, &cp->PipeChildStd[0]);
- }
- else if(cp->PipeNativeSTDIN[0])
- {
- /* Use the provided native pipe. */
- kwsysProcessSetupPipeNative(cp->PipeNativeSTDIN[0], &cp->PipeChildStd[0]);
- }
- else
- {
- /* Explicitly give the child no stdin. */
- cp->PipeChildStd[0] = INVALID_HANDLE_VALUE;
- }
- /* Create the output pipe for the last process.
- We always create this so the pipe thread can run even if we
- do not end up giving the write end to the child below. */
- if(!CreatePipe(&cp->Pipe[KWSYSPE_PIPE_STDOUT].Read,
- &cp->Pipe[KWSYSPE_PIPE_STDOUT].Write, 0, 0))
- {
- kwsysProcessCleanup(cp, GetLastError());
- return;
- }
- if(cp->PipeFileSTDOUT)
- {
- /* Use a file for stdout. */
- DWORD error = kwsysProcessSetupOutputPipeFile(&cp->PipeChildStd[1],
- cp->PipeFileSTDOUT);
- if(error)
- {
- kwsysProcessCleanup(cp, error);
- return;
- }
- }
- else if(cp->PipeSharedSTDOUT)
- {
- /* Use the parent stdout. */
- kwsysProcessSetupSharedPipe(STD_OUTPUT_HANDLE, &cp->PipeChildStd[1]);
- }
- else if(cp->PipeNativeSTDOUT[1])
- {
- /* Use the given handle for stdout. */
- kwsysProcessSetupPipeNative(cp->PipeNativeSTDOUT[1], &cp->PipeChildStd[1]);
- }
- else
- {
- /* Use our pipe for stdout. Duplicate the handle since our waker
- thread will use the original. Do not make it inherited yet. */
- if(!DuplicateHandle(GetCurrentProcess(),
- cp->Pipe[KWSYSPE_PIPE_STDOUT].Write,
- GetCurrentProcess(), &cp->PipeChildStd[1],
- 0, FALSE, DUPLICATE_SAME_ACCESS))
- {
- kwsysProcessCleanup(cp, GetLastError());
- return;
- }
- }
- /* Create stderr pipe to be shared by all processes in the pipeline.
- We always create this so the pipe thread can run even if we do not
- end up giving the write end to the child below. */
- if(!CreatePipe(&cp->Pipe[KWSYSPE_PIPE_STDERR].Read,
- &cp->Pipe[KWSYSPE_PIPE_STDERR].Write, 0, 0))
- {
- kwsysProcessCleanup(cp, GetLastError());
- return;
- }
- if(cp->PipeFileSTDERR)
- {
- /* Use a file for stderr. */
- DWORD error = kwsysProcessSetupOutputPipeFile(&cp->PipeChildStd[2],
- cp->PipeFileSTDERR);
- if(error)
- {
- kwsysProcessCleanup(cp, error);
- return;
- }
- }
- else if(cp->PipeSharedSTDERR)
- {
- /* Use the parent stderr. */
- kwsysProcessSetupSharedPipe(STD_ERROR_HANDLE, &cp->PipeChildStd[2]);
- }
- else if(cp->PipeNativeSTDERR[1])
- {
- /* Use the given handle for stderr. */
- kwsysProcessSetupPipeNative(cp->PipeNativeSTDERR[1], &cp->PipeChildStd[2]);
- }
- else
- {
- /* Use our pipe for stderr. Duplicate the handle since our waker
- thread will use the original. Do not make it inherited yet. */
- if(!DuplicateHandle(GetCurrentProcess(),
- cp->Pipe[KWSYSPE_PIPE_STDERR].Write,
- GetCurrentProcess(), &cp->PipeChildStd[2],
- 0, FALSE, DUPLICATE_SAME_ACCESS))
- {
- kwsysProcessCleanup(cp, GetLastError());
- return;
- }
- }
- /* Create the pipeline of processes. */
- {
- /* Child startup control data. */
- kwsysProcessCreateInformation si;
- HANDLE nextStdInput = cp->PipeChildStd[0];
- /* Initialize startup info data. */
- ZeroMemory(&si, sizeof(si));
- si.StartupInfo.cb = sizeof(si.StartupInfo);
- /* Decide whether a child window should be shown. */
- si.StartupInfo.dwFlags |= STARTF_USESHOWWINDOW;
- si.StartupInfo.wShowWindow =
- (unsigned short)(cp->HideWindow?SW_HIDE:SW_SHOWDEFAULT);
- /* Connect the child's output pipes to the threads. */
- si.StartupInfo.dwFlags |= STARTF_USESTDHANDLES;
- for(i=0; i < cp->NumberOfCommands; ++i)
- {
- /* Setup the process's pipes. */
- si.hStdInput = nextStdInput;
- if (i == cp->NumberOfCommands-1)
- {
- /* The last child gets the overall stdout. */
- nextStdInput = INVALID_HANDLE_VALUE;
- si.hStdOutput = cp->PipeChildStd[1];
- }
- else
- {
- /* Create a pipe to sit between the children. */
- HANDLE p[2] = {INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE};
- if (!CreatePipe(&p[0], &p[1], 0, 0))
- {
- DWORD error = GetLastError();
- if (nextStdInput != cp->PipeChildStd[0])
- {
- kwsysProcessCleanupHandle(&nextStdInput);
- }
- kwsysProcessCleanup(cp, error);
- return;
- }
- nextStdInput = p[0];
- si.hStdOutput = p[1];
- }
- si.hStdError = cp->MergeOutput? cp->PipeChildStd[1] : cp->PipeChildStd[2];
- {
- DWORD error = kwsysProcessCreate(cp, i, &si);
- /* Close our copies of pipes used between children. */
- if (si.hStdInput != cp->PipeChildStd[0])
- {
- kwsysProcessCleanupHandle(&si.hStdInput);
- }
- if (si.hStdOutput != cp->PipeChildStd[1])
- {
- kwsysProcessCleanupHandle(&si.hStdOutput);
- }
- if (si.hStdError != cp->PipeChildStd[2] && !cp->MergeOutput)
- {
- kwsysProcessCleanupHandle(&si.hStdError);
- }
- if (!error)
- {
- cp->ProcessEvents[i+1] = cp->ProcessInformation[i].hProcess;
- }
- else
- {
- if (nextStdInput != cp->PipeChildStd[0])
- {
- kwsysProcessCleanupHandle(&nextStdInput);
- }
- kwsysProcessCleanup(cp, error);
- return;
- }
- }
- }
- }
- /* The parent process does not need the child's pipe ends. */
- for (i=0; i < 3; ++i)
- {
- kwsysProcessCleanupHandle(&cp->PipeChildStd[i]);
- }
- /* Restore the working directory. */
- if(cp->RealWorkingDirectory)
- {
- SetCurrentDirectoryW(cp->RealWorkingDirectory);
- free(cp->RealWorkingDirectory);
- cp->RealWorkingDirectory = 0;
- }
- /* The timeout period starts now. */
- cp->StartTime = kwsysProcessTimeGetCurrent();
- cp->TimeoutTime = kwsysProcessTimeFromDouble(-1);
- /* All processes in the pipeline have been started in suspended
- mode. Resume them all now. */
- for(i=0; i < cp->NumberOfCommands; ++i)
- {
- ResumeThread(cp->ProcessInformation[i].hThread);
- }
- /* ---- It is no longer safe to call kwsysProcessCleanup. ----- */
- /* Tell the pipe threads that a process has started. */
- for(i=0; i < KWSYSPE_PIPE_COUNT; ++i)
- {
- ReleaseSemaphore(cp->Pipe[i].Reader.Ready, 1, 0);
- ReleaseSemaphore(cp->Pipe[i].Waker.Ready, 1, 0);
- }
- /* We don't care about the children's main threads. */
- for(i=0; i < cp->NumberOfCommands; ++i)
- {
- kwsysProcessCleanupHandle(&cp->ProcessInformation[i].hThread);
- }
- /* No pipe has reported data. */
- cp->CurrentIndex = KWSYSPE_PIPE_COUNT;
- cp->PipesLeft = KWSYSPE_PIPE_COUNT;
- /* The process has now started. */
- cp->State = kwsysProcess_State_Executing;
- cp->Detached = cp->OptionDetach;
- }
- /*--------------------------------------------------------------------------*/
- void kwsysProcess_Disown(kwsysProcess* cp)
- {
- int i;
- /* Make sure we are executing a detached process. */
- if(!cp || !cp->Detached || cp->State != kwsysProcess_State_Executing ||
- cp->TimeoutExpired || cp->Killed || cp->Terminated)
- {
- return;
- }
- /* Disable the reading threads. */
- kwsysProcessDisablePipeThreads(cp);
- /* Wait for all pipe threads to reset. */
- for(i=0; i < KWSYSPE_PIPE_COUNT; ++i)
- {
- WaitForSingleObject(cp->Pipe[i].Reader.Reset, INFINITE);
- WaitForSingleObject(cp->Pipe[i].Waker.Reset, INFINITE);
- }
- /* We will not wait for exit, so cleanup now. */
- kwsysProcessCleanup(cp, 0);
- /* The process has been disowned. */
- cp->State = kwsysProcess_State_Disowned;
- }
- /*--------------------------------------------------------------------------*/
- int kwsysProcess_WaitForData(kwsysProcess* cp, char** data, int* length,
- double* userTimeout)
- {
- kwsysProcessTime userStartTime;
- kwsysProcessTime timeoutLength;
- kwsysProcessTime timeoutTime;
- DWORD timeout;
- int user;
- int done = 0;
- int expired = 0;
- int pipeId = kwsysProcess_Pipe_None;
- DWORD w;
- /* Make sure we are executing a process. */
- if(!cp || cp->State != kwsysProcess_State_Executing || cp->Killed ||
- cp->TimeoutExpired)
- {
- return kwsysProcess_Pipe_None;
- }
- /* Record the time at which user timeout period starts. */
- 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 < KWSYSPE_PIPE_COUNT)
- {
- KWSYSPE_DEBUG((stderr, "releasing reader %d\n", cp->CurrentIndex));
- ReleaseSemaphore(cp->Pipe[cp->CurrentIndex].Reader.Go, 1, 0);
- cp->CurrentIndex = KWSYSPE_PIPE_COUNT;
- }
- /* Setup a timeout if required. */
- if(kwsysProcessGetTimeoutLeft(&timeoutTime, user?userTimeout:0,
- &timeoutLength))
- {
- /* Timeout has already expired. */
- expired = 1;
- break;
- }
- if(timeoutTime.QuadPart < 0)
- {
- timeout = INFINITE;
- }
- else
- {
- timeout = kwsysProcessTimeToDWORD(timeoutLength);
- }
- /* Wait for a pipe's thread to signal or a process to terminate. */
- w = WaitForMultipleObjects(cp->ProcessEventsLength, cp->ProcessEvents,
- 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 at the write end. Close the read end and
- inform the wakeup thread it is done with this process. */
- kwsysProcessCleanupHandle(&cp->Pipe[cp->CurrentIndex].Read);
- ReleaseSemaphore(cp->Pipe[cp->CurrentIndex].Waker.Go, 1, 0);
- KWSYSPE_DEBUG((stderr, "wakeup %d\n", cp->CurrentIndex));
- --cp->PipesLeft;
- }
- else if(data && length)
- {
- /* Report this data. */
- *data = cp->Pipe[cp->CurrentIndex].DataBuffer;
- *length = cp->Pipe[cp->CurrentIndex].DataLength;
- switch(cp->CurrentIndex)
- {
- case KWSYSPE_PIPE_STDOUT:
- pipeId = kwsysProcess_Pipe_STDOUT; break;
- case KWSYSPE_PIPE_STDERR:
- pipeId = kwsysProcess_Pipe_STDERR; break;
- }
- done = 1;
- }
- }
- else
- {
- /* A process has terminated. */
- kwsysProcessDestroy(cp, w-WAIT_OBJECT_0);
- }
- }
- /* 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_Pipe_Timeout;
- }
- else
- {
- /* The process timeout has expired. Kill the child now. */
- KWSYSPE_DEBUG((stderr, "killing child because timeout expired\n"));
- kwsysProcess_Kill(cp);
- cp->TimeoutExpired = 1;
- cp->Killed = 0;
- return kwsysProcess_Pipe_None;
- }
- }
- else
- {
- /* The children have terminated and no more data are available. */
- return kwsysProcess_Pipe_None;
- }
- }
- /*--------------------------------------------------------------------------*/
- int kwsysProcess_WaitForExit(kwsysProcess* cp, double* userTimeout)
- {
- int i;
- int pipe;
- /* Make sure we are executing a process. */
- if(!cp || cp->State != kwsysProcess_State_Executing)
- {
- return 1;
- }
- /* Wait for the process to terminate. Ignore all data. */
- while((pipe = kwsysProcess_WaitForData(cp, 0, 0, userTimeout)) > 0)
- {
- if(pipe == kwsysProcess_Pipe_Timeout)
- {
- /* The user timeout has expired. */
- return 0;
- }
- }
- KWSYSPE_DEBUG((stderr, "no more data\n"));
- /* When the last pipe closes in WaitForData, the loop terminates
- without releasing the pipe's thread. Release it now. */
- if(cp->CurrentIndex < KWSYSPE_PIPE_COUNT)
- {
- KWSYSPE_DEBUG((stderr, "releasing reader %d\n", cp->CurrentIndex));
- ReleaseSemaphore(cp->Pipe[cp->CurrentIndex].Reader.Go, 1, 0);
- cp->CurrentIndex = KWSYSPE_PIPE_COUNT;
- }
- /* Wait for all pipe threads to reset. */
- for(i=0; i < KWSYSPE_PIPE_COUNT; ++i)
- {
- KWSYSPE_DEBUG((stderr, "waiting reader reset %d\n", i));
- WaitForSingleObject(cp->Pipe[i].Reader.Reset, INFINITE);
- KWSYSPE_DEBUG((stderr, "waiting waker reset %d\n", i));
- WaitForSingleObject(cp->Pipe[i].Waker.Reset, INFINITE);
- }
- /* ---- It is now safe again to call kwsysProcessCleanup. ----- */
- /* Close all the pipes. */
- kwsysProcessCleanup(cp, 0);
- /* Determine the outcome. */
- if(cp->Killed)
- {
- /* We killed the child. */
- cp->State = kwsysProcess_State_Killed;
- }
- else if(cp->TimeoutExpired)
- {
- /* The timeout expired. */
- cp->State = kwsysProcess_State_Expired;
- }
- else
- {
- /* The children exited. Report the outcome of the last process. */
- cp->ExitCode = cp->CommandExitCodes[cp->NumberOfCommands-1];
- if((cp->ExitCode & 0xF0000000) == 0xC0000000)
- {
- /* Child terminated due to exceptional behavior. */
- cp->State = kwsysProcess_State_Exception;
- cp->ExitValue = 1;
- kwsysProcessSetExitException(cp, cp->ExitCode);
- }
- else
- {
- /* Child exited without exception. */
- cp->State = kwsysProcess_State_Exited;
- cp->ExitException = kwsysProcess_Exception_None;
- cp->ExitValue = cp->ExitCode;
- }
- }
- return 1;
- }
- /*--------------------------------------------------------------------------*/
- void kwsysProcess_Interrupt(kwsysProcess* cp)
- {
- int i;
- /* Make sure we are executing a process. */
- if(!cp || cp->State != kwsysProcess_State_Executing || cp->TimeoutExpired ||
- cp->Killed)
- {
- KWSYSPE_DEBUG((stderr, "interrupt: child not executing\n"));
- return;
- }
- /* Skip actually interrupting the child if it has already terminated. */
- if(cp->Terminated)
- {
- KWSYSPE_DEBUG((stderr, "interrupt: child already terminated\n"));
- return;
- }
- /* Interrupt the children. */
- if (cp->CreateProcessGroup)
- {
- if(cp->ProcessInformation)
- {
- for(i=0; i < cp->NumberOfCommands; ++i)
- {
- /* Make sure the process handle isn't closed (e.g. from disowning). */
- if(cp->ProcessInformation[i].hProcess)
- {
- /* The user created a process group for this process. The group ID
- is the process ID for the original process in the group. Note
- that we have to use Ctrl+Break: Ctrl+C is not allowed for process
- groups. */
- GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT,
- cp->ProcessInformation[i].dwProcessId);
- }
- }
- }
- }
- else
- {
- /* No process group was created. Kill our own process group... */
- GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, 0);
- }
- }
- /*--------------------------------------------------------------------------*/
- void kwsysProcess_Kill(kwsysProcess* cp)
- {
- int i;
- /* Make sure we are executing a process. */
- if(!cp || cp->State != kwsysProcess_State_Executing || cp->TimeoutExpired ||
- cp->Killed)
- {
- KWSYSPE_DEBUG((stderr, "kill: child not executing\n"));
- return;
- }
- /* Disable the reading threads. */
- KWSYSPE_DEBUG((stderr, "kill: disabling pipe threads\n"));
- kwsysProcessDisablePipeThreads(cp);
- /* Skip actually killing the child if it has already terminated. */
- if(cp->Terminated)
- {
- KWSYSPE_DEBUG((stderr, "kill: child already terminated\n"));
- return;
- }
- /* Kill the children. */
- cp->Killed = 1;
- for(i=0; i < cp->NumberOfCommands; ++i)
- {
- kwsysProcessKillTree(cp->ProcessInformation[i].dwProcessId);
- /* Remove from global list of processes and close handles. */
- kwsysProcessesRemove(cp->ProcessInformation[i].hProcess);
- kwsysProcessCleanupHandle(&cp->ProcessInformation[i].hThread);
- kwsysProcessCleanupHandle(&cp->ProcessInformation[i].hProcess);
- }
- /* We are killing the children and ignoring all data. Do not wait
- for them to exit. */
- }
- /*--------------------------------------------------------------------------*/
- /*
- Function executed for each pipe's thread. Argument is a pointer to
- the kwsysProcessPipeData instance for this thread.
- */
- DWORD WINAPI kwsysProcessPipeThreadRead(LPVOID ptd)
- {
- kwsysProcessPipeData* td = (kwsysProcessPipeData*)ptd;
- kwsysProcess* cp = td->Process;
- /* Wait for a process to be ready. */
- while((WaitForSingleObject(td->Reader.Ready, INFINITE), !cp->Deleting))
- {
- /* Read output from the process for this thread's pipe. */
- kwsysProcessPipeThreadReadPipe(cp, td);
- /* Signal the main thread we have reset for a new process. */
- ReleaseSemaphore(td->Reader.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((KWSYSPE_DEBUG((stderr, "wait for read %d\n", td->Index)),
- WaitForSingleObject(td->Reader.Go, INFINITE), !td->Closed))
- {
- KWSYSPE_DEBUG((stderr, "reading %d\n", td->Index));
- /* Read data from the pipe. This may block until data are available. */
- if(!ReadFile(td->Read, td->DataBuffer, KWSYSPE_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;
- KWSYSPE_DEBUG((stderr, "read closed %d\n", td->Index));
- }
- KWSYSPE_DEBUG((stderr, "read %d\n", td->Index));
- /* Wait for our turn to be handled by the main thread. */
- WaitForSingleObject(cp->SharedIndexMutex, INFINITE);
- KWSYSPE_DEBUG((stderr, "reporting read %d\n", td->Index));
- /* Tell the main thread we have something to report. */
- cp->SharedIndex = td->Index;
- ReleaseSemaphore(cp->Full, 1, 0);
- }
- /* We were signalled to exit with our buffer empty. Reset the
- mutex for a new process. */
- KWSYSPE_DEBUG((stderr, "self releasing reader %d\n", td->Index));
- ReleaseSemaphore(td->Reader.Go, 1, 0);
- }
- /*--------------------------------------------------------------------------*/
- /*
- Function executed for each pipe's thread. Argument is a pointer to
- the kwsysProcessPipeData instance for this thread.
- */
- DWORD WINAPI kwsysProcessPipeThreadWake(LPVOID ptd)
- {
- kwsysProcessPipeData* td = (kwsysProcessPipeData*)ptd;
- kwsysProcess* cp = td->Process;
- /* Wait for a process to be ready. */
- while((WaitForSingleObject(td->Waker.Ready, INFINITE), !cp->Deleting))
- {
- /* Wait for a possible wakeup. */
- kwsysProcessPipeThreadWakePipe(cp, td);
- /* Signal the main thread we have reset for a new process. */
- ReleaseSemaphore(td->Waker.Reset, 1, 0);
- }
- return 0;
- }
- /*--------------------------------------------------------------------------*/
- /*
- Function called in each pipe's thread to handle reading thread
- wakeup for one execution of a subprocess.
- */
- void kwsysProcessPipeThreadWakePipe(kwsysProcess* cp, kwsysProcessPipeData* td)
- {
- (void)cp;
- /* Wait for a possible wake command. */
- KWSYSPE_DEBUG((stderr, "wait for wake %d\n", td->Index));
- WaitForSingleObject(td->Waker.Go, INFINITE);
- KWSYSPE_DEBUG((stderr, "waking %d\n", td->Index));
- /* If the pipe is not closed, we need to wake up the reading thread. */
- if(!td->Closed)
- {
- DWORD dummy;
- KWSYSPE_DEBUG((stderr, "waker %d writing byte\n", td->Index));
- WriteFile(td->Write, "", 1, &dummy, 0);
- KWSYSPE_DEBUG((stderr, "waker %d wrote byte\n", td->Index));
- }
- }
- /*--------------------------------------------------------------------------*/
- /* Initialize a process control structure for kwsysProcess_Execute. */
- int kwsysProcessInitialize(kwsysProcess* cp)
- {
- /* Reset internal status flags. */
- cp->TimeoutExpired = 0;
- cp->Terminated = 0;
- cp->Killed = 0;
- cp->ExitException = kwsysProcess_Exception_None;
- cp->ExitCode = 1;
- cp->ExitValue = 1;
- /* Reset error data. */
- cp->ErrorMessage[0] = 0;
- strcpy(cp->ExitExceptionString, "No exception");
- /* Allocate process information for each process. */
- cp->ProcessInformation =
- (PROCESS_INFORMATION*)malloc(sizeof(PROCESS_INFORMATION) *
- cp->NumberOfCommands);
- if(!cp->ProcessInformation)
- {
- return 0;
- }
- ZeroMemory(cp->ProcessInformation,
- sizeof(PROCESS_INFORMATION) * cp->NumberOfCommands);
- if(cp->CommandExitCodes)
- {
- free(cp->CommandExitCodes);
- }
- cp->CommandExitCodes = (DWORD*)malloc(sizeof(DWORD)*cp->NumberOfCommands);
- if(!cp->CommandExitCodes)
- {
- return 0;
- }
- ZeroMemory(cp->CommandExitCodes, sizeof(DWORD)*cp->NumberOfCommands);
- /* Allocate event wait array. The first event is cp->Full, the rest
- are the process termination events. */
- cp->ProcessEvents = (PHANDLE)malloc(sizeof(HANDLE)*(cp->NumberOfCommands+1));
- if(!cp->ProcessEvents)
- {
- return 0;
- }
- ZeroMemory(cp->ProcessEvents, sizeof(HANDLE) * (cp->NumberOfCommands+1));
- cp->ProcessEvents[0] = cp->Full;
- cp->ProcessEventsLength = cp->NumberOfCommands+1;
- /* Allocate space to save the real working directory of this process. */
- if(cp->WorkingDirectory)
- {
- cp->RealWorkingDirectoryLength = GetCurrentDirectoryW(0, 0);
- if(cp->RealWorkingDirectoryLength > 0)
- {
- cp->RealWorkingDirectory = malloc(cp->RealWorkingDirectoryLength * sizeof(wchar_t));
- if(!cp->RealWorkingDirectory)
- {
- return 0;
- }
- }
- }
- {
- int i;
- for (i=0; i < 3; ++i)
- {
- cp->PipeChildStd[i] = INVALID_HANDLE_VALUE;
- }
- }
- return 1;
- }
- /*--------------------------------------------------------------------------*/
- static DWORD kwsysProcessCreateChildHandle(PHANDLE out, HANDLE in, int isStdIn)
- {
- DWORD flags;
- /* Check whether the handle is valid for this process. */
- if (in != INVALID_HANDLE_VALUE && GetHandleInformation(in, &flags))
- {
- /* Use the handle as-is if it is already inherited. */
- if (flags & HANDLE_FLAG_INHERIT)
- {
- *out = in;
- return ERROR_SUCCESS;
- }
- /* Create an inherited copy of this handle. */
- if (DuplicateHandle(GetCurrentProcess(), in, GetCurrentProcess(), out,
- 0, TRUE, DUPLICATE_SAME_ACCESS))
- {
- return ERROR_SUCCESS;
- }
- else
- {
- return GetLastError();
- }
- }
- else
- {
- /* The given handle is not valid for this process. Some child
- processes may break if they do not have a valid standard handle,
- so open NUL to give to the child. */
- SECURITY_ATTRIBUTES sa;
- ZeroMemory(&sa, sizeof(sa));
- sa.nLength = (DWORD)sizeof(sa);
- sa.bInheritHandle = 1;
- *out = CreateFileW(L"NUL",
- (isStdIn ? GENERIC_READ :
- (GENERIC_WRITE | FILE_READ_ATTRIBUTES)),
- FILE_SHARE_READ|FILE_SHARE_WRITE,
- &sa, OPEN_EXISTING, 0, 0);
- return (*out != INVALID_HANDLE_VALUE) ? ERROR_SUCCESS : GetLastError();
- }
- }
- /*--------------------------------------------------------------------------*/
- DWORD kwsysProcessCreate(kwsysProcess* cp, int index,
- kwsysProcessCreateInformation* si)
- {
- DWORD creationFlags;
- DWORD error = ERROR_SUCCESS;
- /* Check if we are currently exiting. */
- if (!kwsysTryEnterCreateProcessSection())
- {
- /* The Ctrl handler is currently working on exiting our process. Rather
- than return an error code, which could cause incorrect conclusions to be
- reached by the caller, we simply hang. (For example, a CMake try_run
- configure step might cause the project to configure wrong.) */
- Sleep(INFINITE);
- }
- /* Create the child in a suspended state so we can wait until all
- children have been created before running any one. */
- creationFlags = CREATE_SUSPENDED;
- if (cp->CreateProcessGroup)
- {
- creationFlags |= CREATE_NEW_PROCESS_GROUP;
- }
- /* Create inherited copies of the handles. */
- (error = kwsysProcessCreateChildHandle(&si->StartupInfo.hStdInput,
- si->hStdInput, 1)) ||
- (error = kwsysProcessCreateChildHandle(&si->StartupInfo.hStdOutput,
- si->hStdOutput, 0)) ||
- (error = kwsysProcessCreateChildHandle(&si->StartupInfo.hStdError,
- si->hStdError, 0)) ||
- /* Create the process. */
- (!CreateProcessW(0, cp->Commands[index], 0, 0, TRUE, creationFlags, 0,
- 0, &si->StartupInfo, &cp->ProcessInformation[index]) &&
- (error = GetLastError()));
- /* Close the inherited copies of the handles. */
- if (si->StartupInfo.hStdInput != si->hStdInput)
- {
- kwsysProcessCleanupHandle(&si->StartupInfo.hStdInput);
- }
- if (si->StartupInfo.hStdOutput != si->hStdOutput)
- {
- kwsysProcessCleanupHandle(&si->StartupInfo.hStdOutput);
- }
- if (si->StartupInfo.hStdError != si->hStdError)
- {
- kwsysProcessCleanupHandle(&si->StartupInfo.hStdError);
- }
- /* Add the process to the global list of processes. */
- if (!error &&
- !kwsysProcessesAdd(cp->ProcessInformation[index].hProcess,
- cp->ProcessInformation[index].dwProcessId, cp->CreateProcessGroup))
- {
- /* This failed for some reason. Kill the suspended process. */
- TerminateProcess(cp->ProcessInformation[index].hProcess, 1);
- /* And clean up... */
- kwsysProcessCleanupHandle(&cp->ProcessInformation[index].hProcess);
- kwsysProcessCleanupHandle(&cp->ProcessInformation[index].hThread);
- strcpy(cp->ErrorMessage, "kwsysProcessesAdd function failed");
- error = ERROR_NOT_ENOUGH_MEMORY; /* Most likely reason. */
- }
- /* If the console Ctrl handler is waiting for us, this will release it... */
- kwsysLeaveCreateProcessSection();
- return error;
- }
- /*--------------------------------------------------------------------------*/
- void kwsysProcessDestroy(kwsysProcess* cp, int event)
- {
- int i;
- int index;
- /* Find the process index for the termination event. */
- for(index=0; index < cp->NumberOfCommands; ++index)
- {
- if(cp->ProcessInformation[index].hProcess == cp->ProcessEvents[event])
- {
- break;
- }
- }
- /* Check the exit code of the process. */
- GetExitCodeProcess(cp->ProcessInformation[index].hProcess,
- &cp->CommandExitCodes[index]);
- /* Remove from global list of processes. */
- kwsysProcessesRemove(cp->ProcessInformation[index].hProcess);
- /* Close the process handle for the terminated process. */
- kwsysProcessCleanupHandle(&cp->ProcessInformation[index].hProcess);
- /* Remove the process from the available events. */
- cp->ProcessEventsLength -= 1;
- for(i=event; i < cp->ProcessEventsLength; ++i)
- {
- cp->ProcessEvents[i] = cp->ProcessEvents[i+1];
- }
- /* Check if all processes have terminated. */
- if(cp->ProcessEventsLength == 1)
- {
- cp->Terminated = 1;
- /* Close our copies of the pipe write handles so the pipe threads
- can detect end-of-data. */
- for(i=0; i < KWSYSPE_PIPE_COUNT; ++i)
- {
- /* TODO: If the child created its own child (our grandchild)
- which inherited a copy of the pipe write-end then the pipe
- may not close and we will still need the waker write pipe.
- However we still want to be able to detect end-of-data in the
- normal case. The reader thread will have to switch to using
- PeekNamedPipe to read the last bit of data from the pipe
- without blocking. This is equivalent to using a non-blocking
- read on posix. */
- KWSYSPE_DEBUG((stderr, "closing wakeup write %d\n", i));
- kwsysProcessCleanupHandle(&cp->Pipe[i].Write);
- }
- }
- }
- /*--------------------------------------------------------------------------*/
- DWORD kwsysProcessSetupOutputPipeFile(PHANDLE phandle, const char* name)
- {
- HANDLE fout;
- wchar_t* wname;
- DWORD error;
- if(!name)
- {
- return ERROR_INVALID_PARAMETER;
- }
- /* Close the existing handle. */
- kwsysProcessCleanupHandle(phandle);
- /* Create a handle to write a file for the pipe. */
- wname = kwsysEncoding_DupToWide(name);
- fout = CreateFileW(wname, GENERIC_WRITE, FILE_SHARE_READ, 0,
- CREATE_ALWAYS, 0, 0);
- error = GetLastError();
- free(wname);
- if(fout == INVALID_HANDLE_VALUE)
- {
- return error;
- }
- /* Assign the replacement handle. */
- *phandle = fout;
- return ERROR_SUCCESS;
- }
- /*--------------------------------------------------------------------------*/
- void kwsysProcessSetupSharedPipe(DWORD nStdHandle, PHANDLE handle)
- {
- /* Close the existing handle. */
- kwsysProcessCleanupHandle(handle);
- /* Store the new standard handle. */
- *handle = GetStdHandle(nStdHandle);
- }
- /*--------------------------------------------------------------------------*/
- void kwsysProcessSetupPipeNative(HANDLE native, PHANDLE handle)
- {
- /* Close the existing handle. */
- kwsysProcessCleanupHandle(handle);
- /* Store the new given handle. */
- *handle = native;
- }
- /*--------------------------------------------------------------------------*/
- /* Close the given handle if it is open. Reset its value to 0. */
- void kwsysProcessCleanupHandle(PHANDLE h)
- {
- if(h && *h && *h != INVALID_HANDLE_VALUE &&
- *h != GetStdHandle(STD_INPUT_HANDLE) &&
- *h != GetStdHandle(STD_OUTPUT_HANDLE) &&
- *h != GetStdHandle(STD_ERROR_HANDLE))
- {
- CloseHandle(*h);
- *h = INVALID_HANDLE_VALUE;
- }
- }
- /*--------------------------------------------------------------------------*/
- /* Close all handles created by kwsysProcess_Execute. */
- void kwsysProcessCleanup(kwsysProcess* cp, DWORD error)
- {
- int i;
- /* If this is an error case, report the error. */
- if(error)
- {
- /* Construct an error message if one has not been provided already. */
- if(cp->ErrorMessage[0] == 0)
- {
- /* Format the error message. */
- wchar_t err_msg[KWSYSPE_PIPE_BUFFER_SIZE];
- DWORD length = FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM |
- FORMAT_MESSAGE_IGNORE_INSERTS, 0, error,
- MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
- err_msg, KWSYSPE_PIPE_BUFFER_SIZE, 0);
- if(length < 1)
- {
- /* FormatMessage failed. Use a default message. */
- _snprintf(cp->ErrorMessage, KWSYSPE_PIPE_BUFFER_SIZE,
- "Process execution failed with error 0x%X. "
- "FormatMessage failed with error 0x%X",
- error, GetLastError());
- }
- if(!WideCharToMultiByte(CP_UTF8, 0, err_msg, -1, cp->ErrorMessage,
- KWSYSPE_PIPE_BUFFER_SIZE, NULL, NULL))
- {
- /* WideCharToMultiByte failed. Use a default message. */
- _snprintf(cp->ErrorMessage, KWSYSPE_PIPE_BUFFER_SIZE,
- "Process execution failed with error 0x%X. "
- "WideCharToMultiByte failed with error 0x%X",
- error, GetLastError());
- }
- }
- /* Remove trailing period and newline, if any. */
- kwsysProcessCleanErrorMessage(cp);
- /* Set the error state. */
- cp->State = kwsysProcess_State_Error;
- /* Cleanup any processes already started in a suspended state. */
- if(cp->ProcessInformation)
- {
- for(i=0; i < cp->NumberOfCommands; ++i)
- {
- if(cp->ProcessInformation[i].hProcess)
- {
- TerminateProcess(cp->ProcessInformation[i].hProcess, 255);
- WaitForSingleObject(cp->ProcessInformation[i].hProcess, INFINITE);
- }
- }
- for(i=0; i < cp->NumberOfCommands; ++i)
- {
- /* Remove from global list of processes and close handles. */
- kwsysProcessesRemove(cp->ProcessInformation[i].hProcess);
- kwsysProcessCleanupHandle(&cp->ProcessInformation[i].hThread);
- kwsysProcessCleanupHandle(&cp->ProcessInformation[i].hProcess);
- }
- }
- /* Restore the working directory. */
- if(cp->RealWorkingDirectory)
- {
- SetCurrentDirectoryW(cp->RealWorkingDirectory);
- }
- }
- /* Free memory. */
- if(cp->ProcessInformation)
- {
- free(cp->ProcessInformation);
- cp->ProcessInformation = 0;
- }
- if(cp->ProcessEvents)
- {
- free(cp->ProcessEvents);
- cp->ProcessEvents = 0;
- }
- if(cp->RealWorkingDirectory)
- {
- free(cp->RealWorkingDirectory);
- cp->RealWorkingDirectory = 0;
- }
- /* Close each pipe. */
- for(i=0; i < KWSYSPE_PIPE_COUNT; ++i)
- {
- kwsysProcessCleanupHandle(&cp->Pipe[i].Write);
- kwsysProcessCleanupHandle(&cp->Pipe[i].Read);
- cp->Pipe[i].Closed = 0;
- }
- for(i=0; i < 3; ++i)
- {
- kwsysProcessCleanupHandle(&cp->PipeChildStd[i]);
- }
- }
- /*--------------------------------------------------------------------------*/
- void kwsysProcessCleanErrorMessage(kwsysProcess* cp)
- {
- /* Remove trailing period and newline, if any. */
- size_t length = strlen(cp->ErrorMessage);
- if(cp->ErrorMessage[length-1] == '\n')
- {
- cp->ErrorMessage[length-1] = 0;
- --length;
- if(length > 0 && cp->ErrorMessage[length-1] == '\r')
- {
- cp->ErrorMessage[length-1] = 0;
- --length;
- }
- }
- if(length > 0 && cp->ErrorMessage[length-1] == '.')
- {
- cp->ErrorMessage[length-1] = 0;
- }
- }
- /*--------------------------------------------------------------------------*/
- /* 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(timeoutTime->QuadPart < 0 ||
- 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(kwsysProcessTime* timeoutTime,
- double* userTimeout,
- 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 && userTimeout && *userTimeout <= 0)
- {
- /* Caller has explicitly requested a zero timeout. */
- timeoutLength->QuadPart = 0;
- }
- 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;
- }
- /*--------------------------------------------------------------------------*/
- #define KWSYSPE_CASE(type, str) \
- cp->ExitException = kwsysProcess_Exception_##type; \
- strcpy(cp->ExitExceptionString, str)
- static void kwsysProcessSetExitException(kwsysProcess* cp, int code)
- {
- switch (code)
- {
- case STATUS_CONTROL_C_EXIT:
- KWSYSPE_CASE(Interrupt, "User interrupt"); break;
- case STATUS_FLOAT_DENORMAL_OPERAND:
- KWSYSPE_CASE(Numerical, "Floating-point exception (denormal operand)"); break;
- case STATUS_FLOAT_DIVIDE_BY_ZERO:
- KWSYSPE_CASE(Numerical, "Divide-by-zero"); break;
- case STATUS_FLOAT_INEXACT_RESULT:
- KWSYSPE_CASE(Numerical, "Floating-point exception (inexact result)"); break;
- case STATUS_FLOAT_INVALID_OPERATION:
- KWSYSPE_CASE(Numerical, "Invalid floating-point operation"); break;
- case STATUS_FLOAT_OVERFLOW:
- KWSYSPE_CASE(Numerical, "Floating-point overflow"); break;
- case STATUS_FLOAT_STACK_CHECK:
- KWSYSPE_CASE(Numerical, "Floating-point stack check failed"); break;
- case STATUS_FLOAT_UNDERFLOW:
- KWSYSPE_CASE(Numerical, "Floating-point underflow"); break;
- #ifdef STATUS_FLOAT_MULTIPLE_FAULTS
- case STATUS_FLOAT_MULTIPLE_FAULTS:
- KWSYSPE_CASE(Numerical, "Floating-point exception (multiple faults)"); break;
- #endif
- #ifdef STATUS_FLOAT_MULTIPLE_TRAPS
- case STATUS_FLOAT_MULTIPLE_TRAPS:
- KWSYSPE_CASE(Numerical, "Floating-point exception (multiple traps)"); break;
- #endif
- case STATUS_INTEGER_DIVIDE_BY_ZERO:
- KWSYSPE_CASE(Numerical, "Integer divide-by-zero"); break;
- case STATUS_INTEGER_OVERFLOW:
- KWSYSPE_CASE(Numerical, "Integer overflow"); break;
- case STATUS_DATATYPE_MISALIGNMENT:
- KWSYSPE_CASE(Fault, "Datatype misalignment"); break;
- case STATUS_ACCESS_VIOLATION:
- KWSYSPE_CASE(Fault, "Access violation"); break;
- case STATUS_IN_PAGE_ERROR:
- KWSYSPE_CASE(Fault, "In-page error"); break;
- case STATUS_INVALID_HANDLE:
- KWSYSPE_CASE(Fault, "Invalid hanlde"); break;
- case STATUS_NONCONTINUABLE_EXCEPTION:
- KWSYSPE_CASE(Fault, "Noncontinuable exception"); break;
- case STATUS_INVALID_DISPOSITION:
- KWSYSPE_CASE(Fault, "Invalid disposition"); break;
- case STATUS_ARRAY_BOUNDS_EXCEEDED:
- KWSYSPE_CASE(Fault, "Array bounds exceeded"); break;
- case STATUS_STACK_OVERFLOW:
- KWSYSPE_CASE(Fault, "Stack overflow"); break;
- case STATUS_ILLEGAL_INSTRUCTION:
- KWSYSPE_CASE(Illegal, "Illegal instruction"); break;
- case STATUS_PRIVILEGED_INSTRUCTION:
- KWSYSPE_CASE(Illegal, "Privileged instruction"); break;
- case STATUS_NO_MEMORY:
- default:
- cp->ExitException = kwsysProcess_Exception_Other;
- _snprintf(cp->ExitExceptionString, KWSYSPE_PIPE_BUFFER_SIZE, "Exit code 0x%x\n", code);
- break;
- }
- }
- #undef KWSYSPE_CASE
- typedef struct kwsysProcess_List_s kwsysProcess_List;
- static kwsysProcess_List* kwsysProcess_List_New(void);
- static void kwsysProcess_List_Delete(kwsysProcess_List* self);
- static int kwsysProcess_List_Update(kwsysProcess_List* self);
- static int kwsysProcess_List_NextProcess(kwsysProcess_List* self);
- static int kwsysProcess_List_GetCurrentProcessId(kwsysProcess_List* self);
- static int kwsysProcess_List_GetCurrentParentId(kwsysProcess_List* self);
- /*--------------------------------------------------------------------------*/
- /* Windows NT 4 API definitions. */
- #define STATUS_INFO_LENGTH_MISMATCH ((NTSTATUS)0xC0000004L)
- typedef LONG NTSTATUS;
- typedef LONG KPRIORITY;
- typedef struct _UNICODE_STRING UNICODE_STRING;
- struct _UNICODE_STRING
- {
- USHORT Length;
- USHORT MaximumLength;
- PWSTR Buffer;
- };
- /* The process information structure. Declare only enough to get
- process identifiers. The rest may be ignored because we use the
- NextEntryDelta to move through an array of instances. */
- typedef struct _SYSTEM_PROCESS_INFORMATION SYSTEM_PROCESS_INFORMATION;
- typedef SYSTEM_PROCESS_INFORMATION* PSYSTEM_PROCESS_INFORMATION;
- struct _SYSTEM_PROCESS_INFORMATION
- {
- ULONG NextEntryDelta;
- ULONG ThreadCount;
- ULONG Reserved1[6];
- LARGE_INTEGER CreateTime;
- LARGE_INTEGER UserTime;
- LARGE_INTEGER KernelTime;
- UNICODE_STRING ProcessName;
- KPRIORITY BasePriority;
- ULONG ProcessId;
- ULONG InheritedFromProcessId;
- };
- /*--------------------------------------------------------------------------*/
- /* Toolhelp32 API definitions. */
- #define TH32CS_SNAPPROCESS 0x00000002
- #if defined(_WIN64)
- typedef unsigned __int64 ProcessULONG_PTR;
- #else
- typedef unsigned long ProcessULONG_PTR;
- #endif
- typedef struct tagPROCESSENTRY32 PROCESSENTRY32;
- typedef PROCESSENTRY32* LPPROCESSENTRY32;
- struct tagPROCESSENTRY32
- {
- DWORD dwSize;
- DWORD cntUsage;
- DWORD th32ProcessID;
- ProcessULONG_PTR th32DefaultHeapID;
- DWORD th32ModuleID;
- DWORD cntThreads;
- DWORD th32ParentProcessID;
- LONG pcPriClassBase;
- DWORD dwFlags;
- char szExeFile[MAX_PATH];
- };
- /*--------------------------------------------------------------------------*/
- /* Windows API function types. */
- typedef HANDLE (WINAPI* CreateToolhelp32SnapshotType)(DWORD, DWORD);
- typedef BOOL (WINAPI* Process32FirstType)(HANDLE, LPPROCESSENTRY32);
- typedef BOOL (WINAPI* Process32NextType)(HANDLE, LPPROCESSENTRY32);
- typedef NTSTATUS (WINAPI* ZwQuerySystemInformationType)(ULONG, PVOID,
- ULONG, PULONG);
- /*--------------------------------------------------------------------------*/
- static int kwsysProcess_List__New_NT4(kwsysProcess_List* self);
- static int kwsysProcess_List__New_Snapshot(kwsysProcess_List* self);
- static void kwsysProcess_List__Delete_NT4(kwsysProcess_List* self);
- static void kwsysProcess_List__Delete_Snapshot(kwsysProcess_List* self);
- static int kwsysProcess_List__Update_NT4(kwsysProcess_List* self);
- static int kwsysProcess_List__Update_Snapshot(kwsysProcess_List* self);
- static int kwsysProcess_List__Next_NT4(kwsysProcess_List* self);
- static int kwsysProcess_List__Next_Snapshot(kwsysProcess_List* self);
- static int kwsysProcess_List__GetProcessId_NT4(kwsysProcess_List* self);
- static int kwsysProcess_List__GetProcessId_Snapshot(kwsysProcess_List* self);
- static int kwsysProcess_List__GetParentId_NT4(kwsysProcess_List* self);
- static int kwsysProcess_List__GetParentId_Snapshot(kwsysProcess_List* self);
- struct kwsysProcess_List_s
- {
- /* Implementation switches at runtime based on version of Windows. */
- int NT4;
- /* Implementation functions and data for NT 4. */
- ZwQuerySystemInformationType P_ZwQuerySystemInformation;
- char* Buffer;
- int BufferSize;
- PSYSTEM_PROCESS_INFORMATION CurrentInfo;
- /* Implementation functions and data for other Windows versions. */
- CreateToolhelp32SnapshotType P_CreateToolhelp32Snapshot;
- Process32FirstType P_Process32First;
- Process32NextType P_Process32Next;
- HANDLE Snapshot;
- PROCESSENTRY32 CurrentEntry;
- };
- /*--------------------------------------------------------------------------*/
- static kwsysProcess_List* kwsysProcess_List_New(void)
- {
- OSVERSIONINFO osv;
- kwsysProcess_List* self;
- /* Allocate and initialize the list object. */
- if(!(self = (kwsysProcess_List*)malloc(sizeof(kwsysProcess_List))))
- {
- return 0;
- }
- memset(self, 0, sizeof(*self));
- /* Select an implementation. */
- ZeroMemory(&osv, sizeof(osv));
- osv.dwOSVersionInfoSize = sizeof(osv);
- #ifdef KWSYS_WINDOWS_DEPRECATED_GetVersionEx
- # pragma warning (push)
- # ifdef __INTEL_COMPILER
- # pragma warning (disable:1478)
- # else
- # pragma warning (disable:4996)
- # endif
- #endif
- GetVersionEx(&osv);
- #ifdef KWSYS_WINDOWS_DEPRECATED_GetVersionEx
- # pragma warning (pop)
- #endif
- self->NT4 = (osv.dwPlatformId == VER_PLATFORM_WIN32_NT &&
- osv.dwMajorVersion < 5)? 1:0;
- /* Initialize the selected implementation. */
- if(!(self->NT4?
- kwsysProcess_List__New_NT4(self) :
- kwsysProcess_List__New_Snapshot(self)))
- {
- kwsysProcess_List_Delete(self);
- return 0;
- }
- /* Update to the current set of processes. */
- if(!kwsysProcess_List_Update(self))
- {
- kwsysProcess_List_Delete(self);
- return 0;
- }
- return self;
- }
- /*--------------------------------------------------------------------------*/
- static void kwsysProcess_List_Delete(kwsysProcess_List* self)
- {
- if(self)
- {
- if(self->NT4)
- {
- kwsysProcess_List__Delete_NT4(self);
- }
- else
- {
- kwsysProcess_List__Delete_Snapshot(self);
- }
- free(self);
- }
- }
- /*--------------------------------------------------------------------------*/
- static int kwsysProcess_List_Update(kwsysProcess_List* self)
- {
- return self? (self->NT4?
- kwsysProcess_List__Update_NT4(self) :
- kwsysProcess_List__Update_Snapshot(self)) : 0;
- }
- /*--------------------------------------------------------------------------*/
- static int kwsysProcess_List_GetCurrentProcessId(kwsysProcess_List* self)
- {
- return self? (self->NT4?
- kwsysProcess_List__GetProcessId_NT4(self) :
- kwsysProcess_List__GetProcessId_Snapshot(self)) : -1;
- }
- /*--------------------------------------------------------------------------*/
- static int kwsysProcess_List_GetCurrentParentId(kwsysProcess_List* self)
- {
- return self? (self->NT4?
- kwsysProcess_List__GetParentId_NT4(self) :
- kwsysProcess_List__GetParentId_Snapshot(self)) : -1;
- }
- /*--------------------------------------------------------------------------*/
- static int kwsysProcess_List_NextProcess(kwsysProcess_List* self)
- {
- return (self? (self->NT4?
- kwsysProcess_List__Next_NT4(self) :
- kwsysProcess_List__Next_Snapshot(self)) : 0);
- }
- /*--------------------------------------------------------------------------*/
- static int kwsysProcess_List__New_NT4(kwsysProcess_List* self)
- {
- /* Get a handle to the NT runtime module that should already be
- loaded in this program. This does not actually increment the
- reference count to the module so we do not need to close the
- handle. */
- HMODULE hNT = GetModuleHandleW(L"ntdll.dll");
- if(hNT)
- {
- /* Get pointers to the needed API functions. */
- self->P_ZwQuerySystemInformation =
- ((ZwQuerySystemInformationType)
- GetProcAddress(hNT, "ZwQuerySystemInformation"));
- }
- if(!self->P_ZwQuerySystemInformation)
- {
- return 0;
- }
- /* Allocate an initial process information buffer. */
- self->BufferSize = 32768;
- self->Buffer = (char*)malloc(self->BufferSize);
- return self->Buffer? 1:0;
- }
- /*--------------------------------------------------------------------------*/
- static void kwsysProcess_List__Delete_NT4(kwsysProcess_List* self)
- {
- /* Free the process information buffer. */
- if(self->Buffer)
- {
- free(self->Buffer);
- }
- }
- /*--------------------------------------------------------------------------*/
- static int kwsysProcess_List__Update_NT4(kwsysProcess_List* self)
- {
- self->CurrentInfo = 0;
- for(;;)
- {
- /* Query number 5 is for system process list. */
- NTSTATUS status =
- self->P_ZwQuerySystemInformation(5, self->Buffer, self->BufferSize, 0);
- if(status == STATUS_INFO_LENGTH_MISMATCH)
- {
- /* The query requires a bigger buffer. */
- int newBufferSize = self->BufferSize * 2;
- char* newBuffer = (char*)malloc(newBufferSize);
- if(newBuffer)
- {
- free(self->Buffer);
- self->Buffer = newBuffer;
- self->BufferSize = newBufferSize;
- }
- else
- {
- return 0;
- }
- }
- else if(status >= 0)
- {
- /* The query succeeded. Initialize traversal of the process list. */
- self->CurrentInfo = (PSYSTEM_PROCESS_INFORMATION)self->Buffer;
- return 1;
- }
- else
- {
- /* The query failed. */
- return 0;
- }
- }
- }
- /*--------------------------------------------------------------------------*/
- static int kwsysProcess_List__Next_NT4(kwsysProcess_List* self)
- {
- if(self->CurrentInfo)
- {
- if(self->CurrentInfo->NextEntryDelta > 0)
- {
- self->CurrentInfo = ((PSYSTEM_PROCESS_INFORMATION)
- ((char*)self->CurrentInfo +
- self->CurrentInfo->NextEntryDelta));
- return 1;
- }
- self->CurrentInfo = 0;
- }
- return 0;
- }
- /*--------------------------------------------------------------------------*/
- static int kwsysProcess_List__GetProcessId_NT4(kwsysProcess_List* self)
- {
- return self->CurrentInfo? self->CurrentInfo->ProcessId : -1;
- }
- /*--------------------------------------------------------------------------*/
- static int kwsysProcess_List__GetParentId_NT4(kwsysProcess_List* self)
- {
- return self->CurrentInfo? self->CurrentInfo->InheritedFromProcessId : -1;
- }
- /*--------------------------------------------------------------------------*/
- static int kwsysProcess_List__New_Snapshot(kwsysProcess_List* self)
- {
- /* Get a handle to the Windows runtime module that should already be
- loaded in this program. This does not actually increment the
- reference count to the module so we do not need to close the
- handle. */
- HMODULE hKernel = GetModuleHandleW(L"kernel32.dll");
- if(hKernel)
- {
- self->P_CreateToolhelp32Snapshot =
- ((CreateToolhelp32SnapshotType)
- GetProcAddress(hKernel, "CreateToolhelp32Snapshot"));
- self->P_Process32First =
- ((Process32FirstType)
- GetProcAddress(hKernel, "Process32First"));
- self->P_Process32Next =
- ((Process32NextType)
- GetProcAddress(hKernel, "Process32Next"));
- }
- return (self->P_CreateToolhelp32Snapshot &&
- self->P_Process32First &&
- self->P_Process32Next)? 1:0;
- }
- /*--------------------------------------------------------------------------*/
- static void kwsysProcess_List__Delete_Snapshot(kwsysProcess_List* self)
- {
- if(self->Snapshot)
- {
- CloseHandle(self->Snapshot);
- }
- }
- /*--------------------------------------------------------------------------*/
- static int kwsysProcess_List__Update_Snapshot(kwsysProcess_List* self)
- {
- if(self->Snapshot)
- {
- CloseHandle(self->Snapshot);
- }
- if(!(self->Snapshot =
- self->P_CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0)))
- {
- return 0;
- }
- ZeroMemory(&self->CurrentEntry, sizeof(self->CurrentEntry));
- self->CurrentEntry.dwSize = sizeof(self->CurrentEntry);
- if(!self->P_Process32First(self->Snapshot, &self->CurrentEntry))
- {
- CloseHandle(self->Snapshot);
- self->Snapshot = 0;
- return 0;
- }
- return 1;
- }
- /*--------------------------------------------------------------------------*/
- static int kwsysProcess_List__Next_Snapshot(kwsysProcess_List* self)
- {
- if(self->Snapshot)
- {
- if(self->P_Process32Next(self->Snapshot, &self->CurrentEntry))
- {
- return 1;
- }
- CloseHandle(self->Snapshot);
- self->Snapshot = 0;
- }
- return 0;
- }
- /*--------------------------------------------------------------------------*/
- static int kwsysProcess_List__GetProcessId_Snapshot(kwsysProcess_List* self)
- {
- return self->Snapshot? self->CurrentEntry.th32ProcessID : -1;
- }
- /*--------------------------------------------------------------------------*/
- static int kwsysProcess_List__GetParentId_Snapshot(kwsysProcess_List* self)
- {
- return self->Snapshot? self->CurrentEntry.th32ParentProcessID : -1;
- }
- /*--------------------------------------------------------------------------*/
- static void kwsysProcessKill(DWORD pid)
- {
- HANDLE h = OpenProcess(PROCESS_TERMINATE, 0, pid);
- if(h)
- {
- TerminateProcess(h, 255);
- WaitForSingleObject(h, INFINITE);
- CloseHandle(h);
- }
- }
- /*--------------------------------------------------------------------------*/
- static void kwsysProcessKillTree(int pid)
- {
- kwsysProcess_List* plist = kwsysProcess_List_New();
- kwsysProcessKill(pid);
- if(plist)
- {
- do
- {
- if(kwsysProcess_List_GetCurrentParentId(plist) == pid)
- {
- int ppid = kwsysProcess_List_GetCurrentProcessId(plist);
- kwsysProcessKillTree(ppid);
- }
- } while(kwsysProcess_List_NextProcess(plist));
- kwsysProcess_List_Delete(plist);
- }
- }
- /*--------------------------------------------------------------------------*/
- static void kwsysProcessDisablePipeThreads(kwsysProcess* cp)
- {
- int i;
- /* If data were just reported data, release the pipe's thread. */
- if(cp->CurrentIndex < KWSYSPE_PIPE_COUNT)
- {
- KWSYSPE_DEBUG((stderr, "releasing reader %d\n", cp->CurrentIndex));
- ReleaseSemaphore(cp->Pipe[cp->CurrentIndex].Reader.Go, 1, 0);
- cp->CurrentIndex = KWSYSPE_PIPE_COUNT;
- }
- /* Wakeup all reading threads that are not on closed pipes. */
- for(i=0; i < KWSYSPE_PIPE_COUNT; ++i)
- {
- /* The wakeup threads will write one byte to the pipe write ends.
- If there are no data in the pipe then this is enough to wakeup
- the reading threads. If there are already data in the pipe
- this may block. We cannot use PeekNamedPipe to check whether
- there are data because an outside process might still be
- writing data if we are disowning it. Also, PeekNamedPipe will
- block if checking a pipe on which the reading thread is
- currently calling ReadPipe. Therefore we need a separate
- thread to call WriteFile. If it blocks, that is okay because
- it will unblock when we close the read end and break the pipe
- below. */
- if(cp->Pipe[i].Read)
- {
- KWSYSPE_DEBUG((stderr, "releasing waker %d\n", i));
- ReleaseSemaphore(cp->Pipe[i].Waker.Go, 1, 0);
- }
- }
- /* Tell pipe threads to reset until we run another process. */
- while(cp->PipesLeft > 0)
- {
- /* The waking threads will cause all reading threads to report.
- Wait for the next one and save its index. */
- KWSYSPE_DEBUG((stderr, "waiting for reader\n"));
- WaitForSingleObject(cp->Full, INFINITE);
- cp->CurrentIndex = cp->SharedIndex;
- ReleaseSemaphore(cp->SharedIndexMutex, 1, 0);
- KWSYSPE_DEBUG((stderr, "got reader %d\n", cp->CurrentIndex));
- /* We are done reading this pipe. Close its read handle. */
- cp->Pipe[cp->CurrentIndex].Closed = 1;
- kwsysProcessCleanupHandle(&cp->Pipe[cp->CurrentIndex].Read);
- --cp->PipesLeft;
- /* Tell the reading thread we are done with the data. It will
- reset immediately because the pipe is closed. */
- ReleaseSemaphore(cp->Pipe[cp->CurrentIndex].Reader.Go, 1, 0);
- }
- }
- /*--------------------------------------------------------------------------*/
- /* Global set of executing processes for use by the Ctrl handler.
- This global instance will be zero-initialized by the compiler.
- Note that the console Ctrl handler runs on a background thread and so
- everything it does must be thread safe. Here, we track the hProcess
- HANDLEs directly instead of kwsysProcess instances, so that we don't have
- to make kwsysProcess thread safe. */
- typedef struct kwsysProcessInstance_s
- {
- HANDLE hProcess;
- DWORD dwProcessId;
- int NewProcessGroup; /* Whether the process was created in a new group. */
- } kwsysProcessInstance;
- typedef struct kwsysProcessInstances_s
- {
- /* Whether we have initialized key fields below, like critical sections. */
- int Initialized;
- /* Ctrl handler runs on a different thread, so we must sync access. */
- CRITICAL_SECTION Lock;
- int Exiting;
- size_t Count;
- size_t Size;
- kwsysProcessInstance* Processes;
- } kwsysProcessInstances;
- static kwsysProcessInstances kwsysProcesses;
- /*--------------------------------------------------------------------------*/
- /* Initialize critial section and set up console Ctrl handler. You MUST call
- this before using any other kwsysProcesses* functions below. */
- static int kwsysProcessesInitialize(void)
- {
- /* Initialize everything if not done already. */
- if(!kwsysProcesses.Initialized)
- {
- InitializeCriticalSection(&kwsysProcesses.Lock);
- /* Set up console ctrl handler. */
- if(!SetConsoleCtrlHandler(kwsysCtrlHandler, TRUE))
- {
- return 0;
- }
- kwsysProcesses.Initialized = 1;
- }
- return 1;
- }
- /*--------------------------------------------------------------------------*/
- /* The Ctrl handler waits on the global list of processes. To prevent an
- orphaned process, do not create a new process if the Ctrl handler is
- already running. Do so by using this function to check if it is ok to
- create a process. */
- static int kwsysTryEnterCreateProcessSection(void)
- {
- /* Enter main critical section; this means creating a process and the Ctrl
- handler are mutually exclusive. */
- EnterCriticalSection(&kwsysProcesses.Lock);
- /* Indicate to the caller if they can create a process. */
- if(kwsysProcesses.Exiting)
- {
- LeaveCriticalSection(&kwsysProcesses.Lock);
- return 0;
- }
- else
- {
- return 1;
- }
- }
- /*--------------------------------------------------------------------------*/
- /* Matching function on successful kwsysTryEnterCreateProcessSection return.
- Make sure you called kwsysProcessesAdd if applicable before calling this.*/
- static void kwsysLeaveCreateProcessSection(void)
- {
- LeaveCriticalSection(&kwsysProcesses.Lock);
- }
- /*--------------------------------------------------------------------------*/
- /* Add new process to global process list. The Ctrl handler will wait for
- the process to exit before it returns. Do not close the process handle
- until after calling kwsysProcessesRemove. The newProcessGroup parameter
- must be set if the process was created with CREATE_NEW_PROCESS_GROUP. */
- static int kwsysProcessesAdd(HANDLE hProcess, DWORD dwProcessid,
- int newProcessGroup)
- {
- if(!kwsysProcessesInitialize() || !hProcess ||
- hProcess == INVALID_HANDLE_VALUE)
- {
- return 0;
- }
- /* Enter the critical section. */
- EnterCriticalSection(&kwsysProcesses.Lock);
- /* Make sure there is enough space for the new process handle. */
- if(kwsysProcesses.Count == kwsysProcesses.Size)
- {
- size_t newSize;
- kwsysProcessInstance *newArray;
- /* Start with enough space for a small number of process handles
- and double the size each time more is needed. */
- newSize = kwsysProcesses.Size? kwsysProcesses.Size*2 : 4;
- /* Try allocating the new block of memory. */
- if(newArray = (kwsysProcessInstance*)malloc(
- newSize*sizeof(kwsysProcessInstance)))
- {
- /* Copy the old process handles to the new memory. */
- if(kwsysProcesses.Count > 0)
- {
- memcpy(newArray, kwsysProcesses.Processes,
- kwsysProcesses.Count * sizeof(kwsysProcessInstance));
- }
- }
- else
- {
- /* Failed to allocate memory for the new process handle set. */
- LeaveCriticalSection(&kwsysProcesses.Lock);
- return 0;
- }
- /* Free original array. */
- free(kwsysProcesses.Processes);
- /* Update original structure with new allocation. */
- kwsysProcesses.Size = newSize;
- kwsysProcesses.Processes = newArray;
- }
- /* Append the new process information to the set. */
- kwsysProcesses.Processes[kwsysProcesses.Count].hProcess = hProcess;
- kwsysProcesses.Processes[kwsysProcesses.Count].dwProcessId = dwProcessid;
- kwsysProcesses.Processes[kwsysProcesses.Count++].NewProcessGroup =
- newProcessGroup;
- /* Leave critical section and return success. */
- LeaveCriticalSection(&kwsysProcesses.Lock);
- return 1;
- }
- /*--------------------------------------------------------------------------*/
- /* Removes process to global process list. */
- static void kwsysProcessesRemove(HANDLE hProcess)
- {
- size_t i;
- if (!hProcess || hProcess == INVALID_HANDLE_VALUE)
- {
- return;
- }
- EnterCriticalSection(&kwsysProcesses.Lock);
- /* Find the given process in the set. */
- for(i=0; i < kwsysProcesses.Count; ++i)
- {
- if(kwsysProcesses.Processes[i].hProcess == hProcess)
- {
- break;
- }
- }
- if(i < kwsysProcesses.Count)
- {
- /* Found it! Remove the process from the set. */
- --kwsysProcesses.Count;
- for(; i < kwsysProcesses.Count; ++i)
- {
- kwsysProcesses.Processes[i] = kwsysProcesses.Processes[i+1];
- }
- /* If this was the last process, free the array. */
- if(kwsysProcesses.Count == 0)
- {
- kwsysProcesses.Size = 0;
- free(kwsysProcesses.Processes);
- kwsysProcesses.Processes = 0;
- }
- }
- LeaveCriticalSection(&kwsysProcesses.Lock);
- }
- /*--------------------------------------------------------------------------*/
- static BOOL WINAPI kwsysCtrlHandler(DWORD dwCtrlType)
- {
- size_t i;
- (void)dwCtrlType;
- /* Enter critical section. */
- EnterCriticalSection(&kwsysProcesses.Lock);
- /* Set flag indicating that we are exiting. */
- kwsysProcesses.Exiting = 1;
- /* If some of our processes were created in a new process group, we must
- manually interrupt them. They won't otherwise receive a Ctrl+C/Break. */
- for(i=0; i < kwsysProcesses.Count; ++i)
- {
- if(kwsysProcesses.Processes[i].NewProcessGroup)
- {
- DWORD groupId = kwsysProcesses.Processes[i].dwProcessId;
- if(groupId)
- {
- GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, groupId);
- }
- }
- }
- /* Wait for each child process to exit. This is the key step that prevents
- us from leaving several orphaned children processes running in the
- background when the user presses Ctrl+C. */
- for(i=0; i < kwsysProcesses.Count; ++i)
- {
- WaitForSingleObject(kwsysProcesses.Processes[i].hProcess, INFINITE);
- }
- /* Leave critical section. */
- LeaveCriticalSection(&kwsysProcesses.Lock);
- /* Continue on to default Ctrl handler (which calls ExitProcess). */
- return FALSE;
- }
|