Add SDL_Process subsystem

This commit is contained in:
Semphris
2024-08-29 13:06:25 -04:00
committed by Sam Lantinga
parent 6c83491116
commit 9eea8234e6
30 changed files with 2414 additions and 1 deletions

View File

@@ -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;

View File

@@ -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

View File

@@ -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
View 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);
}

View 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);

View 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

View 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

View 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