12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610 |
- /*++
- Copyright (c) 2017 Minoca Corp.
- This file is licensed under the terms of the GNU General Public License
- version 3. Alternative licensing terms are available. Contact
- info@minocacorp.com for details. See the LICENSE file at the root of this
- project for complete licensing information.
- Module Name:
- spawn.c
- Abstract:
- This module implements the spawn module, which can be used to launch
- child processes from Chalk.
- Author:
- Evan Green 21-Jun-2017
- Environment:
- Chalk
- --*/
- //
- // ------------------------------------------------------------------- Includes
- //
- #include <assert.h>
- #include <errno.h>
- #include <fcntl.h>
- #include <signal.h>
- #include <stdlib.h>
- #include <stdio.h>
- #include <string.h>
- #include <unistd.h>
- #include "spawnp.h"
- //
- // --------------------------------------------------------------------- Macros
- //
- //
- // ---------------------------------------------------------------- Definitions
- //
- //
- // ------------------------------------------------------ Data Type Definitions
- //
- //
- // ----------------------------------------------- Internal Function Prototypes
- //
- VOID
- CkpChildProcessInit (
- PCK_VM Vm
- );
- VOID
- CkpChildProcessGet (
- PCK_VM Vm
- );
- VOID
- CkpChildProcessSet (
- PCK_VM Vm
- );
- VOID
- CkpChildProcessLaunch (
- PCK_VM Vm
- );
- VOID
- CkpChildProcessPoll (
- PCK_VM Vm
- );
- VOID
- CkpChildProcessWait (
- PCK_VM Vm
- );
- VOID
- CkpChildProcessCommunicate (
- PCK_VM Vm
- );
- VOID
- CkpChildProcessTerminate (
- PCK_VM Vm
- );
- VOID
- CkpChildProcessKill (
- PCK_VM Vm
- );
- INT
- CkpSpawnGetDescriptor (
- PCK_VM Vm,
- PCSTR Name,
- PSPAWN_DESCRIPTOR Descriptor
- );
- PSTR *
- CkpSpawnCreateEnvironment (
- PCK_VM Vm
- );
- INT
- CkpSpawnGetStringList (
- PCK_VM Vm,
- PCSTR Name,
- BOOL Optional,
- char ***NewList
- );
- INT
- CkpSpawnWait (
- PCK_VM Vm,
- PSPAWN_ATTRIBUTES Attributes,
- INT Milliseconds
- );
- INT
- CkpSpawnSetReturnCode (
- PCK_VM Vm,
- PSPAWN_ATTRIBUTES Attributes
- );
- VOID
- CkpDestroySpawnAttributes (
- PVOID Attributes
- );
- VOID
- CkpTearDownSpawnAttributes (
- PSPAWN_ATTRIBUTES Attributes
- );
- VOID
- CkpSpawnRaiseSpawnError (
- PCK_VM Vm,
- PSPAWN_ATTRIBUTES Attributes
- );
- VOID
- CkpSpawnRaiseError (
- PCK_VM Vm,
- PCSTR ExceptionType,
- PCSTR Message
- );
- //
- // -------------------------------------------------------------------- Globals
- //
- CK_VARIABLE_DESCRIPTION CkSpawnModuleValues[] = {
- {CkTypeInteger, "NONE", NULL, SPAWN_NONE},
- {CkTypeInteger, "DEVNULL", NULL, SPAWN_DEVNULL},
- {CkTypeInteger, "PIPE", NULL, SPAWN_PIPE},
- {CkTypeInteger, "OPTION_SHELL", NULL, SPAWN_OPTION_SHELL},
- {CkTypeInteger, "OPTION_CHECK", NULL, SPAWN_OPTION_CHECK},
- {CkTypeInteger, "OPTION_CLOSE_FDS", NULL, SPAWN_OPTION_CLOSE_FDS},
- {CkTypeInteger, "OPTION_NEW_SESSION", NULL, SPAWN_OPTION_NEW_SESSION},
- {CkTypeInteger, "DEBUG_BASIC_LAUNCH", NULL, SPAWN_DEBUG_BASIC_LAUNCH},
- {CkTypeInteger, "DEBUG_DETAILED_LAUNCH", NULL, SPAWN_DEBUG_DETAILED_LAUNCH},
- {CkTypeInteger, "DEBUG_IO", NULL, SPAWN_DEBUG_IO},
- {CkTypeInvalid, NULL, NULL, 0}
- };
- //
- // ------------------------------------------------------------------ Functions
- //
- BOOL
- CkPreloadSpawnModule (
- PCK_VM Vm
- )
- /*++
- Routine Description:
- This routine preloads the spawn module. It is called to make the presence
- of the os module known in cases where the module is statically linked.
- Arguments:
- Vm - Supplies a pointer to the virtual machine.
- Return Value:
- TRUE on success.
- FALSE on failure.
- --*/
- {
- return CkPreloadForeignModule(Vm, "spawn", NULL, NULL, CkpSpawnModuleInit);
- }
- VOID
- CkpSpawnModuleInit (
- PCK_VM Vm
- )
- /*++
- Routine Description:
- This routine populates the spawn module namespace.
- Arguments:
- Vm - Supplies a pointer to the virtual machine.
- Return Value:
- None.
- --*/
- {
- CkPushString(Vm, "SpawnError", 10);
- CkGetVariable(Vm, 0, "Exception");
- CkPushClass(Vm, 0, 0);
- CkSetVariable(Vm, 0, "SpawnError");
- CkPushString(Vm, "TimeoutExpired", 14);
- CkGetVariable(Vm, 0, "Exception");
- CkPushClass(Vm, 0, 0);
- CkSetVariable(Vm, 0, "TimeoutExpired");
- CkPushString(Vm, "ProcessExited", 13);
- CkGetVariable(Vm, 0, "Exception");
- CkPushClass(Vm, 0, 0);
- CkSetVariable(Vm, 0, "ProcessExited");
- CkPushString(Vm, "ChildProcessError", 17);
- CkGetVariable(Vm, 0, "Exception");
- CkPushClass(Vm, 0, 0);
- CkSetVariable(Vm, 0, "ChildProcessError");
- //
- // Register the functions and definitions.
- //
- CkDeclareVariables(Vm, 0, CkSpawnModuleValues);
- //
- // Create the LzmaEncoder class.
- //
- CkPushString(Vm, "ChildProcess", 12);
- CkGetVariable(Vm, 0, "Object");
- CkPushClass(Vm, 0, 2);
- CkPushValue(Vm, -1);
- CkSetVariable(Vm, 0, "ChildProcess");
- CkPushFunction(Vm, CkpChildProcessInit, "__init", 0, 0);
- CkPushString(Vm, "__init", 6);
- CkBindMethod(Vm, 1);
- CkPushFunction(Vm, CkpChildProcessInit, "__init", 1, 0);
- CkPushString(Vm, "__init", 6);
- CkBindMethod(Vm, 1);
- CkPushFunction(Vm, CkpChildProcessGet, "__get", 1, 0);
- CkPushString(Vm, "__get", 5);
- CkBindMethod(Vm, 1);
- CkPushFunction(Vm, CkpChildProcessSet, "__set", 2, 0);
- CkPushString(Vm, "__set", 5);
- CkBindMethod(Vm, 1);
- CkPushFunction(Vm, CkpChildProcessLaunch, "launch", 0, 0);
- CkPushString(Vm, "launch", 6);
- CkBindMethod(Vm, 1);
- CkPushFunction(Vm, CkpChildProcessPoll, "poll", 0, 0);
- CkPushString(Vm, "poll", 4);
- CkBindMethod(Vm, 1);
- CkPushFunction(Vm, CkpChildProcessWait, "wait", 1, 0);
- CkPushString(Vm, "wait", 4);
- CkBindMethod(Vm, 1);
- CkPushFunction(Vm, CkpChildProcessCommunicate, "communicate", 2, 0);
- CkPushString(Vm, "communicate", 11);
- CkBindMethod(Vm, 1);
- CkPushFunction(Vm, CkpChildProcessTerminate, "terminate", 0, 0);
- CkPushString(Vm, "terminate", 9);
- CkBindMethod(Vm, 1);
- CkPushFunction(Vm, CkpChildProcessGet, "kill", 0, 0);
- CkPushString(Vm, "kill", 4);
- CkBindMethod(Vm, 1);
- CkStackPop(Vm);
- return;
- }
- //
- // --------------------------------------------------------- Internal Functions
- //
- VOID
- CkpChildProcessInit (
- PCK_VM Vm
- )
- /*++
- Routine Description:
- This routine initializes a new ChildProcess instance.
- Arguments:
- Vm - Supplies a pointer to the virtual machine.
- Return Value:
- None.
- --*/
- {
- UINTN ArgumentCount;
- PSPAWN_ATTRIBUTES Attributes;
- ArgumentCount = CkGetStackSize(Vm) - 1;
- //
- // Create the dict information, and push an extra copy of the dict.
- //
- CkPushDict(Vm);
- CkPushValue(Vm, -1);
- CkSetField(Vm, 0);
- //
- // Create the attributes structure.
- //
- Attributes = malloc(sizeof(SPAWN_ATTRIBUTES));
- if (Attributes == NULL) {
- CkRaiseBasicException(Vm, "MemoryError", "");
- return;
- }
- memset(Attributes, 0, sizeof(SPAWN_ATTRIBUTES));
- Attributes->Stdin.Fd = -1;
- Attributes->Stdin.CloseFd = -1;
- Attributes->Stdin.ParentPipe = -1;
- Attributes->Stdout.Fd = -1;
- Attributes->Stdout.CloseFd = -1;
- Attributes->Stdout.ParentPipe = -1;
- Attributes->Stderr.Fd = -1;
- Attributes->Stderr.CloseFd = -1;
- Attributes->Stderr.ParentPipe = -1;
- CkPushData(Vm, Attributes, CkpDestroySpawnAttributes);
- CkSetField(Vm, 1);
- //
- // Set the optional args.
- //
- CkPushString(Vm, "args", 4);
- if (ArgumentCount == 1) {
- if (CkIsList(Vm, 1)) {
- CkPushValue(Vm, 1);
- //
- // If the argument isn't a list, create a list and wrap the argument
- // around it, so things like ChildProcess("ls -la") work.
- //
- } else {
- CkPushList(Vm);
- CkPushValue(Vm, 1);
- CkListSet(Vm, -2, 0);
- }
- } else {
- CkPushNull(Vm);
- }
- CkDictSet(Vm, -3);
- CkPushString(Vm, "stdin", 5);
- CkPushInteger(Vm, SPAWN_NONE);
- CkDictSet(Vm, -3);
- CkPushString(Vm, "stdout", 6);
- CkPushInteger(Vm, SPAWN_NONE);
- CkDictSet(Vm, -3);
- CkPushString(Vm, "stderr", 6);
- CkPushInteger(Vm, SPAWN_NONE);
- CkDictSet(Vm, -3);
- CkPushString(Vm, "options", 7);
- CkPushInteger(Vm, 0);
- CkDictSet(Vm, -3);
- CkPushString(Vm, "cwd", 3);
- CkPushNull(Vm);
- CkDictSet(Vm, -3);
- CkPushString(Vm, "env", 3);
- CkPushNull(Vm);
- CkDictSet(Vm, -3);
- CkPushString(Vm, "passFds", 7);
- CkPushNull(Vm);
- CkDictSet(Vm, -3);
- CkPushString(Vm, "executable", 10);
- CkPushNull(Vm);
- CkDictSet(Vm, -3);
- CkPushString(Vm, "return", 6);
- CkPushNull(Vm);
- CkDictSet(Vm, -3);
- CkPushString(Vm, "pid", 3);
- CkPushNull(Vm);
- CkDictSet(Vm, -3);
- return;
- }
- VOID
- CkpChildProcessGet (
- PCK_VM Vm
- )
- /*++
- Routine Description:
- This routine implements the __get function for the ChildProcess. It takes a
- key and returns a value.
- Arguments:
- Vm - Supplies a pointer to the virtual machine.
- Return Value:
- None.
- --*/
- {
- CkGetField(Vm, 0);
- CkPushValue(Vm, 1);
- if (CkDictGet(Vm, 2) == FALSE) {
- CkRaiseBasicException(Vm, "KeyError", "Key not found");
- return;
- }
- CkStackReplace(Vm, 0);
- return;
- }
- VOID
- CkpChildProcessSet (
- PCK_VM Vm
- )
- /*++
- Routine Description:
- This routine implements the __set function for the ChildProcess. It takes
- two arguments: a key and a value, and sets that value for the key in the
- ChildProcess. Returns null.
- Arguments:
- Vm - Supplies a pointer to the virtual machine.
- Return Value:
- None.
- --*/
- {
- CkGetField(Vm, 0);
- CkPushValue(Vm, 1);
- CkPushValue(Vm, 2);
- CkDictSet(Vm, 3);
- CkReturnNull(Vm);
- return;
- }
- VOID
- CkpChildProcessLaunch (
- PCK_VM Vm
- )
- /*++
- Routine Description:
- This routine starts the child process, if it has not yet been started.
- Arguments:
- Vm - Supplies a pointer to the virtual machine.
- Return Value:
- None.
- --*/
- {
- PSPAWN_ATTRIBUTES Attributes;
- UINTN Index;
- INT Status;
- CkGetField(Vm, 1);
- Attributes = CkGetData(Vm, -1);
- CkStackPop(Vm);
- CkGetField(Vm, 0);
- //
- // If the process is already launched, don't launch it again, just return
- // the pid.
- //
- if (Attributes->Pid != 0) {
- if (Attributes->Pid < 0) {
- CkpSpawnRaiseError(Vm, "ProcessExited", "Process exited");
- } else {
- CkReturnInteger(Vm, Attributes->Pid);
- }
- return;
- }
- Status = CkpSpawnGetDescriptor(Vm, "stdin", &(Attributes->Stdin));
- if (Status != 0) {
- goto ChildProcessLaunchEnd;
- }
- Status = CkpSpawnGetDescriptor(Vm, "stdout", &(Attributes->Stdout));
- if (Status != 0) {
- goto ChildProcessLaunchEnd;
- }
- Status = CkpSpawnGetDescriptor(Vm, "stderr", &(Attributes->Stderr));
- if (Status != 0) {
- goto ChildProcessLaunchEnd;
- }
- CkPushString(Vm, "cwd", 3);
- if (CkDictGet(Vm, -2)) {
- Attributes->Cwd = CkGetString(Vm, -1, NULL);
- CkStackPop(Vm);
- }
- Attributes->Environment = CkpSpawnCreateEnvironment(Vm);
- Status = CkpSpawnGetStringList(Vm, "args", FALSE, &(Attributes->Arguments));
- if (Status != 0) {
- goto ChildProcessLaunchEnd;
- }
- CkPushString(Vm, "executable", 10);
- if (CkDictGet(Vm, -2)) {
- Attributes->Executable = CkGetString(Vm, -1, NULL);
- CkStackPop(Vm);
- }
- CkPushString(Vm, "options", 7);
- if (CkDictGet(Vm, -2)) {
- Attributes->Options = CkGetInteger(Vm, -1);
- CkStackPop(Vm);
- //
- // Consider implementing closing all other FDs if it's needed.
- //
- if ((Attributes->Options & SPAWN_OPTION_CLOSE_FDS) != 0) {
- CkpSpawnRaiseError(Vm,
- "ValueError",
- "CLOSE_FDS not currently implemented");
- Status = -1;
- goto ChildProcessLaunchEnd;
- }
- }
- CkPushString(Vm, "debug", 5);
- if (CkDictGet(Vm, -2)) {
- Attributes->Debug = CkGetInteger(Vm, -1);
- CkStackPop(Vm);
- }
- //
- // Get the list of descriptors not to close, if present.
- //
- CkPushString(Vm, "passFds", 7);
- if (CkDictGet(Vm, -2)) {
- Attributes->PassFdCount = CkListSize(Vm, -1);
- Attributes->PassFds = malloc(sizeof(int) * Attributes->PassFdCount);
- if (Attributes->PassFds == NULL) {
- CkRaiseBasicException(Vm, "MemoryError", "");
- Status = -1;
- goto ChildProcessLaunchEnd;
- }
- for (Index = 0; Index < Attributes->PassFdCount; Index += 1) {
- CkListGet(Vm, -1, Index);
- if (!CkIsInteger(Vm, -1)) {
- CkRaiseBasicException(Vm,
- "TypeError",
- "Expected an integer in passFds");
- Status = -1;
- goto ChildProcessLaunchEnd;
- }
- Attributes->PassFds[Index] = CkGetInteger(Vm, -1);
- CkStackPop(Vm);
- }
- CkStackPop(Vm);
- }
- //
- // Call out to the OS-specific part to actually spawn the process.
- //
- Status = CkpOsSpawn(Attributes);
- if (Status != 0) {
- CkpSpawnRaiseSpawnError(Vm, Attributes);
- goto ChildProcessLaunchEnd;
- }
- //
- // Set the in/out/error pipe file descriptors.
- //
- if (Attributes->Stdin.ParentPipe >= 0) {
- CkPushString(Vm, "stdin", 5);
- CkPushInteger(Vm, Attributes->Stdin.ParentPipe);
- CkDictSet(Vm, -3);
- }
- if (Attributes->Stdout.ParentPipe >= 0) {
- CkPushString(Vm, "stdout", 6);
- CkPushInteger(Vm, Attributes->Stdout.ParentPipe);
- CkDictSet(Vm, -3);
- }
- if (Attributes->Stderr.ParentPipe >= 0) {
- CkPushString(Vm, "stderr", 6);
- CkPushInteger(Vm, Attributes->Stderr.ParentPipe);
- CkDictSet(Vm, -3);
- }
- CkStackPop(Vm);
- ChildProcessLaunchEnd:
- if (Attributes->Environment != NULL) {
- free(Attributes->Environment);
- Attributes->Environment = NULL;
- }
- if (Attributes->Arguments != NULL) {
- free(Attributes->Arguments);
- Attributes->Arguments = NULL;
- }
- if (Attributes->PassFds != NULL) {
- free(Attributes->PassFds);
- Attributes->PassFds = NULL;
- }
- if (Attributes->ErrorMessage != NULL) {
- free(Attributes->ErrorMessage);
- Attributes->ErrorMessage = NULL;
- }
- if (Status != 0) {
- CkpTearDownSpawnAttributes(Attributes);
- } else {
- CkReturnInteger(Vm, Attributes->Pid);
- }
- return;
- }
- VOID
- CkpChildProcessPoll (
- PCK_VM Vm
- )
- /*++
- Routine Description:
- This routine determines if the child process has exited yet, and sets the
- returncode if it has.
- Arguments:
- Vm - Supplies a pointer to the virtual machine.
- Return Value:
- None.
- --*/
- {
- PSPAWN_ATTRIBUTES Attributes;
- INT Status;
- CkGetField(Vm, 1);
- Attributes = CkGetData(Vm, -1);
- CkStackPop(Vm);
- Status = CkpSpawnWait(Vm, Attributes, 0);
- if (Status == 1) {
- CkReturnNull(Vm);
- }
- return;
- }
- VOID
- CkpChildProcessWait (
- PCK_VM Vm
- )
- /*++
- Routine Description:
- This routine waits for the child process to exit, and returns its return
- code. On timeout, a TimeoutExpired error is raised. On failure, a
- SpawnError is raised.
- Arguments:
- Vm - Supplies a pointer to the virtual machine.
- Return Value:
- None.
- --*/
- {
- PSPAWN_ATTRIBUTES Attributes;
- INT Status;
- if (!CkCheckArguments(Vm, 1, CkTypeInteger)) {
- return;
- }
- CkGetField(Vm, 1);
- Attributes = CkGetData(Vm, -1);
- CkStackPop(Vm);
- Status = CkpSpawnWait(Vm, Attributes, CkGetInteger(Vm, 1));
- if (Status == 1) {
- CkpSpawnRaiseError(Vm, "TimeoutExpired", "Timeout expired");
- return;
- }
- return;
- }
- VOID
- CkpChildProcessCommunicate (
- PCK_VM Vm
- )
- /*++
- Routine Description:
- This routine communicates with the child process. It takes two arguments:
- an optional input to send to the process and a timeout in milliseconds.
- Upon return, a list containing stdout and stderr data will be returned.
- The caller must have launched the child process with pipe options to get
- any data across. On timeout, a TimeoutExpired error is raised.
- Arguments:
- Vm - Supplies a pointer to the virtual machine.
- Return Value:
- None.
- --*/
- {
- PSPAWN_ATTRIBUTES Attributes;
- PSTR ErrorData;
- size_t ErrorDataSize;
- BOOL ExceptionRaised;
- PCSTR Input;
- UINTN InputSize;
- PSTR OutData;
- size_t OutDataSize;
- INT Status;
- CK_INTEGER Timeout;
- ErrorData = NULL;
- ErrorDataSize = 0;
- ExceptionRaised = FALSE;
- Input = NULL;
- InputSize = 0;
- OutData = NULL;
- OutDataSize = 0;
- Input = CkGetString(Vm, 1, &InputSize);
- Timeout = CkGetInteger(Vm, 2);
- CkGetField(Vm, 1);
- Attributes = CkGetData(Vm, -1);
- CkStackPop(Vm);
- Status = CkpOsCommunicate(Attributes,
- Input,
- InputSize,
- Timeout,
- &OutData,
- &OutDataSize,
- &ErrorData,
- &ErrorDataSize);
- if (Attributes->Pid < 0) {
- if (CkpSpawnSetReturnCode(Vm, Attributes) < 0) {
- ExceptionRaised = TRUE;
- if (Attributes->ErrorMessage != NULL) {
- free(Attributes->ErrorMessage);
- Attributes->ErrorMessage = NULL;
- }
- }
- }
- if (ExceptionRaised == FALSE) {
- if (Status == 1) {
- CkpSpawnRaiseError(Vm, "TimeoutExpired", "Timeout expired");
- ExceptionRaised = TRUE;
- } else if (Status != 0) {
- CkpSpawnRaiseSpawnError(Vm, Attributes);
- ExceptionRaised = TRUE;
- } else if ((InputSize == 0) && (OutDataSize == 0) &&
- (ErrorDataSize == 0)) {
- assert(Attributes->Pid < 0);
- CkpSpawnRaiseError(Vm, "ProcessExited", "Process Exited");
- ExceptionRaised = TRUE;
- }
- }
- if (ExceptionRaised == FALSE) {
- CkPushList(Vm);
- CkPushString(Vm, OutData, OutDataSize);
- CkListSet(Vm, -2, 0);
- CkPushString(Vm, ErrorData, ErrorDataSize);
- CkListSet(Vm, -2, 1);
- CkStackReplace(Vm, 0);
- }
- if (OutData != NULL) {
- free(OutData);
- }
- if (ErrorData != NULL) {
- free(ErrorData);
- }
- return;
- }
- VOID
- CkpChildProcessTerminate (
- PCK_VM Vm
- )
- /*++
- Routine Description:
- This routine sends a SIGTERM to the child process. On Windows, it calls
- TerminateProcess.
- Arguments:
- Vm - Supplies a pointer to the virtual machine.
- Return Value:
- None.
- --*/
- {
- PSPAWN_ATTRIBUTES Attributes;
- INT Status;
- CkGetField(Vm, 1);
- Attributes = CkGetData(Vm, -1);
- CkStackPop(Vm);
- Status = CkpOsSendSignal(Attributes, SIGTERM);
- if (Status != 0) {
- CkpSpawnRaiseSpawnError(Vm, Attributes);
- return;
- }
- return;
- }
- VOID
- CkpChildProcessKill (
- PCK_VM Vm
- )
- /*++
- Routine Description:
- This routine sends a SIGKILL to the child process. On Windows, it calls
- TerminateProcess.
- Arguments:
- Vm - Supplies a pointer to the virtual machine.
- Return Value:
- None.
- --*/
- {
- PSPAWN_ATTRIBUTES Attributes;
- INT Status;
- CkGetField(Vm, 1);
- Attributes = CkGetData(Vm, -1);
- CkStackPop(Vm);
- Status = CkpOsSendSignal(Attributes, SIGKILL);
- if (Status != 0) {
- CkpSpawnRaiseSpawnError(Vm, Attributes);
- return;
- }
- return;
- }
- INT
- CkpSpawnGetDescriptor (
- PCK_VM Vm,
- PCSTR Name,
- PSPAWN_DESCRIPTOR Descriptor
- )
- /*++
- Routine Description:
- This routine gets or sets up a standard descriptor.
- Arguments:
- Vm - Supplies a pointer to the virtual machine.
- Name - Supplies the name of the descriptor to get within the field
- dictionary.
- Descriptor - Supplies a pointer where the descriptor information will be
- returned.
- Return Value:
- 0 on success.
- Non-zero on failure.
- --*/
- {
- CK_INTEGER Integer;
- int Pipe[2];
- CkGetField(Vm, 0);
- CkPushString(Vm, Name, strlen(Name));
- CkDictGet(Vm, -2);
- if (!CkIsInteger(Vm, -1)) {
- CkRaiseBasicException(Vm,
- "TypeError",
- "Expected an integer for %s",
- Name);
- return -1;
- }
- Integer = CkGetInteger(Vm, -1);
- CkStackPop(Vm);
- CkStackPop(Vm);
- if (Integer == SPAWN_DEVNULL) {
- Descriptor->Fd = open(SPAWN_DEVNULL_PATH, O_RDWR);
- if (Descriptor->Fd < 0) {
- CkpSpawnRaiseError(Vm, "SpawnError", "Failed to open null device");
- return -1;
- }
- Descriptor->CloseFd = Descriptor->Fd;
- } else if (Integer == SPAWN_PIPE) {
- if (pipe(Pipe) != 0) {
- CkpSpawnRaiseError(Vm, "SpawnError", strerror(errno));
- return -1;
- }
- if (strcmp(Name, "stdin") == 0) {
- Descriptor->Fd = Pipe[0];
- Descriptor->ParentPipe = Pipe[1];
- } else {
- Descriptor->Fd = Pipe[1];
- Descriptor->ParentPipe = Pipe[0];
- }
- Descriptor->CloseFd = Descriptor->Fd;
- } else if (Integer > 0) {
- Descriptor->Fd = Integer;
- } else {
- Descriptor->Fd = -1;
- }
- return 0;
- }
- PSTR *
- CkpSpawnCreateEnvironment (
- PCK_VM Vm
- )
- /*++
- Routine Description:
- This routine creates an environment from a dictionary.
- Arguments:
- Vm - Supplies a pointer to the virtual machine.
- Return Value:
- 0 on success.
- -1 if an exception was raised.
- --*/
- {
- PSTR *Array;
- PSTR Buffer;
- UINTN Count;
- PSTR Current;
- PSTR End;
- UINTN Index;
- PCSTR Key;
- UINTN KeyLength;
- UINTN NeededSize;
- PSTR NewBuffer;
- UINTN NewCapacity;
- PCSTR Value;
- UINTN ValueLength;
- Array = NULL;
- Buffer = NULL;
- Current = NULL;
- End = NULL;
- CkGetField(Vm, 0);
- CkPushString(Vm, "env", 3);
- if (!CkDictGet(Vm, -2)) {
- CkStackPop(Vm);
- return NULL;
- }
- //
- // Iterate over all the keys in the dictionary.
- //
- Count = 0;
- CkPushNull(Vm);
- while (CkDictIterate(Vm, -2)) {
- Key = CkGetString(Vm, -2, &KeyLength);
- Value = CkGetString(Vm, -1, &ValueLength);
- CkStackPop(Vm);
- CkStackPop(Vm);
- if ((Key == NULL) || (Value == NULL) || (KeyLength == 0)) {
- continue;
- }
- //
- // Space is needed for key=value\0.
- //
- NeededSize = KeyLength + ValueLength + 2;
- if (Current + NeededSize > End) {
- if (Buffer == NULL) {
- NewCapacity = 1024;
- } else {
- NewCapacity = (End - Buffer) * 2;
- }
- while (NewCapacity < (Current - Buffer) + NeededSize) {
- NewCapacity *= 2;
- }
- NewBuffer = realloc(Buffer, NewCapacity);
- if ((NewCapacity >= CK_SPAWN_MAX_OUTPUT) || (NewBuffer == NULL)) {
- free(NewBuffer);
- free(Buffer);
- Buffer = NULL;
- goto SpawnCreateEnvironmentEnd;
- }
- Current = NewBuffer + (Current - Buffer);
- Buffer = NewBuffer;
- End = Buffer + NewCapacity;
- }
- Current += snprintf(Current, End - Current, "%s=%s", Key, Value) + 1;
- Count += 1;
- }
- if (Count == 0) {
- assert(Buffer == NULL);
- goto SpawnCreateEnvironmentEnd;
- }
- //
- // Create a single allocation containing enough space for the array and
- // all the entries.
- //
- Array = malloc(((Count + 1) * sizeof(PSTR)) + (Current - Buffer) + 1);
- if (Array == NULL) {
- goto SpawnCreateEnvironmentEnd;
- }
- //
- // Copy the contents in.
- //
- memcpy(&(Array[Count + 1]), Buffer, Current - Buffer);
- //
- // Assign the array elements within the string.
- //
- Current = (PSTR)&(Array[Count + 1]);
- for (Index = 0; Index < Count; Index += 1) {
- Array[Index] = Current;
- Current += strlen(Current) + 1;
- }
- Array[Index] = NULL;
- *Current = '\0';
- SpawnCreateEnvironmentEnd:
- if (Buffer != NULL) {
- free(Buffer);
- }
- //
- // Pop the iterator, dict, and field.
- //
- CkStackPop(Vm);
- CkStackPop(Vm);
- CkStackPop(Vm);
- return Array;
- }
- INT
- CkpSpawnGetStringList (
- PCK_VM Vm,
- PCSTR Name,
- BOOL Optional,
- char ***NewList
- )
- /*++
- Routine Description:
- This routine creates a string list.
- Arguments:
- Vm - Supplies a pointer to the virtual machine.
- Name - Supplies the name of the list to get within the field
- dictionary.
- Optional - Supplies a boolean indicating if the list is optional or not.
- NewList - Supplies a pointer where the array will be returned.
- Return Value:
- 0 on success.
- -1 if an exception was raised.
- --*/
- {
- char **Array;
- UINTN Index;
- UINTN Size;
- *NewList = NULL;
- CkGetField(Vm, 0);
- CkPushString(Vm, Name, strlen(Name));
- CkDictGet(Vm, -2);
- if ((Optional != FALSE) && (CkIsNull(Vm, -1))) {
- CkStackPop(Vm);
- CkStackPop(Vm);
- return 0;
- }
- if (!CkIsList(Vm, -1)) {
- CkRaiseBasicException(Vm,
- "TypeError",
- "Expected a list for %s",
- Name);
- return -1;
- }
- Size = CkListSize(Vm, -1);
- if ((Optional == FALSE) && (Size == 0)) {
- CkRaiseBasicException(Vm,
- "ValueError",
- "Expected non-empty list for %s",
- Name);
- return -1;
- }
- Array = malloc(sizeof(char *) * (Size + 1));
- if (Array == NULL) {
- CkRaiseBasicException(Vm, "MemoryError", "");
- return -1;
- }
- Array[Size] = NULL;
- for (Index = 0; Index < Size; Index += 1) {
- CkListGet(Vm, -1, Index);
- Array[Index] = (PSTR)CkGetString(Vm, -1, NULL);
- CkStackPop(Vm);
- if (Array[Index] == NULL) {
- CkRaiseBasicException(Vm,
- "TypeError",
- "Expected a string at index %d of %s",
- (int)Index,
- Name);
- break;
- }
- }
- CkStackPop(Vm);
- CkStackPop(Vm);
- if (Index != Size) {
- free(Array);
- return -1;
- }
- *NewList = Array;
- return 0;
- }
- INT
- CkpSpawnWait (
- PCK_VM Vm,
- PSPAWN_ATTRIBUTES Attributes,
- INT Milliseconds
- )
- /*++
- Routine Description:
- This routine waits for the process to exit. It sets the return code if
- the process exited, and sets the return value.
- Arguments:
- Vm - Supplies a pointer to the virtual machine.
- Attributes - Supplies a pointer to the attributes.
- Milliseconds - Supplies the number of milliseconds to wait.
- Return Value:
- 0 on success.
- 1 on timeout.
- Non-zero if an exception was raised.
- --*/
- {
- INT Status;
- if (Attributes->Pid > 0) {
- Status = CkpOsWait(Attributes, Milliseconds);
- if (Status != 0) {
- //
- // If the request timed out, just return back to the caller without
- // necessarily raising an exception.
- //
- if (Status == 1) {
- return Status;
- }
- CkpSpawnRaiseSpawnError(Vm, Attributes);
- //
- // The wait succeeded, so set the return code.
- //
- } else {
- Status = CkpSpawnSetReturnCode(Vm, Attributes);
- if (Status == 0) {
- CkReturnInteger(Vm, Attributes->ReturnCode);
- }
- return 0;
- }
- }
- //
- // If the process is finished, return the return code.
- //
- if (Attributes->Pid == -1) {
- CkReturnInteger(Vm, Attributes->ReturnCode);
- //
- // The process is not yet finished or not yet started, return null.
- //
- } else {
- CkReturnNull(Vm);
- }
- return 0;
- }
- INT
- CkpSpawnSetReturnCode (
- PCK_VM Vm,
- PSPAWN_ATTRIBUTES Attributes
- )
- /*++
- Routine Description:
- This routine sets the publicly visible return code. If the check option is
- set, it may raise an exception as well.
- Arguments:
- Vm - Supplies a pointer to the virtual machine.
- Attributes - Supplies a pointer to the attributes.
- Return Value:
- 0 on success.
- -1 if an exception was raised.
- --*/
- {
- CHAR Message[128];
- CkGetField(Vm, 0);
- CkPushString(Vm, "returncode", 10);
- CkPushInteger(Vm, Attributes->ReturnCode);
- CkDictSet(Vm, -3);
- CkStackPop(Vm);
- if (((Attributes->Options & SPAWN_OPTION_CHECK) != 0) &&
- (Attributes->ReturnCode != 0)) {
- snprintf(Message,
- sizeof(Message),
- "Child exited with status %d",
- Attributes->ReturnCode);
- CkpSpawnRaiseError(Vm, "ChildProcessError", Message);
- return -1;
- }
- return 0;
- }
- VOID
- CkpDestroySpawnAttributes (
- PVOID Attributes
- )
- /*++
- Routine Description:
- This routine closes all resources associated with a spawn attributes
- structure and frees the structure.
- Arguments:
- Attributes - Supplies a pointer to the attributes to tear down.
- Return Value:
- None.
- --*/
- {
- CkpTearDownSpawnAttributes(Attributes);
- free(Attributes);
- return;
- }
- VOID
- CkpTearDownSpawnAttributes (
- PSPAWN_ATTRIBUTES Attributes
- )
- /*++
- Routine Description:
- This routine closes all resources associated with a spawn attributes
- structure.
- Arguments:
- Attributes - Supplies a pointer to the attributes to tear down.
- Return Value:
- None.
- --*/
- {
- assert(Attributes->Environment == NULL);
- assert(Attributes->Arguments == NULL);
- assert(Attributes->PassFds == NULL);
- assert(Attributes->ErrorMessage == NULL);
- if (Attributes->Stdin.ParentPipe >= 0) {
- close(Attributes->Stdin.ParentPipe);
- Attributes->Stdin.ParentPipe = -1;
- }
- if (Attributes->Stdin.CloseFd >= 0) {
- close(Attributes->Stdin.CloseFd);
- Attributes->Stdin.CloseFd = -1;
- }
- if (Attributes->Stdout.ParentPipe >= 0) {
- close(Attributes->Stdout.ParentPipe);
- Attributes->Stdout.ParentPipe = -1;
- }
- if (Attributes->Stdout.CloseFd >= 0) {
- close(Attributes->Stdout.CloseFd);
- Attributes->Stdout.CloseFd = -1;
- }
- if (Attributes->Stderr.ParentPipe >= 0) {
- close(Attributes->Stderr.ParentPipe);
- Attributes->Stderr.ParentPipe = -1;
- }
- if (Attributes->Stderr.CloseFd >= 0) {
- close(Attributes->Stderr.CloseFd);
- Attributes->Stderr.CloseFd = -1;
- }
- CkpOsTearDownSpawnAttributes(Attributes);
- return;
- }
- VOID
- CkpSpawnRaiseSpawnError (
- PCK_VM Vm,
- PSPAWN_ATTRIBUTES Attributes
- )
- /*++
- Routine Description:
- This routine raises a spawn error. If there is an error message it is used
- and freed. Otherwise the errno description is used.
- Arguments:
- Vm - Supplies a pointer to the virtual machine.
- Attributes - Supplies a pointer to the attributes to tear down.
- Return Value:
- None.
- --*/
- {
- if (Attributes->ErrorMessage != NULL) {
- CkpSpawnRaiseError(Vm, "SpawnError", Attributes->ErrorMessage);
- free(Attributes->ErrorMessage);
- Attributes->ErrorMessage = NULL;
- } else {
- CkpSpawnRaiseError(Vm, "SpawnError", strerror(errno));
- }
- return;
- }
- VOID
- CkpSpawnRaiseError (
- PCK_VM Vm,
- PCSTR ExceptionType,
- PCSTR Message
- )
- /*++
- Routine Description:
- This routine raises an error with a message.
- Arguments:
- Vm - Supplies a pointer to the virtual machine.
- ExceptionType - Supplies a pointer to the type of exception to raise.
- Message - Supplies the message to send along with the exception.
- Return Value:
- None. The foreign function should return as soon as possible and not
- manipulate the Chalk stack any longer.
- --*/
- {
- //
- // Create an exception.
- //
- CkPushModule(Vm, "spawn");
- CkGetVariable(Vm, -1, ExceptionType);
- CkPushString(Vm, Message, strlen(Message));
- CkCall(Vm, 1);
- //
- // Raise the exception.
- //
- CkRaiseException(Vm, -1);
- return;
- }
|