1
0
mirror of https://github.com/Ylianst/MeshAgent synced 2025-12-06 00:13:33 +00:00
Files
MeshAgent/microscript/ILibDuktape_ChildProcess.c
Ylian Saint-Hilaire 7adf0dddd4 Updated headers.
2022-02-04 14:43:55 -08:00

800 lines
30 KiB
C

/*
Copyright 2006 - 2022 Intel Corporation
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#include "ILibDuktape_ChildProcess.h"
#include "ILibDuktapeModSearch.h"
#include "../microstack/ILibParsers.h"
#include "../microstack/ILibProcessPipe.h"
#include "ILibDuktape_Helpers.h"
#include "ILibDuktape_ReadableStream.h"
#include "ILibDuktape_WritableStream.h"
#include "ILibDuktape_EventEmitter.h"
#include "ILibDuktape_ScriptContainer.h"
#ifdef WIN32
#include <process.h>
#endif
#define ILibDuktape_ChildProcess_Process "\xFF_ChildProcess_Process"
#define ILibDuktape_ChildProcess_MemBuf "\xFF_ChildProcess_MemBuf"
extern int g_displayFinalizerMessages;
typedef struct ILibDuktape_ChildProcess_SubProcess
{
duk_context *ctx;
void *subProcess;
void *chain;
ILibProcessPipe_Process childProcess;
#ifdef WIN32
int dispatchFlags;
#endif
ILibDuktape_readableStream *stdOut;
ILibDuktape_readableStream *stdErr;
ILibDuktape_WritableStream *stdIn;
int exitCode;
}ILibDuktape_ChildProcess_SubProcess;
void ILibDuktape_ChildProcess_DeleteBackReferences(duk_context *ctx, duk_idx_t i, char *name)
{
if (duk_has_prop_string(ctx, i, name))
{
duk_get_prop_string(ctx, i, name); // [sub]
duk_del_prop_string(ctx, -1, "parent");
duk_pop(ctx); // ...
}
}
void ILibDuktape_ChildProcess_SubProcess_StdOut_OnPause(ILibDuktape_readableStream *sender, void *user)
{
ILibDuktape_ChildProcess_SubProcess *p = (ILibDuktape_ChildProcess_SubProcess*)user;
if (ILibMemory_CanaryOK(p->childProcess))
{
ILibProcessPipe_Pipe_Pause(ILibProcessPipe_Process_GetStdOut(p->childProcess));
}
}
void ILibDuktape_ChildProcess_SubProcess_StdOut_OnResume(ILibDuktape_readableStream *sender, void *user)
{
ILibDuktape_ChildProcess_SubProcess *p = (ILibDuktape_ChildProcess_SubProcess*)user;
if (ILibMemory_CanaryOK(p->childProcess))
{
ILibProcessPipe_Pipe_Resume(ILibProcessPipe_Process_GetStdOut(p->childProcess));
}
}
void ILibDuktape_ChildProcess_SubProcess_StdErr_OnPause(ILibDuktape_readableStream *sender, void *user)
{
ILibDuktape_ChildProcess_SubProcess *p = (ILibDuktape_ChildProcess_SubProcess*)user;
if (ILibMemory_CanaryOK(p->childProcess))
{
ILibProcessPipe_Pipe_Pause(ILibProcessPipe_Process_GetStdErr(p->childProcess));
}
}
void ILibDuktape_ChildProcess_SubProcess_StdErr_OnResume(ILibDuktape_readableStream *sender, void *user)
{
ILibDuktape_ChildProcess_SubProcess *p = (ILibDuktape_ChildProcess_SubProcess*)user;
if (ILibMemory_CanaryOK(p->childProcess))
{
ILibProcessPipe_Pipe_Resume(ILibProcessPipe_Process_GetStdErr(p->childProcess));
}
}
ILibTransport_DoneState ILibDuktape_ChildProcess_SubProcess_StdIn_WriteHandler(ILibDuktape_WritableStream *stream, char *buffer, int bufferLen, void *user)
{
ILibDuktape_ChildProcess_SubProcess *p = (ILibDuktape_ChildProcess_SubProcess*)user;
if (ILibMemory_CanaryOK(p->childProcess))
{
if (g_displayFinalizerMessages)
{
duk_push_this(stream->ctx);
if (!duk_has_prop_string(stream->ctx, -1, ILibDuktape_EventEmitter_FinalizerDebugMessage))
{
char tmp[100] = { 0 };
memcpy_s(tmp, sizeof(tmp), buffer, bufferLen > sizeof(tmp) ? sizeof(tmp) - 1 : bufferLen);
duk_push_string(stream->ctx, tmp);
duk_put_prop_string(stream->ctx, -2, ILibDuktape_EventEmitter_FinalizerDebugMessage);
printf(" => [%s]\n", tmp);
}
duk_pop(stream->ctx);
}
return(ILibProcessPipe_Process_WriteStdIn(p->childProcess, buffer, bufferLen, ILibTransport_MemoryOwnership_USER));
}
else
{
return(ILibTransport_DoneState_ERROR);
}
}
void ILibDuktape_ChildProcess_SubProcess_StdIn_EndHandler(ILibDuktape_WritableStream *sender, void *user)
{
ILibDuktape_ChildProcess_SubProcess *p = (ILibDuktape_ChildProcess_SubProcess*)user;
if (ILibMemory_CanaryOK(p->childProcess))
{
ILibProcessPipe_Process_CloseStdIn(p->childProcess);
}
}
void ILibDuktape_ChildProcess_SubProcess_ExitHandler(ILibProcessPipe_Process sender, int exitCode, void* user);
void ILibDuktape_ChildProcess_SubProcess_ExitHandler_sink1(void *chain, void *user)
{
ILibDuktape_ChildProcess_SubProcess *p = (ILibDuktape_ChildProcess_SubProcess*)user;
if (!ILibMemory_CanaryOK(p)) { return; }
ILibDuktape_ChildProcess_SubProcess_ExitHandler(NULL, p->exitCode, p);
}
void ILibDuktape_ChildProcess_SubProcess_ExitHandler(ILibProcessPipe_Process sender, int exitCode, void* user)
{
ILibDuktape_ChildProcess_SubProcess *p = (ILibDuktape_ChildProcess_SubProcess*)user;
if (!ILibMemory_CanaryOK(p)) { return; }
p->exitCode = exitCode;
p->childProcess = NULL;
duk_push_heapptr(p->ctx, p->subProcess); // [childProcess]
#if defined(_POSIX)
if (duk_has_prop_string(p->ctx, -1, "_sigsink"))
{
ILibDuktape_EventEmitter_SetupRemoveListener(p->ctx, ILibDuktape_GetProcessObject(p->ctx), "SIGCHLD"); //......][remove][process][SIGCHLD]
duk_get_prop_string(p->ctx, -4, "_sigsink"); // [childProcess][remove][process][SIGCHLD][func]
duk_del_prop_string(p->ctx, -1, "_child");
duk_pcall_method(p->ctx, 2); duk_pop(p->ctx); // [childProcess]
}
#endif
if (Duktape_GetIntPropertyValue(p->ctx, -1, "\xFF_WaitExit", 0) != 0)
{
ILibChain_EndContinue(Duktape_GetChain(p->ctx));
}
duk_get_prop_string(p->ctx, -1, "emit"); // [childProcess][emit]
duk_swap_top(p->ctx, -2); // [emit][this]
duk_push_string(p->ctx, "exit"); // [emit][this][exit]
duk_push_int(p->ctx, p->exitCode); // [emit][this][exit][exitCode]
duk_push_null(p->ctx); // [emit][this][exit][exitCode][sig]
if (duk_pcall_method(p->ctx, 3) != 0) { ILibDuktape_Process_UncaughtExceptionEx(p->ctx, "child_process.subProcess.exit(): "); }
duk_pop(p->ctx);
duk_push_heapptr(p->ctx, p->subProcess); // [childProcess]
ILibDuktape_ChildProcess_DeleteBackReferences(p->ctx, -1, "stdin");
ILibDuktape_ChildProcess_DeleteBackReferences(p->ctx, -1, "stdout");
ILibDuktape_ChildProcess_DeleteBackReferences(p->ctx, -1, "stderr");
duk_pop(p->ctx); // ...
}
void ILibDuktape_ChildProcess_SubProcess_StdOutHandler(ILibProcessPipe_Process sender, char *buffer, size_t bufferLen, size_t* bytesConsumed, void* user)
{
ILibDuktape_ChildProcess_SubProcess *p = (ILibDuktape_ChildProcess_SubProcess*)user;
if (!ILibMemory_CanaryOK(p)) { return; }
ILibDuktape_readableStream_WriteData(p->stdOut, buffer, bufferLen);
*bytesConsumed = bufferLen;
}
void ILibDuktape_ChildProcess_SubProcess_StdErrHandler(ILibProcessPipe_Process sender, char *buffer, size_t bufferLen, size_t* bytesConsumed, void* user)
{
ILibDuktape_ChildProcess_SubProcess *p = (ILibDuktape_ChildProcess_SubProcess*)user;
if (!ILibMemory_CanaryOK(p)) { return; }
ILibDuktape_readableStream_WriteData(p->stdErr, buffer, bufferLen);
*bytesConsumed = bufferLen;
}
void ILibDuktape_ChildProcess_SubProcess_SendOK(ILibProcessPipe_Process sender, void* user)
{
ILibDuktape_ChildProcess_SubProcess *p = (ILibDuktape_ChildProcess_SubProcess*)user;
if (!ILibMemory_CanaryOK(p)) { return; }
ILibDuktape_WritableStream_Ready(p->stdIn);
}
duk_ret_t ILibDuktape_ChildProcess_Kill(duk_context *ctx)
{
duk_push_this(ctx);
ILibDuktape_ChildProcess_SubProcess *p = (ILibDuktape_ChildProcess_SubProcess*)Duktape_GetBufferProperty(ctx, -1, ILibDuktape_ChildProcess_MemBuf);
if (p != NULL)
{
if (p->childProcess != NULL)
{
if (duk_ctx_shutting_down(ctx) == 0)
{
ILibProcessPipe_Process_SoftKill(p->childProcess);
}
else
{
ILibProcessPipe_Process_HardKill(p->childProcess);
}
}
}
return(0);
}
duk_ret_t ILibDuktape_ChildProcess_waitExit(duk_context *ctx)
{
ILibChain_Continue_Result continueResult;
int ret = 0;
int timeout = duk_is_number(ctx, 0) ? duk_require_int(ctx, 0) : -1;
void *chain = Duktape_GetChain(ctx);
if (ILibIsChainBeingDestroyed(chain))
{
return(ILibDuktape_Error(ctx, "Cannot waitExit() because current thread is exiting"));
}
duk_push_this(ctx); // [spawnedProcess]
//char *_target = Duktape_GetStringPropertyValue(ctx, -1, "_target", NULL);
if (!ILibChain_IsLinkAlive(Duktape_GetPointerProperty(ctx, -1, ILibDuktape_ChildProcess_Manager)))
{
return(ILibDuktape_Error(ctx, "Cannot waitExit() because JS Engine is exiting"));
}
if (ILibChain_GetContinuationState(chain) != ILibChain_ContinuationState_CONTINUE)
{
duk_push_int(ctx, 1); // [spawnedProcess][flag]
duk_put_prop_string(ctx, -2, "\xFF_WaitExit"); // [spawnedProcess]
}
void *mods[] = { ILibGetBaseTimer(Duktape_GetChain(ctx)), Duktape_GetPointerProperty(ctx, -1, ILibDuktape_ChildProcess_Manager), ILibDuktape_Process_GetSignalListener(ctx) };
#ifdef WIN32
HANDLE handles[] = { NULL, NULL, NULL, NULL, NULL };
ILibProcessPipe_Process p = Duktape_GetPointerProperty(ctx, -1, ILibDuktape_ChildProcess_Process);
ILibProcessPipe_Process_GetWaitHandles(p, &(handles[0]), &(handles[1]), &(handles[2]), &(handles[3]));
continueResult = ILibChain_Continue(chain, (ILibChain_Link**)mods, 2, timeout, (HANDLE**)handles);
#else
continueResult = ILibChain_Continue(chain, (ILibChain_Link**)mods, 3, timeout);
#endif
switch (continueResult)
{
case ILibChain_Continue_Result_ERROR_INVALID_STATE:
ret = ILibDuktape_Error(ctx, "waitExit() already in progress");
break;
case ILibChain_Continue_Result_ERROR_CHAIN_EXITING:
ret = ILibDuktape_Error(ctx, "waitExit() aborted because thread is exiting");
break;
case ILibChain_Continue_Result_ERROR_EMPTY_SET:
ret = ILibDuktape_Error(ctx, "waitExit() cannot wait on empty set");
break;
default:
ret = 0;
break;
}
return(ret);
}
duk_ret_t ILibDuktape_ChildProcess_SpawnedProcess_Finalizer(duk_context *ctx)
{
#ifdef WIN32
ILibDuktape_ChildProcess_SubProcess *retVal = (ILibDuktape_ChildProcess_SubProcess*)Duktape_GetBufferProperty(ctx, 0, ILibDuktape_ChildProcess_MemBuf);
ILibProcessPipe_Process_RemoveHandlers(retVal->childProcess);
#endif
duk_get_prop_string(ctx, 0, "kill"); // [kill]
duk_dup(ctx, 0); // [kill][this]
duk_call_method(ctx, 0);
return(0);
}
#ifndef WIN32
duk_ret_t ILibDuktape_ChildProcess_tcsetsize(duk_context *ctx)
{
duk_push_this(ctx);
int fd = (int)Duktape_GetIntPropertyValue(ctx, -1, "pty", 0);
struct winsize ws;
ws.ws_row = (int)duk_require_int(ctx, 0);
ws.ws_col = (int)duk_require_int(ctx, 1);
if (ioctl(fd, TIOCSWINSZ, &ws) == -1)
{
return(ILibDuktape_Error(ctx, "Error making TIOCSWINSZ/IOCTL"));
}
return(0);
}
#endif
duk_ret_t ILibDuktape_SpawnedProcess_descriptorSetter(duk_context *ctx)
{
duk_push_this(ctx);
ILibDuktape_ChildProcess_SubProcess *retVal = (ILibDuktape_ChildProcess_SubProcess*)Duktape_GetBufferProperty(ctx, -1, ILibDuktape_ChildProcess_MemBuf);
if (retVal != NULL)
{
duk_push_string(ctx, ILibProcessPipe_Process_GetMetadata(retVal->childProcess)); // [string]
duk_get_prop_string(ctx, -1, "split"); // [string][split]
duk_swap_top(ctx, -2); // [split][this]
duk_push_string(ctx, " [EXIT]"); // [split][this][delim]
duk_call_method(ctx, 1); // [array]
duk_get_prop_string(ctx, -1, "shift"); // [array][shift]
duk_swap_top(ctx, -2); // [shift][this]
duk_call_method(ctx, 0); // [string]
duk_push_sprintf(ctx, "%s, %s", duk_get_string(ctx, -1), duk_require_string(ctx, 0)); // [string][newVal]
if (g_displayFinalizerMessages)
{
duk_push_this(ctx); // [string][newVal][obj]
duk_dup(ctx, -2); // [string][newVal][obj][val]
printf("\nSETTING: %s\n", duk_get_string(ctx, -1));
duk_put_prop_string(ctx, -2, ILibDuktape_EventEmitter_FinalizerDebugMessage); // [string][newVal][obj]
duk_pop(ctx); // [string][newVal]
}
ILibProcessPipe_Process_ResetMetadata(retVal->childProcess, (char*)duk_get_string(ctx, -1));
}
return(0);
}
#if defined(_POSIX)
extern void ILibProcessPipe_Process_Destroy(void *p);
duk_ret_t ILibDuktape_SpawnedProcess_SIGCHLD_sink(duk_context *ctx)
{
int statusCode = duk_require_int(ctx, 1);
int pid = duk_require_int(ctx, 2);
duk_push_current_function(ctx); // [func]
duk_get_prop_string(ctx, -1, "_child"); // [func][child]
void *child = duk_get_heapptr(ctx, -1);
if (Duktape_GetIntPropertyValue(ctx, -1, "pid", -1) == pid)
{
// This SIGCHLD is for us. Let's unhook from SIGCHLD
duk_del_prop_string(ctx, -1, "_sigsink");
ILibDuktape_EventEmitter_SetupRemoveListener(ctx, ILibDuktape_GetProcessObject(ctx), "SIGCHLD"); // [remove][this][SIGCHLD]
duk_push_current_function(ctx); // [remove][this][SIGCHLD][func]
duk_pcall_method(ctx, 2);
// Let's chec to see if we were detached or not
duk_push_heapptr(ctx, child); // [child]
if (!duk_has_prop_string(ctx, -1, "stdout"))
{
// We are detached, so we can just emit 'exit' and be done
ILibDuktape_EventEmitter_SetupEmit(ctx, child, "exit"); // [child][emit][this][exit]
duk_push_int(ctx, statusCode); // [child][emit][this][exit][code]
duk_push_null(ctx); // [child][emit][this][exit][code][null]
duk_call_method(ctx, 3); duk_pop(ctx); // [child]
}
else
{
// We are not detached, so we need to call the same method that broken pipe would've
ILibDuktape_ChildProcess_SubProcess *childprocess = (ILibDuktape_ChildProcess_SubProcess*)Duktape_GetBufferProperty(ctx, -1, ILibDuktape_ChildProcess_MemBuf);
if (childprocess != NULL)
{
ILibDuktape_ChildProcess_SubProcess_ExitHandler(childprocess->childProcess, statusCode, childprocess);
}
duk_push_heapptr(ctx, child);
ILibDuktape_ChildProcess_DeleteBackReferences(ctx, -1, "stdin");
ILibDuktape_ChildProcess_DeleteBackReferences(ctx, -1, "stdout");
ILibDuktape_ChildProcess_DeleteBackReferences(ctx, -1, "stderr");
duk_pop(ctx);
}
duk_push_current_function(ctx); // [func]
duk_del_prop_string(ctx, -1, "_child");
duk_pop(ctx); // ...
duk_push_heapptr(ctx, child); // [child]
void *mProcess = Duktape_GetPointerProperty(ctx, -1, ILibDuktape_ChildProcess_Process);
if (mProcess != NULL)
{
duk_del_prop_string(ctx, -1, ILibDuktape_ChildProcess_Process);
duk_del_prop_string(ctx, -1, ILibDuktape_ChildProcess_MemBuf);
ILibProcessPipe_Process_Destroy(mProcess);
}
}
return(0);
}
#endif
ILibDuktape_ChildProcess_SubProcess* ILibDuktape_ChildProcess_SpawnedProcess_PUSH(duk_context *ctx, ILibProcessPipe_Process mProcess, void *callback)
{
duk_push_object(ctx); // [ChildProcess]
ILibDuktape_WriteID(ctx, "childProcess.subProcess");
duk_push_pointer(ctx, mProcess); // [ChildProcess][ptr]
duk_put_prop_string(ctx, -2, ILibDuktape_ChildProcess_Process); // [ChildProcess]
ILibDuktape_ChildProcess_SubProcess *retVal = (ILibDuktape_ChildProcess_SubProcess*)Duktape_PushBuffer(ctx, sizeof(ILibDuktape_ChildProcess_SubProcess));
duk_put_prop_string(ctx, -2, ILibDuktape_ChildProcess_MemBuf); // [ChildProcess]
retVal->ctx = ctx;
retVal->subProcess = duk_get_heapptr(ctx, -1);
retVal->childProcess = mProcess;
retVal->chain = Duktape_GetChain(ctx);
ILibDuktape_CreateReadonlyProperty_int(ctx, "pid", ILibProcessPipe_Process_GetPID(mProcess));
ILibDuktape_EventEmitter *emitter = ILibDuktape_EventEmitter_Create(ctx);
ILibDuktape_EventEmitter_CreateEventEx(emitter, "exit");
ILibDuktape_EventEmitter_CreateEventEx(emitter, "error");
ILibDuktape_CreateInstanceMethod(ctx, "kill", ILibDuktape_ChildProcess_Kill, 0);
ILibDuktape_CreateInstanceMethod(ctx, "waitExit", ILibDuktape_ChildProcess_waitExit, DUK_VARARGS);
if (ILibProcessPipe_Process_IsDetached(mProcess) == 0)
{
ILibDuktape_EventEmitter_PrependOnce(ctx, -1, "~", ILibDuktape_ChildProcess_SpawnedProcess_Finalizer); // Kill child if object is collected while process is alive
duk_push_object(ctx);
ILibDuktape_WriteID(ctx, "childProcess.subProcess.stdout");
duk_dup(ctx, -2); duk_put_prop_string(ctx, -2, "parent");
retVal->stdOut = ILibDuktape_ReadableStream_Init(ctx, ILibDuktape_ChildProcess_SubProcess_StdOut_OnPause, ILibDuktape_ChildProcess_SubProcess_StdOut_OnResume, retVal);
ILibDuktape_CreateReadonlyProperty(ctx, "stdout");
duk_push_object(ctx);
ILibDuktape_WriteID(ctx, "childProcess.subProcess.stderr");
duk_dup(ctx, -2); duk_put_prop_string(ctx, -2, "parent");
retVal->stdErr = ILibDuktape_ReadableStream_Init(ctx, ILibDuktape_ChildProcess_SubProcess_StdErr_OnPause, ILibDuktape_ChildProcess_SubProcess_StdErr_OnResume, retVal);
ILibDuktape_CreateReadonlyProperty(ctx, "stderr");
duk_push_object(ctx);
ILibDuktape_WriteID(ctx, "childProcess.subProcess.stdin");
duk_dup(ctx, -2); duk_put_prop_string(ctx, -2, "parent");
retVal->stdIn = ILibDuktape_WritableStream_Init(ctx, ILibDuktape_ChildProcess_SubProcess_StdIn_WriteHandler, ILibDuktape_ChildProcess_SubProcess_StdIn_EndHandler, retVal);
ILibDuktape_CreateReadonlyProperty(ctx, "stdin");
#ifndef WIN32
if (ILibProcessPipe_Process_GetPTY(mProcess) != 0)
{
duk_push_int(ctx, ILibProcessPipe_Process_GetPTY(mProcess));
ILibDuktape_CreateReadonlyProperty(ctx, "pty");
ILibDuktape_CreateInstanceMethod(ctx, "tcsetsize", ILibDuktape_ChildProcess_tcsetsize, 2);
}
#endif
if (callback != NULL) { ILibDuktape_EventEmitter_AddOnce(emitter, "exit", callback); }
char tmp[255];
sprintf_s(tmp, sizeof(tmp), "childProcess (pid=%d)", ILibProcessPipe_Process_GetPID(mProcess));
ILibProcessPipe_Process_ResetMetadata(mProcess, tmp);
ILibProcessPipe_Process_AddHandlers(mProcess, 4096, ILibDuktape_ChildProcess_SubProcess_ExitHandler,
ILibDuktape_ChildProcess_SubProcess_StdOutHandler,
ILibDuktape_ChildProcess_SubProcess_StdErrHandler,
ILibDuktape_ChildProcess_SubProcess_SendOK, retVal);
}
else
{
if (callback != NULL) { ILibDuktape_EventEmitter_AddOnce(emitter, "exit", callback); }
}
#if defined(_POSIX)
ILibDuktape_EventEmitter_SetupOn(ctx, ILibDuktape_GetProcessObject(ctx), "SIGCHLD"); // [child][on][process][SIGCHLD]
duk_push_c_function(ctx, ILibDuktape_SpawnedProcess_SIGCHLD_sink, DUK_VARARGS); // [child][on][process][SIGCHLD][func]
duk_dup(ctx, -5); // [child][on][process][SIGCHLD][func][child]
duk_put_prop_string(ctx, -2, "_child"); // [child][on][process][SIGCHLD][func]
duk_dup(ctx, -1); // [child][on][process][SIGCHLD][func][func]
duk_put_prop_string(ctx, -6, "_sigsink"); // [child][on][process][SIGCHLD][func]
duk_pcall_method(ctx, 2); duk_pop(ctx); // [child]
#endif
ILibDuktape_CreateEventWithSetterEx(ctx, "descriptorMetadata", ILibDuktape_SpawnedProcess_descriptorSetter);
return(retVal);
}
duk_ret_t ILibDuktape_ChildProcess_Manager_Finalizer(duk_context *ctx)
{
duk_get_prop_string(ctx, 0, ILibDuktape_ChildProcess_Manager);
ILibProcessPipe_Manager manager = (ILibProcessPipe_Manager)duk_get_pointer(ctx, -1);
ILibChain_SafeRemove(((ILibChain_Link*)manager)->ParentChain, manager);
return(0);
}
duk_ret_t ILibDuktape_ChildProcess_execFile(duk_context *ctx)
{
int nargs = duk_get_top(ctx);
duk_push_this(ctx);
duk_get_prop_string(ctx, -1, ILibDuktape_ChildProcess_Manager);
ILibProcessPipe_Manager manager = (ILibProcessPipe_Manager)duk_get_pointer(ctx, -1);
duk_size_t targetLen;
char *target = (char*)duk_get_lstring(ctx, 0, &targetLen);
char **args = NULL;
int i, x;
void *callback = NULL;
ILibProcessPipe_Process p = NULL;
ILibProcessPipe_SpawnTypes spawnType = ILibProcessPipe_SpawnTypes_DEFAULT;
int uid = -1;
char **envargs = NULL;
if (nargs > 32) { return(ILibDuktape_Error(ctx, "Too many parameters")); }
for (i = 0; i < nargs; ++i)
{
if (duk_is_array(ctx, i) != 0)
{
if (duk_get_length(ctx, i) > 255) { return(ILibDuktape_Error(ctx, "Array too big")); }
int arrLen = (int)duk_get_length(ctx, i);
#ifdef WIN32
args = (char**)_alloca((arrLen + 1) * sizeof(char*));
#else
args = (char**)alloca((arrLen + 1) * sizeof(char*));
#endif
for (x = 0; x < arrLen; ++x)
{
duk_get_prop_index(ctx, i, x);
args[x] = (char*)duk_get_string(ctx, -1);
}
args[x] = NULL;
}
else if (duk_is_function(ctx, i))
{
callback = duk_get_heapptr(ctx, i);
}
else if (duk_is_object(ctx, i))
{
// Options
spawnType = (ILibProcessPipe_SpawnTypes)Duktape_GetIntPropertyValue(ctx, i, "type", (int)ILibProcessPipe_SpawnTypes_DEFAULT);
uid = Duktape_GetIntPropertyValue(ctx, i, "uid", -1);
#ifdef WIN32
if (uid >= 0 && spawnType == ILibProcessPipe_SpawnTypes_USER) { spawnType = ILibProcessPipe_SpawnTypes_SPECIFIED_USER; }
#endif
if (Duktape_GetBooleanProperty(ctx, i, "detached", 0) != 0) { spawnType |= ILibProcessPipe_SpawnTypes_POSIX_DETACHED; }
if (duk_has_prop_string(ctx, i, "env"))
{
int ecount = 0;
duk_get_prop_string(ctx, i, "env"); // [env]
#ifndef WIN32
if (spawnType == ILibProcessPipe_SpawnTypes_TERM)
{
duk_push_string(ctx, "xterm-256color"); // [env][xterm]
duk_put_prop_string(ctx, -2, "TERM"); // [env]
}
#endif
duk_enum(ctx, -1, DUK_ENUM_OWN_PROPERTIES_ONLY); // [env][enum]
while (duk_next(ctx, -1, 0))
{
++ecount;
duk_pop(ctx); // [env][enum]
}
if (ecount > 0)
{
duk_pop(ctx); // [env]
duk_enum(ctx, -1, DUK_ENUM_OWN_PROPERTIES_ONLY); // [env][enum]
envargs = (char**)duk_push_fixed_buffer(ctx, (ecount+1) * 2*sizeof(void*)); // [env][enum][buf]
memset(envargs, 0, (ecount + 1) * 2*sizeof(void*));
duk_insert(ctx, -3); // [buf][env][enum]
ecount = 0;
while (duk_next(ctx, -1, 1)) // [buf][env][enum][key][val]
{
envargs[ecount] = (char*)duk_get_string(ctx, -2);
envargs[ecount + 1] = (char*)duk_to_string(ctx, -1);
ecount += 2;
duk_pop_2(ctx); // [buf][env][enum]
}
}
}
}
}
#ifdef WIN32
if (target[0] == '%')
{
size_t evsize;
int pctx = ILibString_IndexOf(target + 1, (int)targetLen - 1, "%", 1);
if (pctx > 0)
{
memcpy_s(ILibScratchPad, sizeof(ILibScratchPad), target + 1, pctx);
ILibScratchPad[pctx] = 0;
getenv_s(&evsize, ILibScratchPad2, sizeof(ILibScratchPad2), ILibScratchPad);
if (evsize > 0)
{
strncpy_s(ILibScratchPad2 + evsize - 1, sizeof(ILibScratchPad2) - evsize, target + pctx + 2, targetLen - pctx - 2);
target = ILibScratchPad2;
}
}
}
#endif
#ifdef WIN32
p = ILibProcessPipe_Manager_SpawnProcessEx4(manager, target, args, spawnType, (void*)(ILibPtrCAST)(uint64_t)(uid < 0 ? 0 : uid), envargs, 0);
#else
p = ILibProcessPipe_Manager_SpawnProcessEx4(manager, target, args, spawnType, (void*)(ILibPtrCAST)(uint64_t)uid, envargs, 0);
#endif
if (p == NULL)
{
return(ILibDuktape_Error(ctx, "child_process.execFile(): Could not exec [%s]", target));
}
ILibDuktape_ChildProcess_SpawnedProcess_PUSH(ctx, p, callback);
if (g_displayFinalizerMessages)
{
printf("++++ childProcess.subProcess (pid: %u, %s) [%p]\n", ILibProcessPipe_Process_GetPID(p), target, duk_get_heapptr(ctx, -1));
duk_push_string(ctx, target);
duk_put_prop_string(ctx, -2, ILibDuktape_EventEmitter_FinalizerDebugMessage);
}
duk_push_string(ctx, target); duk_put_prop_string(ctx, -2, "_target");
duk_push_pointer(ctx, manager); duk_put_prop_string(ctx, -2, ILibDuktape_ChildProcess_Manager);
return(1);
}
duk_ret_t ILibDuktape_ChildProcess_execve(duk_context *ctx)
{
int nargs = duk_get_top(ctx);
int i;
void **args = NULL;
void **env = NULL;
char *path = (char*)duk_require_string(ctx, 0);
#ifdef WIN32
int tmplen;
WCHAR* wtmp;
#endif
if (nargs < 3 || !(duk_is_object(ctx, 2) && duk_has_prop_string(ctx, 2, "env")))
{
duk_push_this(ctx); // [childprocess]
duk_prepare_method_call(ctx, -1, "_execve"); // [childprocess][execve][this]
duk_dup(ctx, 0); duk_dup(ctx, 1); // [childprocess][execve][this][path][parms]
if (nargs > 2 && duk_is_object(ctx, 2))
{
duk_dup(ctx, 2); // [childprocess][execve][this][path][parms][options]
}
else
{
duk_push_object(ctx); // [childprocess][execve][this][path][parms][options]
}
duk_eval_string(ctx, "process.env"); // [childprocess][execve][this][path][parms][options][env]
duk_put_prop_string(ctx, -2, "env"); // [childprocess][execve][this][path][parms][options]
duk_call_method(ctx, 3);
return(ILibDuktape_Error(ctx, "execve() error"));
}
duk_push_array(ctx); // [WCHAR_ARRAY]
args = (void**)ILibMemory_SmartAllocate(sizeof(char*) * (1 + duk_get_length(ctx, 1)));
for (i = 0; i < (int)duk_get_length(ctx, 1); ++i)
{
duk_get_prop_index(ctx, 1, (duk_uarridx_t)i); // [WCHAR_ARRAY][arg]
args[i] = (void*)duk_get_string(ctx, -1);
#ifdef WIN32
tmplen = ILibUTF8ToWideCount((char*)args[i]);
wtmp = (WCHAR*)duk_push_fixed_buffer(ctx, sizeof(WCHAR) * tmplen); // [WCHAR_ARRAY][arg][buffer]
duk_array_push(ctx, -3); // [WCHAR_ARRAY][arg]
args[i] = (void*)ILibUTF8ToWideEx((char*)args[i], -1, wtmp, tmplen); // [WCHAR_ARRAY][arg]
#endif
duk_pop(ctx); // [WCHAR_ARRAY]
}
if (nargs > 2 && duk_is_object(ctx, 2) && duk_has_prop_string(ctx, 2, "env"))
{
duk_get_prop_string(ctx, 2, "env"); // [WCHAR_ARRAY][obj]
duk_push_array(ctx); // [WCHAR_ARRAY][obj][array]
duk_enum(ctx, -2, DUK_ENUM_OWN_PROPERTIES_ONLY); // [WCHAR_ARRAY][obj][array][enum]
while (duk_next(ctx, -1, 1)) // [WCHAR_ARRAY][obj][array][enum][key][value]
{
duk_push_sprintf(ctx, "%s=%s", duk_get_string(ctx, -2), duk_get_string(ctx, -1)); // [WCHAR_ARRAY][obj][array][enum][key][value][string]
duk_array_push(ctx, -5); // [WCHAR_ARRAY][obj][array][enum][key][value]
duk_pop_2(ctx); // [WCHAR_ARRAY][obj][array][enum]
}
duk_pop(ctx); // [WCHAR_ARRAY][obj][array]
env = (void**)ILibMemory_SmartAllocate(sizeof(char*) * (1 + duk_get_length(ctx, -1)));
for (i = 0; i < (int)duk_get_length(ctx, -1); ++i)
{
duk_get_prop_index(ctx, -1, (duk_uarridx_t)i); // [WCHAR_ARRAY][obj][array][arg]
env[i] = (char*)duk_get_string(ctx, -1);
#ifdef WIN32
tmplen = ILibUTF8ToWideCount((char*)env[i]);
wtmp = (WCHAR*)duk_push_fixed_buffer(ctx, tmplen * sizeof(WCHAR)); // [WCHAR_ARRAY][obj][array][arg][buffer]
duk_array_push(ctx, -5); // [WCHAR_ARRAY][obj][array][arg]
env[i] = (void*)ILibUTF8ToWideEx((char*)env[i], -1, wtmp, tmplen);
#endif
duk_pop(ctx); // [WCHAR_ARRAY][obj][array]
}
}
#ifndef WIN32
//
// We must close all open descriptors first, since the "new" process will have no idea about any that are still open
//
if (nargs > 2 && duk_is_object(ctx, 2) && Duktape_GetBooleanProperty(ctx, 2, "close", 1) != 0)
{
int d;
duk_eval_string(ctx, "require('util-descriptors').getOpenDescriptors();"); // [array]
while (duk_get_length(ctx, -1) > 0)
{
duk_array_pop(ctx, -1); // [array][fd]
d = duk_get_int(ctx, -1); duk_pop(ctx); // [array]
if (d > 2) { close(d); } // [array]
}
}
execve(path, (char**)args, (char**)env);
return(ILibDuktape_Error(ctx, "_execve() returned error: %d ", errno));
#else
if (_wexecve(ILibUTF8ToWide(path, -1), (WCHAR**)args, (WCHAR**)env) < 0)
{
return(ILibDuktape_Error(ctx, "_wexecve() failed"));
}
else
{
_exit(0);
}
#endif
}
void ILibDuktape_ChildProcess_PUSH(duk_context *ctx, void *chain)
{
duk_push_object(ctx);
ILibDuktape_WriteID(ctx, "childProcess");
duk_push_pointer(ctx, (void*)ILibProcessPipe_Manager_Create(chain));
duk_put_prop_string(ctx, -2, ILibDuktape_ChildProcess_Manager);
ILibDuktape_CreateFinalizer(ctx, ILibDuktape_ChildProcess_Manager_Finalizer);
ILibDuktape_CreateInstanceMethod(ctx, "execFile", ILibDuktape_ChildProcess_execFile, DUK_VARARGS);
ILibDuktape_CreateInstanceMethod(ctx, "_execve", ILibDuktape_ChildProcess_execve, DUK_VARARGS);
duk_push_object(ctx);
duk_push_int(ctx, 0);
duk_put_prop_string(ctx, -2, "DEFAULT");
duk_push_int(ctx, 1);
duk_put_prop_string(ctx, -2, "USER");
duk_push_int(ctx, 2);
duk_put_prop_string(ctx, -2, "WINLOGON");
duk_push_int(ctx, 3);
duk_put_prop_string(ctx, -2, "TERM");
duk_push_int(ctx, 4);
duk_put_prop_string(ctx, -2, "DETACHED");
duk_put_prop_string(ctx, -2, "SpawnTypes");
char flags[] = "exports.c_iflags = {'IGNBRK': 01, 'BRKINT': 02, 'IGNPAR': 04, 'PARMRK': 010,'INPCK': 020, 'ISTRIP': 040, 'INLCR': 0100, 'IGNCR': 0200, 'ICRNL': 0400, 'IUCLC': 01000, 'IXON': 02000, 'IXANY': 04000, 'IXOFF': 010000, 'IMAXBEL': 020000};\
exports.c_oflags = {'OPOST': 001, 'OLCUC': 002, 'ONLCR': 004, 'OCRNL': 010, 'ONOCR': 020, 'ONLRET': 040, 'OFILL': 0100, 'OFDEL': 0200};\
exports.c_lflags = {'ISIG': 001, 'ICANON': 002, 'ECHO': 010, 'ECHOE': 020, 'ECHOK': 040, 'ECHONL': 0100, 'NOFLSH': 0200, 'IEXTEN': 0400, 'TOSTOP': 0100000, 'ITOSTOP': 0100000}\
";
ILibDuktape_ModSearch_AddHandler_AlsoIncludeJS(ctx, flags, sizeof(flags) - 1);
}
void ILibDuktape_ChildProcess_Init(duk_context *ctx)
{
ILibDuktape_ModSearch_AddHandler(ctx, "child_process", ILibDuktape_ChildProcess_PUSH);
}
#ifdef __DOXY__
/*!
\implements EventEmitter
\brief The child_process module provides the ability to spawn child processes. <b>Note:</b> To use, must <b>require('child_process')</b>
*/
class ChildProcess
{
public:
/*!
\brief The specified file is spawned as a child process
\param file \<String\> Required. The name or path of the executable file to run
\param args \<String[]\> Optional. List of string arguments
\param options <Object> Optional. \n
cwd \<String\> Current working directory\n
env <Object> Environment key-value pairs\n
timeout <number> <b>Default</b>: 0\n
\returns \<ChildProcess\>
*/
static ChildProcess execFile(file[, args][, options][, callback]);
/*!
\brief Event emitted whenever process cannot be killed or spawned
\param err <Error> The Error
*/
void error;
/*!
\brief Event emitted after the child process ends
\param code <number> Exit code
\param signal \<String\> Not used.
*/
void exit;
/*!
\brief Process ID of the child process
*/
Integer pid;
/*!
\brief Sends SIGTERM to child process
*/
void kill();
/*!
\brief StdOut ReadableStream
*/
ReadableStream stdout;
/*!
\brief StdErr ReadableStream
*/
ReadableStream stderr;
/*!
\brief StdIn WritableStream
*/
WritableStream stdin;
};
#endif