1
0
mirror of https://github.com/Ylianst/MeshAgent synced 2025-12-29 06:33:22 +00:00
Files
MeshAgent/microscript/ILibDuktape_HECI.c
2018-01-12 11:50:04 -08:00

1023 lines
35 KiB
C

#include "ILibDuktape_HECI.h"
#include "ILibDuktapeModSearch.h"
#include "ILibDuktape_Helpers.h"
#include "ILibDuktape_DuplexStream.h"
#include "ILibDuktape_EventEmitter.h"
#include "ILibDuktape_ChildProcess.h"
#include "../microstack/ILibParsers.h"
#include "../microstack/ILibProcessPipe.h"
#include "../microstack/ILibRemoteLogging.h"
#ifdef WIN32
#include <windows.h>
#include <setupapi.h>
#include <initguid.h>
#include <winioctl.h>
DEFINE_GUID(GUID_DEVINTERFACE_HECI, 0xE2D1FF34, 0x3458, 0x49A9, 0x88, 0xDA, 0x8E, 0x69, 0x15, 0xCE, 0x9B, 0xE5);
#elif defined(_POSIX)
typedef struct HECI_client
{
unsigned int max_msg_length;
unsigned char protocol_version;
unsigned char reserved[3];
}HECI_client;
typedef struct uuid_le
{
unsigned char b[16];
}uuid_le;
struct HECI_CONNECT_client_data
{
union
{
uuid_le uuid;
HECI_client properties;
};
};
#endif
#define ILibDuktape_HECI_ChainLink "\xFF_HECI_ChainLink"
#define ILibDuktape_HECI_Descriptor "\xFF_HECI_Descriptor"
#define ILibDuktape_HECI_ChildProcess "\xFF_HECI_ChildProcess"
#define ILibDuktape_HECI_Q "\xFF_HECI_Q"
#define ILibDuktape_HECI_IoctlWaitHandle "\xFF_HECI_IoctlWaitHandle"
#define ILibDuktape_HECI_Child "\xFF_HECI_Child"
#define ILibDuktape_HECI_Parent "\xFF_HECI_Parent"
#define ILibDuktape_HECI_MaxBufferSize "\xFF_HECI_MaxBufSize"
#define ILibDuktape_HECI_SessionMemPtr "\xFF_HECI_SessionMemPtr"
#define ILibDuktape_HECI_Session_NoPipeline "\xFF_HECI_Session_NoPipeline"
#ifdef __DOXY__
/*!
\implements EventEmitter
\brief JavaScript object interface for HECI calls. <b>require('heci')</b> to use;
*/
class Heci
{
public:
/*!
\brief Performs an Ioctl on the HECI device
\param code <Integer> Ioctl Code to invoke
\param inBuffer \<Buffer\> Input data for Ioctl. Can be null
\param outBuffer \<Buffer\> Optional. Output data from Ioctl. Must be specified if Ioctl code returns data
\param callback <Function> Dispatched when a response is received from the HECI device\n
status <Integer> Success Code. 0 = Success, Error code on failure\n
buffer \<Buffer\> Output Buffer\n
args <Any> Optional parameters that were passed in\n
\param args <any> Optional arguments to pass when the callback is called
*/
void doIoctl(code, inBuffer[, outBuffer], callback[, ...args]);
};
#endif
typedef struct ILibDuktape_HECI_ioctl_data
{
duk_context *ctx;
void *heciObject;
void *data;
void *Q;
void *chain;
ILibProcessPipe_Manager pipeManager;
#ifdef WIN32
OVERLAPPED v;
HANDLE device;
DWORD bytesReceived;
#elif defined(_POSIX)
int device;
#endif
int code;
char *outBuffer;
void *outBuffer_obj;
duk_size_t outBufferLen;
duk_size_t bufferLen;
char buffer[];
}ILibDuktape_HECI_ioctl_data;
typedef struct ILibDuktape_HECI_Session
{
void *chain;
int noPipelining;
ILibDuktape_DuplexStream *stream;
#ifdef WIN32
OVERLAPPED v;
OVERLAPPED wv;
ILibProcessPipe_Manager mgr;
HANDLE descriptor;
#else
int descriptor;
#endif
ILibQueue PendingWrites;
duk_size_t bufferSize;
char buffer[];
}ILibDuktape_HECI_Session;
typedef struct ILibDuktape_HECI_WriteState
{
ILibDuktape_HECI_Session *session;
int returnIgnored;
#ifndef WIN32
int bufferOffset;
#endif
int bufferLen;
char buffer[];
}ILibDuktape_HECI_WriteState;
typedef struct HECI_chainLink
{
ILibChain_Link link;
duk_context *ctx;
void *Q;
ILibDuktape_HECI_Session *session;
void *heciObject;
int descriptor;
int paused;
}HECI_chainLink;
void ILibDuktape_HECI_Push(duk_context *ctx, void *chain);
ILibTransport_DoneState ILibDuktape_HECI_Session_WriteHandler_Process(ILibDuktape_HECI_Session *session);
#ifdef WIN32
HANDLE ILibDuktape_HECI_windowsInit()
{
PSP_DEVICE_INTERFACE_DETAIL_DATA deviceDetail = NULL;
HDEVINFO hDeviceInfo;
DWORD bufferSize;
SP_DEVICE_INTERFACE_DATA interfaceData;
LONG ii = 0;
HANDLE retVal = NULL;
// Find all devices that have our interface
hDeviceInfo = SetupDiGetClassDevs((LPGUID)&GUID_DEVINTERFACE_HECI, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
if (hDeviceInfo == INVALID_HANDLE_VALUE)
{
return(NULL);
}
// Setup the interface data struct
interfaceData.cbSize = sizeof(interfaceData);
for (ii = 0;
SetupDiEnumDeviceInterfaces(hDeviceInfo, NULL, (LPGUID)&GUID_DEVINTERFACE_HECI, ii, &interfaceData);
++ii)
{
// Found our device instance
if (!SetupDiGetDeviceInterfaceDetail(hDeviceInfo, &interfaceData, NULL, 0, &bufferSize, NULL))
{
DWORD err = GetLastError();
if (err != ERROR_INSUFFICIENT_BUFFER)
{
continue;
}
}
// Allocate a big enough buffer to get detail data
deviceDetail = (PSP_DEVICE_INTERFACE_DETAIL_DATA)ILibMemory_AllocateA(bufferSize);
if (deviceDetail == NULL) { continue; }
// Setup the device interface struct
deviceDetail->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
// Try again to get the device interface detail info
if (!SetupDiGetDeviceInterfaceDetail(hDeviceInfo, &interfaceData, deviceDetail, bufferSize, NULL, NULL))
{
deviceDetail = NULL;
continue;
}
break;
}
SetupDiDestroyDeviceInfoList(hDeviceInfo);
if (deviceDetail == NULL) { return(NULL); }
retVal = CreateFile(deviceDetail->DevicePath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
if (retVal == INVALID_HANDLE_VALUE) { return(NULL); }
return(retVal);
}
#else
int ILibDuktape_HECI_linuxInit()
{
int fd, flags;
if ((fd = open("/dev/mei", O_RDWR)) == -1 && (fd = open("/dev/mei0", O_RDWR)) == -1)
{
return(-1);
}
else
{
flags = fcntl(fd, F_GETFL, 0);
if (fcntl(fd, F_SETFL, O_NONBLOCK | flags) == -1) { printf("Failed to set O_NONBLOCK\n"); close(fd); fd = -1; }
return(fd);
}
}
#endif
duk_ret_t ILibDuktape_HECI_SessionFinalizer(duk_context *ctx)
{
return(0);
}
void ILibDuktape_HECI_Session_EmitErrorEvent(void *chain, void *session)
{
if (ILibIsRunningOnChainThread(chain) == 0) { ILibChain_RunOnMicrostackThreadEx(chain, ILibDuktape_HECI_Session_EmitErrorEvent, session); }
ILibDuktape_HECI_Session *s = (ILibDuktape_HECI_Session*)session;
duk_context *ctx = s->stream->readableStream->ctx;
duk_push_heapptr(ctx, s->stream->ParentObject); // [session]
duk_get_prop_string(ctx, -1, "emit"); // [session][emit]
duk_swap_top(ctx, -2); // [emit][this]
duk_push_string(ctx, "error"); // [emit][this][error]
duk_push_error_object(ctx, DUK_ERR_ERROR, "HECI Connection Error"); // [emit][this][error][err]
if (duk_pcall_method(ctx, 2) != 0) { ILibDuktape_Process_UncaughtExceptionEx(ctx, "HECI.session.onError(): "); }
duk_pop(ctx); // ...
}
void ILibDuktape_HECI_Session_EmitStreamReady(void *chain, void *session)
{
if (ILibIsRunningOnChainThread(chain) == 0) { ILibChain_RunOnMicrostackThreadEx(chain, ILibDuktape_HECI_Session_EmitStreamReady, session); }
ILibDuktape_DuplexStream_Ready(((ILibDuktape_HECI_Session*)session)->stream);
}
#ifdef WIN32
BOOL ILibDuktape_HECI_Session_WriteHandler_Ready(HANDLE event, void* user)
{
ILibDuktape_HECI_Session *session = (ILibDuktape_HECI_Session*)user;
DWORD bytesWritten;
ILibProcessPipe_WaitHandle_Remove(session->mgr, session->wv.hEvent);
if (session->noPipelining == 0)
{
ILibDuktape_HECI_WriteState *state = (ILibDuktape_HECI_WriteState*)ILibQueue_DeQueue(session->PendingWrites);
free(state);
}
if (GetOverlappedResult(session->descriptor, &(session->wv), &bytesWritten, FALSE) == 0)
{
// Broken Connection
ILibDuktape_HECI_Session_EmitErrorEvent(session->chain, (void*)session);
}
else
{
if (session->noPipelining == 0)
{
// Write Completed
ILibDuktape_HECI_Session_WriteHandler_Process(session);
}
}
return(TRUE);
}
#endif
ILibTransport_DoneState ILibDuktape_HECI_Session_WriteHandler_Process(ILibDuktape_HECI_Session *session)
{
ILibTransport_DoneState retVal = ILibTransport_DoneState_ERROR;
int returnIgnored = 0;
#ifdef WIN32
DWORD bytesWritten;
BOOL result = TRUE;
#else
size_t bytesWritten;
#endif
while (ILibQueue_GetCount(session->PendingWrites) > 0)
{
ILibDuktape_HECI_WriteState *state = (ILibDuktape_HECI_WriteState*)ILibQueue_PeekQueue(session->PendingWrites);
returnIgnored = state->returnIgnored;
#ifdef WIN32
if ((result = WriteFile(state->session->descriptor, state->buffer, state->bufferLen, &bytesWritten, &(state->session->wv))) == TRUE)
{
if (session->noPipelining == 0) { ILibQueue_DeQueue(state->session->PendingWrites); free(state); }
}
else
{
break;
}
#elif defined(_POSIX)
bytesWritten = write(state->session->descriptor, state->buffer + state->bufferOffset, state->bufferLen - state->bufferOffset);
if (bytesWritten > 0)
{
state->bufferOffset += bytesWritten;
if (state->bufferOffset == state->bufferLen) { ILibQueue_DeQueue(state->session->PendingWrites); free(state); retVal = session->noPipelining == 0 ? ILibTransport_DoneState_COMPLETE:ILibTransport_DoneState_INCOMPLETE; }
}
else
{
if (errno != EAGAIN)
{
// Error Occured
retVal = ILibTransport_DoneState_ERROR;
ILibDuktape_HECI_Session_EmitErrorEvent(session->chain, (void*)session);
}
else
{
retVal = ILibTransport_DoneState_INCOMPLETE;
}
break;
}
#endif
if (session->noPipelining != 0) { break; }
}
#ifdef WIN32
if (result == FALSE)
{
if (GetLastError() == ERROR_IO_PENDING)
{
// Not done writing
retVal = ILibTransport_DoneState_INCOMPLETE;
ILibProcessPipe_WaitHandle_Add(session->mgr, session->wv.hEvent, session, ILibDuktape_HECI_Session_WriteHandler_Ready);
}
else
{
// Error Occured
retVal = ILibTransport_DoneState_ERROR;
ILibDuktape_HECI_Session_EmitErrorEvent(session->chain, (void*)session);
}
}
else
{
if (session->noPipelining == 0)
{
// No more Pending Writes
retVal = ILibTransport_DoneState_COMPLETE;
if (returnIgnored != 0) { ILibDuktape_HECI_Session_EmitStreamReady(session->chain, (void*)session); }
}
else
{
retVal = ILibTransport_DoneState_INCOMPLETE;
}
}
#else
if (ILibQueue_GetCount(session->PendingWrites) == 0 && session->noPipelining == 0)
{
// No more Pending Writes
retVal = ILibTransport_DoneState_COMPLETE;
if (returnIgnored != 0) { ILibDuktape_HECI_Session_EmitStreamReady(session->chain, (void*)session); }
}
#endif
return(retVal);
}
#ifdef WIN32
void __stdcall ILibDuktape_HECI_Session_WriteHandler(ULONG_PTR obj)
{
// This Method is always dispatched from the WindowsRunLoop APC Thread
ILibDuktape_HECI_WriteState *state = (ILibDuktape_HECI_WriteState*)obj;
ILibQueue_EnQueue(state->session->PendingWrites, state);
if (ILibQueue_GetCount(state->session->PendingWrites) == 1)
{
// No Pending Writes, so we can go ahead and send out the first block
ILibDuktape_HECI_Session_WriteHandler_Process(state->session);
}
}
#elif defined(_POSIX)
ILibTransport_DoneState ILibDuktape_HECI_Session_WriteHandler(void *chain, ILibDuktape_HECI_WriteState* state)
{
// This Method is always dispatched from the Microstack Thread
ILibQueue_EnQueue(state->session->PendingWrites, state);
if (ILibQueue_GetCount(state->session->PendingWrites) == 1)
{
return(ILibDuktape_HECI_Session_WriteHandler_Process(state->session));
}
else
{
return(ILibTransport_DoneState_INCOMPLETE);
}
}
#endif
ILibTransport_DoneState ILibDuktape_HECI_Session_WriteSink_NoPipeline(void *chain, void *user)
{
// This is always called from the Microstack Thread
ILibDuktape_HECI_WriteState *state = (ILibDuktape_HECI_WriteState*)user;
ILibQueue_EnQueue(state->session->PendingWrites, state);
if (ILibQueue_GetCount(state->session->PendingWrites) == 1)
{
return(ILibDuktape_HECI_Session_WriteHandler_Process(state->session));
}
else
{
return(ILibTransport_DoneState_INCOMPLETE);
}
}
ILibTransport_DoneState ILibDuktape_HECI_Session_WriteSink(ILibDuktape_DuplexStream *stream, char *buffer, int bufferLen, void *user)
{
if ((duk_size_t)bufferLen > ((ILibDuktape_HECI_Session*)user)->bufferSize) { return(ILibTransport_DoneState_ERROR); }
ILibDuktape_HECI_Session *session = (ILibDuktape_HECI_Session*)user;
ILibDuktape_HECI_WriteState *state = (ILibDuktape_HECI_WriteState*)ILibMemory_Allocate(bufferLen + sizeof(ILibDuktape_HECI_WriteState), 0, NULL, NULL);
state->session = session;
state->bufferLen = bufferLen;
memcpy_s(state->buffer, bufferLen, buffer, bufferLen);
if (session->noPipelining == 0)
{
#if defined(WIN32)
state->returnIgnored = 1;
QueueUserAPC((PAPCFUNC)ILibDuktape_HECI_Session_WriteHandler, ILibProcessPipe_Manager_GetWorkerThread(session->mgr), (ULONG_PTR)state);
#elif defined(_POSIX)
if (ILibIsRunningOnChainThread(stream->readableStream->chain) != 0)
{
return(ILibDuktape_HECI_Session_WriteHandler(NULL, state));
}
else
{
state->returnIgnored = 1;
ILibChain_RunOnMicrostackThreadEx(stream->readableStream->chain, (ILibChain_StartEvent)ILibDuktape_HECI_Session_WriteHandler, state);
}
#endif
}
else
{
// We can't pipeline write requests
if (ILibIsRunningOnChainThread(stream->readableStream->chain) != 0)
{
return(ILibDuktape_HECI_Session_WriteSink_NoPipeline(stream->readableStream->chain, state));
}
else
{
state->returnIgnored = 1;
ILibChain_RunOnMicrostackThreadEx(stream->readableStream->chain, (ILibChain_StartEvent)ILibDuktape_HECI_Session_WriteSink_NoPipeline, state);
}
}
return(ILibTransport_DoneState_INCOMPLETE);
}
void ILibDuktape_HECI_Session_EndSink(ILibDuktape_DuplexStream *stream, void *user)
{
}
void ILibDuktape_HECI_Session_PauseSink(ILibDuktape_DuplexStream *sender, void *user)
{
#ifdef WIN32
ILibDuktape_HECI_Session *session = (ILibDuktape_HECI_Session*)user;
// To Pause, all we need to do is remove our handle
ILibProcessPipe_WaitHandle_Remove(session->mgr, session->v.hEvent);
#else
UNREFERENCED_PARAMETER(sender);
UNREFERENCED_PARAMETER(user);
#endif
}
#ifdef WIN32
BOOL ILibDuktape_HECI_Session_ReceiveSink(HANDLE event, void* user);
#endif
void ILibDuktape_HECI_Session_ResumeSink_NoPipeline(void *chain, void *user)
{
// This is always called from the Microstack Thread
ILibDuktape_HECI_Session *session = (ILibDuktape_HECI_Session*)user;
ILibDuktape_HECI_WriteState *state = (ILibDuktape_HECI_WriteState*)ILibQueue_DeQueue(session->PendingWrites);
free(state);
if (ILibQueue_GetCount(session->PendingWrites) == 0)
{
ILibDuktape_HECI_Session_EmitStreamReady(session->chain, session);
}
else
{
ILibDuktape_HECI_Session_WriteHandler_Process(session);
}
}
void ILibDuktape_HECI_Session_ResumeSink(ILibDuktape_DuplexStream *sender, void *user)
{
ILibDuktape_HECI_Session *session = (ILibDuktape_HECI_Session*)user;
if (session->noPipelining != 0)
{
ILibChain_RunOnMicrostackThread(sender->readableStream->chain, ILibDuktape_HECI_Session_ResumeSink_NoPipeline, session);
}
#ifdef WIN32
DWORD bytesRead;
// To Resume, we need to ReadFile, then re-add our waithandle
BOOL result = ReadFile(session->descriptor, session->buffer, (DWORD)session->bufferSize, &bytesRead, &(session->v));
if (result == TRUE || GetLastError() == ERROR_IO_PENDING)
{
ILibProcessPipe_WaitHandle_Add(session->mgr, session->v.hEvent, session, ILibDuktape_HECI_Session_ReceiveSink);
}
#endif
}
#ifdef WIN32
BOOL ILibDuktape_HECI_Session_ReceiveSink(HANDLE event, void* user)
{
ILibDuktape_HECI_Session *session = (ILibDuktape_HECI_Session*)user;
DWORD bytesRead;
do
{
if (GetOverlappedResult(session->descriptor, &(session->v), &bytesRead, FALSE) == FALSE) { break; }
ILibDuktape_DuplexStream_WriteData(session->stream, session->buffer, bytesRead);
} while (session->stream->readableStream->paused == 0 && ReadFile(session->descriptor, session->buffer, (DWORD)session->bufferSize, &bytesRead, &(session->v)) == TRUE);
if (session->stream->readableStream->paused == 0 && GetLastError() != ERROR_IO_PENDING)
{
// Broken Connection
ILibProcessPipe_WaitHandle_Remove(session->mgr, session->v.hEvent); // Remove ourselves from processing loop
ILibDuktape_DuplexStream_WriteEnd(session->stream);
}
return(TRUE);
}
void __stdcall ILibDuktape_HECI_Session_Start(ULONG_PTR obj)
{
ILibDuktape_HECI_Session *session = (ILibDuktape_HECI_Session*)obj;
DWORD bytesRead;
BOOL result = ReadFile(session->descriptor, session->buffer, (DWORD)session->bufferSize, &bytesRead, &(session->v));
ILibProcessPipe_WaitHandle_Add(session->mgr, session->v.hEvent, session, ILibDuktape_HECI_Session_ReceiveSink);
}
#endif
duk_ret_t ILibDuktape_HECI_create_OnClientConnect(duk_context *ctx)
{
int statusCode = duk_require_int(ctx, 0);
ILibDuktape_HECI_Session *session = NULL;
duk_dup(ctx, 2); // [Session]
if (statusCode != 0)
{
duk_get_prop_string(ctx, -1, "emit"); // [session][emit]
duk_swap_top(ctx, -2); // [emit][this]
duk_push_string(ctx, "error"); // [emit][this][error]
duk_push_error_object(ctx, DUK_ERR_ERROR, "HECI Connection Error [%d]", statusCode); // [emit][this][error][err]
if (duk_pcall_method(ctx, 2) != 0) { ILibDuktape_Process_UncaughtExceptionEx(ctx, "HECI.session.onError(): "); }
duk_pop(ctx); // ...
}
else
{
duk_size_t bufferLen;
char *buffer = (char*)Duktape_GetBuffer(ctx, 1, &bufferLen);
if (bufferLen > 4)
{
duk_push_int(ctx, ((int*)buffer)[0]);
duk_put_prop_string(ctx, -2, ILibDuktape_HECI_MaxBufferSize); // [session]
duk_push_fixed_buffer(ctx, sizeof(ILibDuktape_HECI_Session) + ((int*)buffer)[0]); // [session][buffer]
session = (ILibDuktape_HECI_Session*)Duktape_GetBuffer(ctx, -1, NULL);
memset(session, 0, sizeof(ILibDuktape_HECI_Session) + ((int*)buffer)[0]);
duk_put_prop_string(ctx, -2, ILibDuktape_HECI_SessionMemPtr); // [session]
#ifdef WIN32
session->v.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
session->wv.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
#endif
session->chain = Duktape_GetChain(ctx);
session->bufferSize = (duk_size_t)((int*)buffer)[0];
session->stream = ILibDuktape_DuplexStream_Init(ctx,
ILibDuktape_HECI_Session_WriteSink, ILibDuktape_HECI_Session_EndSink,
ILibDuktape_HECI_Session_PauseSink, ILibDuktape_HECI_Session_ResumeSink, session);
ILibDuktape_CreateReadonlyProperty_int(ctx, "maxBufferSize", (int)session->bufferSize);
session->PendingWrites = ILibQueue_Create();
duk_push_current_function(ctx);
session->noPipelining = Duktape_GetIntPropertyValue(ctx, -1, ILibDuktape_HECI_Session_NoPipeline, 0);
duk_pop(ctx);
#ifdef _POSIX
//printf("Session: %p\n", session);
duk_get_prop_string(ctx, -1, ILibDuktape_HECI_Child); // [session][heci]
duk_get_prop_string(ctx, -1, ILibDuktape_HECI_ChainLink); // [session][heci][link]
HECI_chainLink *link = (HECI_chainLink*)duk_get_pointer(ctx, -1);
link->session = session;
duk_pop_2(ctx); // [session]
#endif
//printf("NoPipeline: %d\n", session->noPipelining);
}
else
{
// Even tho it was a success, the result buffer is invalid
duk_get_prop_string(ctx, -1, "emit"); // [session][emit]
duk_swap_top(ctx, -2); // [emit][this]
duk_push_string(ctx, "error"); // [emit][this][error]
duk_push_error_object(ctx, DUK_ERR_ERROR, "HECI Connection Error"); // [emit][this][error][err]
if (duk_pcall_method(ctx, 2) != 0) { ILibDuktape_Process_UncaughtExceptionEx(ctx, "HECI.session.onError(): "); }
duk_pop(ctx); // ...
}
}
if (session != NULL)
{
// Hookup the Send/Receive logic
#ifdef WIN32
duk_push_this(ctx); // [HECI]
session->descriptor = (HANDLE)Duktape_GetPointerProperty(ctx, -1, ILibDuktape_HECI_Descriptor);
duk_get_prop_string(ctx, -1, ILibDuktape_HECI_ChildProcess); // [HECI][childProcess]
duk_get_prop_string(ctx, -1, ILibDuktape_ChildProcess_Manager); // [HECI][childProcess][manager]
session->mgr = (ILibProcessPipe_Manager)duk_get_pointer(ctx, -1);
QueueUserAPC((PAPCFUNC)ILibDuktape_HECI_Session_Start, ILibProcessPipe_Manager_GetWorkerThread(session->mgr), (ULONG_PTR)session);
#else
duk_push_this(ctx); // [HECI]
session->descriptor = Duktape_GetIntPropertyValue(ctx, -1, ILibDuktape_HECI_Descriptor, -1);
ILibForceUnBlockChain(session->chain);
#endif
duk_dup(ctx, 2);
duk_get_prop_string(ctx, -1, "emit"); // [session][emit]
duk_swap_top(ctx, -2); // [emit][this]
duk_push_string(ctx, "connect"); // [emit][this][connect]
if (duk_pcall_method(ctx, 1) != 0) { ILibDuktape_Process_UncaughtExceptionEx(ctx, "HECI.session.onConnect(): "); }
duk_pop(ctx); // ...
}
return(0);
}
duk_ret_t ILibDuktape_HECI_Session_connect(duk_context *ctx)
{
int i;
int nargs = duk_get_top(ctx);
duk_push_this(ctx); // [Session]
duk_get_prop_string(ctx, -1, ILibDuktape_HECI_Child); // [Session][HECI]
duk_remove(ctx, -2); // [HECI]
duk_get_prop_string(ctx, -1, "doIoctl"); // [HECI][func]
duk_swap_top(ctx, -2); // [doIoctl][this]
duk_get_prop_string(ctx, -1, "IOCTL"); // [doIoctl][this][IOCTL]
duk_get_prop_string(ctx, -1, "CLIENT_CONNECT"); // [doIoctl][this][IOCTL][CLIENT_CONNECT]
duk_remove(ctx, -2); // [doIoctl][this][CLIENT_CONNECT]
duk_dup(ctx, 0); // [doIoctl][this][CLIENT_CONNECT][guid]
duk_push_fixed_buffer(ctx, 16); // [doIoctl][this][CLIENT_CONNECT][guid][outBuffer]
duk_push_c_function(ctx, ILibDuktape_HECI_create_OnClientConnect, DUK_VARARGS); // [doIoctl][this][CLIENT_CONNECT][guid][outBuffer][callback]
duk_push_int(ctx, 0);
duk_put_prop_string(ctx, -2, ILibDuktape_HECI_Session_NoPipeline);
for (i = 1; i < nargs; ++i)
{
if (duk_is_function(ctx, i)) { ILibDuktape_EventEmitter_AddOnce(ILibDuktape_EventEmitter_GetEmitter_fromThis(ctx), "connect", duk_require_heapptr(ctx, i)); }
else if (duk_is_object(ctx, i))
{
int noPipeline = Duktape_GetIntPropertyValue(ctx, i, "noPipeline", 0);
duk_push_int(ctx, noPipeline);
duk_put_prop_string(ctx, -2, ILibDuktape_HECI_Session_NoPipeline);
}
}
duk_push_this(ctx); // [doIoctl][this][CLIENT_CONNECT][guid][outBuffer][callback][Session]
duk_call_method(ctx, 5); // [retVal]
duk_pop(ctx); // ...
return(0);
}
duk_ret_t ILibDuktape_HECI_create(duk_context *ctx)
{
duk_push_object(ctx); // [Session]
ILibDuktape_HECI_Push(ctx, NULL); // [Session][HECI]
duk_dup(ctx, -2); // [Session][HECI][Session]
duk_put_prop_string(ctx, -2, ILibDuktape_HECI_Parent); // [Session][HECI]
duk_put_prop_string(ctx, -2, ILibDuktape_HECI_Child); // [Session]
ILibDuktape_EventEmitter *emitter = ILibDuktape_EventEmitter_Create(ctx);
ILibDuktape_EventEmitter_CreateEventEx(emitter, "connect");
ILibDuktape_EventEmitter_CreateEventEx(emitter, "error");
ILibDuktape_CreateProperty_InstanceMethod(ctx, "connect", ILibDuktape_HECI_Session_connect, DUK_VARARGS);
ILibDuktape_CreateFinalizer(ctx, ILibDuktape_HECI_SessionFinalizer);
return(1);
}
void ILibDuktape_HECI_IoctlHandler_Dispatch(void *chain, void *user)
{
ILibDuktape_HECI_ioctl_data *data = (ILibDuktape_HECI_ioctl_data*)user;
duk_size_t count;
int i;
duk_context *ctx = data->ctx;
duk_push_heapptr(data->ctx, data->data); // [array]
duk_push_heapptr(data->ctx, data->heciObject); // [array][heci]
duk_get_prop_index(data->ctx, -2, 2); // [array][heci][callback]
duk_swap_top(data->ctx, -2); // [array][callback][this]
count = duk_get_length(data->ctx, -3);
duk_push_int(data->ctx, data->code); // [array][callback][this][status]
duk_get_prop_index(data->ctx, -4, 1); // [array][callback][this][status][buffer]
for (i = 3; i < (int)count; ++i)
{
duk_get_prop_index(data->ctx, -i - 2, i); // [array][callback][this][status][buffer][...args...]
}
if (duk_pcall_method(data->ctx, (duk_idx_t)count - 1) != 0) { ILibDuktape_Process_UncaughtExceptionEx(data->ctx, "heci.ioctlHandler_Dispatch.callback(): "); }
duk_pop_2(data->ctx); // ...
duk_push_heapptr(data->ctx, data->heciObject); // [heci]
ILibDuktape_Push_ObjectStash(data->ctx); // [heci][stash]
duk_del_prop_string(data->ctx, -1, Duktape_GetStashKey(data->data)); // (This will free data internally)
duk_pop_2(ctx); // ...
}
#ifdef WIN32
void ILibDuktape_HECI_NextIoctl(ILibQueue q);
BOOL ILibDuktape_HECI_IoctlHandler(HANDLE h, void *user)
{
ILibDuktape_HECI_ioctl_data *data = (ILibDuktape_HECI_ioctl_data*)user;
ILibQueue Q = data->Q;
BOOL result = GetOverlappedResult(data->device, &(data->v), &(data->bytesReceived), FALSE);
ILibQueue_DeQueue(data->Q);
ILibProcessPipe_WaitHandle_Remove(data->pipeManager, h);
data->code = result == TRUE ? 0 : (int)GetLastError();
ILibChain_RunOnMicrostackThread(data->chain, ILibDuktape_HECI_IoctlHandler_Dispatch, data);
if (ILibQueue_GetCount(Q) > 0)
{
void ILibDuktape_HECI_NextIoctl(Q);
}
return(TRUE);
}
void ILibDuktape_HECI_NextIoctl(ILibQueue q)
{
ILibDuktape_HECI_ioctl_data *data = (ILibDuktape_HECI_ioctl_data*)ILibQueue_PeekQueue(q);
int res;
data->bytesReceived = 0;
ResetEvent(data->v.hEvent);
res = DeviceIoControl(data->device, (DWORD)data->code, data->buffer, (DWORD)data->bufferLen, data->outBuffer, (DWORD)data->outBufferLen, &(data->bytesReceived), &(data->v));
ILibProcessPipe_WaitHandle_Add(data->pipeManager, data->v.hEvent, data, ILibDuktape_HECI_IoctlHandler);
}
void __stdcall ILibDuktape_HECI_apc_AddIoctl(ULONG_PTR obj)
{
ILibDuktape_HECI_ioctl_data *data = (ILibDuktape_HECI_ioctl_data*)obj;
ILibQueue_EnQueue(data->Q, data);
if (ILibQueue_GetCount(data->Q) == 1)
{
ILibDuktape_HECI_NextIoctl(data->Q);
}
}
#endif
#ifdef _POSIX
void ILibDuktape_HECI_AddIoctl(ILibDuktape_HECI_ioctl_data *data)
{
ILibQueue_EnQueue(data->Q, data);
if (ILibQueue_GetCount(data->Q) == 1)
{
ILibForceUnBlockChain(data->chain);
}
}
#endif
duk_ret_t ILibDuktape_HECI_doIoctl(duk_context *ctx)
{
int code = duk_require_int(ctx, 0);
duk_size_t bufferLen = 0;
char *buffer = duk_is_null(ctx, 1) ? NULL : (char*)Duktape_GetBuffer(ctx, 1, &bufferLen);
int nargs = duk_get_top(ctx);
int i;
ILibQueue Q;
duk_size_t outBufferLen;
char *outBuffer;
int cbx;
if (duk_is_buffer(ctx, 2) || duk_is_buffer_data(ctx, 2))
{
outBuffer = (char*)Duktape_GetBuffer(ctx, 2, &outBufferLen);
cbx = 3;
}
else
{
outBuffer = NULL;
outBufferLen = 0;
cbx = 2;
}
#ifdef _POSIX
if (outBuffer == NULL)
{
outBuffer = buffer;
outBufferLen = bufferLen;
}
else
{
if (bufferLen < outBufferLen) { return(ILibDuktape_Error(ctx, "HECI.doIoctl(): Output Buffer too small")); }
memcpy_s(outBuffer, outBufferLen, buffer, bufferLen);
}
#endif
duk_require_function(ctx, cbx);
duk_push_this(ctx); // [heci]
duk_get_prop_string(ctx, -1, ILibDuktape_HECI_Q); // [heci][q]
Q = (ILibQueue)duk_get_pointer(ctx, -1);
duk_pop(ctx); // [heci]
ILibDuktape_Push_ObjectStash(ctx); // [heci][stash]
duk_push_array(ctx); // [heci][stash][array]
ILibDuktape_HECI_ioctl_data *data;
duk_push_fixed_buffer(ctx, bufferLen + sizeof(ILibDuktape_HECI_ioctl_data)); // [heci][stash][array][state]
data = (ILibDuktape_HECI_ioctl_data*)Duktape_GetBuffer(ctx, -1, NULL);
memset(data, 0, sizeof(ILibDuktape_HECI_ioctl_data));
duk_put_prop_index(ctx, -2, 0); // [heci][stash][array]
if (outBufferLen > 0)
{ // [heci][stash][array][buffer]
duk_dup(ctx, 2);
}
else
{
duk_push_null(ctx); // [heci][stash][array][buffer]
}
duk_put_prop_index(ctx, -2, 1); // [heci][stash][array]
data->ctx = ctx;
duk_dup(ctx, cbx); // [heci][stash][array][callback]
duk_put_prop_index(ctx, -2, 2); // [heci][stash][array]
#ifdef WIN32
duk_get_prop_string(ctx, -3, ILibDuktape_HECI_IoctlWaitHandle); // [heci][stash][array][handle]
data->v.hEvent = (HANDLE)duk_get_pointer(ctx, -1);
duk_pop(ctx); // [heci][stash][array]
#endif
duk_get_prop_string(ctx, -3, ILibDuktape_HECI_Descriptor); // [heci][stash][array][descriptor]
#ifdef WIN32
data->device = (HANDLE)duk_get_pointer(ctx, -1);
#elif defined(_POSIX)
data->device = duk_get_int(ctx, -1);
#endif
duk_pop(ctx); // [heci][stash][array]
data->chain = Duktape_GetChain(ctx);
data->Q = Q;
data->code = code;
data->outBuffer = outBuffer;
data->outBufferLen = outBufferLen;
data->heciObject = duk_get_heapptr(ctx, -3);
data->bufferLen = bufferLen;
data->data = duk_get_heapptr(ctx, -1);
memcpy_s(data->buffer, bufferLen, buffer, bufferLen);
for (i = cbx + 1; i < nargs; ++i)
{
duk_dup(ctx, i); // [heci][stash][array][object]
duk_put_prop_index(ctx, -2, i-1); // [heci][stash][array]
}
duk_put_prop_string(ctx, -2, Duktape_GetStashKey(duk_get_heapptr(ctx, -1))); // [heci][stash]
#ifdef WIN32
duk_get_prop_string(ctx, -2, ILibDuktape_HECI_ChildProcess); // [heci][stash][childProcess]
duk_get_prop_string(ctx, -1, ILibDuktape_ChildProcess_Manager); // [heci][stash][childProcess][manager]
data->pipeManager = (ILibProcessPipe_Manager)duk_get_pointer(ctx, -1);
QueueUserAPC((PAPCFUNC)ILibDuktape_HECI_apc_AddIoctl, ILibProcessPipe_Manager_GetWorkerThread(data->pipeManager), (ULONG_PTR)data);
#elif defined(_POSIX)
ILibDuktape_HECI_AddIoctl(data);
#endif
return(0);
}
duk_ret_t ILibDuktape_HECI_Finalizer(duk_context *ctx)
{
#ifdef WIN32
HANDLE h = Duktape_GetPointerProperty(ctx, 0, ILibDuktape_HECI_IoctlWaitHandle);
if (h != NULL) { CloseHandle(h); }
#endif
if (duk_has_prop_string(ctx, 0, ILibDuktape_HECI_Q))
{
duk_get_prop_string(ctx, 0, ILibDuktape_HECI_Q);
ILibQueue_Destroy((ILibQueue)duk_get_pointer(ctx, -1));
}
#ifdef _POSIX
if (duk_has_prop_string(ctx, 0, ILibDuktape_HECI_ChainLink))
{
duk_get_prop_string(ctx, 0, ILibDuktape_HECI_ChainLink);
HECI_chainLink *h = (HECI_chainLink*)duk_get_pointer(ctx, -1);
h->ctx = NULL;
h->heciObject = NULL;
ILibChain_SafeRemove(h->link.ParentChain, h);
}
#endif
return(0);
}
#ifndef WIN32
void ILibDuktape_HECI_PreSelect(void* object, fd_set *readset, fd_set *writeset, fd_set *errorset, int* blocktime)
{
int result;
HECI_chainLink *h = (HECI_chainLink*)object;
//printf("h = %p, descriptor = %d, paused = %d, session = %p\n", (void*)h, h->descriptor, h->paused, (void*)h->session);
if (h->descriptor <= 0) { return; }
if (h->paused == 0 && h->session != NULL) { FD_SET(h->descriptor, readset); }
if (h->session != NULL && ILibQueue_GetCount(h->session->PendingWrites) > 0) { FD_SET(h->descriptor, writeset); }
while (ILibQueue_GetCount(h->Q) > 0 && h->paused == 0)
{
ILibDuktape_HECI_ioctl_data *data = (ILibDuktape_HECI_ioctl_data*)ILibQueue_DeQueue(h->Q);
switch (data->code)
{
case 0x00:
break;
case 0x01:
case 0x02:
case 0x03:
result = ioctl(h->descriptor, _IOC(_IOC_READ | _IOC_WRITE, 'H', data->code, data->outBufferLen), data->outBuffer);
data->code = result ? errno : 0;
ILibDuktape_HECI_IoctlHandler_Dispatch(NULL, data);
break;
default:
break;
}
}
}
void ILibDuktape_HECI_PostSelect(void* object, int slct, fd_set *readset, fd_set *writeset, fd_set *errorset)
{
HECI_chainLink *h = (HECI_chainLink*)object;
if (h->descriptor <= 0) { return; }
if (FD_ISSET(h->descriptor, readset))
{
//printf("session = %p\n", (void*)h->session);
//printf("Attempting to read: %d bytes from %p\n", h->session->bufferSize, (void*)h->session->buffer);
int bytesRead = read(h->descriptor, h->session->buffer, h->session->bufferSize);
if (bytesRead >= 0)
{
ILibDuktape_DuplexStream_WriteData(h->session->stream, h->session->buffer, bytesRead);
}
else
{
ILibDuktape_DuplexStream_WriteEnd(h->session->stream);
}
}
if (FD_ISSET(h->descriptor, writeset))
{
printf("Writeset\n");
}
}
void ILibDuktape_HECI_Destroy(void *object)
{
HECI_chainLink *h = (HECI_chainLink*)object;
if (h->ctx != NULL && h->heciObject != NULL)
{
duk_push_heapptr(h->ctx, h->heciObject); // [heci]
duk_del_prop_string(h->ctx, -1, ILibDuktape_HECI_ChainLink);
duk_pop(h->ctx); // ...
}
close(h->descriptor);
}
#endif
void ILibDuktape_HECI_Push(duk_context *ctx, void *chain)
{
duk_push_object(ctx); // [HECI]
ILibDuktape_CreateFinalizer(ctx, ILibDuktape_HECI_Finalizer);
#ifdef WIN32
HANDLE h = ILibDuktape_HECI_windowsInit();
if (h == NULL) { duk_push_string(ctx, "error initializing HECI"); duk_throw(ctx); }
duk_push_pointer(ctx, h); // [HECI][HANDLE]
duk_put_prop_string(ctx, -2, ILibDuktape_HECI_Descriptor); // [HECI]
if (duk_peval_string(ctx, "require('child_process');") != 0) // [HECI][child_process]
{
duk_push_string(ctx, "Error instantiating dependency 'child_process'"); duk_throw(ctx); return;
}
duk_put_prop_string(ctx, -2, ILibDuktape_HECI_ChildProcess); // [HECI]
duk_push_pointer(ctx, CreateEvent(NULL, TRUE, FALSE, NULL));
duk_put_prop_string(ctx, -2, ILibDuktape_HECI_IoctlWaitHandle); // [HECI]
#elif defined(_POSIX)
int h = ILibDuktape_HECI_linuxInit();
if (h < 0) { duk_push_string(ctx, "error initializing HECI"); duk_throw(ctx); }
duk_push_int(ctx, h); // [HECI][descriptor]
duk_put_prop_string(ctx, -2, ILibDuktape_HECI_Descriptor); // [HECI]
HECI_chainLink *hlink = ILibMemory_Allocate(sizeof(HECI_chainLink), 0, NULL, NULL);
hlink->ctx = ctx;
hlink->descriptor = h;
hlink->link.PreSelectHandler = ILibDuktape_HECI_PreSelect;
hlink->link.PostSelectHandler = ILibDuktape_HECI_PostSelect;
hlink->link.DestroyHandler = ILibDuktape_HECI_Destroy;
hlink->Q = ILibQueue_Create();
duk_push_pointer(ctx, hlink); // [HECI][link]
duk_put_prop_string(ctx, -2, ILibDuktape_HECI_ChainLink); // [HECI]
ILibChain_SafeAdd(Duktape_GetChain(ctx), hlink);
#endif
if (chain != NULL) { ILibDuktape_CreateInstanceMethod(ctx, "create", ILibDuktape_HECI_create, 0); }
ILibDuktape_CreateInstanceMethod(ctx, "doIoctl", ILibDuktape_HECI_doIoctl, DUK_VARARGS);
#ifdef _POSIX
duk_push_pointer(ctx, hlink->Q); // [HECI][Q]
#else
duk_push_pointer(ctx, ILibQueue_Create()); // [HECI][Q]
#endif
duk_put_prop_string(ctx, -2, ILibDuktape_HECI_Q); // [HECI]
duk_push_object(ctx);
#ifdef WIN32
ILibDuktape_CreateReadonlyProperty_int(ctx, "HECI_VERSION", (int)(CTL_CODE(0x8000, 0x800, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)));
ILibDuktape_CreateReadonlyProperty_int(ctx, "CLIENT_CONNECT", (int)(CTL_CODE(0x8000, 0x801, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)));
#elif defined(_POSIX)
ILibDuktape_CreateReadonlyProperty_int(ctx, "HECI_VERSION", (int)0x00);
ILibDuktape_CreateReadonlyProperty_int(ctx, "CLIENT_CONNECT", (int)0x01);
#endif
ILibDuktape_CreateReadonlyProperty(ctx, "IOCTL");
duk_push_object(ctx);
duk_peval_string(ctx, "Buffer.from('DBA4336776047B4EB3AFBCFC29BEE7A7', 'hex');");
ILibDuktape_CreateReadonlyProperty(ctx, "LME");
duk_peval_string(ctx, "Buffer.from('2800F812B7B42D4BACA846E0FF65814C', 'hex');");
ILibDuktape_CreateReadonlyProperty(ctx, "AMT");
ILibDuktape_CreateReadonlyProperty(ctx, "GUIDS");
} // KLOCKWORK: We are not losing reference to created Event... It is freed in the object finalizer 'ILibDuktape_HECI_Finalizer'
void ILibDuktape_HECI_Init(duk_context *ctx)
{
ILibDuktape_ModSearch_AddHandler(ctx, "heci", ILibDuktape_HECI_Push);
}