Milestone 5: deliver embedded RDP sessions and lifecycle hardening
This commit is contained in:
31
third_party/FreeRDP/winpr/libwinpr/thread/CMakeLists.txt
vendored
Normal file
31
third_party/FreeRDP/winpr/libwinpr/thread/CMakeLists.txt
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
# WinPR: Windows Portable Runtime
|
||||
# libwinpr-thread cmake build script
|
||||
#
|
||||
# Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
|
||||
#
|
||||
# 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.
|
||||
|
||||
winpr_module_add(
|
||||
apc.h
|
||||
apc.c
|
||||
argv.c
|
||||
process.c
|
||||
processor.c
|
||||
thread.c
|
||||
thread.h
|
||||
tls.c
|
||||
)
|
||||
|
||||
if(BUILD_TESTING_INTERNAL OR BUILD_TESTING)
|
||||
add_subdirectory(test)
|
||||
endif()
|
||||
9
third_party/FreeRDP/winpr/libwinpr/thread/ModuleOptions.cmake
vendored
Normal file
9
third_party/FreeRDP/winpr/libwinpr/thread/ModuleOptions.cmake
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
set(MINWIN_LAYER "1")
|
||||
set(MINWIN_GROUP "core")
|
||||
set(MINWIN_MAJOR_VERSION "1")
|
||||
set(MINWIN_MINOR_VERSION "1")
|
||||
set(MINWIN_SHORT_NAME "processthreads")
|
||||
set(MINWIN_LONG_NAME "Process and Thread Functions")
|
||||
set(MODULE_LIBRARY_NAME
|
||||
"api-ms-win-${MINWIN_GROUP}-${MINWIN_SHORT_NAME}-l${MINWIN_LAYER}-${MINWIN_MAJOR_VERSION}-${MINWIN_MINOR_VERSION}"
|
||||
)
|
||||
272
third_party/FreeRDP/winpr/libwinpr/thread/apc.c
vendored
Normal file
272
third_party/FreeRDP/winpr/libwinpr/thread/apc.c
vendored
Normal file
@@ -0,0 +1,272 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* APC implementation
|
||||
*
|
||||
* Copyright 2021 David Fort <contact@hardening-consulting.com>
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
#ifndef _WIN32
|
||||
|
||||
#include "apc.h"
|
||||
#include "thread.h"
|
||||
#include "../log.h"
|
||||
#include "../synch/pollset.h"
|
||||
#include <winpr/assert.h>
|
||||
|
||||
#define TAG WINPR_TAG("apc")
|
||||
|
||||
BOOL apc_init(APC_QUEUE* apc)
|
||||
{
|
||||
pthread_mutexattr_t attr;
|
||||
BOOL ret = FALSE;
|
||||
|
||||
WINPR_ASSERT(apc);
|
||||
|
||||
pthread_mutexattr_init(&attr);
|
||||
if (pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE) != 0)
|
||||
{
|
||||
WLog_ERR(TAG, "failed to initialize mutex attributes to recursive");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
memset(apc, 0, sizeof(*apc));
|
||||
|
||||
if (pthread_mutex_init(&apc->mutex, &attr) != 0)
|
||||
{
|
||||
WLog_ERR(TAG, "failed to initialize main thread APC mutex");
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = TRUE;
|
||||
out:
|
||||
pthread_mutexattr_destroy(&attr);
|
||||
return ret;
|
||||
}
|
||||
|
||||
BOOL apc_uninit(APC_QUEUE* apc)
|
||||
{
|
||||
WINPR_ASSERT(apc);
|
||||
return pthread_mutex_destroy(&apc->mutex) == 0;
|
||||
}
|
||||
|
||||
void apc_register(WINPR_THREAD* thread, WINPR_APC_ITEM* addItem)
|
||||
{
|
||||
WINPR_APC_ITEM** nextp = nullptr;
|
||||
APC_QUEUE* apc = nullptr;
|
||||
|
||||
WINPR_ASSERT(thread);
|
||||
WINPR_ASSERT(addItem);
|
||||
|
||||
apc = &thread->apc;
|
||||
WINPR_ASSERT(apc);
|
||||
|
||||
pthread_mutex_lock(&apc->mutex);
|
||||
if (apc->tail)
|
||||
{
|
||||
nextp = &apc->tail->next;
|
||||
addItem->last = apc->tail;
|
||||
}
|
||||
else
|
||||
{
|
||||
nextp = &apc->head;
|
||||
}
|
||||
|
||||
*nextp = addItem;
|
||||
apc->tail = addItem;
|
||||
apc->length++;
|
||||
|
||||
addItem->markedForRemove = FALSE;
|
||||
addItem->boundThread = GetCurrentThreadId();
|
||||
addItem->linked = TRUE;
|
||||
pthread_mutex_unlock(&apc->mutex);
|
||||
}
|
||||
|
||||
static inline void apc_item_remove(APC_QUEUE* apc, WINPR_APC_ITEM* item)
|
||||
{
|
||||
WINPR_ASSERT(apc);
|
||||
WINPR_ASSERT(item);
|
||||
|
||||
if (!item->last)
|
||||
apc->head = item->next;
|
||||
else
|
||||
item->last->next = item->next;
|
||||
|
||||
if (!item->next)
|
||||
apc->tail = item->last;
|
||||
else
|
||||
item->next->last = item->last;
|
||||
|
||||
apc->length--;
|
||||
}
|
||||
|
||||
APC_REMOVE_RESULT apc_remove(WINPR_APC_ITEM* item)
|
||||
{
|
||||
WINPR_THREAD* thread = winpr_GetCurrentThread();
|
||||
APC_QUEUE* apc = nullptr;
|
||||
APC_REMOVE_RESULT ret = APC_REMOVE_OK;
|
||||
|
||||
WINPR_ASSERT(item);
|
||||
|
||||
if (!item->linked)
|
||||
return APC_REMOVE_OK;
|
||||
|
||||
if (item->boundThread != GetCurrentThreadId())
|
||||
{
|
||||
WLog_ERR(TAG, "removing an APC entry should be done in the creating thread");
|
||||
return APC_REMOVE_ERROR;
|
||||
}
|
||||
|
||||
if (!thread)
|
||||
{
|
||||
WLog_ERR(TAG, "unable to retrieve current thread");
|
||||
return APC_REMOVE_ERROR;
|
||||
}
|
||||
|
||||
apc = &thread->apc;
|
||||
WINPR_ASSERT(apc);
|
||||
|
||||
pthread_mutex_lock(&apc->mutex);
|
||||
if (apc->treatingCompletions)
|
||||
{
|
||||
item->markedForRemove = TRUE;
|
||||
ret = APC_REMOVE_DELAY_FREE;
|
||||
goto out;
|
||||
}
|
||||
|
||||
apc_item_remove(apc, item);
|
||||
|
||||
out:
|
||||
pthread_mutex_unlock(&apc->mutex);
|
||||
item->boundThread = 0xFFFFFFFF;
|
||||
item->linked = FALSE;
|
||||
return ret;
|
||||
}
|
||||
|
||||
BOOL apc_collectFds(WINPR_THREAD* thread, WINPR_POLL_SET* set, BOOL* haveAutoSignaled)
|
||||
{
|
||||
WINPR_APC_ITEM* item = nullptr;
|
||||
BOOL ret = FALSE;
|
||||
APC_QUEUE* apc = nullptr;
|
||||
|
||||
WINPR_ASSERT(thread);
|
||||
WINPR_ASSERT(haveAutoSignaled);
|
||||
|
||||
apc = &thread->apc;
|
||||
WINPR_ASSERT(apc);
|
||||
|
||||
*haveAutoSignaled = FALSE;
|
||||
pthread_mutex_lock(&apc->mutex);
|
||||
item = apc->head;
|
||||
for (; item; item = item->next)
|
||||
{
|
||||
if (item->alwaysSignaled)
|
||||
{
|
||||
*haveAutoSignaled = TRUE;
|
||||
}
|
||||
else if (!pollset_add(set, item->pollFd, item->pollMode))
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = TRUE;
|
||||
out:
|
||||
pthread_mutex_unlock(&apc->mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int apc_executeCompletions(WINPR_THREAD* thread, WINPR_POLL_SET* set, size_t startIndex)
|
||||
{
|
||||
APC_QUEUE* apc = nullptr;
|
||||
WINPR_APC_ITEM* nextItem = nullptr;
|
||||
size_t idx = startIndex;
|
||||
int ret = 0;
|
||||
|
||||
WINPR_ASSERT(thread);
|
||||
|
||||
apc = &thread->apc;
|
||||
WINPR_ASSERT(apc);
|
||||
|
||||
pthread_mutex_lock(&apc->mutex);
|
||||
apc->treatingCompletions = TRUE;
|
||||
|
||||
/* first pass to compute signaled items */
|
||||
for (WINPR_APC_ITEM* item = apc->head; item; item = item->next)
|
||||
{
|
||||
item->isSignaled = item->alwaysSignaled || pollset_isSignaled(set, idx);
|
||||
if (!item->alwaysSignaled)
|
||||
idx++;
|
||||
}
|
||||
|
||||
/* second pass: run completions */
|
||||
for (WINPR_APC_ITEM* item = apc->head; item; item = nextItem)
|
||||
{
|
||||
if (item->isSignaled)
|
||||
{
|
||||
if (item->completion && !item->markedForRemove)
|
||||
item->completion(item->completionArgs);
|
||||
ret++;
|
||||
}
|
||||
|
||||
nextItem = item->next;
|
||||
}
|
||||
|
||||
/* third pass: to do final cleanup */
|
||||
for (WINPR_APC_ITEM* item = apc->head; item; item = nextItem)
|
||||
{
|
||||
nextItem = item->next;
|
||||
|
||||
if (item->markedForRemove)
|
||||
{
|
||||
apc_item_remove(apc, item);
|
||||
if (item->markedForFree)
|
||||
free(item);
|
||||
}
|
||||
}
|
||||
|
||||
apc->treatingCompletions = FALSE;
|
||||
pthread_mutex_unlock(&apc->mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void apc_cleanupThread(WINPR_THREAD* thread)
|
||||
{
|
||||
WINPR_APC_ITEM* item = nullptr;
|
||||
WINPR_APC_ITEM* nextItem = nullptr;
|
||||
APC_QUEUE* apc = nullptr;
|
||||
|
||||
WINPR_ASSERT(thread);
|
||||
|
||||
apc = &thread->apc;
|
||||
WINPR_ASSERT(apc);
|
||||
|
||||
pthread_mutex_lock(&apc->mutex);
|
||||
item = apc->head;
|
||||
for (; item; item = nextItem)
|
||||
{
|
||||
nextItem = item->next;
|
||||
|
||||
if (item->type == APC_TYPE_HANDLE_FREE)
|
||||
item->completion(item->completionArgs);
|
||||
|
||||
item->last = item->next = nullptr;
|
||||
item->linked = FALSE;
|
||||
if (item->markedForFree)
|
||||
free(item);
|
||||
}
|
||||
|
||||
apc->head = apc->tail = nullptr;
|
||||
pthread_mutex_unlock(&apc->mutex);
|
||||
}
|
||||
|
||||
#endif
|
||||
85
third_party/FreeRDP/winpr/libwinpr/thread/apc.h
vendored
Normal file
85
third_party/FreeRDP/winpr/libwinpr/thread/apc.h
vendored
Normal file
@@ -0,0 +1,85 @@
|
||||
/**
|
||||
* WinPR: Windows Portable Runtime
|
||||
* APC implementation
|
||||
*
|
||||
* Copyright 2021 David Fort <contact@hardening-consulting.com>
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef WINPR_APC_H
|
||||
#define WINPR_APC_H
|
||||
|
||||
#include <winpr/winpr.h>
|
||||
#include <winpr/wtypes.h>
|
||||
|
||||
#ifndef _WIN32
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
typedef struct winpr_thread WINPR_THREAD;
|
||||
typedef struct winpr_APC_item WINPR_APC_ITEM;
|
||||
typedef struct winpr_poll_set WINPR_POLL_SET;
|
||||
|
||||
typedef void (*apc_treatment)(LPVOID arg);
|
||||
|
||||
typedef enum
|
||||
{
|
||||
APC_TYPE_USER,
|
||||
APC_TYPE_TIMER,
|
||||
APC_TYPE_HANDLE_FREE
|
||||
} ApcType;
|
||||
|
||||
struct winpr_APC_item
|
||||
{
|
||||
ApcType type;
|
||||
int pollFd;
|
||||
DWORD pollMode;
|
||||
apc_treatment completion;
|
||||
LPVOID completionArgs;
|
||||
BOOL markedForFree;
|
||||
|
||||
/* private fields used by the APC */
|
||||
BOOL alwaysSignaled;
|
||||
BOOL isSignaled;
|
||||
DWORD boundThread;
|
||||
BOOL linked;
|
||||
BOOL markedForRemove;
|
||||
WINPR_APC_ITEM *last, *next;
|
||||
};
|
||||
|
||||
typedef enum
|
||||
{
|
||||
APC_REMOVE_OK,
|
||||
APC_REMOVE_ERROR,
|
||||
APC_REMOVE_DELAY_FREE
|
||||
} APC_REMOVE_RESULT;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
pthread_mutex_t mutex;
|
||||
DWORD length;
|
||||
WINPR_APC_ITEM *head, *tail;
|
||||
BOOL treatingCompletions;
|
||||
} APC_QUEUE;
|
||||
|
||||
BOOL apc_init(APC_QUEUE* apc);
|
||||
BOOL apc_uninit(APC_QUEUE* apc);
|
||||
void apc_register(WINPR_THREAD* thread, WINPR_APC_ITEM* addItem);
|
||||
APC_REMOVE_RESULT apc_remove(WINPR_APC_ITEM* item);
|
||||
BOOL apc_collectFds(WINPR_THREAD* thread, WINPR_POLL_SET* set, BOOL* haveAutoSignaled);
|
||||
int apc_executeCompletions(WINPR_THREAD* thread, WINPR_POLL_SET* set, size_t startIndex);
|
||||
void apc_cleanupThread(WINPR_THREAD* thread);
|
||||
#endif
|
||||
|
||||
#endif /* WINPR_APC_H */
|
||||
285
third_party/FreeRDP/winpr/libwinpr/thread/argv.c
vendored
Normal file
285
third_party/FreeRDP/winpr/libwinpr/thread/argv.c
vendored
Normal file
@@ -0,0 +1,285 @@
|
||||
/**
|
||||
* WinPR: Windows Portable Runtime
|
||||
* Process Argument Vector Functions
|
||||
*
|
||||
* Copyright 2013 Marc-Andre Moreau <marcandre.moreau@gmail.com>
|
||||
*
|
||||
* 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 <winpr/config.h>
|
||||
|
||||
#include <winpr/crt.h>
|
||||
#include <winpr/handle.h>
|
||||
|
||||
#include <winpr/thread.h>
|
||||
|
||||
#ifdef WINPR_HAVE_UNISTD_H
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include "../log.h"
|
||||
#define TAG WINPR_TAG("thread")
|
||||
|
||||
/**
|
||||
* CommandLineToArgvW function:
|
||||
* http://msdn.microsoft.com/en-us/library/windows/desktop/bb776391/
|
||||
*
|
||||
* CommandLineToArgvW has a special interpretation of backslash characters
|
||||
* when they are followed by a quotation mark character ("), as follows:
|
||||
*
|
||||
* 2n backslashes followed by a quotation mark produce n backslashes followed by a quotation mark.
|
||||
* (2n) + 1 backslashes followed by a quotation mark again produce n backslashes followed by a
|
||||
* quotation mark. n backslashes not followed by a quotation mark simply produce n backslashes.
|
||||
*
|
||||
* The address returned by CommandLineToArgvW is the address of the first element in an array of
|
||||
* LPWSTR values; the number of pointers in this array is indicated by pNumArgs. Each pointer to a
|
||||
* null-terminated Unicode string represents an individual argument found on the command line.
|
||||
*
|
||||
* CommandLineToArgvW allocates a block of contiguous memory for pointers to the argument strings,
|
||||
* and for the argument strings themselves; the calling application must free the memory used by the
|
||||
* argument list when it is no longer needed. To free the memory, use a single call to the LocalFree
|
||||
* function.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Parsing C++ Command-Line Arguments:
|
||||
* http://msdn.microsoft.com/en-us/library/windows/desktop/17w5ykft
|
||||
*
|
||||
* Microsoft C/C++ startup code uses the following rules when
|
||||
* interpreting arguments given on the operating system command line:
|
||||
*
|
||||
* Arguments are delimited by white space, which is either a space or a tab.
|
||||
*
|
||||
* The caret character (^) is not recognized as an escape character or delimiter.
|
||||
* The character is handled completely by the command-line parser in the operating
|
||||
* system before being passed to the argv array in the program.
|
||||
*
|
||||
* A string surrounded by double quotation marks ("string") is interpreted as a
|
||||
* single argument, regardless of white space contained within. A quoted string
|
||||
* can be embedded in an argument.
|
||||
*
|
||||
* A double quotation mark preceded by a backslash (\") is interpreted as a
|
||||
* literal double quotation mark character (").
|
||||
*
|
||||
* Backslashes are interpreted literally, unless they immediately
|
||||
* precede a double quotation mark.
|
||||
*
|
||||
* If an even number of backslashes is followed by a double quotation mark,
|
||||
* one backslash is placed in the argv array for every pair of backslashes,
|
||||
* and the double quotation mark is interpreted as a string delimiter.
|
||||
*
|
||||
* If an odd number of backslashes is followed by a double quotation mark,
|
||||
* one backslash is placed in the argv array for every pair of backslashes,
|
||||
* and the double quotation mark is "escaped" by the remaining backslash,
|
||||
* causing a literal double quotation mark (") to be placed in argv.
|
||||
*
|
||||
*/
|
||||
|
||||
LPSTR* CommandLineToArgvA(LPCSTR lpCmdLine, int* pNumArgs)
|
||||
{
|
||||
const char* p = nullptr;
|
||||
size_t length = 0;
|
||||
const char* pBeg = nullptr;
|
||||
const char* pEnd = nullptr;
|
||||
char* buffer = nullptr;
|
||||
char* pOutput = nullptr;
|
||||
int numArgs = 0;
|
||||
LPSTR* pArgs = nullptr;
|
||||
size_t maxNumArgs = 0;
|
||||
size_t maxBufferSize = 0;
|
||||
size_t cmdLineLength = 0;
|
||||
BOOL* lpEscapedChars = nullptr;
|
||||
LPSTR lpEscapedCmdLine = nullptr;
|
||||
|
||||
if (!lpCmdLine)
|
||||
return nullptr;
|
||||
|
||||
if (!pNumArgs)
|
||||
return nullptr;
|
||||
|
||||
pArgs = nullptr;
|
||||
lpEscapedCmdLine = nullptr;
|
||||
cmdLineLength = strlen(lpCmdLine);
|
||||
lpEscapedChars = (BOOL*)calloc(cmdLineLength + 1, sizeof(BOOL));
|
||||
|
||||
if (!lpEscapedChars)
|
||||
return nullptr;
|
||||
|
||||
if (strstr(lpCmdLine, "\\\""))
|
||||
{
|
||||
size_t n = 0;
|
||||
const char* pLastEnd = nullptr;
|
||||
lpEscapedCmdLine = (char*)calloc(cmdLineLength + 1, sizeof(char));
|
||||
|
||||
if (!lpEscapedCmdLine)
|
||||
{
|
||||
free(lpEscapedChars);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
p = (const char*)lpCmdLine;
|
||||
pLastEnd = (const char*)lpCmdLine;
|
||||
pOutput = (char*)lpEscapedCmdLine;
|
||||
|
||||
while (p < &lpCmdLine[cmdLineLength])
|
||||
{
|
||||
pBeg = strstr(p, "\\\"");
|
||||
|
||||
if (!pBeg)
|
||||
{
|
||||
length = strlen(p);
|
||||
CopyMemory(pOutput, p, length);
|
||||
pOutput += length;
|
||||
break;
|
||||
}
|
||||
|
||||
pEnd = pBeg + 2;
|
||||
|
||||
while (pBeg >= lpCmdLine)
|
||||
{
|
||||
if (*pBeg != '\\')
|
||||
{
|
||||
pBeg++;
|
||||
break;
|
||||
}
|
||||
|
||||
pBeg--;
|
||||
}
|
||||
|
||||
n = WINPR_ASSERTING_INT_CAST(size_t, ((pEnd - pBeg) - 1));
|
||||
length = WINPR_ASSERTING_INT_CAST(size_t, (pBeg - pLastEnd));
|
||||
CopyMemory(pOutput, p, length);
|
||||
pOutput += length;
|
||||
p += length;
|
||||
|
||||
for (size_t i = 0; i < (n / 2); i++)
|
||||
*pOutput++ = '\\';
|
||||
|
||||
p += n + 1;
|
||||
|
||||
if ((n % 2) != 0)
|
||||
lpEscapedChars[pOutput - lpEscapedCmdLine] = TRUE;
|
||||
|
||||
*pOutput++ = '"';
|
||||
pLastEnd = p;
|
||||
}
|
||||
|
||||
*pOutput++ = '\0';
|
||||
lpCmdLine = (LPCSTR)lpEscapedCmdLine;
|
||||
cmdLineLength = strlen(lpCmdLine);
|
||||
}
|
||||
|
||||
maxNumArgs = 2;
|
||||
p = (const char*)lpCmdLine;
|
||||
|
||||
while (p < lpCmdLine + cmdLineLength)
|
||||
{
|
||||
p += strcspn(p, " \t");
|
||||
p += strspn(p, " \t");
|
||||
maxNumArgs++;
|
||||
}
|
||||
|
||||
maxBufferSize = (maxNumArgs * (sizeof(char*))) + (cmdLineLength + 1);
|
||||
buffer = calloc(maxBufferSize, sizeof(char));
|
||||
|
||||
if (!buffer)
|
||||
{
|
||||
free(lpEscapedCmdLine);
|
||||
free(lpEscapedChars);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
pArgs = (LPSTR*)buffer;
|
||||
pOutput = &buffer[maxNumArgs * (sizeof(char*))];
|
||||
p = (const char*)lpCmdLine;
|
||||
|
||||
while (p < lpCmdLine + cmdLineLength)
|
||||
{
|
||||
pBeg = p;
|
||||
|
||||
while (1)
|
||||
{
|
||||
p += strcspn(p, " \t\"\0");
|
||||
|
||||
if ((*p != '"') || !lpEscapedChars[p - lpCmdLine])
|
||||
break;
|
||||
|
||||
p++;
|
||||
}
|
||||
|
||||
if (*p != '"')
|
||||
{
|
||||
/* no whitespace escaped with double quotes */
|
||||
length = WINPR_ASSERTING_INT_CAST(size_t, (p - pBeg));
|
||||
CopyMemory(pOutput, pBeg, length);
|
||||
pOutput[length] = '\0';
|
||||
pArgs[numArgs++] = pOutput;
|
||||
pOutput += (length + 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
p++;
|
||||
|
||||
while (1)
|
||||
{
|
||||
p += strcspn(p, "\"\0");
|
||||
|
||||
if ((*p != '"') || !lpEscapedChars[p - lpCmdLine])
|
||||
break;
|
||||
|
||||
p++;
|
||||
}
|
||||
|
||||
if (*p != '"')
|
||||
WLog_ERR(TAG, "parsing error: uneven number of unescaped double quotes!");
|
||||
|
||||
if (*p)
|
||||
{
|
||||
p++;
|
||||
|
||||
if (*p)
|
||||
p += strcspn(p, " \t\0");
|
||||
}
|
||||
|
||||
pArgs[numArgs++] = pOutput;
|
||||
|
||||
while (pBeg < p)
|
||||
{
|
||||
if (*pBeg != '"')
|
||||
*pOutput++ = *pBeg;
|
||||
|
||||
pBeg++;
|
||||
}
|
||||
|
||||
*pOutput++ = '\0';
|
||||
}
|
||||
|
||||
p += strspn(p, " \t");
|
||||
}
|
||||
|
||||
free(lpEscapedCmdLine);
|
||||
free(lpEscapedChars);
|
||||
*pNumArgs = numArgs;
|
||||
return pArgs;
|
||||
}
|
||||
|
||||
#ifndef _WIN32
|
||||
|
||||
LPWSTR* CommandLineToArgvW(WINPR_ATTR_UNUSED LPCWSTR lpCmdLine, WINPR_ATTR_UNUSED int* pNumArgs)
|
||||
{
|
||||
WLog_ERR("TODO", "TODO: Implement");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
#endif
|
||||
634
third_party/FreeRDP/winpr/libwinpr/thread/process.c
vendored
Normal file
634
third_party/FreeRDP/winpr/libwinpr/thread/process.c
vendored
Normal file
@@ -0,0 +1,634 @@
|
||||
/**
|
||||
* WinPR: Windows Portable Runtime
|
||||
* Process Thread Functions
|
||||
*
|
||||
* Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
|
||||
* Copyright 2014 DI (FH) Martin Haimberger <martin.haimberger@thincast.com>
|
||||
*
|
||||
* 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 <winpr/config.h>
|
||||
|
||||
#include <winpr/handle.h>
|
||||
#include "../handle/nonehandle.h"
|
||||
|
||||
#include <winpr/thread.h>
|
||||
#include <winpr/wlog.h>
|
||||
|
||||
/**
|
||||
* CreateProcessA
|
||||
* CreateProcessW
|
||||
* CreateProcessAsUserA
|
||||
* CreateProcessAsUserW
|
||||
* ExitProcess
|
||||
* GetCurrentProcess
|
||||
* GetCurrentProcessId
|
||||
* GetExitCodeProcess
|
||||
* GetProcessHandleCount
|
||||
* GetProcessId
|
||||
* GetProcessIdOfThread
|
||||
* GetProcessMitigationPolicy
|
||||
* GetProcessTimes
|
||||
* GetProcessVersion
|
||||
* OpenProcess
|
||||
* OpenProcessToken
|
||||
* ProcessIdToSessionId
|
||||
* SetProcessAffinityUpdateMode
|
||||
* SetProcessMitigationPolicy
|
||||
* SetProcessShutdownParameters
|
||||
* TerminateProcess
|
||||
*/
|
||||
|
||||
#ifndef _WIN32
|
||||
|
||||
#include <winpr/assert.h>
|
||||
#include <winpr/crt.h>
|
||||
#include <winpr/path.h>
|
||||
#include <winpr/environment.h>
|
||||
|
||||
#include <grp.h>
|
||||
|
||||
#include <signal.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
#ifdef __linux__
|
||||
#include <sys/syscall.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#endif /* __linux__ */
|
||||
|
||||
#include "thread.h"
|
||||
|
||||
#include "../security/security.h"
|
||||
|
||||
#ifndef NSIG
|
||||
#ifdef SIGMAX
|
||||
#define NSIG SIGMAX
|
||||
#else
|
||||
#define NSIG 64
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/**
|
||||
* If the file name does not contain a directory path, the system searches for the executable file
|
||||
* in the following sequence:
|
||||
*
|
||||
* 1) The directory from which the application loaded.
|
||||
* 2) The current directory for the parent process.
|
||||
* 3) The 32-bit Windows system directory. Use the GetSystemDirectory function to get the path of
|
||||
* this directory. 4) The 16-bit Windows system directory. There is no function that obtains the
|
||||
* path of this directory, but it is searched. The name of this directory is System. 5) The Windows
|
||||
* directory. Use the GetWindowsDirectory function to get the path of this directory. 6) The
|
||||
* directories that are listed in the PATH environment variable. Note that this function does not
|
||||
* search the per-application path specified by the App Paths registry key. To include this
|
||||
* per-application path in the search sequence, use the ShellExecute function.
|
||||
*/
|
||||
|
||||
static char* FindApplicationPath(char* application)
|
||||
{
|
||||
LPCSTR pathName = "PATH";
|
||||
char* path = nullptr;
|
||||
char* save = nullptr;
|
||||
DWORD nSize = 0;
|
||||
LPSTR lpSystemPath = nullptr;
|
||||
char* filename = nullptr;
|
||||
|
||||
if (!application)
|
||||
return nullptr;
|
||||
|
||||
if (application[0] == '/')
|
||||
return _strdup(application);
|
||||
|
||||
nSize = GetEnvironmentVariableA(pathName, nullptr, 0);
|
||||
|
||||
if (!nSize)
|
||||
return _strdup(application);
|
||||
|
||||
lpSystemPath = (LPSTR)malloc(nSize);
|
||||
|
||||
if (!lpSystemPath)
|
||||
return nullptr;
|
||||
|
||||
if (GetEnvironmentVariableA(pathName, lpSystemPath, nSize) != nSize - 1)
|
||||
{
|
||||
free(lpSystemPath);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
save = nullptr;
|
||||
path = strtok_s(lpSystemPath, ":", &save);
|
||||
|
||||
while (path)
|
||||
{
|
||||
filename = GetCombinedPath(path, application);
|
||||
|
||||
if (winpr_PathFileExists(filename))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
free(filename);
|
||||
filename = nullptr;
|
||||
path = strtok_s(nullptr, ":", &save);
|
||||
}
|
||||
|
||||
free(lpSystemPath);
|
||||
return filename;
|
||||
}
|
||||
|
||||
static HANDLE CreateProcessHandle(pid_t pid);
|
||||
static BOOL ProcessHandleCloseHandle(HANDLE handle);
|
||||
|
||||
static BOOL CreateProcessExA(HANDLE hToken, WINPR_ATTR_UNUSED DWORD dwLogonFlags,
|
||||
LPCSTR lpApplicationName, WINPR_ATTR_UNUSED LPSTR lpCommandLine,
|
||||
WINPR_ATTR_UNUSED LPSECURITY_ATTRIBUTES lpProcessAttributes,
|
||||
WINPR_ATTR_UNUSED LPSECURITY_ATTRIBUTES lpThreadAttributes,
|
||||
WINPR_ATTR_UNUSED BOOL bInheritHandles,
|
||||
WINPR_ATTR_UNUSED DWORD dwCreationFlags, LPVOID lpEnvironment,
|
||||
LPCSTR lpCurrentDirectory, LPSTARTUPINFOA lpStartupInfo,
|
||||
LPPROCESS_INFORMATION lpProcessInformation)
|
||||
{
|
||||
pid_t pid = 0;
|
||||
int numArgs = 0;
|
||||
LPSTR* pArgs = nullptr;
|
||||
char** envp = nullptr;
|
||||
char* filename = nullptr;
|
||||
HANDLE thread = nullptr;
|
||||
HANDLE process = nullptr;
|
||||
WINPR_ACCESS_TOKEN* token = nullptr;
|
||||
LPTCH lpszEnvironmentBlock = nullptr;
|
||||
BOOL ret = FALSE;
|
||||
sigset_t oldSigMask;
|
||||
sigset_t newSigMask;
|
||||
BOOL restoreSigMask = FALSE;
|
||||
numArgs = 0;
|
||||
lpszEnvironmentBlock = nullptr;
|
||||
/* https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createprocessa
|
||||
*/
|
||||
if (lpCommandLine)
|
||||
pArgs = CommandLineToArgvA(lpCommandLine, &numArgs);
|
||||
else
|
||||
pArgs = CommandLineToArgvA(lpApplicationName, &numArgs);
|
||||
|
||||
if (!pArgs)
|
||||
return FALSE;
|
||||
|
||||
token = (WINPR_ACCESS_TOKEN*)hToken;
|
||||
|
||||
if (lpEnvironment)
|
||||
{
|
||||
envp = EnvironmentBlockToEnvpA(lpEnvironment);
|
||||
}
|
||||
else
|
||||
{
|
||||
lpszEnvironmentBlock = GetEnvironmentStrings();
|
||||
|
||||
if (!lpszEnvironmentBlock)
|
||||
goto finish;
|
||||
|
||||
envp = EnvironmentBlockToEnvpA(lpszEnvironmentBlock);
|
||||
}
|
||||
|
||||
if (!envp)
|
||||
goto finish;
|
||||
|
||||
filename = FindApplicationPath(pArgs[0]);
|
||||
|
||||
if (nullptr == filename)
|
||||
goto finish;
|
||||
|
||||
/* block all signals so that the child can safely reset the caller's handlers */
|
||||
sigfillset(&newSigMask);
|
||||
restoreSigMask = !pthread_sigmask(SIG_SETMASK, &newSigMask, &oldSigMask);
|
||||
/* fork and exec */
|
||||
pid = fork();
|
||||
|
||||
if (pid < 0)
|
||||
{
|
||||
/* fork failure */
|
||||
goto finish;
|
||||
}
|
||||
|
||||
if (pid == 0)
|
||||
{
|
||||
/* child process */
|
||||
#ifndef __sun
|
||||
int maxfd = 0;
|
||||
#endif
|
||||
sigset_t set = WINPR_C_ARRAY_INIT;
|
||||
struct sigaction act = WINPR_C_ARRAY_INIT;
|
||||
/* set default signal handlers */
|
||||
act.sa_handler = SIG_DFL;
|
||||
act.sa_flags = 0;
|
||||
sigemptyset(&act.sa_mask);
|
||||
|
||||
for (int sig = 1; sig < NSIG; sig++)
|
||||
sigaction(sig, &act, nullptr);
|
||||
|
||||
/* unblock all signals */
|
||||
sigfillset(&set);
|
||||
pthread_sigmask(SIG_UNBLOCK, &set, nullptr);
|
||||
|
||||
if (lpStartupInfo)
|
||||
{
|
||||
int handle_fd = 0;
|
||||
handle_fd = winpr_Handle_getFd(lpStartupInfo->hStdOutput);
|
||||
|
||||
if (handle_fd != -1)
|
||||
dup2(handle_fd, STDOUT_FILENO);
|
||||
|
||||
handle_fd = winpr_Handle_getFd(lpStartupInfo->hStdError);
|
||||
|
||||
if (handle_fd != -1)
|
||||
dup2(handle_fd, STDERR_FILENO);
|
||||
|
||||
handle_fd = winpr_Handle_getFd(lpStartupInfo->hStdInput);
|
||||
|
||||
if (handle_fd != -1)
|
||||
dup2(handle_fd, STDIN_FILENO);
|
||||
}
|
||||
|
||||
#ifdef __sun
|
||||
closefrom(3);
|
||||
#else
|
||||
#ifdef F_MAXFD // on some BSD derivates
|
||||
maxfd = fcntl(0, F_MAXFD);
|
||||
#else
|
||||
{
|
||||
const long rc = sysconf(_SC_OPEN_MAX);
|
||||
if ((rc < INT32_MIN) || (rc > INT32_MAX))
|
||||
goto finish;
|
||||
maxfd = (int)rc;
|
||||
}
|
||||
#endif
|
||||
|
||||
for (int fd = 3; fd < maxfd; fd++)
|
||||
close(fd);
|
||||
|
||||
#endif // __sun
|
||||
|
||||
if (token)
|
||||
{
|
||||
if (token->GroupId)
|
||||
{
|
||||
int rc = setgid((gid_t)token->GroupId);
|
||||
|
||||
if (rc < 0)
|
||||
{
|
||||
}
|
||||
else
|
||||
{
|
||||
initgroups(token->Username, (gid_t)token->GroupId);
|
||||
}
|
||||
}
|
||||
|
||||
if (token->UserId)
|
||||
{
|
||||
int rc = setuid((uid_t)token->UserId);
|
||||
if (rc != 0)
|
||||
goto finish;
|
||||
}
|
||||
}
|
||||
|
||||
/* TODO: add better cwd handling and error checking */
|
||||
if (lpCurrentDirectory && strlen(lpCurrentDirectory) > 0)
|
||||
{
|
||||
int rc = chdir(lpCurrentDirectory);
|
||||
if (rc != 0)
|
||||
goto finish;
|
||||
}
|
||||
|
||||
if (execve(filename, pArgs, envp) < 0)
|
||||
{
|
||||
/* execve failed - end the process */
|
||||
_exit(1);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* parent process */
|
||||
}
|
||||
|
||||
process = CreateProcessHandle(pid);
|
||||
|
||||
if (!process)
|
||||
{
|
||||
goto finish;
|
||||
}
|
||||
|
||||
thread = CreateNoneHandle();
|
||||
|
||||
if (!thread)
|
||||
{
|
||||
ProcessHandleCloseHandle(process);
|
||||
goto finish;
|
||||
}
|
||||
|
||||
lpProcessInformation->hProcess = process;
|
||||
lpProcessInformation->hThread = thread;
|
||||
lpProcessInformation->dwProcessId = (DWORD)pid;
|
||||
lpProcessInformation->dwThreadId = (DWORD)pid;
|
||||
ret = TRUE;
|
||||
finish:
|
||||
|
||||
/* restore caller's original signal mask */
|
||||
if (restoreSigMask)
|
||||
pthread_sigmask(SIG_SETMASK, &oldSigMask, nullptr);
|
||||
|
||||
free(filename);
|
||||
free((void*)pArgs);
|
||||
|
||||
if (lpszEnvironmentBlock)
|
||||
FreeEnvironmentStrings(lpszEnvironmentBlock);
|
||||
|
||||
if (envp)
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
while (envp[i])
|
||||
{
|
||||
free(envp[i]);
|
||||
i++;
|
||||
}
|
||||
|
||||
free((void*)envp);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
BOOL CreateProcessA(LPCSTR lpApplicationName, LPSTR lpCommandLine,
|
||||
LPSECURITY_ATTRIBUTES lpProcessAttributes,
|
||||
LPSECURITY_ATTRIBUTES lpThreadAttributes, BOOL bInheritHandles,
|
||||
DWORD dwCreationFlags, LPVOID lpEnvironment, LPCSTR lpCurrentDirectory,
|
||||
LPSTARTUPINFOA lpStartupInfo, LPPROCESS_INFORMATION lpProcessInformation)
|
||||
{
|
||||
return CreateProcessExA(nullptr, 0, lpApplicationName, lpCommandLine, lpProcessAttributes,
|
||||
lpThreadAttributes, bInheritHandles, dwCreationFlags, lpEnvironment,
|
||||
lpCurrentDirectory, lpStartupInfo, lpProcessInformation);
|
||||
}
|
||||
|
||||
BOOL CreateProcessW(WINPR_ATTR_UNUSED LPCWSTR lpApplicationName,
|
||||
WINPR_ATTR_UNUSED LPWSTR lpCommandLine,
|
||||
WINPR_ATTR_UNUSED LPSECURITY_ATTRIBUTES lpProcessAttributes,
|
||||
WINPR_ATTR_UNUSED LPSECURITY_ATTRIBUTES lpThreadAttributes,
|
||||
WINPR_ATTR_UNUSED BOOL bInheritHandles, WINPR_ATTR_UNUSED DWORD dwCreationFlags,
|
||||
WINPR_ATTR_UNUSED LPVOID lpEnvironment,
|
||||
WINPR_ATTR_UNUSED LPCWSTR lpCurrentDirectory,
|
||||
WINPR_ATTR_UNUSED LPSTARTUPINFOW lpStartupInfo,
|
||||
WINPR_ATTR_UNUSED LPPROCESS_INFORMATION lpProcessInformation)
|
||||
{
|
||||
WLog_ERR("TODO", "TODO: implement");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
BOOL CreateProcessAsUserA(HANDLE hToken, LPCSTR lpApplicationName, LPSTR lpCommandLine,
|
||||
LPSECURITY_ATTRIBUTES lpProcessAttributes,
|
||||
LPSECURITY_ATTRIBUTES lpThreadAttributes, BOOL bInheritHandles,
|
||||
DWORD dwCreationFlags, LPVOID lpEnvironment, LPCSTR lpCurrentDirectory,
|
||||
LPSTARTUPINFOA lpStartupInfo, LPPROCESS_INFORMATION lpProcessInformation)
|
||||
{
|
||||
return CreateProcessExA(hToken, 0, lpApplicationName, lpCommandLine, lpProcessAttributes,
|
||||
lpThreadAttributes, bInheritHandles, dwCreationFlags, lpEnvironment,
|
||||
lpCurrentDirectory, lpStartupInfo, lpProcessInformation);
|
||||
}
|
||||
|
||||
BOOL CreateProcessAsUserW(WINPR_ATTR_UNUSED HANDLE hToken,
|
||||
WINPR_ATTR_UNUSED LPCWSTR lpApplicationName,
|
||||
WINPR_ATTR_UNUSED LPWSTR lpCommandLine,
|
||||
WINPR_ATTR_UNUSED LPSECURITY_ATTRIBUTES lpProcessAttributes,
|
||||
WINPR_ATTR_UNUSED LPSECURITY_ATTRIBUTES lpThreadAttributes,
|
||||
WINPR_ATTR_UNUSED BOOL bInheritHandles,
|
||||
WINPR_ATTR_UNUSED DWORD dwCreationFlags,
|
||||
WINPR_ATTR_UNUSED LPVOID lpEnvironment,
|
||||
WINPR_ATTR_UNUSED LPCWSTR lpCurrentDirectory,
|
||||
WINPR_ATTR_UNUSED LPSTARTUPINFOW lpStartupInfo,
|
||||
WINPR_ATTR_UNUSED LPPROCESS_INFORMATION lpProcessInformation)
|
||||
{
|
||||
WLog_ERR("TODO", "TODO: implement");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
BOOL CreateProcessWithLogonA(
|
||||
WINPR_ATTR_UNUSED LPCSTR lpUsername, WINPR_ATTR_UNUSED LPCSTR lpDomain,
|
||||
WINPR_ATTR_UNUSED LPCSTR lpPassword, WINPR_ATTR_UNUSED DWORD dwLogonFlags,
|
||||
WINPR_ATTR_UNUSED LPCSTR lpApplicationName, WINPR_ATTR_UNUSED LPSTR lpCommandLine,
|
||||
WINPR_ATTR_UNUSED DWORD dwCreationFlags, WINPR_ATTR_UNUSED LPVOID lpEnvironment,
|
||||
WINPR_ATTR_UNUSED LPCSTR lpCurrentDirectory, WINPR_ATTR_UNUSED LPSTARTUPINFOA lpStartupInfo,
|
||||
WINPR_ATTR_UNUSED LPPROCESS_INFORMATION lpProcessInformation)
|
||||
{
|
||||
WLog_ERR("TODO", "TODO: implement");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
BOOL CreateProcessWithLogonW(
|
||||
WINPR_ATTR_UNUSED LPCWSTR lpUsername, WINPR_ATTR_UNUSED LPCWSTR lpDomain,
|
||||
WINPR_ATTR_UNUSED LPCWSTR lpPassword, WINPR_ATTR_UNUSED DWORD dwLogonFlags,
|
||||
WINPR_ATTR_UNUSED LPCWSTR lpApplicationName, WINPR_ATTR_UNUSED LPWSTR lpCommandLine,
|
||||
WINPR_ATTR_UNUSED DWORD dwCreationFlags, WINPR_ATTR_UNUSED LPVOID lpEnvironment,
|
||||
WINPR_ATTR_UNUSED LPCWSTR lpCurrentDirectory, WINPR_ATTR_UNUSED LPSTARTUPINFOW lpStartupInfo,
|
||||
WINPR_ATTR_UNUSED LPPROCESS_INFORMATION lpProcessInformation)
|
||||
{
|
||||
WLog_ERR("TODO", "TODO: implement");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
BOOL CreateProcessWithTokenA(WINPR_ATTR_UNUSED HANDLE hToken, WINPR_ATTR_UNUSED DWORD dwLogonFlags,
|
||||
LPCSTR lpApplicationName, LPSTR lpCommandLine, DWORD dwCreationFlags,
|
||||
LPVOID lpEnvironment, LPCSTR lpCurrentDirectory,
|
||||
LPSTARTUPINFOA lpStartupInfo,
|
||||
LPPROCESS_INFORMATION lpProcessInformation)
|
||||
{
|
||||
return CreateProcessExA(nullptr, 0, lpApplicationName, lpCommandLine, nullptr, nullptr, FALSE,
|
||||
dwCreationFlags, lpEnvironment, lpCurrentDirectory, lpStartupInfo,
|
||||
lpProcessInformation);
|
||||
}
|
||||
|
||||
BOOL CreateProcessWithTokenW(WINPR_ATTR_UNUSED HANDLE hToken, WINPR_ATTR_UNUSED DWORD dwLogonFlags,
|
||||
WINPR_ATTR_UNUSED LPCWSTR lpApplicationName,
|
||||
WINPR_ATTR_UNUSED LPWSTR lpCommandLine,
|
||||
WINPR_ATTR_UNUSED DWORD dwCreationFlags,
|
||||
WINPR_ATTR_UNUSED LPVOID lpEnvironment,
|
||||
WINPR_ATTR_UNUSED LPCWSTR lpCurrentDirectory,
|
||||
WINPR_ATTR_UNUSED LPSTARTUPINFOW lpStartupInfo,
|
||||
WINPR_ATTR_UNUSED LPPROCESS_INFORMATION lpProcessInformation)
|
||||
{
|
||||
WLog_ERR("TODO", "TODO: implement");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
VOID ExitProcess(UINT uExitCode)
|
||||
{
|
||||
// NOLINTNEXTLINE(concurrency-mt-unsafe)
|
||||
exit((int)uExitCode);
|
||||
}
|
||||
|
||||
BOOL GetExitCodeProcess(HANDLE hProcess, LPDWORD lpExitCode)
|
||||
{
|
||||
WINPR_PROCESS* process = nullptr;
|
||||
|
||||
if (!hProcess)
|
||||
return FALSE;
|
||||
|
||||
if (!lpExitCode)
|
||||
return FALSE;
|
||||
|
||||
process = (WINPR_PROCESS*)hProcess;
|
||||
*lpExitCode = process->dwExitCode;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
HANDLE _GetCurrentProcess(VOID)
|
||||
{
|
||||
WLog_ERR("TODO", "TODO: implement");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
DWORD GetCurrentProcessId(VOID)
|
||||
{
|
||||
return ((DWORD)getpid());
|
||||
}
|
||||
|
||||
BOOL TerminateProcess(HANDLE hProcess, WINPR_ATTR_UNUSED UINT uExitCode)
|
||||
{
|
||||
WINPR_PROCESS* process = nullptr;
|
||||
process = (WINPR_PROCESS*)hProcess;
|
||||
|
||||
if (!process || (process->pid <= 0))
|
||||
return FALSE;
|
||||
|
||||
if (kill(process->pid, SIGTERM))
|
||||
return FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static BOOL ProcessHandleCloseHandle(HANDLE handle)
|
||||
{
|
||||
WINPR_PROCESS* process = (WINPR_PROCESS*)handle;
|
||||
WINPR_ASSERT(process);
|
||||
if (process->fd >= 0)
|
||||
{
|
||||
close(process->fd);
|
||||
process->fd = -1;
|
||||
}
|
||||
free(process);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static BOOL ProcessHandleIsHandle(HANDLE handle)
|
||||
{
|
||||
return WINPR_HANDLE_IS_HANDLED(handle, HANDLE_TYPE_PROCESS, FALSE);
|
||||
}
|
||||
|
||||
static int ProcessGetFd(HANDLE handle)
|
||||
{
|
||||
WINPR_PROCESS* process = (WINPR_PROCESS*)handle;
|
||||
|
||||
if (!ProcessHandleIsHandle(handle))
|
||||
return -1;
|
||||
|
||||
return process->fd;
|
||||
}
|
||||
|
||||
static DWORD ProcessCleanupHandle(HANDLE handle)
|
||||
{
|
||||
WINPR_PROCESS* process = (WINPR_PROCESS*)handle;
|
||||
|
||||
WINPR_ASSERT(process);
|
||||
if (process->fd > 0)
|
||||
{
|
||||
if (waitpid(process->pid, &process->status, WNOHANG) == process->pid)
|
||||
process->dwExitCode = (DWORD)process->status;
|
||||
}
|
||||
return WAIT_OBJECT_0;
|
||||
}
|
||||
|
||||
static HANDLE_OPS ops = { ProcessHandleIsHandle,
|
||||
ProcessHandleCloseHandle,
|
||||
ProcessGetFd,
|
||||
ProcessCleanupHandle, /* CleanupHandle */
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr };
|
||||
|
||||
static int pidfd_open(pid_t pid)
|
||||
{
|
||||
#ifdef __linux__
|
||||
#if !defined(__NR_pidfd_open)
|
||||
#define __NR_pidfd_open 434
|
||||
#endif /* __NR_pidfd_open */
|
||||
|
||||
#ifndef PIDFD_NONBLOCK
|
||||
#define PIDFD_NONBLOCK O_NONBLOCK
|
||||
#endif /* PIDFD_NONBLOCK */
|
||||
|
||||
long fd = syscall(__NR_pidfd_open, pid, PIDFD_NONBLOCK);
|
||||
if (fd < 0 && errno == EINVAL)
|
||||
{
|
||||
/* possibly PIDFD_NONBLOCK is not supported, let's try to create a pidfd and set it
|
||||
* non blocking afterward */
|
||||
int flags = 0;
|
||||
fd = syscall(__NR_pidfd_open, pid, 0);
|
||||
if ((fd < 0) || (fd > INT32_MAX))
|
||||
return -1;
|
||||
|
||||
flags = fcntl((int)fd, F_GETFL);
|
||||
if ((flags < 0) || fcntl((int)fd, F_SETFL, flags | O_NONBLOCK) < 0)
|
||||
{
|
||||
close((int)fd);
|
||||
fd = -1;
|
||||
}
|
||||
}
|
||||
if ((fd < 0) || (fd > INT32_MAX))
|
||||
return -1;
|
||||
return (int)fd;
|
||||
#else
|
||||
return -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
HANDLE CreateProcessHandle(pid_t pid)
|
||||
{
|
||||
WINPR_PROCESS* process = nullptr;
|
||||
process = (WINPR_PROCESS*)calloc(1, sizeof(WINPR_PROCESS));
|
||||
|
||||
if (!process)
|
||||
return nullptr;
|
||||
|
||||
process->pid = pid;
|
||||
process->common.Type = HANDLE_TYPE_PROCESS;
|
||||
process->common.ops = &ops;
|
||||
process->fd = pidfd_open(pid);
|
||||
if (process->fd >= 0)
|
||||
process->common.Mode = WINPR_FD_READ;
|
||||
return (HANDLE)process;
|
||||
}
|
||||
|
||||
#endif
|
||||
41
third_party/FreeRDP/winpr/libwinpr/thread/processor.c
vendored
Normal file
41
third_party/FreeRDP/winpr/libwinpr/thread/processor.c
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
/**
|
||||
* WinPR: Windows Portable Runtime
|
||||
* Process Thread Functions
|
||||
*
|
||||
* Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
|
||||
*
|
||||
* 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 <winpr/config.h>
|
||||
|
||||
#include <winpr/handle.h>
|
||||
|
||||
#include <winpr/thread.h>
|
||||
|
||||
/**
|
||||
* GetCurrentProcessorNumber
|
||||
* GetCurrentProcessorNumberEx
|
||||
* GetThreadIdealProcessorEx
|
||||
* SetThreadIdealProcessorEx
|
||||
* IsProcessorFeaturePresent
|
||||
*/
|
||||
|
||||
#ifndef _WIN32
|
||||
|
||||
DWORD GetCurrentProcessorNumber(VOID)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
23
third_party/FreeRDP/winpr/libwinpr/thread/test/CMakeLists.txt
vendored
Normal file
23
third_party/FreeRDP/winpr/libwinpr/thread/test/CMakeLists.txt
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
set(MODULE_NAME "TestThread")
|
||||
set(MODULE_PREFIX "TEST_THREAD")
|
||||
|
||||
disable_warnings_for_directory(${CMAKE_CURRENT_BINARY_DIR})
|
||||
|
||||
set(${MODULE_PREFIX}_DRIVER ${MODULE_NAME}.c)
|
||||
|
||||
set(${MODULE_PREFIX}_TESTS TestThreadCommandLineToArgv.c TestThreadCreateProcess.c TestThreadExitThread.c)
|
||||
|
||||
create_test_sourcelist(${MODULE_PREFIX}_SRCS ${${MODULE_PREFIX}_DRIVER} ${${MODULE_PREFIX}_TESTS})
|
||||
|
||||
add_executable(${MODULE_NAME} ${${MODULE_PREFIX}_SRCS})
|
||||
|
||||
target_link_libraries(${MODULE_NAME} winpr)
|
||||
|
||||
set_target_properties(${MODULE_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${TESTING_OUTPUT_DIRECTORY}")
|
||||
|
||||
foreach(test ${${MODULE_PREFIX}_TESTS})
|
||||
get_filename_component(TestName ${test} NAME_WE)
|
||||
add_test(${TestName} ${TESTING_OUTPUT_DIRECTORY}/${MODULE_NAME} ${TestName})
|
||||
endforeach()
|
||||
|
||||
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "WinPR/Test")
|
||||
116
third_party/FreeRDP/winpr/libwinpr/thread/test/TestThreadCommandLineToArgv.c
vendored
Normal file
116
third_party/FreeRDP/winpr/libwinpr/thread/test/TestThreadCommandLineToArgv.c
vendored
Normal file
@@ -0,0 +1,116 @@
|
||||
|
||||
#include <stdio.h>
|
||||
#include <winpr/crt.h>
|
||||
#include <winpr/thread.h>
|
||||
|
||||
static const char* test_args_line_1 = "app.exe abc d e";
|
||||
|
||||
static const char* test_args_list_1[] = { "app.exe", "abc", "d", "e" };
|
||||
|
||||
static const char* test_args_line_2 = "app.exe abc \t def";
|
||||
|
||||
static const char* test_args_list_2[] = { "app.exe", "abc", "def" };
|
||||
|
||||
static const char* test_args_line_3 = "app.exe \"abc\" d e";
|
||||
|
||||
static const char* test_args_list_3[] = { "app.exe", "abc", "d", "e" };
|
||||
|
||||
static const char* test_args_line_4 = "app.exe a\\\\b d\"e f\"g h";
|
||||
|
||||
static const char* test_args_list_4[] = { "app.exe", "a\\\\b", "de fg", "h" };
|
||||
|
||||
static const char* test_args_line_5 = "app.exe a\\\\\\\"b c d";
|
||||
|
||||
static const char* test_args_list_5[] = { "app.exe", "a\\\"b", "c", "d" };
|
||||
|
||||
static const char* test_args_line_6 = "app.exe a\\\\\\\\\"b c\" d e";
|
||||
|
||||
static const char* test_args_list_6[] = { "app.exe", "a\\\\b c", "d", "e" };
|
||||
|
||||
static const char* test_args_line_7 = "app.exe a\\\\\\\\\"b c\" d e f\\\\\\\\\"g h\" i j";
|
||||
|
||||
static const char* test_args_list_7[] = { "app.exe", "a\\\\b c", "d", "e", "f\\\\g h", "i", "j" };
|
||||
|
||||
static const char* test_args_line_8 = "app.exe arg1 \"arg2\"";
|
||||
|
||||
static const char* test_args_list_8[] = { "app.exe", "arg1", "arg2" };
|
||||
|
||||
static BOOL test_command_line_parsing_case(const char* line, const char** list, size_t expect)
|
||||
{
|
||||
BOOL rc = FALSE;
|
||||
int numArgs = 0;
|
||||
|
||||
printf("Parsing: %s\n", line);
|
||||
|
||||
LPSTR* pArgs = CommandLineToArgvA(line, &numArgs);
|
||||
if (numArgs < 0)
|
||||
{
|
||||
(void)fprintf(stderr, "expected %" PRIuz " arguments, got %d return\n", expect, numArgs);
|
||||
goto fail;
|
||||
}
|
||||
if (numArgs != expect)
|
||||
{
|
||||
(void)fprintf(stderr, "expected %" PRIuz " arguments, got %d from '%s'\n", expect, numArgs,
|
||||
line);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if ((numArgs > 0) && !pArgs)
|
||||
{
|
||||
(void)fprintf(stderr, "expected %d arguments, got nullptr return\n", numArgs);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
printf("pNumArgs: %d\n", numArgs);
|
||||
|
||||
for (int i = 0; i < numArgs; i++)
|
||||
{
|
||||
printf("argv[%d] = %s\n", i, pArgs[i]);
|
||||
if (strcmp(pArgs[i], list[i]) != 0)
|
||||
{
|
||||
(void)fprintf(stderr, "failed at argument %d: got '%s' but expected '%s'\n", i,
|
||||
pArgs[i], list[i]);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
rc = TRUE;
|
||||
fail:
|
||||
free((void*)pArgs);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int TestThreadCommandLineToArgv(int argc, char* argv[])
|
||||
{
|
||||
|
||||
WINPR_UNUSED(argc);
|
||||
WINPR_UNUSED(argv);
|
||||
|
||||
if (!test_command_line_parsing_case(test_args_line_1, test_args_list_1,
|
||||
ARRAYSIZE(test_args_list_1)))
|
||||
return -1;
|
||||
if (!test_command_line_parsing_case(test_args_line_2, test_args_list_2,
|
||||
ARRAYSIZE(test_args_list_2)))
|
||||
return -1;
|
||||
if (!test_command_line_parsing_case(test_args_line_3, test_args_list_3,
|
||||
ARRAYSIZE(test_args_list_3)))
|
||||
return -1;
|
||||
if (!test_command_line_parsing_case(test_args_line_4, test_args_list_4,
|
||||
ARRAYSIZE(test_args_list_4)))
|
||||
return -1;
|
||||
if (!test_command_line_parsing_case(test_args_line_5, test_args_list_5,
|
||||
ARRAYSIZE(test_args_list_5)))
|
||||
return -1;
|
||||
if (!test_command_line_parsing_case(test_args_line_6, test_args_list_6,
|
||||
ARRAYSIZE(test_args_list_6)))
|
||||
return -1;
|
||||
if (!test_command_line_parsing_case(test_args_line_7, test_args_list_7,
|
||||
ARRAYSIZE(test_args_list_7)))
|
||||
return -1;
|
||||
if (!test_command_line_parsing_case(test_args_line_8, test_args_list_8,
|
||||
ARRAYSIZE(test_args_list_8)))
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
156
third_party/FreeRDP/winpr/libwinpr/thread/test/TestThreadCreateProcess.c
vendored
Normal file
156
third_party/FreeRDP/winpr/libwinpr/thread/test/TestThreadCreateProcess.c
vendored
Normal file
@@ -0,0 +1,156 @@
|
||||
|
||||
#include <stdio.h>
|
||||
#include <winpr/crt.h>
|
||||
#include <winpr/tchar.h>
|
||||
#include <winpr/synch.h>
|
||||
#include <winpr/thread.h>
|
||||
#include <winpr/environment.h>
|
||||
#include <winpr/pipe.h>
|
||||
|
||||
#define TESTENV_A "HELLO=WORLD"
|
||||
#define TESTENV_T _T(TESTENV_A)
|
||||
|
||||
int TestThreadCreateProcess(int argc, char* argv[])
|
||||
{
|
||||
BOOL status = 0;
|
||||
DWORD exitCode = 0;
|
||||
LPCTSTR lpApplicationName = nullptr;
|
||||
|
||||
#ifdef _WIN32
|
||||
TCHAR lpCommandLine[200] = _T("cmd /C set");
|
||||
#else
|
||||
TCHAR lpCommandLine[200] = _T("printenv");
|
||||
#endif
|
||||
|
||||
// LPTSTR lpCommandLine;
|
||||
LPSECURITY_ATTRIBUTES lpProcessAttributes = nullptr;
|
||||
LPSECURITY_ATTRIBUTES lpThreadAttributes = nullptr;
|
||||
BOOL bInheritHandles = 0;
|
||||
DWORD dwCreationFlags = 0;
|
||||
LPVOID lpEnvironment = nullptr;
|
||||
LPCTSTR lpCurrentDirectory = nullptr;
|
||||
STARTUPINFO StartupInfo = WINPR_C_ARRAY_INIT;
|
||||
PROCESS_INFORMATION ProcessInformation = WINPR_C_ARRAY_INIT;
|
||||
LPTCH lpszEnvironmentBlock = nullptr;
|
||||
HANDLE pipe_read = nullptr;
|
||||
HANDLE pipe_write = nullptr;
|
||||
char buf[1024] = WINPR_C_ARRAY_INIT;
|
||||
DWORD read_bytes = 0;
|
||||
int ret = 0;
|
||||
SECURITY_ATTRIBUTES saAttr;
|
||||
|
||||
WINPR_UNUSED(argc);
|
||||
WINPR_UNUSED(argv);
|
||||
|
||||
lpszEnvironmentBlock = GetEnvironmentStrings();
|
||||
|
||||
lpApplicationName = nullptr;
|
||||
|
||||
lpProcessAttributes = nullptr;
|
||||
lpThreadAttributes = nullptr;
|
||||
bInheritHandles = FALSE;
|
||||
dwCreationFlags = 0;
|
||||
#ifdef _UNICODE
|
||||
dwCreationFlags |= CREATE_UNICODE_ENVIRONMENT;
|
||||
#endif
|
||||
lpEnvironment = lpszEnvironmentBlock;
|
||||
lpCurrentDirectory = nullptr;
|
||||
StartupInfo.cb = sizeof(STARTUPINFO);
|
||||
|
||||
status = CreateProcess(lpApplicationName, lpCommandLine, lpProcessAttributes,
|
||||
lpThreadAttributes, bInheritHandles, dwCreationFlags, lpEnvironment,
|
||||
lpCurrentDirectory, &StartupInfo, &ProcessInformation);
|
||||
|
||||
if (!status)
|
||||
{
|
||||
printf("CreateProcess failed. error=%" PRIu32 "\n", GetLastError());
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (WaitForSingleObject(ProcessInformation.hProcess, 5000) != WAIT_OBJECT_0)
|
||||
{
|
||||
printf("Failed to wait for first process. error=%" PRIu32 "\n", GetLastError());
|
||||
return 1;
|
||||
}
|
||||
|
||||
exitCode = 0;
|
||||
status = GetExitCodeProcess(ProcessInformation.hProcess, &exitCode);
|
||||
|
||||
printf("GetExitCodeProcess status: %" PRId32 "\n", status);
|
||||
printf("Process exited with code: 0x%08" PRIX32 "\n", exitCode);
|
||||
|
||||
(void)CloseHandle(ProcessInformation.hProcess);
|
||||
(void)CloseHandle(ProcessInformation.hThread);
|
||||
FreeEnvironmentStrings(lpszEnvironmentBlock);
|
||||
|
||||
/* Test stdin,stdout,stderr redirection */
|
||||
|
||||
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
|
||||
saAttr.bInheritHandle = TRUE;
|
||||
saAttr.lpSecurityDescriptor = nullptr;
|
||||
|
||||
if (!CreatePipe(&pipe_read, &pipe_write, &saAttr, 0))
|
||||
{
|
||||
printf("Pipe creation failed. error=%" PRIu32 "\n", GetLastError());
|
||||
return 1;
|
||||
}
|
||||
|
||||
bInheritHandles = TRUE;
|
||||
|
||||
ZeroMemory(&StartupInfo, sizeof(STARTUPINFO));
|
||||
StartupInfo.cb = sizeof(STARTUPINFO);
|
||||
StartupInfo.hStdOutput = pipe_write;
|
||||
StartupInfo.hStdError = pipe_write;
|
||||
StartupInfo.dwFlags = STARTF_USESTDHANDLES;
|
||||
|
||||
ZeroMemory(&ProcessInformation, sizeof(PROCESS_INFORMATION));
|
||||
|
||||
if (!(lpEnvironment = calloc(1, sizeof(TESTENV_T) + sizeof(TCHAR))))
|
||||
{
|
||||
printf("Failed to allocate environment buffer. error=%" PRIu32 "\n", GetLastError());
|
||||
return 1;
|
||||
}
|
||||
memcpy(lpEnvironment, (void*)TESTENV_T, sizeof(TESTENV_T));
|
||||
|
||||
status = CreateProcess(lpApplicationName, lpCommandLine, lpProcessAttributes,
|
||||
lpThreadAttributes, bInheritHandles, dwCreationFlags, lpEnvironment,
|
||||
lpCurrentDirectory, &StartupInfo, &ProcessInformation);
|
||||
|
||||
free(lpEnvironment);
|
||||
|
||||
if (!status)
|
||||
{
|
||||
(void)CloseHandle(pipe_read);
|
||||
(void)CloseHandle(pipe_write);
|
||||
printf("CreateProcess failed. error=%" PRIu32 "\n", GetLastError());
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (WaitForSingleObject(ProcessInformation.hProcess, 5000) != WAIT_OBJECT_0)
|
||||
{
|
||||
printf("Failed to wait for second process. error=%" PRIu32 "\n", GetLastError());
|
||||
return 1;
|
||||
}
|
||||
|
||||
ZeroMemory(buf, sizeof(buf));
|
||||
ReadFile(pipe_read, buf, sizeof(buf) - 1, &read_bytes, nullptr);
|
||||
if (!strstr((const char*)buf, TESTENV_A))
|
||||
{
|
||||
printf("No or unexpected data read from pipe\n");
|
||||
ret = 1;
|
||||
}
|
||||
|
||||
(void)CloseHandle(pipe_read);
|
||||
(void)CloseHandle(pipe_write);
|
||||
|
||||
exitCode = 0;
|
||||
status = GetExitCodeProcess(ProcessInformation.hProcess, &exitCode);
|
||||
|
||||
printf("GetExitCodeProcess status: %" PRId32 "\n", status);
|
||||
printf("Process exited with code: 0x%08" PRIX32 "\n", exitCode);
|
||||
|
||||
(void)CloseHandle(ProcessInformation.hProcess);
|
||||
(void)CloseHandle(ProcessInformation.hThread);
|
||||
|
||||
return ret;
|
||||
}
|
||||
53
third_party/FreeRDP/winpr/libwinpr/thread/test/TestThreadExitThread.c
vendored
Normal file
53
third_party/FreeRDP/winpr/libwinpr/thread/test/TestThreadExitThread.c
vendored
Normal file
@@ -0,0 +1,53 @@
|
||||
// Copyright © 2015 Hewlett-Packard Development Company, L.P.
|
||||
|
||||
#include <winpr/file.h>
|
||||
#include <winpr/synch.h>
|
||||
#include <winpr/thread.h>
|
||||
|
||||
static DWORD WINAPI thread_func(LPVOID arg)
|
||||
{
|
||||
WINPR_UNUSED(arg);
|
||||
|
||||
/* exists of the thread the quickest as possible */
|
||||
ExitThread(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int TestThreadExitThread(int argc, char* argv[])
|
||||
{
|
||||
HANDLE thread = nullptr;
|
||||
DWORD waitResult = 0;
|
||||
|
||||
WINPR_UNUSED(argc);
|
||||
WINPR_UNUSED(argv);
|
||||
|
||||
/* FIXME: create some noise to better guaranty the test validity and
|
||||
* decrease the number of loops */
|
||||
for (int i = 0; i < 100; i++)
|
||||
{
|
||||
thread = CreateThread(nullptr, 0, thread_func, nullptr, 0, nullptr);
|
||||
|
||||
if (thread == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
(void)fprintf(stderr, "Got an invalid thread!\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
waitResult = WaitForSingleObject(thread, 300);
|
||||
if (waitResult != WAIT_OBJECT_0)
|
||||
{
|
||||
/* When the thread exits before the internal thread_list
|
||||
* was updated, ExitThread() is not able to retrieve the
|
||||
* related WINPR_THREAD object and is not able to signal
|
||||
* the end of the thread. Therefore WaitForSingleObject
|
||||
* never get the signal.
|
||||
*/
|
||||
(void)fprintf(
|
||||
stderr, "300ms should have been enough for the thread to be in a signaled state\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
(void)CloseHandle(thread);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
1123
third_party/FreeRDP/winpr/libwinpr/thread/thread.c
vendored
Normal file
1123
third_party/FreeRDP/winpr/libwinpr/thread/thread.c
vendored
Normal file
File diff suppressed because it is too large
Load Diff
85
third_party/FreeRDP/winpr/libwinpr/thread/thread.h
vendored
Normal file
85
third_party/FreeRDP/winpr/libwinpr/thread/thread.h
vendored
Normal file
@@ -0,0 +1,85 @@
|
||||
/**
|
||||
* WinPR: Windows Portable Runtime
|
||||
* Process Thread Functions
|
||||
*
|
||||
* Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
|
||||
* Copyright 2015 Hewlett-Packard Development Company, L.P.
|
||||
* Copyright 2021 David Fort <contact@hardening-consulting.com>
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef WINPR_THREAD_PRIVATE_H
|
||||
#define WINPR_THREAD_PRIVATE_H
|
||||
|
||||
#ifndef _WIN32
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
#include <winpr/thread.h>
|
||||
|
||||
#include "../handle/handle.h"
|
||||
#include "../synch/event.h"
|
||||
#include "apc.h"
|
||||
|
||||
typedef void* (*pthread_start_routine)(void*);
|
||||
typedef struct winpr_APC_item WINPR_APC_ITEM;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
WINPR_ALIGN64 pthread_mutex_t mux;
|
||||
WINPR_ALIGN64 pthread_cond_t cond;
|
||||
WINPR_ALIGN64 BOOL val;
|
||||
} mux_condition_bundle;
|
||||
|
||||
struct winpr_thread
|
||||
{
|
||||
WINPR_HANDLE common;
|
||||
|
||||
WINPR_ALIGN64 BOOL started;
|
||||
WINPR_ALIGN64 WINPR_EVENT_IMPL event;
|
||||
WINPR_ALIGN64 BOOL mainProcess;
|
||||
WINPR_ALIGN64 BOOL detached;
|
||||
WINPR_ALIGN64 BOOL joined;
|
||||
WINPR_ALIGN64 BOOL exited;
|
||||
WINPR_ALIGN64 DWORD dwExitCode;
|
||||
WINPR_ALIGN64 pthread_t thread;
|
||||
WINPR_ALIGN64 size_t dwStackSize;
|
||||
WINPR_ALIGN64 LPVOID lpParameter;
|
||||
WINPR_ALIGN64 pthread_mutex_t mutex;
|
||||
mux_condition_bundle isRunning;
|
||||
mux_condition_bundle isCreated;
|
||||
WINPR_ALIGN64 LPTHREAD_START_ROUTINE lpStartAddress;
|
||||
WINPR_ALIGN64 LPSECURITY_ATTRIBUTES lpThreadAttributes;
|
||||
WINPR_ALIGN64 APC_QUEUE apc;
|
||||
#if defined(WITH_DEBUG_THREADS)
|
||||
WINPR_ALIGN64 void* create_stack;
|
||||
WINPR_ALIGN64 void* exit_stack;
|
||||
#endif
|
||||
};
|
||||
|
||||
WINPR_THREAD* winpr_GetCurrentThread(VOID);
|
||||
|
||||
typedef struct
|
||||
{
|
||||
WINPR_HANDLE common;
|
||||
|
||||
pid_t pid;
|
||||
int status;
|
||||
DWORD dwExitCode;
|
||||
int fd;
|
||||
} WINPR_PROCESS;
|
||||
|
||||
#endif
|
||||
|
||||
#endif /* WINPR_THREAD_PRIVATE_H */
|
||||
72
third_party/FreeRDP/winpr/libwinpr/thread/tls.c
vendored
Normal file
72
third_party/FreeRDP/winpr/libwinpr/thread/tls.c
vendored
Normal file
@@ -0,0 +1,72 @@
|
||||
/**
|
||||
* WinPR: Windows Portable Runtime
|
||||
* Process Thread Functions
|
||||
*
|
||||
* Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
|
||||
*
|
||||
* 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 <winpr/config.h>
|
||||
|
||||
#include <winpr/handle.h>
|
||||
|
||||
#include <winpr/thread.h>
|
||||
|
||||
/**
|
||||
* TlsAlloc
|
||||
* TlsFree
|
||||
* TlsGetValue
|
||||
* TlsSetValue
|
||||
*/
|
||||
|
||||
#ifndef _WIN32
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
DWORD TlsAlloc(void)
|
||||
{
|
||||
pthread_key_t key = 0;
|
||||
|
||||
if (pthread_key_create(&key, nullptr) != 0)
|
||||
return TLS_OUT_OF_INDEXES;
|
||||
|
||||
return key;
|
||||
}
|
||||
|
||||
LPVOID TlsGetValue(DWORD dwTlsIndex)
|
||||
{
|
||||
LPVOID value = nullptr;
|
||||
pthread_key_t key = 0;
|
||||
key = (pthread_key_t)dwTlsIndex;
|
||||
value = (LPVOID)pthread_getspecific(key);
|
||||
return value;
|
||||
}
|
||||
|
||||
BOOL TlsSetValue(DWORD dwTlsIndex, LPVOID lpTlsValue)
|
||||
{
|
||||
pthread_key_t key = 0;
|
||||
key = (pthread_key_t)dwTlsIndex;
|
||||
pthread_setspecific(key, lpTlsValue);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL TlsFree(DWORD dwTlsIndex)
|
||||
{
|
||||
pthread_key_t key = 0;
|
||||
key = (pthread_key_t)dwTlsIndex;
|
||||
pthread_key_delete(key);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user