From a3f69396f6f989c4724c3e16b7dbb054f9ed3073 Mon Sep 17 00:00:00 2001 From: Bryan Roe Date: Fri, 11 Jan 2019 12:42:16 -0800 Subject: [PATCH] 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 --- microstack/ILibProcessPipe.c | 172 ++++++++++++++++++++++++++++++----- microstack/ILibProcessPipe.h | 15 ++- 2 files changed, 159 insertions(+), 28 deletions(-) diff --git a/microstack/ILibProcessPipe.c b/microstack/ILibProcessPipe.c index 7fcd231..7c99a24 100644 --- a/microstack/ILibProcessPipe.c +++ b/microstack/ILibProcessPipe.c @@ -35,8 +35,10 @@ limitations under the License. #include #include #include +#ifndef __APPLE__ #include #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); diff --git a/microstack/ILibProcessPipe.h b/microstack/ILibProcessPipe.h index 2f49d68..660c377 100644 --- a/microstack/ILibProcessPipe.h +++ b/microstack/ILibProcessPipe.h @@ -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