mirror of
https://github.com/libsdl-org/SDL.git
synced 2025-10-15 14:26:01 +00:00
Add SDL_Process subsystem
This commit is contained in:
@@ -97,6 +97,8 @@ SDL3_0.0.0 {
|
||||
SDL_CreateMutex;
|
||||
SDL_CreatePalette;
|
||||
SDL_CreatePopupWindow;
|
||||
SDL_CreateProcess;
|
||||
SDL_CreateProcessWithProperties;
|
||||
SDL_CreateProperties;
|
||||
SDL_CreateRWLock;
|
||||
SDL_CreateRenderer;
|
||||
@@ -127,6 +129,7 @@ SDL3_0.0.0 {
|
||||
SDL_DestroyHapticEffect;
|
||||
SDL_DestroyMutex;
|
||||
SDL_DestroyPalette;
|
||||
SDL_DestroyProcess;
|
||||
SDL_DestroyProperties;
|
||||
SDL_DestroyRWLock;
|
||||
SDL_DestroyRenderer;
|
||||
@@ -420,6 +423,7 @@ SDL3_0.0.0 {
|
||||
SDL_GetPreferredLocales;
|
||||
SDL_GetPrimaryDisplay;
|
||||
SDL_GetPrimarySelectionText;
|
||||
SDL_GetProcessProperties;
|
||||
SDL_GetPropertyType;
|
||||
SDL_GetRGB;
|
||||
SDL_GetRGBA;
|
||||
@@ -590,6 +594,7 @@ SDL3_0.0.0 {
|
||||
SDL_IsTablet;
|
||||
SDL_JoystickConnected;
|
||||
SDL_JoystickEventsEnabled;
|
||||
SDL_KillProcess;
|
||||
SDL_LoadBMP;
|
||||
SDL_LoadBMP_IO;
|
||||
SDL_LoadFile;
|
||||
@@ -676,6 +681,7 @@ SDL3_0.0.0 {
|
||||
SDL_QuitSubSystem;
|
||||
SDL_RaiseWindow;
|
||||
SDL_ReadIO;
|
||||
SDL_ReadProcess;
|
||||
SDL_ReadS16BE;
|
||||
SDL_ReadS16LE;
|
||||
SDL_ReadS32BE;
|
||||
@@ -951,6 +957,7 @@ SDL3_0.0.0 {
|
||||
SDL_WaitEventTimeout;
|
||||
SDL_WaitForGPUFences;
|
||||
SDL_WaitForGPUIdle;
|
||||
SDL_WaitProcess;
|
||||
SDL_WaitSemaphore;
|
||||
SDL_WaitSemaphoreTimeout;
|
||||
SDL_WaitThread;
|
||||
@@ -961,6 +968,7 @@ SDL3_0.0.0 {
|
||||
SDL_WindowSupportsGPUPresentMode;
|
||||
SDL_WindowSupportsGPUSwapchainComposition;
|
||||
SDL_WriteIO;
|
||||
SDL_WriteProcess;
|
||||
SDL_WriteS16BE;
|
||||
SDL_WriteS16LE;
|
||||
SDL_WriteS32BE;
|
||||
|
@@ -122,6 +122,8 @@
|
||||
#define SDL_CreateMutex SDL_CreateMutex_REAL
|
||||
#define SDL_CreatePalette SDL_CreatePalette_REAL
|
||||
#define SDL_CreatePopupWindow SDL_CreatePopupWindow_REAL
|
||||
#define SDL_CreateProcess SDL_CreateProcess_REAL
|
||||
#define SDL_CreateProcessWithProperties SDL_CreateProcessWithProperties_REAL
|
||||
#define SDL_CreateProperties SDL_CreateProperties_REAL
|
||||
#define SDL_CreateRWLock SDL_CreateRWLock_REAL
|
||||
#define SDL_CreateRenderer SDL_CreateRenderer_REAL
|
||||
@@ -152,6 +154,7 @@
|
||||
#define SDL_DestroyHapticEffect SDL_DestroyHapticEffect_REAL
|
||||
#define SDL_DestroyMutex SDL_DestroyMutex_REAL
|
||||
#define SDL_DestroyPalette SDL_DestroyPalette_REAL
|
||||
#define SDL_DestroyProcess SDL_DestroyProcess_REAL
|
||||
#define SDL_DestroyProperties SDL_DestroyProperties_REAL
|
||||
#define SDL_DestroyRWLock SDL_DestroyRWLock_REAL
|
||||
#define SDL_DestroyRenderer SDL_DestroyRenderer_REAL
|
||||
@@ -445,6 +448,7 @@
|
||||
#define SDL_GetPreferredLocales SDL_GetPreferredLocales_REAL
|
||||
#define SDL_GetPrimaryDisplay SDL_GetPrimaryDisplay_REAL
|
||||
#define SDL_GetPrimarySelectionText SDL_GetPrimarySelectionText_REAL
|
||||
#define SDL_GetProcessProperties SDL_GetProcessProperties_REAL
|
||||
#define SDL_GetPropertyType SDL_GetPropertyType_REAL
|
||||
#define SDL_GetRGB SDL_GetRGB_REAL
|
||||
#define SDL_GetRGBA SDL_GetRGBA_REAL
|
||||
@@ -615,6 +619,7 @@
|
||||
#define SDL_IsTablet SDL_IsTablet_REAL
|
||||
#define SDL_JoystickConnected SDL_JoystickConnected_REAL
|
||||
#define SDL_JoystickEventsEnabled SDL_JoystickEventsEnabled_REAL
|
||||
#define SDL_KillProcess SDL_KillProcess_REAL
|
||||
#define SDL_LoadBMP SDL_LoadBMP_REAL
|
||||
#define SDL_LoadBMP_IO SDL_LoadBMP_IO_REAL
|
||||
#define SDL_LoadFile SDL_LoadFile_REAL
|
||||
@@ -701,6 +706,7 @@
|
||||
#define SDL_QuitSubSystem SDL_QuitSubSystem_REAL
|
||||
#define SDL_RaiseWindow SDL_RaiseWindow_REAL
|
||||
#define SDL_ReadIO SDL_ReadIO_REAL
|
||||
#define SDL_ReadProcess SDL_ReadProcess_REAL
|
||||
#define SDL_ReadS16BE SDL_ReadS16BE_REAL
|
||||
#define SDL_ReadS16LE SDL_ReadS16LE_REAL
|
||||
#define SDL_ReadS32BE SDL_ReadS32BE_REAL
|
||||
@@ -976,6 +982,7 @@
|
||||
#define SDL_WaitEventTimeout SDL_WaitEventTimeout_REAL
|
||||
#define SDL_WaitForGPUFences SDL_WaitForGPUFences_REAL
|
||||
#define SDL_WaitForGPUIdle SDL_WaitForGPUIdle_REAL
|
||||
#define SDL_WaitProcess SDL_WaitProcess_REAL
|
||||
#define SDL_WaitSemaphore SDL_WaitSemaphore_REAL
|
||||
#define SDL_WaitSemaphoreTimeout SDL_WaitSemaphoreTimeout_REAL
|
||||
#define SDL_WaitThread SDL_WaitThread_REAL
|
||||
@@ -986,6 +993,7 @@
|
||||
#define SDL_WindowSupportsGPUPresentMode SDL_WindowSupportsGPUPresentMode_REAL
|
||||
#define SDL_WindowSupportsGPUSwapchainComposition SDL_WindowSupportsGPUSwapchainComposition_REAL
|
||||
#define SDL_WriteIO SDL_WriteIO_REAL
|
||||
#define SDL_WriteProcess SDL_WriteProcess_REAL
|
||||
#define SDL_WriteS16BE SDL_WriteS16BE_REAL
|
||||
#define SDL_WriteS16LE SDL_WriteS16LE_REAL
|
||||
#define SDL_WriteS32BE SDL_WriteS32BE_REAL
|
||||
|
@@ -142,6 +142,8 @@ SDL_DYNAPI_PROC(int,SDL_CreateHapticEffect,(SDL_Haptic *a, const SDL_HapticEffec
|
||||
SDL_DYNAPI_PROC(SDL_Mutex*,SDL_CreateMutex,(void),(),return)
|
||||
SDL_DYNAPI_PROC(SDL_Palette*,SDL_CreatePalette,(int a),(a),return)
|
||||
SDL_DYNAPI_PROC(SDL_Window*,SDL_CreatePopupWindow,(SDL_Window *a, int b, int c, int d, int e, SDL_WindowFlags f),(a,b,c,d,e,f),return)
|
||||
SDL_DYNAPI_PROC(SDL_Process*,SDL_CreateProcess,(const char * const *a, SDL_bool b),(a,b),return)
|
||||
SDL_DYNAPI_PROC(SDL_Process*,SDL_CreateProcessWithProperties,(SDL_PropertiesID a),(a),return)
|
||||
SDL_DYNAPI_PROC(SDL_PropertiesID,SDL_CreateProperties,(void),(),return)
|
||||
SDL_DYNAPI_PROC(SDL_RWLock*,SDL_CreateRWLock,(void),(),return)
|
||||
SDL_DYNAPI_PROC(SDL_Renderer*,SDL_CreateRenderer,(SDL_Window *a, const char *b),(a,b),return)
|
||||
@@ -172,6 +174,7 @@ SDL_DYNAPI_PROC(void,SDL_DestroyGPUDevice,(SDL_GPUDevice *a),(a),)
|
||||
SDL_DYNAPI_PROC(void,SDL_DestroyHapticEffect,(SDL_Haptic *a, int b),(a,b),)
|
||||
SDL_DYNAPI_PROC(void,SDL_DestroyMutex,(SDL_Mutex *a),(a),)
|
||||
SDL_DYNAPI_PROC(void,SDL_DestroyPalette,(SDL_Palette *a),(a),)
|
||||
SDL_DYNAPI_PROC(void,SDL_DestroyProcess,(SDL_Process *a),(a),)
|
||||
SDL_DYNAPI_PROC(void,SDL_DestroyProperties,(SDL_PropertiesID a),(a),)
|
||||
SDL_DYNAPI_PROC(void,SDL_DestroyRWLock,(SDL_RWLock *a),(a),)
|
||||
SDL_DYNAPI_PROC(void,SDL_DestroyRenderer,(SDL_Renderer *a),(a),)
|
||||
@@ -465,6 +468,7 @@ SDL_DYNAPI_PROC(char*,SDL_GetPrefPath,(const char *a, const char *b),(a,b),retur
|
||||
SDL_DYNAPI_PROC(SDL_Locale**,SDL_GetPreferredLocales,(int *a),(a),return)
|
||||
SDL_DYNAPI_PROC(SDL_DisplayID,SDL_GetPrimaryDisplay,(void),(),return)
|
||||
SDL_DYNAPI_PROC(char*,SDL_GetPrimarySelectionText,(void),(),return)
|
||||
SDL_DYNAPI_PROC(SDL_PropertiesID,SDL_GetProcessProperties,(SDL_Process *a),(a),return)
|
||||
SDL_DYNAPI_PROC(SDL_PropertyType,SDL_GetPropertyType,(SDL_PropertiesID a, const char *b),(a,b),return)
|
||||
SDL_DYNAPI_PROC(void,SDL_GetRGB,(Uint32 a, const SDL_PixelFormatDetails *b, const SDL_Palette *c, Uint8 *d, Uint8 *e, Uint8 *f),(a,b,c,d,e,f),)
|
||||
SDL_DYNAPI_PROC(void,SDL_GetRGBA,(Uint32 a, const SDL_PixelFormatDetails *b, const SDL_Palette *c, Uint8 *d, Uint8 *e, Uint8 *f, Uint8 *g),(a,b,c,d,e,f,g),)
|
||||
@@ -634,6 +638,7 @@ SDL_DYNAPI_PROC(SDL_bool,SDL_IsMouseHaptic,(void),(),return)
|
||||
SDL_DYNAPI_PROC(SDL_bool,SDL_IsTablet,(void),(),return)
|
||||
SDL_DYNAPI_PROC(SDL_bool,SDL_JoystickConnected,(SDL_Joystick *a),(a),return)
|
||||
SDL_DYNAPI_PROC(SDL_bool,SDL_JoystickEventsEnabled,(void),(),return)
|
||||
SDL_DYNAPI_PROC(SDL_bool,SDL_KillProcess,(SDL_Process *a, SDL_bool b),(a,b),return)
|
||||
SDL_DYNAPI_PROC(SDL_Surface*,SDL_LoadBMP,(const char *a),(a),return)
|
||||
SDL_DYNAPI_PROC(SDL_Surface*,SDL_LoadBMP_IO,(SDL_IOStream *a, SDL_bool b),(a,b),return)
|
||||
SDL_DYNAPI_PROC(void*,SDL_LoadFile,(const char *a, size_t *b),(a,b),return)
|
||||
@@ -712,6 +717,7 @@ SDL_DYNAPI_PROC(void,SDL_Quit,(void),(),)
|
||||
SDL_DYNAPI_PROC(void,SDL_QuitSubSystem,(SDL_InitFlags a),(a),)
|
||||
SDL_DYNAPI_PROC(SDL_bool,SDL_RaiseWindow,(SDL_Window *a),(a),return)
|
||||
SDL_DYNAPI_PROC(size_t,SDL_ReadIO,(SDL_IOStream *a, void *b, size_t c),(a,b,c),return)
|
||||
SDL_DYNAPI_PROC(void*,SDL_ReadProcess,(SDL_Process *a, size_t *b, int *c),(a,b,c),return)
|
||||
SDL_DYNAPI_PROC(SDL_bool,SDL_ReadS16BE,(SDL_IOStream *a, Sint16 *b),(a,b),return)
|
||||
SDL_DYNAPI_PROC(SDL_bool,SDL_ReadS16LE,(SDL_IOStream *a, Sint16 *b),(a,b),return)
|
||||
SDL_DYNAPI_PROC(SDL_bool,SDL_ReadS32BE,(SDL_IOStream *a, Sint32 *b),(a,b),return)
|
||||
@@ -986,6 +992,7 @@ SDL_DYNAPI_PROC(SDL_bool,SDL_WaitEvent,(SDL_Event *a),(a),return)
|
||||
SDL_DYNAPI_PROC(SDL_bool,SDL_WaitEventTimeout,(SDL_Event *a, Sint32 b),(a,b),return)
|
||||
SDL_DYNAPI_PROC(void,SDL_WaitForGPUFences,(SDL_GPUDevice *a, SDL_bool b, SDL_GPUFence *const *c, Uint32 d),(a,b,c,d),)
|
||||
SDL_DYNAPI_PROC(void,SDL_WaitForGPUIdle,(SDL_GPUDevice *a),(a),)
|
||||
SDL_DYNAPI_PROC(SDL_bool,SDL_WaitProcess,(SDL_Process *a, SDL_bool b, int *c),(a,b,c),return)
|
||||
SDL_DYNAPI_PROC(void,SDL_WaitSemaphore,(SDL_Semaphore *a),(a),)
|
||||
SDL_DYNAPI_PROC(SDL_bool,SDL_WaitSemaphoreTimeout,(SDL_Semaphore *a, Sint32 b),(a,b),return)
|
||||
SDL_DYNAPI_PROC(void,SDL_WaitThread,(SDL_Thread *a, int *b),(a,b),)
|
||||
@@ -996,6 +1003,7 @@ SDL_DYNAPI_PROC(SDL_bool,SDL_WindowHasSurface,(SDL_Window *a),(a),return)
|
||||
SDL_DYNAPI_PROC(SDL_bool,SDL_WindowSupportsGPUPresentMode,(SDL_GPUDevice *a, SDL_Window *b, SDL_GPUPresentMode c),(a,b,c),return)
|
||||
SDL_DYNAPI_PROC(SDL_bool,SDL_WindowSupportsGPUSwapchainComposition,(SDL_GPUDevice *a, SDL_Window *b, SDL_GPUSwapchainComposition c),(a,b,c),return)
|
||||
SDL_DYNAPI_PROC(size_t,SDL_WriteIO,(SDL_IOStream *a, const void *b, size_t c),(a,b,c),return)
|
||||
SDL_DYNAPI_PROC(SDL_bool,SDL_WriteProcess,(SDL_Process *a, const void *b, size_t c, SDL_bool d),(a,b,c,d),return)
|
||||
SDL_DYNAPI_PROC(SDL_bool,SDL_WriteS16BE,(SDL_IOStream *a, Sint16 b),(a,b),return)
|
||||
SDL_DYNAPI_PROC(SDL_bool,SDL_WriteS16LE,(SDL_IOStream *a, Sint16 b),(a,b),return)
|
||||
SDL_DYNAPI_PROC(SDL_bool,SDL_WriteS32BE,(SDL_IOStream *a, Sint32 b),(a,b),return)
|
||||
|
204
src/process/SDL_process.c
Normal file
204
src/process/SDL_process.c
Normal file
@@ -0,0 +1,204 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2024 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "SDL_internal.h"
|
||||
|
||||
#include "SDL_sysprocess.h"
|
||||
|
||||
|
||||
SDL_Process *SDL_CreateProcess(const char * const *args, SDL_bool pipe_stdio)
|
||||
{
|
||||
if (!args || !args[0] || !args[0][0]) {
|
||||
SDL_InvalidParamError("args");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
SDL_Process *process;
|
||||
SDL_PropertiesID props = SDL_CreateProperties();
|
||||
SDL_SetPointerProperty(props, SDL_PROP_PROCESS_CREATE_ARGS_POINTER, (void *)args);
|
||||
if (pipe_stdio) {
|
||||
SDL_SetNumberProperty(props, SDL_PROP_PROCESS_CREATE_STDIN_NUMBER, SDL_PROCESS_STDIO_APP);
|
||||
SDL_SetNumberProperty(props, SDL_PROP_PROCESS_CREATE_STDOUT_NUMBER, SDL_PROCESS_STDIO_APP);
|
||||
}
|
||||
process = SDL_CreateProcessWithProperties(props);
|
||||
SDL_DestroyProperties(props);
|
||||
return process;
|
||||
}
|
||||
|
||||
SDL_Process *SDL_CreateProcessWithProperties(SDL_PropertiesID props)
|
||||
{
|
||||
const char * const *args = SDL_GetPointerProperty(props, SDL_PROP_PROCESS_CREATE_ARGS_POINTER, NULL);
|
||||
if (!args || !args[0] || !args[0][0]) {
|
||||
SDL_InvalidParamError("SDL_PROP_PROCESS_CREATE_ARGS_POINTER");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
SDL_Process *process = (SDL_Process *)SDL_calloc(1, sizeof(*process));
|
||||
if (!process) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
process->props = SDL_CreateProperties();
|
||||
if (!process->props) {
|
||||
SDL_DestroyProcess(process);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!SDL_SYS_CreateProcessWithProperties(process, props)) {
|
||||
SDL_DestroyProcess(process);
|
||||
return NULL;
|
||||
}
|
||||
process->alive = true;
|
||||
return process;
|
||||
}
|
||||
|
||||
SDL_PropertiesID SDL_GetProcessProperties(SDL_Process *process)
|
||||
{
|
||||
if (!process) {
|
||||
return SDL_InvalidParamError("process");
|
||||
}
|
||||
return process->props;
|
||||
}
|
||||
|
||||
void *SDL_ReadProcess(SDL_Process *process, size_t *datasize, int *exitcode)
|
||||
{
|
||||
void *result;
|
||||
|
||||
if (datasize) {
|
||||
*datasize = 0;
|
||||
}
|
||||
if (exitcode) {
|
||||
*exitcode = -1;
|
||||
}
|
||||
|
||||
if (!process) {
|
||||
SDL_InvalidParamError("process");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
SDL_IOStream *io = (SDL_IOStream *)SDL_GetPointerProperty(process->props, SDL_PROP_PROCESS_STDOUT_POINTER, NULL);
|
||||
if (!io) {
|
||||
SDL_SetError("Process not created with I/O enabled");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
result = SDL_LoadFile_IO(io, datasize, false);
|
||||
|
||||
SDL_WaitProcess(process, true, exitcode);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
SDL_bool SDL_WriteProcess(SDL_Process *process, const void *ptr, size_t size, SDL_bool closeio)
|
||||
{
|
||||
bool result = false;
|
||||
SDL_IOStream *io = NULL;
|
||||
|
||||
if (!process) {
|
||||
SDL_InvalidParamError("process");
|
||||
goto done;
|
||||
}
|
||||
|
||||
io = (SDL_IOStream *)SDL_GetPointerProperty(process->props, SDL_PROP_PROCESS_STDIN_POINTER, NULL);
|
||||
if (!io) {
|
||||
SDL_SetError("Process not created with I/O enabled");
|
||||
goto done;
|
||||
}
|
||||
|
||||
size_t written = 0, left = size;
|
||||
while (left > 0) {
|
||||
size_t amount = SDL_WriteIO(io, (Uint8 *)ptr + written, left);
|
||||
if (amount > 0) {
|
||||
written += amount;
|
||||
left -= amount;
|
||||
continue;
|
||||
} else if (SDL_GetIOStatus(io) == SDL_IO_STATUS_NOT_READY) {
|
||||
// Wait for the stream to be ready
|
||||
SDL_Delay(1);
|
||||
continue;
|
||||
}
|
||||
|
||||
// The stream status will remain set for the caller to check
|
||||
break;
|
||||
}
|
||||
|
||||
result = (written == size);
|
||||
|
||||
done:
|
||||
if (result) {
|
||||
result = SDL_FlushIO(io);
|
||||
}
|
||||
if (closeio) {
|
||||
result &= SDL_CloseIO(io);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
SDL_bool SDL_KillProcess(SDL_Process *process, SDL_bool force)
|
||||
{
|
||||
if (!process) {
|
||||
return SDL_InvalidParamError("process");
|
||||
}
|
||||
|
||||
if (!process->alive) {
|
||||
return SDL_SetError("Process isn't running");
|
||||
}
|
||||
|
||||
return SDL_SYS_KillProcess(process, force);
|
||||
}
|
||||
|
||||
SDL_bool SDL_WaitProcess(SDL_Process *process, SDL_bool block, int *exitcode)
|
||||
{
|
||||
if (!process) {
|
||||
return SDL_InvalidParamError("process");
|
||||
}
|
||||
|
||||
if (!process->alive) {
|
||||
if (exitcode) {
|
||||
*exitcode = process->exitcode;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (SDL_SYS_WaitProcess(process, block, &process->exitcode)) {
|
||||
process->alive = false;
|
||||
if (exitcode) {
|
||||
*exitcode = process->exitcode;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void SDL_DestroyProcess(SDL_Process *process)
|
||||
{
|
||||
if (!process) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check to see if the process has exited, will reap zombies on POSIX platforms
|
||||
if (process->alive) {
|
||||
SDL_WaitProcess(process, false, NULL);
|
||||
}
|
||||
|
||||
SDL_SYS_DestroyProcess(process);
|
||||
SDL_DestroyProperties(process->props);
|
||||
SDL_free(process);
|
||||
}
|
36
src/process/SDL_sysprocess.h
Normal file
36
src/process/SDL_sysprocess.h
Normal file
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2024 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "SDL_internal.h"
|
||||
|
||||
typedef struct SDL_ProcessData SDL_ProcessData;
|
||||
|
||||
struct SDL_Process
|
||||
{
|
||||
bool alive;
|
||||
int exitcode;
|
||||
SDL_PropertiesID props;
|
||||
SDL_ProcessData *internal;
|
||||
};
|
||||
|
||||
bool SDL_SYS_CreateProcessWithProperties(SDL_Process *process, SDL_PropertiesID props);
|
||||
bool SDL_SYS_KillProcess(SDL_Process *process, SDL_bool force);
|
||||
bool SDL_SYS_WaitProcess(SDL_Process *process, SDL_bool block, int *exitcode);
|
||||
void SDL_SYS_DestroyProcess(SDL_Process *process);
|
48
src/process/dummy/SDL_dummyprocess.c
Normal file
48
src/process/dummy/SDL_dummyprocess.c
Normal file
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2024 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "SDL_internal.h"
|
||||
|
||||
#ifdef SDL_PROCESS_DUMMY
|
||||
|
||||
#include "../SDL_sysprocess.h"
|
||||
|
||||
|
||||
bool SDL_SYS_CreateProcessWithProperties(SDL_Process *process, SDL_PropertiesID props)
|
||||
{
|
||||
return SDL_Unsupported();
|
||||
}
|
||||
|
||||
bool SDL_SYS_KillProcess(SDL_Process *process, SDL_bool force)
|
||||
{
|
||||
return SDL_Unsupported();
|
||||
}
|
||||
|
||||
bool SDL_SYS_WaitProcess(SDL_Process *process, SDL_bool block, int *exitcode)
|
||||
{
|
||||
return SDL_Unsupported();
|
||||
}
|
||||
|
||||
void SDL_SYS_DestroyProcess(SDL_Process *process)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
#endif // SDL_PROCESS_DUMMY
|
401
src/process/posix/SDL_posixprocess.c
Normal file
401
src/process/posix/SDL_posixprocess.c
Normal file
@@ -0,0 +1,401 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2024 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "SDL_internal.h"
|
||||
|
||||
#ifdef SDL_PROCESS_POSIX
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <signal.h>
|
||||
#include <spawn.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
#include "../SDL_sysprocess.h"
|
||||
#include "../../file/SDL_iostream_c.h"
|
||||
|
||||
#if defined(SDL_PLATFORM_MACOS)
|
||||
#include <crt_externs.h>
|
||||
#define environ (*_NSGetEnviron())
|
||||
#elif defined(SDL_PLATFORM_FREEBSD)
|
||||
#include <dlfcn.h>
|
||||
#define environ ((char **)dlsym(RTLD_DEFAULT, "environ"))
|
||||
#else
|
||||
extern char **environ;
|
||||
#endif
|
||||
|
||||
#define READ_END 0
|
||||
#define WRITE_END 1
|
||||
|
||||
struct SDL_ProcessData {
|
||||
pid_t pid;
|
||||
};
|
||||
|
||||
static void CleanupStream(void *userdata, void *value)
|
||||
{
|
||||
SDL_Process *process = (SDL_Process *)value;
|
||||
const char *property = (const char *)userdata;
|
||||
|
||||
SDL_ClearProperty(process->props, property);
|
||||
}
|
||||
|
||||
static bool SetupStream(SDL_Process *process, int fd, const char *mode, const char *property)
|
||||
{
|
||||
// Set the file descriptor to non-blocking mode
|
||||
fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK);
|
||||
|
||||
FILE *fp = fdopen(fd, mode);
|
||||
if (!fp) {
|
||||
return false;
|
||||
}
|
||||
|
||||
SDL_IOStream *io = SDL_IOFromFP(fp, true);
|
||||
if (!io) {
|
||||
return false;
|
||||
}
|
||||
|
||||
SDL_SetPointerPropertyWithCleanup(SDL_GetIOProperties(io), "SDL.internal.process", process, CleanupStream, (void *)property);
|
||||
SDL_SetPointerProperty(process->props, property, io);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool CreatePipe(int fds[2])
|
||||
{
|
||||
if (pipe(fds) < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Make sure the pipe isn't accidentally inherited by another thread creating a process
|
||||
fcntl(fds[READ_END], F_SETFD, fcntl(fds[READ_END], F_GETFD) | FD_CLOEXEC);
|
||||
fcntl(fds[WRITE_END], F_SETFD, fcntl(fds[WRITE_END], F_GETFD) | FD_CLOEXEC);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool GetStreamFD(SDL_PropertiesID props, const char *property, int *result)
|
||||
{
|
||||
SDL_IOStream *io = (SDL_IOStream *)SDL_GetPointerProperty(props, property, NULL);
|
||||
if (!io) {
|
||||
SDL_SetError("%s is not set", property);
|
||||
return false;
|
||||
}
|
||||
|
||||
int fd = (int)SDL_GetNumberProperty(SDL_GetIOProperties(io), SDL_PROP_IOSTREAM_FILE_DESCRIPTOR_NUMBER, -1);
|
||||
if (fd < 0) {
|
||||
SDL_SetError("%s doesn't have SDL_PROP_IOSTREAM_FILE_DESCRIPTOR_NUMBER available", property);
|
||||
return false;
|
||||
}
|
||||
|
||||
*result = fd;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SDL_SYS_CreateProcessWithProperties(SDL_Process *process, SDL_PropertiesID props)
|
||||
{
|
||||
char * const *args = SDL_GetPointerProperty(props, SDL_PROP_PROCESS_CREATE_ARGS_POINTER, NULL);
|
||||
char * const *env = SDL_GetPointerProperty(props, SDL_PROP_PROCESS_CREATE_ENVIRONMENT_POINTER, environ);
|
||||
SDL_ProcessIO stdin_option = (SDL_ProcessIO)SDL_GetNumberProperty(props, SDL_PROP_PROCESS_CREATE_STDIN_NUMBER, SDL_PROCESS_STDIO_NULL);
|
||||
SDL_ProcessIO stdout_option = (SDL_ProcessIO)SDL_GetNumberProperty(props, SDL_PROP_PROCESS_CREATE_STDOUT_NUMBER, SDL_PROCESS_STDIO_INHERITED);
|
||||
SDL_ProcessIO stderr_option = (SDL_ProcessIO)SDL_GetNumberProperty(props, SDL_PROP_PROCESS_CREATE_STDERR_NUMBER, SDL_PROCESS_STDIO_INHERITED);
|
||||
bool redirect_stderr = SDL_GetBooleanProperty(props, SDL_PROP_PROCESS_CREATE_STDERR_TO_STDOUT_BOOLEAN, false) &&
|
||||
!SDL_HasProperty(props, SDL_PROP_PROCESS_CREATE_STDERR_NUMBER);
|
||||
int stdin_pipe[2] = { -1, -1 };
|
||||
int stdout_pipe[2] = { -1, -1 };
|
||||
int stderr_pipe[2] = { -1, -1 };
|
||||
int fd = -1;
|
||||
|
||||
// Keep the malloc() before exec() so that an OOM won't run a process at all
|
||||
SDL_ProcessData *data = SDL_calloc(1, sizeof(*data));
|
||||
if (!data) {
|
||||
return false;
|
||||
}
|
||||
process->internal = data;
|
||||
|
||||
posix_spawnattr_t attr;
|
||||
posix_spawn_file_actions_t fa;
|
||||
|
||||
if (posix_spawnattr_init(&attr) != 0) {
|
||||
SDL_SetError("posix_spawnattr_init failed: %s", strerror(errno));
|
||||
goto posix_spawn_fail_none;
|
||||
}
|
||||
|
||||
if (posix_spawn_file_actions_init(&fa) != 0) {
|
||||
SDL_SetError("posix_spawn_file_actions_init failed: %s", strerror(errno));
|
||||
goto posix_spawn_fail_attr;
|
||||
}
|
||||
|
||||
switch (stdin_option) {
|
||||
case SDL_PROCESS_STDIO_REDIRECT:
|
||||
if (!GetStreamFD(props, SDL_PROP_PROCESS_CREATE_STDIN_POINTER, &fd)) {
|
||||
goto posix_spawn_fail_all;
|
||||
}
|
||||
if (posix_spawn_file_actions_adddup2(&fa, fd, STDIN_FILENO) != 0) {
|
||||
SDL_SetError("posix_spawn_file_actions_adddup2 failed: %s", strerror(errno));
|
||||
goto posix_spawn_fail_all;
|
||||
}
|
||||
if (posix_spawn_file_actions_addclose(&fa, fd) != 0) {
|
||||
SDL_SetError("posix_spawn_file_actions_addclose failed: %s", strerror(errno));
|
||||
goto posix_spawn_fail_all;
|
||||
}
|
||||
break;
|
||||
case SDL_PROCESS_STDIO_APP:
|
||||
if (!CreatePipe(stdin_pipe)) {
|
||||
goto posix_spawn_fail_all;
|
||||
}
|
||||
if (posix_spawn_file_actions_addclose(&fa, stdin_pipe[WRITE_END]) != 0) {
|
||||
SDL_SetError("posix_spawn_file_actions_addclose failed: %s", strerror(errno));
|
||||
goto posix_spawn_fail_all;
|
||||
}
|
||||
if (posix_spawn_file_actions_adddup2(&fa, stdin_pipe[READ_END], STDIN_FILENO) != 0) {
|
||||
SDL_SetError("posix_spawn_file_actions_adddup2 failed: %s", strerror(errno));
|
||||
goto posix_spawn_fail_all;
|
||||
}
|
||||
if (posix_spawn_file_actions_addclose(&fa, stdin_pipe[READ_END]) != 0) {
|
||||
SDL_SetError("posix_spawn_file_actions_addclose failed: %s", strerror(errno));
|
||||
goto posix_spawn_fail_all;
|
||||
}
|
||||
break;
|
||||
case SDL_PROCESS_STDIO_NULL:
|
||||
if (posix_spawn_file_actions_addopen(&fa, STDIN_FILENO, "/dev/null", O_RDONLY, 0) != 0) {
|
||||
SDL_SetError("posix_spawn_file_actions_addopen failed: %s", strerror(errno));
|
||||
goto posix_spawn_fail_all;
|
||||
}
|
||||
break;
|
||||
case SDL_PROCESS_STDIO_INHERITED:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
switch (stdout_option) {
|
||||
case SDL_PROCESS_STDIO_REDIRECT:
|
||||
if (!GetStreamFD(props, SDL_PROP_PROCESS_CREATE_STDOUT_POINTER, &fd)) {
|
||||
goto posix_spawn_fail_all;
|
||||
}
|
||||
if (posix_spawn_file_actions_adddup2(&fa, fd, STDOUT_FILENO) != 0) {
|
||||
SDL_SetError("posix_spawn_file_actions_adddup2 failed: %s", strerror(errno));
|
||||
goto posix_spawn_fail_all;
|
||||
}
|
||||
if (posix_spawn_file_actions_addclose(&fa, fd) != 0) {
|
||||
SDL_SetError("posix_spawn_file_actions_addclose failed: %s", strerror(errno));
|
||||
goto posix_spawn_fail_all;
|
||||
}
|
||||
break;
|
||||
case SDL_PROCESS_STDIO_APP:
|
||||
if (!CreatePipe(stdout_pipe)) {
|
||||
goto posix_spawn_fail_all;
|
||||
}
|
||||
if (posix_spawn_file_actions_addclose(&fa, stdout_pipe[READ_END]) != 0) {
|
||||
SDL_SetError("posix_spawn_file_actions_addclose failed: %s", strerror(errno));
|
||||
goto posix_spawn_fail_all;
|
||||
}
|
||||
if (posix_spawn_file_actions_adddup2(&fa, stdout_pipe[WRITE_END], STDOUT_FILENO) != 0) {
|
||||
SDL_SetError("posix_spawn_file_actions_adddup2 failed: %s", strerror(errno));
|
||||
goto posix_spawn_fail_all;
|
||||
}
|
||||
if (posix_spawn_file_actions_addclose(&fa, stdout_pipe[WRITE_END]) != 0) {
|
||||
SDL_SetError("posix_spawn_file_actions_addclose failed: %s", strerror(errno));
|
||||
goto posix_spawn_fail_all;
|
||||
}
|
||||
break;
|
||||
case SDL_PROCESS_STDIO_NULL:
|
||||
if (posix_spawn_file_actions_addopen(&fa, STDOUT_FILENO, "/dev/null", O_WRONLY, 0644) != 0) {
|
||||
SDL_SetError("posix_spawn_file_actions_addopen failed: %s", strerror(errno));
|
||||
goto posix_spawn_fail_all;
|
||||
}
|
||||
break;
|
||||
case SDL_PROCESS_STDIO_INHERITED:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (redirect_stderr) {
|
||||
if (posix_spawn_file_actions_adddup2(&fa, STDOUT_FILENO, STDERR_FILENO) != 0) {
|
||||
SDL_SetError("posix_spawn_file_actions_adddup2 failed: %s", strerror(errno));
|
||||
goto posix_spawn_fail_all;
|
||||
}
|
||||
} else {
|
||||
switch (stderr_option) {
|
||||
case SDL_PROCESS_STDIO_REDIRECT:
|
||||
if (!GetStreamFD(props, SDL_PROP_PROCESS_CREATE_STDERR_POINTER, &fd)) {
|
||||
goto posix_spawn_fail_all;
|
||||
}
|
||||
if (posix_spawn_file_actions_adddup2(&fa, fd, STDERR_FILENO) != 0) {
|
||||
SDL_SetError("posix_spawn_file_actions_adddup2 failed: %s", strerror(errno));
|
||||
goto posix_spawn_fail_all;
|
||||
}
|
||||
if (posix_spawn_file_actions_addclose(&fa, fd) != 0) {
|
||||
SDL_SetError("posix_spawn_file_actions_addclose failed: %s", strerror(errno));
|
||||
goto posix_spawn_fail_all;
|
||||
}
|
||||
break;
|
||||
case SDL_PROCESS_STDIO_APP:
|
||||
if (!CreatePipe(stderr_pipe)) {
|
||||
goto posix_spawn_fail_all;
|
||||
}
|
||||
if (posix_spawn_file_actions_addclose(&fa, stderr_pipe[READ_END]) != 0) {
|
||||
SDL_SetError("posix_spawn_file_actions_addclose failed: %s", strerror(errno));
|
||||
goto posix_spawn_fail_all;
|
||||
}
|
||||
if (posix_spawn_file_actions_adddup2(&fa, stderr_pipe[WRITE_END], STDERR_FILENO) != 0) {
|
||||
SDL_SetError("posix_spawn_file_actions_adddup2 failed: %s", strerror(errno));
|
||||
goto posix_spawn_fail_all;
|
||||
}
|
||||
if (posix_spawn_file_actions_addclose(&fa, stderr_pipe[WRITE_END]) != 0) {
|
||||
SDL_SetError("posix_spawn_file_actions_addclose failed: %s", strerror(errno));
|
||||
goto posix_spawn_fail_all;
|
||||
}
|
||||
break;
|
||||
case SDL_PROCESS_STDIO_NULL:
|
||||
if (posix_spawn_file_actions_addopen(&fa, STDERR_FILENO, "/dev/null", O_WRONLY, 0644) != 0) {
|
||||
SDL_SetError("posix_spawn_file_actions_addopen failed: %s", strerror(errno));
|
||||
goto posix_spawn_fail_all;
|
||||
}
|
||||
break;
|
||||
case SDL_PROCESS_STDIO_INHERITED:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Spawn the new process
|
||||
if (posix_spawnp(&data->pid, args[0], &fa, &attr, args, env) != 0) {
|
||||
SDL_SetError("posix_spawn failed: %s", strerror(errno));
|
||||
goto posix_spawn_fail_all;
|
||||
}
|
||||
SDL_SetNumberProperty(process->props, SDL_PROP_PROCESS_PID_NUMBER, data->pid);
|
||||
|
||||
if (stdin_option == SDL_PROCESS_STDIO_APP) {
|
||||
if (!SetupStream(process, stdin_pipe[WRITE_END], "wb", SDL_PROP_PROCESS_STDIN_POINTER)) {
|
||||
close(stdin_pipe[WRITE_END]);
|
||||
}
|
||||
close(stdin_pipe[READ_END]);
|
||||
}
|
||||
|
||||
if (stdout_option == SDL_PROCESS_STDIO_APP) {
|
||||
if (!SetupStream(process, stdout_pipe[READ_END], "rb", SDL_PROP_PROCESS_STDOUT_POINTER)) {
|
||||
close(stdout_pipe[READ_END]);
|
||||
}
|
||||
close(stdout_pipe[WRITE_END]);
|
||||
}
|
||||
|
||||
if (stderr_option == SDL_PROCESS_STDIO_APP) {
|
||||
if (!SetupStream(process, stderr_pipe[READ_END], "rb", SDL_PROP_PROCESS_STDERR_POINTER)) {
|
||||
close(stderr_pipe[READ_END]);
|
||||
}
|
||||
close(stderr_pipe[WRITE_END]);
|
||||
}
|
||||
|
||||
posix_spawn_file_actions_destroy(&fa);
|
||||
posix_spawnattr_destroy(&attr);
|
||||
|
||||
return true;
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
posix_spawn_fail_all:
|
||||
posix_spawn_file_actions_destroy(&fa);
|
||||
|
||||
posix_spawn_fail_attr:
|
||||
posix_spawnattr_destroy(&attr);
|
||||
|
||||
posix_spawn_fail_none:
|
||||
if (stdin_pipe[READ_END] >= 0) {
|
||||
close(stdin_pipe[READ_END]);
|
||||
}
|
||||
if (stdin_pipe[WRITE_END] >= 0) {
|
||||
close(stdin_pipe[WRITE_END]);
|
||||
}
|
||||
if (stdout_pipe[READ_END] >= 0) {
|
||||
close(stdout_pipe[READ_END]);
|
||||
}
|
||||
if (stdout_pipe[WRITE_END] >= 0) {
|
||||
close(stdout_pipe[WRITE_END]);
|
||||
}
|
||||
if (stderr_pipe[READ_END] >= 0) {
|
||||
close(stderr_pipe[READ_END]);
|
||||
}
|
||||
if (stderr_pipe[WRITE_END] >= 0) {
|
||||
close(stderr_pipe[WRITE_END]);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SDL_SYS_KillProcess(SDL_Process *process, SDL_bool force)
|
||||
{
|
||||
int ret = kill(process->internal->pid, force ? SIGKILL : SIGTERM);
|
||||
if (ret == 0) {
|
||||
return true;
|
||||
} else {
|
||||
return SDL_SetError("Could not kill(): %s", strerror(errno));
|
||||
}
|
||||
}
|
||||
|
||||
bool SDL_SYS_WaitProcess(SDL_Process *process, SDL_bool block, int *exitcode)
|
||||
{
|
||||
int wstatus = 0;
|
||||
int ret = waitpid(process->internal->pid, &wstatus, block ? 0 : WNOHANG);
|
||||
|
||||
if (ret < 0) {
|
||||
return SDL_SetError("Could not waitpid(): %s", strerror(errno));
|
||||
}
|
||||
|
||||
if (ret == 0) {
|
||||
SDL_ClearError();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (WIFEXITED(wstatus)) {
|
||||
*exitcode = WEXITSTATUS(wstatus);
|
||||
} else if (WIFSIGNALED(wstatus)) {
|
||||
*exitcode = -WTERMSIG(wstatus);
|
||||
} else {
|
||||
*exitcode = -255;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void SDL_SYS_DestroyProcess(SDL_Process *process)
|
||||
{
|
||||
SDL_IOStream *io;
|
||||
|
||||
io = (SDL_IOStream *)SDL_GetPointerProperty(process->props, SDL_PROP_PROCESS_STDIN_POINTER, NULL);
|
||||
if (io) {
|
||||
SDL_CloseIO(io);
|
||||
}
|
||||
|
||||
io = (SDL_IOStream *)SDL_GetPointerProperty(process->props, SDL_PROP_PROCESS_STDOUT_POINTER, NULL);
|
||||
if (io) {
|
||||
SDL_CloseIO(io);
|
||||
}
|
||||
|
||||
io = (SDL_IOStream *)SDL_GetPointerProperty(process->props, SDL_PROP_PROCESS_STDERR_POINTER, NULL);
|
||||
if (io) {
|
||||
SDL_CloseIO(io);
|
||||
}
|
||||
|
||||
SDL_free(process->internal);
|
||||
}
|
||||
|
||||
#endif // SDL_PROCESS_POSIX
|
452
src/process/windows/SDL_windowsprocess.c
Normal file
452
src/process/windows/SDL_windowsprocess.c
Normal file
@@ -0,0 +1,452 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2024 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "SDL_internal.h"
|
||||
|
||||
#ifdef SDL_PROCESS_WINDOWS
|
||||
|
||||
#include "../../core/windows/SDL_windows.h"
|
||||
#include "../SDL_sysprocess.h"
|
||||
#include "../../file/SDL_iostream_c.h"
|
||||
|
||||
#define READ_END 0
|
||||
#define WRITE_END 1
|
||||
|
||||
struct SDL_ProcessData {
|
||||
PROCESS_INFORMATION process_information;
|
||||
};
|
||||
|
||||
static void CleanupStream(void *userdata, void *value)
|
||||
{
|
||||
SDL_Process *process = (SDL_Process *)value;
|
||||
const char *property = (const char *)userdata;
|
||||
|
||||
SDL_ClearProperty(process->props, property);
|
||||
}
|
||||
|
||||
static bool SetupStream(SDL_Process *process, HANDLE handle, const char *mode, const char *property)
|
||||
{
|
||||
SDL_IOStream *io = SDL_IOFromHandle(handle, mode, true);
|
||||
if (!io) {
|
||||
return false;
|
||||
}
|
||||
|
||||
SDL_SetPointerPropertyWithCleanup(SDL_GetIOProperties(io), "SDL.internal.process", process, CleanupStream, (void *)property);
|
||||
SDL_SetPointerProperty(process->props, property, io);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool SetupRedirect(SDL_PropertiesID props, const char *property, HANDLE *result)
|
||||
{
|
||||
SDL_IOStream *io = (SDL_IOStream *)SDL_GetPointerProperty(props, property, NULL);
|
||||
if (!io) {
|
||||
SDL_SetError("%s is not set", property);
|
||||
return false;
|
||||
}
|
||||
|
||||
HANDLE handle = (HANDLE)SDL_GetPointerProperty(SDL_GetIOProperties(io), SDL_PROP_IOSTREAM_WINDOWS_HANDLE_POINTER, INVALID_HANDLE_VALUE);
|
||||
if (handle == INVALID_HANDLE_VALUE) {
|
||||
SDL_SetError("%s doesn't have SDL_PROP_IOSTREAM_WINDOWS_HANDLE_POINTER available", property);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!DuplicateHandle(GetCurrentProcess(), handle,
|
||||
GetCurrentProcess(), result,
|
||||
0, TRUE, DUPLICATE_SAME_ACCESS)) {
|
||||
WIN_SetError("DuplicateHandle()");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool join_arguments(const char * const *args, char **args_out)
|
||||
{
|
||||
size_t len;
|
||||
int i;
|
||||
int i_out;
|
||||
char *result;
|
||||
|
||||
len = 0;
|
||||
for (i = 0; args[i]; i++) {
|
||||
const char *a = args[i];
|
||||
|
||||
/* two double quotes to surround an argument with */
|
||||
len += 2;
|
||||
|
||||
for (; *a; a++) {
|
||||
switch (*a) {
|
||||
case '"':
|
||||
len += 2;
|
||||
break;
|
||||
default:
|
||||
len += 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* space separator or final '\0' */
|
||||
len += 1;
|
||||
}
|
||||
|
||||
result = SDL_malloc(len);
|
||||
if (!result) {
|
||||
*args_out = NULL;
|
||||
return false;
|
||||
}
|
||||
|
||||
i_out = 0;
|
||||
for (i = 0; args[i]; i++) {
|
||||
const char *a = args[i];
|
||||
|
||||
result[i_out++] = '"';
|
||||
for (; *a; a++) {
|
||||
switch (*a) {
|
||||
case '"':
|
||||
result[i_out++] = '\\';
|
||||
result[i_out++] = *a;
|
||||
break;
|
||||
default:
|
||||
result[i_out++] = *a;
|
||||
break;
|
||||
}
|
||||
}
|
||||
result[i_out++] = '"';
|
||||
result[i_out++] = ' ';
|
||||
}
|
||||
SDL_assert(i_out == len);
|
||||
result[len - 1] = '\0';
|
||||
*args_out = result;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool join_env(const char * const *env, char **environment_out)
|
||||
{
|
||||
size_t len;
|
||||
const char * const *var;
|
||||
char *result;
|
||||
|
||||
if (!env) {
|
||||
*environment_out = NULL;
|
||||
return true;
|
||||
}
|
||||
|
||||
len = 0;
|
||||
for (var = env; *var; var++) {
|
||||
len += SDL_strlen(*var) + 1;
|
||||
}
|
||||
result = SDL_malloc(len + 1);
|
||||
if (!result) {
|
||||
return false;
|
||||
}
|
||||
|
||||
len = 0;
|
||||
for (var = env; *var; var++) {
|
||||
size_t l = SDL_strlen(*var);
|
||||
SDL_memcpy(result + len, *var, l);
|
||||
result[len + l] = '\0';
|
||||
len += l + 1;
|
||||
}
|
||||
result[len] = '\0';
|
||||
|
||||
*environment_out = result;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SDL_SYS_CreateProcessWithProperties(SDL_Process *process, SDL_PropertiesID props)
|
||||
{
|
||||
const char * const *args = SDL_GetPointerProperty(props, SDL_PROP_PROCESS_CREATE_ARGS_POINTER, NULL);
|
||||
const char * const *env = SDL_GetPointerProperty(props, SDL_PROP_PROCESS_CREATE_ENVIRONMENT_POINTER, NULL);
|
||||
SDL_ProcessIO stdin_option = (SDL_ProcessIO)SDL_GetNumberProperty(props, SDL_PROP_PROCESS_CREATE_STDIN_NUMBER, SDL_PROCESS_STDIO_NULL);
|
||||
SDL_ProcessIO stdout_option = (SDL_ProcessIO)SDL_GetNumberProperty(props, SDL_PROP_PROCESS_CREATE_STDOUT_NUMBER, SDL_PROCESS_STDIO_INHERITED);
|
||||
SDL_ProcessIO stderr_option = (SDL_ProcessIO)SDL_GetNumberProperty(props, SDL_PROP_PROCESS_CREATE_STDERR_NUMBER, SDL_PROCESS_STDIO_INHERITED);
|
||||
bool redirect_stderr = SDL_GetBooleanProperty(props, SDL_PROP_PROCESS_CREATE_STDERR_TO_STDOUT_BOOLEAN, false) &&
|
||||
!SDL_HasProperty(props, SDL_PROP_PROCESS_CREATE_STDERR_NUMBER);
|
||||
char *createprocess_cmdline = NULL;
|
||||
char *createprocess_env = NULL;
|
||||
STARTUPINFOA startup_info;
|
||||
DWORD creation_flags;
|
||||
char *create_process_cwd;
|
||||
SECURITY_ATTRIBUTES security_attributes;
|
||||
HANDLE stdin_pipe[2] = { INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE };
|
||||
HANDLE stdout_pipe[2] = { INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE };
|
||||
HANDLE stderr_pipe[2] = { INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE };
|
||||
bool result = false;
|
||||
|
||||
// Keep the malloc() before exec() so that an OOM won't run a process at all
|
||||
SDL_ProcessData *data = SDL_calloc(1, sizeof(*data));
|
||||
if (!data) {
|
||||
return false;
|
||||
}
|
||||
process->internal = data;
|
||||
|
||||
if (!join_arguments(args, &createprocess_cmdline)) {
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (!join_env(env, &createprocess_env)) {
|
||||
goto done;
|
||||
}
|
||||
|
||||
creation_flags = 0;
|
||||
|
||||
SDL_zero(startup_info);
|
||||
startup_info.cb = sizeof(startup_info);
|
||||
startup_info.dwFlags |= STARTF_USESTDHANDLES;
|
||||
startup_info.hStdInput = INVALID_HANDLE_VALUE;
|
||||
startup_info.hStdOutput = INVALID_HANDLE_VALUE;
|
||||
startup_info.hStdError = INVALID_HANDLE_VALUE;
|
||||
|
||||
SDL_zero(security_attributes);
|
||||
security_attributes.nLength = sizeof(SECURITY_ATTRIBUTES);
|
||||
security_attributes.bInheritHandle = TRUE;
|
||||
security_attributes.lpSecurityDescriptor = NULL;
|
||||
|
||||
switch (stdin_option) {
|
||||
case SDL_PROCESS_STDIO_REDIRECT:
|
||||
if (!SetupRedirect(props, SDL_PROP_PROCESS_CREATE_STDIN_POINTER, &startup_info.hStdInput)) {
|
||||
goto done;
|
||||
}
|
||||
break;
|
||||
case SDL_PROCESS_STDIO_APP:
|
||||
if (!CreatePipe(&stdin_pipe[READ_END], &stdin_pipe[WRITE_END], &security_attributes, 0)) {
|
||||
stdin_pipe[READ_END] = INVALID_HANDLE_VALUE;
|
||||
stdin_pipe[WRITE_END] = INVALID_HANDLE_VALUE;
|
||||
goto done;
|
||||
}
|
||||
if (!SetHandleInformation(stdin_pipe[WRITE_END], HANDLE_FLAG_INHERIT, 0) ) {
|
||||
WIN_SetError("SetHandleInformation()");
|
||||
goto done;
|
||||
}
|
||||
startup_info.hStdInput = stdin_pipe[READ_END];
|
||||
break;
|
||||
case SDL_PROCESS_STDIO_NULL:
|
||||
startup_info.hStdInput = CreateFile(TEXT("\\\\.\\NUL"), GENERIC_ALL, 0, &security_attributes, OPEN_EXISTING, 0, NULL);
|
||||
break;
|
||||
case SDL_PROCESS_STDIO_INHERITED:
|
||||
default:
|
||||
if (!DuplicateHandle(GetCurrentProcess(), GetStdHandle(STD_INPUT_HANDLE),
|
||||
GetCurrentProcess(), &startup_info.hStdInput,
|
||||
0, TRUE, DUPLICATE_SAME_ACCESS)) {
|
||||
startup_info.hStdInput = INVALID_HANDLE_VALUE;
|
||||
WIN_SetError("DuplicateHandle()");
|
||||
goto done;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
switch (stdout_option) {
|
||||
case SDL_PROCESS_STDIO_REDIRECT:
|
||||
if (!SetupRedirect(props, SDL_PROP_PROCESS_CREATE_STDOUT_POINTER, &startup_info.hStdOutput)) {
|
||||
goto done;
|
||||
}
|
||||
break;
|
||||
case SDL_PROCESS_STDIO_APP:
|
||||
if (!CreatePipe(&stdout_pipe[READ_END], &stdout_pipe[WRITE_END], &security_attributes, 0)) {
|
||||
stdout_pipe[READ_END] = INVALID_HANDLE_VALUE;
|
||||
stdout_pipe[WRITE_END] = INVALID_HANDLE_VALUE;
|
||||
goto done;
|
||||
}
|
||||
if (!SetHandleInformation(stdout_pipe[READ_END], HANDLE_FLAG_INHERIT, 0) ) {
|
||||
WIN_SetError("SetHandleInformation()");
|
||||
goto done;
|
||||
}
|
||||
startup_info.hStdOutput = stdout_pipe[WRITE_END];
|
||||
break;
|
||||
case SDL_PROCESS_STDIO_NULL:
|
||||
startup_info.hStdOutput = CreateFile(TEXT("\\\\.\\NUL"), GENERIC_ALL, 0, &security_attributes, OPEN_EXISTING, 0, NULL);
|
||||
break;
|
||||
case SDL_PROCESS_STDIO_INHERITED:
|
||||
default:
|
||||
if (!DuplicateHandle(GetCurrentProcess(), GetStdHandle(STD_OUTPUT_HANDLE),
|
||||
GetCurrentProcess(), &startup_info.hStdOutput,
|
||||
0, TRUE, DUPLICATE_SAME_ACCESS)) {
|
||||
startup_info.hStdOutput = INVALID_HANDLE_VALUE;
|
||||
WIN_SetError("DuplicateHandle()");
|
||||
goto done;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (redirect_stderr) {
|
||||
if (!DuplicateHandle(GetCurrentProcess(), startup_info.hStdOutput,
|
||||
GetCurrentProcess(), &startup_info.hStdError,
|
||||
0, TRUE, DUPLICATE_SAME_ACCESS)) {
|
||||
startup_info.hStdError = INVALID_HANDLE_VALUE;
|
||||
WIN_SetError("DuplicateHandle()");
|
||||
goto done;
|
||||
}
|
||||
} else {
|
||||
switch (stderr_option) {
|
||||
case SDL_PROCESS_STDIO_REDIRECT:
|
||||
if (!SetupRedirect(props, SDL_PROP_PROCESS_CREATE_STDERR_POINTER, &startup_info.hStdError)) {
|
||||
goto done;
|
||||
}
|
||||
break;
|
||||
case SDL_PROCESS_STDIO_APP:
|
||||
if (!CreatePipe(&stderr_pipe[READ_END], &stderr_pipe[WRITE_END], &security_attributes, 0)) {
|
||||
stderr_pipe[READ_END] = INVALID_HANDLE_VALUE;
|
||||
stderr_pipe[WRITE_END] = INVALID_HANDLE_VALUE;
|
||||
goto done;
|
||||
}
|
||||
if (!SetHandleInformation(stderr_pipe[READ_END], HANDLE_FLAG_INHERIT, 0) ) {
|
||||
WIN_SetError("SetHandleInformation()");
|
||||
goto done;
|
||||
}
|
||||
startup_info.hStdError = stderr_pipe[WRITE_END];
|
||||
break;
|
||||
case SDL_PROCESS_STDIO_NULL:
|
||||
startup_info.hStdError = CreateFile(TEXT("\\\\.\\NUL"), GENERIC_ALL, 0, &security_attributes, OPEN_EXISTING, 0, NULL);
|
||||
break;
|
||||
case SDL_PROCESS_STDIO_INHERITED:
|
||||
default:
|
||||
if (!DuplicateHandle(GetCurrentProcess(), GetStdHandle(STD_ERROR_HANDLE),
|
||||
GetCurrentProcess(), &startup_info.hStdError,
|
||||
0, TRUE, DUPLICATE_SAME_ACCESS)) {
|
||||
startup_info.hStdError = INVALID_HANDLE_VALUE;
|
||||
WIN_SetError("DuplicateHandle()");
|
||||
goto done;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: This should use CreateProcessW()
|
||||
// FIXME: current directory as extended option? SDL_CreatProcessWithProperties
|
||||
create_process_cwd = NULL;
|
||||
if (!CreateProcessA(NULL, createprocess_cmdline, NULL, NULL, TRUE, creation_flags, createprocess_env, create_process_cwd, &startup_info, &data->process_information)) {
|
||||
WIN_SetError("CreateProcessA");
|
||||
goto done;
|
||||
}
|
||||
|
||||
SDL_SetNumberProperty(process->props, SDL_PROP_PROCESS_PID_NUMBER, data->process_information.dwProcessId);
|
||||
|
||||
if (stdin_option == SDL_PROCESS_STDIO_APP) {
|
||||
if (!SetupStream(process, stdin_pipe[WRITE_END], "wb", SDL_PROP_PROCESS_STDIN_POINTER)) {
|
||||
CloseHandle(stdin_pipe[WRITE_END]);
|
||||
stdin_pipe[WRITE_END] = INVALID_HANDLE_VALUE;
|
||||
}
|
||||
}
|
||||
if (stdout_option == SDL_PROCESS_STDIO_APP) {
|
||||
if (!SetupStream(process, stdout_pipe[READ_END], "rb", SDL_PROP_PROCESS_STDOUT_POINTER)) {
|
||||
CloseHandle(stdout_pipe[READ_END]);
|
||||
stdout_pipe[READ_END] = INVALID_HANDLE_VALUE;
|
||||
}
|
||||
}
|
||||
if (stderr_option == SDL_PROCESS_STDIO_APP) {
|
||||
if (!SetupStream(process, stderr_pipe[READ_END], "rb", SDL_PROP_PROCESS_STDERR_POINTER)) {
|
||||
CloseHandle(stderr_pipe[READ_END]);
|
||||
stderr_pipe[READ_END] = INVALID_HANDLE_VALUE;
|
||||
}
|
||||
}
|
||||
|
||||
result = true;
|
||||
|
||||
done:
|
||||
if (startup_info.hStdInput != INVALID_HANDLE_VALUE &&
|
||||
startup_info.hStdInput != stdin_pipe[READ_END]) {
|
||||
CloseHandle(startup_info.hStdInput);
|
||||
}
|
||||
if (startup_info.hStdOutput != INVALID_HANDLE_VALUE &&
|
||||
startup_info.hStdOutput != stdout_pipe[WRITE_END]) {
|
||||
CloseHandle(startup_info.hStdOutput);
|
||||
}
|
||||
if (startup_info.hStdError != INVALID_HANDLE_VALUE &&
|
||||
startup_info.hStdError != stderr_pipe[WRITE_END]) {
|
||||
CloseHandle(startup_info.hStdError);
|
||||
}
|
||||
if (stdin_pipe[READ_END] != INVALID_HANDLE_VALUE) {
|
||||
CloseHandle(stdin_pipe[READ_END]);
|
||||
}
|
||||
if (stdout_pipe[WRITE_END] != INVALID_HANDLE_VALUE) {
|
||||
CloseHandle(stdout_pipe[WRITE_END]);
|
||||
}
|
||||
if (stderr_pipe[WRITE_END] != INVALID_HANDLE_VALUE) {
|
||||
CloseHandle(stderr_pipe[WRITE_END]);
|
||||
}
|
||||
SDL_free(createprocess_cmdline);
|
||||
SDL_free(createprocess_env);
|
||||
|
||||
if (!result) {
|
||||
if (stdin_pipe[WRITE_END] != INVALID_HANDLE_VALUE) {
|
||||
CloseHandle(stdin_pipe[WRITE_END]);
|
||||
}
|
||||
if (stdout_pipe[READ_END] != INVALID_HANDLE_VALUE) {
|
||||
CloseHandle(stdout_pipe[READ_END]);
|
||||
}
|
||||
if (stderr_pipe[READ_END] != INVALID_HANDLE_VALUE) {
|
||||
CloseHandle(stderr_pipe[READ_END]);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool SDL_SYS_KillProcess(SDL_Process *process, SDL_bool force)
|
||||
{
|
||||
if (!TerminateProcess(process->internal->process_information.hProcess, 1)) {
|
||||
return WIN_SetError("TerminateProcess failed");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SDL_SYS_WaitProcess(SDL_Process *process, SDL_bool block, int *exitcode)
|
||||
{
|
||||
DWORD result;
|
||||
|
||||
result = WaitForSingleObject(process->internal->process_information.hProcess, block ? INFINITE : 0);
|
||||
|
||||
if (result == WAIT_OBJECT_0 || result == WAIT_TIMEOUT) {
|
||||
DWORD rc;
|
||||
if (!GetExitCodeProcess(process->internal->process_information.hProcess, &rc)) {
|
||||
return WIN_SetError("GetExitCodeProcess");
|
||||
}
|
||||
if (exitcode) {
|
||||
*exitcode = (int)rc;
|
||||
}
|
||||
return true;
|
||||
} else if (result == WAIT_FAILED) {
|
||||
return WIN_SetError("WaitForSingleObject(hProcess) returned WAIT_FAILED");
|
||||
} else {
|
||||
SDL_ClearError();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void SDL_SYS_DestroyProcess(SDL_Process *process)
|
||||
{
|
||||
SDL_ProcessData *data = process->internal;
|
||||
SDL_IOStream *io;
|
||||
|
||||
io = (SDL_IOStream *)SDL_GetPointerProperty(process->props, SDL_PROP_PROCESS_STDIN_POINTER, NULL);
|
||||
if (io) {
|
||||
SDL_CloseIO(io);
|
||||
}
|
||||
io = (SDL_IOStream *)SDL_GetPointerProperty(process->props, SDL_PROP_PROCESS_STDERR_POINTER, NULL);
|
||||
if (io) {
|
||||
SDL_CloseIO(io);
|
||||
}
|
||||
io = (SDL_IOStream *)SDL_GetPointerProperty(process->props, SDL_PROP_PROCESS_STDOUT_POINTER, NULL);
|
||||
if (io) {
|
||||
SDL_CloseIO(io);
|
||||
}
|
||||
CloseHandle(data->process_information.hThread);
|
||||
CloseHandle(data->process_information.hProcess);
|
||||
SDL_free(data);
|
||||
}
|
||||
|
||||
#endif // SDL_PROCESS_WINDOWS
|
Reference in New Issue
Block a user