1
0
mirror of https://github.com/Ylianst/MeshAgent synced 2025-12-15 15:53:55 +00:00

Added MacOS support

Added ability to spawn process with specific user, under linux
Fixed edge case memory leak caused when we were trying to QueueUserAPC to a thread we were already on, which also caused a 3 second delay.
Changed WindowsRunLoop, to detect bad HANDLEs, and event an error on them, and remove them from list
Fixed linux bug, where a spawned child called 'exit' instead of '_exit' in an error condition
This commit is contained in:
Bryan Roe
2019-01-11 12:42:16 -08:00
parent c0db749e8e
commit a3f69396f6
2 changed files with 159 additions and 28 deletions

View File

@@ -35,8 +35,10 @@ limitations under the License.
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#ifndef __APPLE__
#include <pty.h>
#endif
#endif
#define CONSOLE_SCREEN_WIDTH 80
#define CONSOLE_SCREEN_HEIGHT 25
@@ -120,8 +122,6 @@ typedef struct ILibProcessPipe_WriteData
ILibTransport_MemoryOwnership ownership;
}ILibProcessPipe_WriteData;
const int ILibMemory_ILibProcessPipe_Pipe_CONTAINERSIZE = sizeof(ILibProcessPipe_PipeObject);
ILibProcessPipe_WriteData* ILibProcessPipe_WriteData_Create(char* buffer, int bufferLen, ILibTransport_MemoryOwnership ownership)
{
ILibProcessPipe_WriteData* retVal;
@@ -153,13 +153,15 @@ ILibProcessPipe_Pipe ILibProcessPipe_Process_GetStdOut(ILibProcessPipe_Process p
}
#ifdef WIN32
BOOL ILibProcessPipe_Process_OnExit(HANDLE event, void* user);
BOOL ILibProcessPipe_Process_OnExit(HANDLE event, ILibWaitHandle_ErrorStatus errors, void* user);
typedef struct ILibProcessPipe_WaitHandle
{
ILibProcessPipe_Manager_Object *parent;
HANDLE event;
void *user;
ILibProcessPipe_WaitHandle_Handler callback;
int timeRemaining;
int timeout;
}ILibProcessPipe_WaitHandle;
HANDLE ILibProcessPipe_Manager_GetWorkerThread(ILibProcessPipe_Manager mgr)
{
@@ -200,6 +202,7 @@ void ILibProcessPipe_WaitHandle_Remove(ILibProcessPipe_Manager mgr, HANDLE event
void __stdcall ILibProcessPipe_WaitHandle_Add_APC(ULONG_PTR obj)
{
ILibProcessPipe_WaitHandle *waitHandle = (ILibProcessPipe_WaitHandle*)obj;
if (waitHandle->timeout > 0) { waitHandle->timeRemaining = waitHandle->timeout; }
ILibLinkedList_AddTail(waitHandle->parent->ActivePipes, waitHandle);
}
void ILibProcessPipe_WaitHandle_AddEx(ILibProcessPipe_Manager mgr, ILibProcessPipe_WaitHandle *waitHandle)
@@ -208,8 +211,8 @@ void ILibProcessPipe_WaitHandle_AddEx(ILibProcessPipe_Manager mgr, ILibProcessPi
if (manager->workerThreadID == GetCurrentThreadId())
{
// We're on the same thread, so we can just add it in
if (waitHandle->timeout > 0) { waitHandle->timeRemaining = waitHandle->timeout; }
ILibLinkedList_AddTail(manager->ActivePipes, waitHandle);
SetEvent(manager->updateEvent);
}
else
@@ -218,7 +221,7 @@ void ILibProcessPipe_WaitHandle_AddEx(ILibProcessPipe_Manager mgr, ILibProcessPi
QueueUserAPC((PAPCFUNC)ILibProcessPipe_WaitHandle_Add_APC, manager->workerThread, (ULONG_PTR)waitHandle);
}
}
void ILibProcessPipe_WaitHandle_Add(ILibProcessPipe_Manager mgr, HANDLE event, void *user, ILibProcessPipe_WaitHandle_Handler callback)
void ILibProcessPipe_WaitHandle_Add_WithNonZeroTimeout(ILibProcessPipe_Manager mgr, HANDLE event, int milliseconds, void *user, ILibProcessPipe_WaitHandle_Handler callback)
{
ILibProcessPipe_Manager_Object *manager = (ILibProcessPipe_Manager_Object*)mgr;
ILibProcessPipe_WaitHandle *waitHandle;
@@ -228,6 +231,7 @@ void ILibProcessPipe_WaitHandle_Add(ILibProcessPipe_Manager mgr, HANDLE event, v
waitHandle->event = event;
waitHandle->user = user;
waitHandle->callback = callback;
waitHandle->timeout = milliseconds;
ILibProcessPipe_WaitHandle_AddEx(mgr, waitHandle);
}
@@ -237,13 +241,14 @@ void ILibProcessPipe_Manager_WindowsRunLoopEx(void *arg)
ILibProcessPipe_Manager_Object *manager = (ILibProcessPipe_Manager_Object*)arg;
HANDLE hList[FD_SETSIZE * (2*sizeof(HANDLE))];
ILibLinkedList active = manager->ActivePipes;
uint64_t timestamp1;
DWORD elapsedTime = 0;
void* node;
ILibProcessPipe_WaitHandle* data;
int i;
int i, jx;
DWORD x;
DWORD maxTimeout = INFINITE;
memset(hList, 0, sizeof(HANDLE)*FD_SETSIZE);
manager->workerThreadID = GetCurrentThreadId();
@@ -251,9 +256,9 @@ void ILibProcessPipe_Manager_WindowsRunLoopEx(void *arg)
{
hList[0] = manager->updateEvent;
i = 1;
//Prepare the rest of the WaitHandle Array, for the WaitForMultipleObject call
maxTimeout = INFINITE;
node = ILibLinkedList_GetNode_Head(active);
while (node != NULL)
{
@@ -263,21 +268,39 @@ void ILibProcessPipe_Manager_WindowsRunLoopEx(void *arg)
hList[i] = data->event;
hList[i + FD_SETSIZE] = (HANDLE)data;
if (data->timeout > 0)
{
if (elapsedTime > 0)
{
// We popped out of the wait, but didn't decrement timeRemaining yet
if (data->timeRemaining < (int)elapsedTime)
{
data->timeRemaining = 0;
}
else
{
data->timeRemaining -= (int)elapsedTime;
}
}
if ((DWORD)data->timeRemaining < maxTimeout) { maxTimeout = (DWORD)data->timeRemaining; }
}
++i;
}
node = ILibLinkedList_GetNextNode(node);
}
ILibRemoteLogging_printf(ILibChainGetLogger(manager->ChainLink.ParentChain), ILibRemoteLogging_Modules_Microstack_Generic, ILibRemoteLogging_Flags_VerbosityLevel_1, "WindowsRunLoopEx(): Number of Handles: %d", i);
timestamp1 = (uint64_t)ILibGetUptime();
while ((x = WaitForMultipleObjectsEx(i, hList, FALSE, INFINITE, TRUE)) != WAIT_IO_COMPLETION)
while ((x = WaitForMultipleObjectsEx(i, hList, FALSE, maxTimeout, TRUE)) != WAIT_IO_COMPLETION)
{
if (x == WAIT_FAILED || (x-WAIT_OBJECT_0) == 0) { break; }
elapsedTime = (DWORD)((uint64_t)ILibGetUptime() - timestamp1);
if (x == WAIT_FAILED || x == WAIT_TIMEOUT || (x-WAIT_OBJECT_0) == 0) { break; }
data = (ILibProcessPipe_WaitHandle*)hList[x + FD_SETSIZE];
manager->activeWaitHandle = data;
if (data != NULL && data->callback != NULL)
{
if (data->callback(data->event, data->user) == FALSE)
if (data->callback(data->event, ILibWaitHandle_ErrorStatus_NONE, data->user) == FALSE)
{
// FALSE means to remove the WaitHandle
void *node = ILibLinkedList_GetNode_Search(manager->ActivePipes, ILibProcessPipe_Manager_WindowsWaitHandles_Remove_Comparer, data->event);
@@ -289,6 +312,96 @@ void ILibProcessPipe_Manager_WindowsRunLoopEx(void *arg)
}
}
}
if (maxTimeout != INFINITE)
{
// Need to recalculate maxTimeout
for (jx = 1; jx < i; ++jx)
{
data = (ILibProcessPipe_WaitHandle*)hList[jx + FD_SETSIZE];
if (data->timeout > 0) // Timeout was specified
{
if (data->timeRemaining > (int)elapsedTime) // We can just decrement time left
{
data->timeRemaining -= (int)elapsedTime;
}
else
{
data->timeRemaining = 0; // Already expired
}
if (data->timeRemaining < (int)maxTimeout)
{
maxTimeout = (DWORD)data->timeRemaining; // Set the new maxTimeout
}
}
}
}
}
if (x == WAIT_IO_COMPLETION) { elapsedTime = (DWORD)((uint64_t)ILibGetUptime() - timestamp1); }
if (x == WAIT_FAILED)
{
for (jx = 1; jx < i; ++jx)
{
if (WaitForSingleObject(hList[jx], 0) == WAIT_FAILED)
{
// This handle is invalid, so let's error it out and remove it
node = ILibLinkedList_GetNode_Search(manager->ActivePipes, ILibProcessPipe_Manager_WindowsWaitHandles_Remove_Comparer, hList[jx]);
if (node != NULL)
{
// Propagate an Error up
data = (ILibProcessPipe_WaitHandle*)hList[jx + FD_SETSIZE];
if (data != NULL && data->callback != NULL)
{
manager->activeWaitHandle = data;
data->callback(data->event, ILibWaitHandle_ErrorStatus_INVALID_HANDLE, data->user);
}
// Remove the wait handle
free(ILibLinkedList_GetDataFromNode(node));
ILibLinkedList_Remove(node);
break;
}
}
}
}
if (x == WAIT_TIMEOUT)
{
for (jx = 1; jx < i; ++jx)
{
data = (ILibProcessPipe_WaitHandle*)hList[jx + FD_SETSIZE];
if (data->timeout > 0)
{
// A timeout was specified, so lets check it
if (data->timeRemaining <= (int)elapsedTime)
{
data->timeRemaining = 0;
}
else
{
data->timeRemaining -= (int)elapsedTime;
}
if (data->timeRemaining == 0)
{
manager->activeWaitHandle = data;
if (data->callback == NULL || data->callback(data->event, ILibWaitHandle_ErrorStatus_TIMEOUT, data->user) == FALSE)
{
// Remove Wait Handle
node = ILibLinkedList_GetNode_Search(manager->ActivePipes, ILibProcessPipe_Manager_WindowsWaitHandles_Remove_Comparer, data->event);
if (node != NULL)
{
free(ILibLinkedList_GetDataFromNode(node));
ILibLinkedList_Remove(node);
}
}
else
{
// Reset timeout
data->timeRemaining = data->timeout;
}
}
}
}
elapsedTime = 0; // Set this to zero, becuase we already decremented everyone's time remaining
}
ResetEvent(manager->updateEvent);
}
@@ -401,6 +514,8 @@ ILibProcessPipe_Manager ILibProcessPipe_Manager_Create(void *chain)
void ILibProcessPipe_FreePipe(ILibProcessPipe_PipeObject *pipeObject)
{
if (!ILibMemory_CanaryOK(pipeObject)) { return; }
#ifdef WIN32
if (pipeObject->mPipe_ReadEnd != NULL) { CloseHandle(pipeObject->mPipe_ReadEnd); }
if (pipeObject->mPipe_WriteEnd != NULL) { CloseHandle(pipeObject->mPipe_WriteEnd); }
@@ -424,7 +539,7 @@ void ILibProcessPipe_FreePipe(ILibProcessPipe_PipeObject *pipeObject)
if (pipeObject->mProcess->stdOut == pipeObject) { pipeObject->mProcess->stdOut = NULL; }
if (pipeObject->mProcess->stdErr == pipeObject) { pipeObject->mProcess->stdErr = NULL; }
}
free(pipeObject);
ILibMemory_Free(pipeObject);
}
#ifdef WIN32
@@ -444,7 +559,7 @@ ILibProcessPipe_Pipe ILibProcessPipe_Pipe_CreateFromExistingWithExtraMemory(ILib
{
ILibProcessPipe_PipeObject* retVal = NULL;
retVal = (ILibProcessPipe_PipeObject*)ILibMemory_Allocate(ILibMemory_ILibProcessPipe_Pipe_CONTAINERSIZE, extraMemorySize, NULL, NULL);
retVal = ILibMemory_SmartAllocateEx(sizeof(ILibProcessPipe_PipeObject), extraMemorySize);
retVal->manager = (ILibProcessPipe_Manager_Object*)manager;
#ifdef WIN32
@@ -479,7 +594,7 @@ ILibProcessPipe_PipeObject* ILibProcessPipe_CreatePipe(ILibProcessPipe_Manager m
int fd[2];
#endif
retVal = (ILibProcessPipe_PipeObject*)ILibMemory_Allocate(ILibMemory_ILibProcessPipe_Pipe_CONTAINERSIZE, extraMemorySize, NULL, NULL);
retVal = (ILibProcessPipe_PipeObject*)ILibMemory_SmartAllocateEx(sizeof(ILibProcessPipe_PipeObject), extraMemorySize);
retVal->brokenPipeHandler = brokenPipeHandler;
retVal->manager = (ILibProcessPipe_Manager_Object*)manager;
@@ -707,7 +822,10 @@ ILibProcessPipe_Process ILibProcessPipe_Manager_SpawnProcessEx3(ILibProcessPipe_
if (token != NULL) { CloseHandle(token); token = NULL; }
if (userToken != NULL) { CloseHandle(userToken); userToken = NULL; }
#else
int UID = (int)(uint64_t)sid;
int UID = (int)(uint64_t)(ILibPtrCAST)sid;
#ifdef __APPLE__
if (spawnType == ILibProcessPipe_SpawnTypes_TERM) { spawnType = ILibProcessPipe_SpawnTypes_DEFAULT; }
#endif
if (spawnType == ILibProcessPipe_SpawnTypes_TERM)
{
int pipe;
@@ -716,8 +834,9 @@ ILibProcessPipe_Process ILibProcessPipe_Manager_SpawnProcessEx3(ILibProcessPipe_
w.ws_col = CONSOLE_SCREEN_WIDTH;
w.ws_xpixel = 0;
w.ws_ypixel = 0;
#ifndef __APPLE__
pid = forkpty(&pipe, NULL, NULL, &w);
#endif
retVal->stdIn = ILibProcessPipe_Pipe_CreateFromExistingWithExtraMemory(pipeManager, pipe, extraMemorySize);
retVal->stdIn->mProcess = retVal;
retVal->stdOut = ILibProcessPipe_Pipe_CreateFromExistingWithExtraMemory(pipeManager, pipe, extraMemorySize);
@@ -772,12 +891,12 @@ ILibProcessPipe_Process ILibProcessPipe_Manager_SpawnProcessEx3(ILibProcessPipe_
close(retVal->stdOut->mPipe_WriteEnd);
}
}
if (UID != -1)
if (UID != -1 && UID != 0)
{
setuid((uid_t)UID);
ignore_result(setuid((uid_t)UID));
}
execv(target, parameters);
exit(1);
_exit(1);
}
if (spawnType != ILibProcessPipe_SpawnTypes_TERM && spawnType != ILibProcessPipe_SpawnTypes_DETACHED)
{
@@ -811,11 +930,14 @@ void ILibProcessPipe_Pipe_SwapBuffers(ILibProcessPipe_Pipe obj, char* newBuffer,
pipeObject->totalRead = newBufferTotalBytesRead;
}
#ifdef WIN32
BOOL ILibProcessPipe_Process_ReadHandler(HANDLE event, void* user)
BOOL ILibProcessPipe_Process_ReadHandler(HANDLE event, ILibWaitHandle_ErrorStatus errors, void* user)
#else
void ILibProcessPipe_Process_ReadHandler(void* user)
#endif
{
#ifdef WIN32
if (errors != ILibWaitHandle_ErrorStatus_NONE) { return(FALSE); }
#endif
ILibProcessPipe_PipeObject *pipeObject = (ILibProcessPipe_PipeObject*)user;
int consumed;
int err;
@@ -942,8 +1064,9 @@ void ILibProcessPipe_Process_ReadHandler(void* user)
#endif
}
#ifdef WIN32
BOOL ILibProcessPipe_Process_WindowsWriteHandler(HANDLE event, void* user)
BOOL ILibProcessPipe_Process_WindowsWriteHandler(HANDLE event, ILibWaitHandle_ErrorStatus errors, void* user)
{
if (errors != ILibWaitHandle_ErrorStatus_NONE) { return(FALSE); }
ILibProcessPipe_PipeObject *pipeObject = (ILibProcessPipe_PipeObject*)user;
BOOL result;
DWORD bytesWritten;
@@ -1147,6 +1270,7 @@ void __stdcall ILibProcessPipe_Pipe_ResumeEx_APC(ULONG_PTR obj)
#endif
void ILibProcessPipe_Pipe_ResumeEx(ILibProcessPipe_PipeObject* p)
{
if (!ILibMemory_CanaryOK(p)) { return; }
ILibRemoteLogging_printf(ILibChainGetLogger(p->manager->ChainLink.ParentChain), ILibRemoteLogging_Modules_Microstack_Generic, ILibRemoteLogging_Flags_VerbosityLevel_1, "ProcessPipe.ResumeEx(): processingLoop = %d", p->processingLoop);
#ifdef WIN32
@@ -1181,6 +1305,7 @@ void ILibProcessPipe_Pipe_ResumeEx(ILibProcessPipe_PipeObject* p)
void ILibProcessPipe_Pipe_Resume(ILibProcessPipe_Pipe pipeObject)
{
ILibProcessPipe_PipeObject *p = (ILibProcessPipe_PipeObject*)pipeObject;
if (!ILibMemory_CanaryOK(p)) { return; }
#ifdef WIN32
if (p->mOverlapped == NULL)
{
@@ -1322,9 +1447,10 @@ void ILibProcessPipe_Process_OnExit_ChainSink(void *chain, void *user)
// We can't destroy this now, because we're on the MicrostackThread. We must destroy this on the WindowsRunLoop Thread.
QueueUserAPC((PAPCFUNC)ILibProcessPipe_Process_OnExit_ChainSink_DestroySink, j->parent->workerThread, (ULONG_PTR)j);
}
BOOL ILibProcessPipe_Process_OnExit(HANDLE event, void* user)
BOOL ILibProcessPipe_Process_OnExit(HANDLE event, ILibWaitHandle_ErrorStatus errors, void* user)
{
ILibProcessPipe_Process_Object* j = (ILibProcessPipe_Process_Object*)user;
if (errors != ILibWaitHandle_ErrorStatus_NONE) { return(FALSE); }
UNREFERENCED_PARAMETER(event);
ILibProcessPipe_WaitHandle_Remove(j->parent, j->hProcess);

View File

@@ -43,9 +43,6 @@ typedef enum ILibProcessPipe_SpawnTypes
ILibProcessPipe_SpawnTypes_SPECIFIED_USER = 5
}ILibProcessPipe_SpawnTypes;
extern const int ILibMemory_ILibProcessPipe_Pipe_CONTAINERSIZE;
#ifdef WIN32
typedef enum ILibProcessPipe_Pipe_ReaderHandleType
{
@@ -91,8 +88,16 @@ pid_t ILibProcessPipe_Process_GetPID(ILibProcessPipe_Process p);
#ifdef WIN32
typedef BOOL(*ILibProcessPipe_WaitHandle_Handler)(HANDLE event, void* user);
void ILibProcessPipe_WaitHandle_Add(ILibProcessPipe_Manager mgr, HANDLE event, void *user, ILibProcessPipe_WaitHandle_Handler callback);
typedef enum ILibWaitHandle_ErrorStatus
{
ILibWaitHandle_ErrorStatus_NONE = 0,
ILibWaitHandle_ErrorStatus_INVALID_HANDLE = 1,
ILibWaitHandle_ErrorStatus_TIMEOUT = 2
}ILibWaitHandle_ErrorStatus;
typedef BOOL(*ILibProcessPipe_WaitHandle_Handler)(HANDLE event, ILibWaitHandle_ErrorStatus status, void* user);
void ILibProcessPipe_WaitHandle_Add_WithNonZeroTimeout(ILibProcessPipe_Manager mgr, HANDLE event, int milliseconds, void *user, ILibProcessPipe_WaitHandle_Handler callback);
#define ILibProcessPipe_WaitHandle_Add(processPipeManager, eventHandle, user, callback) ILibProcessPipe_WaitHandle_Add_WithNonZeroTimeout(processPipeManager, eventHandle, 0, user, callback)
void ILibProcessPipe_WaitHandle_Remove(ILibProcessPipe_Manager mgr, HANDLE event);
#endif
#define ILibTransports_ProcessPipe 0x60