summaryrefslogtreecommitdiff
path: root/contrib/SDL-3.2.8/src/process
diff options
context:
space:
mode:
author3gg <3gg@shellblade.net>2025-12-27 12:03:39 -0800
committer3gg <3gg@shellblade.net>2025-12-27 12:03:39 -0800
commit5a079a2d114f96d4847d1ee305d5b7c16eeec50e (patch)
tree8926ab44f168acf787d8e19608857b3af0f82758 /contrib/SDL-3.2.8/src/process
Initial commit
Diffstat (limited to 'contrib/SDL-3.2.8/src/process')
-rw-r--r--contrib/SDL-3.2.8/src/process/SDL_process.c196
-rw-r--r--contrib/SDL-3.2.8/src/process/SDL_sysprocess.h37
-rw-r--r--contrib/SDL-3.2.8/src/process/dummy/SDL_dummyprocess.c48
-rw-r--r--contrib/SDL-3.2.8/src/process/posix/SDL_posixprocess.c483
-rw-r--r--contrib/SDL-3.2.8/src/process/windows/SDL_windowsprocess.c557
5 files changed, 1321 insertions, 0 deletions
diff --git a/contrib/SDL-3.2.8/src/process/SDL_process.c b/contrib/SDL-3.2.8/src/process/SDL_process.c
new file mode 100644
index 0000000..3ccf5d5
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/process/SDL_process.c
@@ -0,0 +1,196 @@
1/*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
4
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any damages
7 arising from the use of this software.
8
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it
11 freely, subject to the following restrictions:
12
13 1. The origin of this software must not be misrepresented; you must not
14 claim that you wrote the original software. If you use this software
15 in a product, an acknowledgment in the product documentation would be
16 appreciated but is not required.
17 2. Altered source versions must be plainly marked as such, and must not be
18 misrepresented as being the original software.
19 3. This notice may not be removed or altered from any source distribution.
20*/
21#include "SDL_internal.h"
22
23#include "SDL_sysprocess.h"
24
25
26SDL_Process *SDL_CreateProcess(const char * const *args, bool pipe_stdio)
27{
28 if (!args || !args[0] || !args[0][0]) {
29 SDL_InvalidParamError("args");
30 return NULL;
31 }
32
33 SDL_Process *process;
34 SDL_PropertiesID props = SDL_CreateProperties();
35 SDL_SetPointerProperty(props, SDL_PROP_PROCESS_CREATE_ARGS_POINTER, (void *)args);
36 if (pipe_stdio) {
37 SDL_SetNumberProperty(props, SDL_PROP_PROCESS_CREATE_STDIN_NUMBER, SDL_PROCESS_STDIO_APP);
38 SDL_SetNumberProperty(props, SDL_PROP_PROCESS_CREATE_STDOUT_NUMBER, SDL_PROCESS_STDIO_APP);
39 }
40 process = SDL_CreateProcessWithProperties(props);
41 SDL_DestroyProperties(props);
42 return process;
43}
44
45SDL_Process *SDL_CreateProcessWithProperties(SDL_PropertiesID props)
46{
47 const char * const *args = SDL_GetPointerProperty(props, SDL_PROP_PROCESS_CREATE_ARGS_POINTER, NULL);
48 if (!args || !args[0] || !args[0][0]) {
49 SDL_InvalidParamError("SDL_PROP_PROCESS_CREATE_ARGS_POINTER");
50 return NULL;
51 }
52
53 SDL_Process *process = (SDL_Process *)SDL_calloc(1, sizeof(*process));
54 if (!process) {
55 return NULL;
56 }
57 process->background = SDL_GetBooleanProperty(props, SDL_PROP_PROCESS_CREATE_BACKGROUND_BOOLEAN, false);
58
59 process->props = SDL_CreateProperties();
60 if (!process->props) {
61 SDL_DestroyProcess(process);
62 return NULL;
63 }
64 SDL_SetBooleanProperty(process->props, SDL_PROP_PROCESS_BACKGROUND_BOOLEAN, process->background);
65
66 if (!SDL_SYS_CreateProcessWithProperties(process, props)) {
67 SDL_DestroyProcess(process);
68 return NULL;
69 }
70 process->alive = true;
71 return process;
72}
73
74SDL_PropertiesID SDL_GetProcessProperties(SDL_Process *process)
75{
76 if (!process) {
77 return SDL_InvalidParamError("process");
78 }
79 return process->props;
80}
81
82void *SDL_ReadProcess(SDL_Process *process, size_t *datasize, int *exitcode)
83{
84 void *result;
85
86 if (datasize) {
87 *datasize = 0;
88 }
89 if (exitcode) {
90 *exitcode = -1;
91 }
92
93 if (!process) {
94 SDL_InvalidParamError("process");
95 return NULL;
96 }
97
98 SDL_IOStream *io = (SDL_IOStream *)SDL_GetPointerProperty(process->props, SDL_PROP_PROCESS_STDOUT_POINTER, NULL);
99 if (!io) {
100 SDL_SetError("Process not created with I/O enabled");
101 return NULL;
102 }
103
104 result = SDL_LoadFile_IO(io, datasize, false);
105
106 SDL_WaitProcess(process, true, exitcode);
107
108 return result;
109}
110
111SDL_IOStream *SDL_GetProcessInput(SDL_Process *process)
112{
113 if (!process) {
114 SDL_InvalidParamError("process");
115 return NULL;
116 }
117
118 SDL_IOStream *io = (SDL_IOStream *)SDL_GetPointerProperty(process->props, SDL_PROP_PROCESS_STDIN_POINTER, NULL);
119 if (!io) {
120 SDL_SetError("Process not created with standard input available");
121 return NULL;
122 }
123
124 return io;
125}
126
127SDL_IOStream *SDL_GetProcessOutput(SDL_Process *process)
128{
129 if (!process) {
130 SDL_InvalidParamError("process");
131 return NULL;
132 }
133
134 SDL_IOStream *io = (SDL_IOStream *)SDL_GetPointerProperty(process->props, SDL_PROP_PROCESS_STDOUT_POINTER, NULL);
135 if (!io) {
136 SDL_SetError("Process not created with standard output available");
137 return NULL;
138 }
139
140 return io;
141}
142
143bool SDL_KillProcess(SDL_Process *process, bool force)
144{
145 if (!process) {
146 return SDL_InvalidParamError("process");
147 }
148
149 if (!process->alive) {
150 return SDL_SetError("Process isn't running");
151 }
152
153 return SDL_SYS_KillProcess(process, force);
154}
155
156bool SDL_WaitProcess(SDL_Process *process, bool block, int *exitcode)
157{
158 if (!process) {
159 return SDL_InvalidParamError("process");
160 }
161
162 if (!process->alive) {
163 if (exitcode) {
164 *exitcode = process->exitcode;
165 }
166 return true;
167 }
168
169 if (SDL_SYS_WaitProcess(process, block, &process->exitcode)) {
170 process->alive = false;
171 if (exitcode) {
172 if (process->background) {
173 process->exitcode = 0;
174 }
175 *exitcode = process->exitcode;
176 }
177 return true;
178 }
179 return false;
180}
181
182void SDL_DestroyProcess(SDL_Process *process)
183{
184 if (!process) {
185 return;
186 }
187
188 // Check to see if the process has exited, will reap zombies on POSIX platforms
189 if (process->alive) {
190 SDL_WaitProcess(process, false, NULL);
191 }
192
193 SDL_SYS_DestroyProcess(process);
194 SDL_DestroyProperties(process->props);
195 SDL_free(process);
196}
diff --git a/contrib/SDL-3.2.8/src/process/SDL_sysprocess.h b/contrib/SDL-3.2.8/src/process/SDL_sysprocess.h
new file mode 100644
index 0000000..839ef9d
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/process/SDL_sysprocess.h
@@ -0,0 +1,37 @@
1/*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
4
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any damages
7 arising from the use of this software.
8
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it
11 freely, subject to the following restrictions:
12
13 1. The origin of this software must not be misrepresented; you must not
14 claim that you wrote the original software. If you use this software
15 in a product, an acknowledgment in the product documentation would be
16 appreciated but is not required.
17 2. Altered source versions must be plainly marked as such, and must not be
18 misrepresented as being the original software.
19 3. This notice may not be removed or altered from any source distribution.
20*/
21#include "SDL_internal.h"
22
23typedef struct SDL_ProcessData SDL_ProcessData;
24
25struct SDL_Process
26{
27 bool alive;
28 bool background;
29 int exitcode;
30 SDL_PropertiesID props;
31 SDL_ProcessData *internal;
32};
33
34bool SDL_SYS_CreateProcessWithProperties(SDL_Process *process, SDL_PropertiesID props);
35bool SDL_SYS_KillProcess(SDL_Process *process, bool force);
36bool SDL_SYS_WaitProcess(SDL_Process *process, bool block, int *exitcode);
37void SDL_SYS_DestroyProcess(SDL_Process *process);
diff --git a/contrib/SDL-3.2.8/src/process/dummy/SDL_dummyprocess.c b/contrib/SDL-3.2.8/src/process/dummy/SDL_dummyprocess.c
new file mode 100644
index 0000000..ba69e4a
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/process/dummy/SDL_dummyprocess.c
@@ -0,0 +1,48 @@
1/*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
4
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any damages
7 arising from the use of this software.
8
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it
11 freely, subject to the following restrictions:
12
13 1. The origin of this software must not be misrepresented; you must not
14 claim that you wrote the original software. If you use this software
15 in a product, an acknowledgment in the product documentation would be
16 appreciated but is not required.
17 2. Altered source versions must be plainly marked as such, and must not be
18 misrepresented as being the original software.
19 3. This notice may not be removed or altered from any source distribution.
20*/
21#include "SDL_internal.h"
22
23#ifdef SDL_PROCESS_DUMMY
24
25#include "../SDL_sysprocess.h"
26
27
28bool SDL_SYS_CreateProcessWithProperties(SDL_Process *process, SDL_PropertiesID props)
29{
30 return SDL_Unsupported();
31}
32
33bool SDL_SYS_KillProcess(SDL_Process *process, bool force)
34{
35 return SDL_Unsupported();
36}
37
38bool SDL_SYS_WaitProcess(SDL_Process *process, bool block, int *exitcode)
39{
40 return SDL_Unsupported();
41}
42
43void SDL_SYS_DestroyProcess(SDL_Process *process)
44{
45 return;
46}
47
48#endif // SDL_PROCESS_DUMMY
diff --git a/contrib/SDL-3.2.8/src/process/posix/SDL_posixprocess.c b/contrib/SDL-3.2.8/src/process/posix/SDL_posixprocess.c
new file mode 100644
index 0000000..98b9f75
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/process/posix/SDL_posixprocess.c
@@ -0,0 +1,483 @@
1/*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
4
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any damages
7 arising from the use of this software.
8
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it
11 freely, subject to the following restrictions:
12
13 1. The origin of this software must not be misrepresented; you must not
14 claim that you wrote the original software. If you use this software
15 in a product, an acknowledgment in the product documentation would be
16 appreciated but is not required.
17 2. Altered source versions must be plainly marked as such, and must not be
18 misrepresented as being the original software.
19 3. This notice may not be removed or altered from any source distribution.
20*/
21#include "SDL_internal.h"
22
23#ifdef SDL_PROCESS_POSIX
24
25#include <dirent.h>
26#include <fcntl.h>
27#include <errno.h>
28#include <signal.h>
29#include <spawn.h>
30#include <stdio.h>
31#include <stdlib.h>
32#include <string.h>
33#include <unistd.h>
34#include <sys/wait.h>
35
36#include "../SDL_sysprocess.h"
37#include "../../io/SDL_iostream_c.h"
38
39
40#define READ_END 0
41#define WRITE_END 1
42
43struct SDL_ProcessData {
44 pid_t pid;
45};
46
47static void CleanupStream(void *userdata, void *value)
48{
49 SDL_Process *process = (SDL_Process *)value;
50 const char *property = (const char *)userdata;
51
52 SDL_ClearProperty(process->props, property);
53}
54
55static bool SetupStream(SDL_Process *process, int fd, const char *mode, const char *property)
56{
57 // Set the file descriptor to non-blocking mode
58 fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK);
59
60 SDL_IOStream *io = SDL_IOFromFD(fd, true);
61 if (!io) {
62 return false;
63 }
64
65 SDL_SetPointerPropertyWithCleanup(SDL_GetIOProperties(io), "SDL.internal.process", process, CleanupStream, (void *)property);
66 SDL_SetPointerProperty(process->props, property, io);
67 return true;
68}
69
70static void IgnoreSignal(int sig)
71{
72 struct sigaction action;
73
74 sigaction(SIGPIPE, NULL, &action);
75#ifdef HAVE_SA_SIGACTION
76 if (action.sa_handler == SIG_DFL && (void (*)(int))action.sa_sigaction == SIG_DFL) {
77#else
78 if (action.sa_handler == SIG_DFL) {
79#endif
80 action.sa_handler = SIG_IGN;
81 sigaction(sig, &action, NULL);
82 }
83}
84
85static bool CreatePipe(int fds[2])
86{
87 if (pipe(fds) < 0) {
88 return false;
89 }
90
91 // Make sure the pipe isn't accidentally inherited by another thread creating a process
92 fcntl(fds[READ_END], F_SETFD, fcntl(fds[READ_END], F_GETFD) | FD_CLOEXEC);
93 fcntl(fds[WRITE_END], F_SETFD, fcntl(fds[WRITE_END], F_GETFD) | FD_CLOEXEC);
94
95 // Make sure we don't crash if we write when the pipe is closed
96 IgnoreSignal(SIGPIPE);
97
98 return true;
99}
100
101static bool GetStreamFD(SDL_PropertiesID props, const char *property, int *result)
102{
103 SDL_IOStream *io = (SDL_IOStream *)SDL_GetPointerProperty(props, property, NULL);
104 if (!io) {
105 SDL_SetError("%s is not set", property);
106 return false;
107 }
108
109 int fd = (int)SDL_GetNumberProperty(SDL_GetIOProperties(io), SDL_PROP_IOSTREAM_FILE_DESCRIPTOR_NUMBER, -1);
110 if (fd < 0) {
111 SDL_SetError("%s doesn't have SDL_PROP_IOSTREAM_FILE_DESCRIPTOR_NUMBER available", property);
112 return false;
113 }
114
115 *result = fd;
116 return true;
117}
118
119static bool AddFileDescriptorCloseActions(posix_spawn_file_actions_t *fa)
120{
121 DIR *dir = opendir("/proc/self/fd");
122 if (dir) {
123 struct dirent *entry;
124 while ((entry = readdir(dir)) != NULL) {
125 int fd = SDL_atoi(entry->d_name);
126 if (fd <= STDERR_FILENO) {
127 continue;
128 }
129
130 int flags = fcntl(fd, F_GETFD);
131 if (flags < 0 || (flags & FD_CLOEXEC)) {
132 continue;
133 }
134 if (posix_spawn_file_actions_addclose(fa, fd) != 0) {
135 closedir(dir);
136 return SDL_SetError("posix_spawn_file_actions_addclose failed: %s", strerror(errno));
137 }
138 }
139 closedir(dir);
140 } else {
141 for (int fd = (int)(sysconf(_SC_OPEN_MAX) - 1); fd > STDERR_FILENO; --fd) {
142 int flags = fcntl(fd, F_GETFD);
143 if (flags < 0 || (flags & FD_CLOEXEC)) {
144 continue;
145 }
146 if (posix_spawn_file_actions_addclose(fa, fd) != 0) {
147 return SDL_SetError("posix_spawn_file_actions_addclose failed: %s", strerror(errno));
148 }
149 }
150 }
151 return true;
152}
153
154bool SDL_SYS_CreateProcessWithProperties(SDL_Process *process, SDL_PropertiesID props)
155{
156 char * const *args = SDL_GetPointerProperty(props, SDL_PROP_PROCESS_CREATE_ARGS_POINTER, NULL);
157 SDL_Environment *env = SDL_GetPointerProperty(props, SDL_PROP_PROCESS_CREATE_ENVIRONMENT_POINTER, SDL_GetEnvironment());
158 char **envp = NULL;
159 SDL_ProcessIO stdin_option = (SDL_ProcessIO)SDL_GetNumberProperty(props, SDL_PROP_PROCESS_CREATE_STDIN_NUMBER, SDL_PROCESS_STDIO_NULL);
160 SDL_ProcessIO stdout_option = (SDL_ProcessIO)SDL_GetNumberProperty(props, SDL_PROP_PROCESS_CREATE_STDOUT_NUMBER, SDL_PROCESS_STDIO_INHERITED);
161 SDL_ProcessIO stderr_option = (SDL_ProcessIO)SDL_GetNumberProperty(props, SDL_PROP_PROCESS_CREATE_STDERR_NUMBER, SDL_PROCESS_STDIO_INHERITED);
162 bool redirect_stderr = SDL_GetBooleanProperty(props, SDL_PROP_PROCESS_CREATE_STDERR_TO_STDOUT_BOOLEAN, false) &&
163 !SDL_HasProperty(props, SDL_PROP_PROCESS_CREATE_STDERR_NUMBER);
164 int stdin_pipe[2] = { -1, -1 };
165 int stdout_pipe[2] = { -1, -1 };
166 int stderr_pipe[2] = { -1, -1 };
167 int fd = -1;
168
169 // Keep the malloc() before exec() so that an OOM won't run a process at all
170 envp = SDL_GetEnvironmentVariables(env);
171 if (!envp) {
172 return false;
173 }
174
175 SDL_ProcessData *data = SDL_calloc(1, sizeof(*data));
176 if (!data) {
177 SDL_free(envp);
178 return false;
179 }
180 process->internal = data;
181
182 posix_spawnattr_t attr;
183 posix_spawn_file_actions_t fa;
184
185 if (posix_spawnattr_init(&attr) != 0) {
186 SDL_SetError("posix_spawnattr_init failed: %s", strerror(errno));
187 goto posix_spawn_fail_none;
188 }
189
190 if (posix_spawn_file_actions_init(&fa) != 0) {
191 SDL_SetError("posix_spawn_file_actions_init failed: %s", strerror(errno));
192 goto posix_spawn_fail_attr;
193 }
194
195 // Background processes don't have access to the terminal
196 if (process->background) {
197 if (stdin_option == SDL_PROCESS_STDIO_INHERITED) {
198 stdin_option = SDL_PROCESS_STDIO_NULL;
199 }
200 if (stdout_option == SDL_PROCESS_STDIO_INHERITED) {
201 stdout_option = SDL_PROCESS_STDIO_NULL;
202 }
203 if (stderr_option == SDL_PROCESS_STDIO_INHERITED) {
204 stderr_option = SDL_PROCESS_STDIO_NULL;
205 }
206 }
207
208 switch (stdin_option) {
209 case SDL_PROCESS_STDIO_REDIRECT:
210 if (!GetStreamFD(props, SDL_PROP_PROCESS_CREATE_STDIN_POINTER, &fd)) {
211 goto posix_spawn_fail_all;
212 }
213 if (posix_spawn_file_actions_adddup2(&fa, fd, STDIN_FILENO) != 0) {
214 SDL_SetError("posix_spawn_file_actions_adddup2 failed: %s", strerror(errno));
215 goto posix_spawn_fail_all;
216 }
217 break;
218 case SDL_PROCESS_STDIO_APP:
219 if (!CreatePipe(stdin_pipe)) {
220 goto posix_spawn_fail_all;
221 }
222 if (posix_spawn_file_actions_adddup2(&fa, stdin_pipe[READ_END], STDIN_FILENO) != 0) {
223 SDL_SetError("posix_spawn_file_actions_adddup2 failed: %s", strerror(errno));
224 goto posix_spawn_fail_all;
225 }
226 break;
227 case SDL_PROCESS_STDIO_NULL:
228 if (posix_spawn_file_actions_addopen(&fa, STDIN_FILENO, "/dev/null", O_RDONLY, 0) != 0) {
229 SDL_SetError("posix_spawn_file_actions_addopen failed: %s", strerror(errno));
230 goto posix_spawn_fail_all;
231 }
232 break;
233 case SDL_PROCESS_STDIO_INHERITED:
234 default:
235 break;
236 }
237
238 switch (stdout_option) {
239 case SDL_PROCESS_STDIO_REDIRECT:
240 if (!GetStreamFD(props, SDL_PROP_PROCESS_CREATE_STDOUT_POINTER, &fd)) {
241 goto posix_spawn_fail_all;
242 }
243 if (posix_spawn_file_actions_adddup2(&fa, fd, STDOUT_FILENO) != 0) {
244 SDL_SetError("posix_spawn_file_actions_adddup2 failed: %s", strerror(errno));
245 goto posix_spawn_fail_all;
246 }
247 break;
248 case SDL_PROCESS_STDIO_APP:
249 if (!CreatePipe(stdout_pipe)) {
250 goto posix_spawn_fail_all;
251 }
252 if (posix_spawn_file_actions_adddup2(&fa, stdout_pipe[WRITE_END], STDOUT_FILENO) != 0) {
253 SDL_SetError("posix_spawn_file_actions_adddup2 failed: %s", strerror(errno));
254 goto posix_spawn_fail_all;
255 }
256 break;
257 case SDL_PROCESS_STDIO_NULL:
258 if (posix_spawn_file_actions_addopen(&fa, STDOUT_FILENO, "/dev/null", O_WRONLY, 0644) != 0) {
259 SDL_SetError("posix_spawn_file_actions_addopen failed: %s", strerror(errno));
260 goto posix_spawn_fail_all;
261 }
262 break;
263 case SDL_PROCESS_STDIO_INHERITED:
264 default:
265 break;
266 }
267
268 if (redirect_stderr) {
269 if (posix_spawn_file_actions_adddup2(&fa, STDOUT_FILENO, STDERR_FILENO) != 0) {
270 SDL_SetError("posix_spawn_file_actions_adddup2 failed: %s", strerror(errno));
271 goto posix_spawn_fail_all;
272 }
273 } else {
274 switch (stderr_option) {
275 case SDL_PROCESS_STDIO_REDIRECT:
276 if (!GetStreamFD(props, SDL_PROP_PROCESS_CREATE_STDERR_POINTER, &fd)) {
277 goto posix_spawn_fail_all;
278 }
279 if (posix_spawn_file_actions_adddup2(&fa, fd, STDERR_FILENO) != 0) {
280 SDL_SetError("posix_spawn_file_actions_adddup2 failed: %s", strerror(errno));
281 goto posix_spawn_fail_all;
282 }
283 break;
284 case SDL_PROCESS_STDIO_APP:
285 if (!CreatePipe(stderr_pipe)) {
286 goto posix_spawn_fail_all;
287 }
288 if (posix_spawn_file_actions_adddup2(&fa, stderr_pipe[WRITE_END], STDERR_FILENO) != 0) {
289 SDL_SetError("posix_spawn_file_actions_adddup2 failed: %s", strerror(errno));
290 goto posix_spawn_fail_all;
291 }
292 break;
293 case SDL_PROCESS_STDIO_NULL:
294 if (posix_spawn_file_actions_addopen(&fa, STDERR_FILENO, "/dev/null", O_WRONLY, 0644) != 0) {
295 SDL_SetError("posix_spawn_file_actions_addopen failed: %s", strerror(errno));
296 goto posix_spawn_fail_all;
297 }
298 break;
299 case SDL_PROCESS_STDIO_INHERITED:
300 default:
301 break;
302 }
303 }
304
305 if (!AddFileDescriptorCloseActions(&fa)) {
306 goto posix_spawn_fail_all;
307 }
308
309 // Spawn the new process
310 if (process->background) {
311 int status = -1;
312 #ifdef SDL_PLATFORM_APPLE // Apple has vfork marked as deprecated and (as of macOS 10.12) is almost identical to calling fork() anyhow.
313 const pid_t pid = fork();
314 const char *forkname = "fork";
315 #else
316 const pid_t pid = vfork();
317 const char *forkname = "vfork";
318 #endif
319 switch (pid) {
320 case -1:
321 SDL_SetError("%s() failed: %s", forkname, strerror(errno));
322 goto posix_spawn_fail_all;
323
324 case 0:
325 // Detach from the terminal and launch the process
326 setsid();
327 if (posix_spawnp(&data->pid, args[0], &fa, &attr, args, envp) != 0) {
328 _exit(errno);
329 }
330 _exit(0);
331
332 default:
333 if (waitpid(pid, &status, 0) < 0) {
334 SDL_SetError("waitpid() failed: %s", strerror(errno));
335 goto posix_spawn_fail_all;
336 }
337 if (status != 0) {
338 SDL_SetError("posix_spawn() failed: %s", strerror(status));
339 goto posix_spawn_fail_all;
340 }
341 break;
342 }
343 } else {
344 if (posix_spawnp(&data->pid, args[0], &fa, &attr, args, envp) != 0) {
345 SDL_SetError("posix_spawn() failed: %s", strerror(errno));
346 goto posix_spawn_fail_all;
347 }
348 }
349 SDL_SetNumberProperty(process->props, SDL_PROP_PROCESS_PID_NUMBER, data->pid);
350
351 if (stdin_option == SDL_PROCESS_STDIO_APP) {
352 if (!SetupStream(process, stdin_pipe[WRITE_END], "wb", SDL_PROP_PROCESS_STDIN_POINTER)) {
353 close(stdin_pipe[WRITE_END]);
354 }
355 close(stdin_pipe[READ_END]);
356 }
357
358 if (stdout_option == SDL_PROCESS_STDIO_APP) {
359 if (!SetupStream(process, stdout_pipe[READ_END], "rb", SDL_PROP_PROCESS_STDOUT_POINTER)) {
360 close(stdout_pipe[READ_END]);
361 }
362 close(stdout_pipe[WRITE_END]);
363 }
364
365 if (stderr_option == SDL_PROCESS_STDIO_APP) {
366 if (!SetupStream(process, stderr_pipe[READ_END], "rb", SDL_PROP_PROCESS_STDERR_POINTER)) {
367 close(stderr_pipe[READ_END]);
368 }
369 close(stderr_pipe[WRITE_END]);
370 }
371
372 posix_spawn_file_actions_destroy(&fa);
373 posix_spawnattr_destroy(&attr);
374 SDL_free(envp);
375
376 return true;
377
378 /* --------------------------------------------------------------------- */
379
380posix_spawn_fail_all:
381 posix_spawn_file_actions_destroy(&fa);
382
383posix_spawn_fail_attr:
384 posix_spawnattr_destroy(&attr);
385
386posix_spawn_fail_none:
387 if (stdin_pipe[READ_END] >= 0) {
388 close(stdin_pipe[READ_END]);
389 }
390 if (stdin_pipe[WRITE_END] >= 0) {
391 close(stdin_pipe[WRITE_END]);
392 }
393 if (stdout_pipe[READ_END] >= 0) {
394 close(stdout_pipe[READ_END]);
395 }
396 if (stdout_pipe[WRITE_END] >= 0) {
397 close(stdout_pipe[WRITE_END]);
398 }
399 if (stderr_pipe[READ_END] >= 0) {
400 close(stderr_pipe[READ_END]);
401 }
402 if (stderr_pipe[WRITE_END] >= 0) {
403 close(stderr_pipe[WRITE_END]);
404 }
405 SDL_free(envp);
406 return false;
407}
408
409bool SDL_SYS_KillProcess(SDL_Process *process, bool force)
410{
411 int ret = kill(process->internal->pid, force ? SIGKILL : SIGTERM);
412 if (ret == 0) {
413 return true;
414 } else {
415 return SDL_SetError("Could not kill(): %s", strerror(errno));
416 }
417}
418
419bool SDL_SYS_WaitProcess(SDL_Process *process, bool block, int *exitcode)
420{
421 int wstatus = 0;
422 int ret;
423 pid_t pid = process->internal->pid;
424
425 if (process->background) {
426 // We can't wait on the status, so we'll poll to see if it's alive
427 if (block) {
428 while (kill(pid, 0) == 0) {
429 SDL_Delay(10);
430 }
431 } else {
432 if (kill(pid, 0) == 0) {
433 return false;
434 }
435 }
436 *exitcode = 0;
437 return true;
438 } else {
439 ret = waitpid(pid, &wstatus, block ? 0 : WNOHANG);
440 if (ret < 0) {
441 return SDL_SetError("Could not waitpid(): %s", strerror(errno));
442 }
443
444 if (ret == 0) {
445 SDL_ClearError();
446 return false;
447 }
448
449 if (WIFEXITED(wstatus)) {
450 *exitcode = WEXITSTATUS(wstatus);
451 } else if (WIFSIGNALED(wstatus)) {
452 *exitcode = -WTERMSIG(wstatus);
453 } else {
454 *exitcode = -255;
455 }
456
457 return true;
458 }
459}
460
461void SDL_SYS_DestroyProcess(SDL_Process *process)
462{
463 SDL_IOStream *io;
464
465 io = (SDL_IOStream *)SDL_GetPointerProperty(process->props, SDL_PROP_PROCESS_STDIN_POINTER, NULL);
466 if (io) {
467 SDL_CloseIO(io);
468 }
469
470 io = (SDL_IOStream *)SDL_GetPointerProperty(process->props, SDL_PROP_PROCESS_STDOUT_POINTER, NULL);
471 if (io) {
472 SDL_CloseIO(io);
473 }
474
475 io = (SDL_IOStream *)SDL_GetPointerProperty(process->props, SDL_PROP_PROCESS_STDERR_POINTER, NULL);
476 if (io) {
477 SDL_CloseIO(io);
478 }
479
480 SDL_free(process->internal);
481}
482
483#endif // SDL_PROCESS_POSIX
diff --git a/contrib/SDL-3.2.8/src/process/windows/SDL_windowsprocess.c b/contrib/SDL-3.2.8/src/process/windows/SDL_windowsprocess.c
new file mode 100644
index 0000000..c1aee5c
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/process/windows/SDL_windowsprocess.c
@@ -0,0 +1,557 @@
1/*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
4
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any damages
7 arising from the use of this software.
8
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it
11 freely, subject to the following restrictions:
12
13 1. The origin of this software must not be misrepresented; you must not
14 claim that you wrote the original software. If you use this software
15 in a product, an acknowledgment in the product documentation would be
16 appreciated but is not required.
17 2. Altered source versions must be plainly marked as such, and must not be
18 misrepresented as being the original software.
19 3. This notice may not be removed or altered from any source distribution.
20*/
21#include "SDL_internal.h"
22
23#ifdef SDL_PROCESS_WINDOWS
24
25#include "../../core/windows/SDL_windows.h"
26#include "../SDL_sysprocess.h"
27#include "../../io/SDL_iostream_c.h"
28
29#define READ_END 0
30#define WRITE_END 1
31
32struct SDL_ProcessData {
33 PROCESS_INFORMATION process_information;
34};
35
36static void CleanupStream(void *userdata, void *value)
37{
38 SDL_Process *process = (SDL_Process *)value;
39 const char *property = (const char *)userdata;
40
41 SDL_ClearProperty(process->props, property);
42}
43
44static bool SetupStream(SDL_Process *process, HANDLE handle, const char *mode, const char *property)
45{
46 SDL_IOStream *io = SDL_IOFromHandle(handle, mode, true);
47 if (!io) {
48 return false;
49 }
50
51 SDL_SetPointerPropertyWithCleanup(SDL_GetIOProperties(io), "SDL.internal.process", process, CleanupStream, (void *)property);
52 SDL_SetPointerProperty(process->props, property, io);
53 return true;
54}
55
56static bool SetupRedirect(SDL_PropertiesID props, const char *property, HANDLE *result)
57{
58 SDL_IOStream *io = (SDL_IOStream *)SDL_GetPointerProperty(props, property, NULL);
59 if (!io) {
60 SDL_SetError("%s is not set", property);
61 return false;
62 }
63
64 HANDLE handle = (HANDLE)SDL_GetPointerProperty(SDL_GetIOProperties(io), SDL_PROP_IOSTREAM_WINDOWS_HANDLE_POINTER, INVALID_HANDLE_VALUE);
65 if (handle == INVALID_HANDLE_VALUE) {
66 SDL_SetError("%s doesn't have SDL_PROP_IOSTREAM_WINDOWS_HANDLE_POINTER available", property);
67 return false;
68 }
69
70 if (!DuplicateHandle(GetCurrentProcess(), handle,
71 GetCurrentProcess(), result,
72 0, TRUE, DUPLICATE_SAME_ACCESS)) {
73 WIN_SetError("DuplicateHandle()");
74 return false;
75 }
76
77 if (GetFileType(*result) == FILE_TYPE_PIPE) {
78 DWORD wait_mode = PIPE_WAIT;
79 if (!SetNamedPipeHandleState(*result, &wait_mode, NULL, NULL)) {
80 WIN_SetError("SetNamedPipeHandleState()");
81 return false;
82 }
83 }
84 return true;
85}
86
87static bool is_batch_file_path(const char *path) {
88 size_t len_path = SDL_strlen(path);
89 if (len_path < 4) {
90 return false;
91 }
92 if (SDL_strcasecmp(path + len_path - 4, ".bat") == 0 || SDL_strcasecmp(path + len_path - 4, ".cmd") == 0) {
93 return true;
94 }
95 return false;
96}
97
98static bool join_arguments(const char * const *args, LPWSTR *args_out)
99{
100 size_t len;
101 int i;
102 size_t i_out;
103 char *result;
104 bool batch_file = is_batch_file_path(args[0]);
105
106 len = 0;
107 for (i = 0; args[i]; i++) {
108 const char *a = args[i];
109
110 /* two double quotes to surround an argument with */
111 len += 2;
112
113 for (; *a; a++) {
114 switch (*a) {
115 case '"':
116 len += 2;
117 break;
118 case '\\':
119 /* only escape backslashes that precede a double quote */
120 len += (a[1] == '"' || a[1] == '\0') ? 2 : 1;
121 break;
122 case ' ':
123 case '^':
124 case '&':
125 case '|':
126 case '<':
127 case '>':
128 if (batch_file) {
129 len += 2;
130 } else {
131 len += 1;
132 }
133 break;
134 default:
135 len += 1;
136 break;
137 }
138 }
139 /* space separator or final '\0' */
140 len += 1;
141 }
142
143 result = SDL_malloc(len);
144 if (!result) {
145 *args_out = NULL;
146 return false;
147 }
148
149 i_out = 0;
150 for (i = 0; args[i]; i++) {
151 const char *a = args[i];
152
153 result[i_out++] = '"';
154 for (; *a; a++) {
155 switch (*a) {
156 case '"':
157 if (batch_file) {
158 result[i_out++] = '"';
159 } else {
160 result[i_out++] = '\\';
161 }
162 result[i_out++] = *a;
163 break;
164 case '\\':
165 result[i_out++] = *a;
166 if (a[1] == '"' || a[1] == '\0') {
167 result[i_out++] = *a;
168 }
169 break;
170 case ' ':
171 if (batch_file) {
172 result[i_out++] = '^';
173 }
174 result[i_out++] = *a;
175 break;
176 case '^':
177 case '&':
178 case '|':
179 case '<':
180 case '>':
181 if (batch_file) {
182 result[i_out++] = '^';
183 }
184 result[i_out++] = *a;
185 break;
186 default:
187 result[i_out++] = *a;
188 break;
189 }
190 }
191 result[i_out++] = '"';
192 result[i_out++] = ' ';
193 }
194 SDL_assert(i_out == len);
195 result[len - 1] = '\0';
196
197 *args_out = (LPWSTR)SDL_iconv_string("UTF-16LE", "UTF-8", (const char *)result, len);
198 SDL_free(result);
199 if (!args_out) {
200 return false;
201 }
202 return true;
203}
204
205static bool join_env(char **env, LPWSTR *env_out)
206{
207 size_t len;
208 char **var;
209 char *result;
210
211 len = 0;
212 for (var = env; *var; var++) {
213 len += SDL_strlen(*var) + 1;
214 }
215 result = SDL_malloc(len + 1);
216 if (!result) {
217 return false;
218 }
219
220 len = 0;
221 for (var = env; *var; var++) {
222 size_t l = SDL_strlen(*var);
223 SDL_memcpy(result + len, *var, l);
224 result[len + l] = '\0';
225 len += l + 1;
226 }
227 result[len] = '\0';
228
229 *env_out = (LPWSTR)SDL_iconv_string("UTF-16LE", "UTF-8", (const char *)result, len);
230 SDL_free(result);
231 if (!*env_out) {
232 return false;
233 }
234 return true;
235}
236
237bool SDL_SYS_CreateProcessWithProperties(SDL_Process *process, SDL_PropertiesID props)
238{
239 const char * const *args = SDL_GetPointerProperty(props, SDL_PROP_PROCESS_CREATE_ARGS_POINTER, NULL);
240 SDL_Environment *env = SDL_GetPointerProperty(props, SDL_PROP_PROCESS_CREATE_ENVIRONMENT_POINTER, SDL_GetEnvironment());
241 char **envp = NULL;
242 SDL_ProcessIO stdin_option = (SDL_ProcessIO)SDL_GetNumberProperty(props, SDL_PROP_PROCESS_CREATE_STDIN_NUMBER, SDL_PROCESS_STDIO_NULL);
243 SDL_ProcessIO stdout_option = (SDL_ProcessIO)SDL_GetNumberProperty(props, SDL_PROP_PROCESS_CREATE_STDOUT_NUMBER, SDL_PROCESS_STDIO_INHERITED);
244 SDL_ProcessIO stderr_option = (SDL_ProcessIO)SDL_GetNumberProperty(props, SDL_PROP_PROCESS_CREATE_STDERR_NUMBER, SDL_PROCESS_STDIO_INHERITED);
245 bool redirect_stderr = SDL_GetBooleanProperty(props, SDL_PROP_PROCESS_CREATE_STDERR_TO_STDOUT_BOOLEAN, false) &&
246 !SDL_HasProperty(props, SDL_PROP_PROCESS_CREATE_STDERR_NUMBER);
247 LPWSTR createprocess_cmdline = NULL;
248 LPWSTR createprocess_env = NULL;
249 STARTUPINFOW startup_info;
250 DWORD creation_flags;
251 SECURITY_ATTRIBUTES security_attributes;
252 HANDLE stdin_pipe[2] = { INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE };
253 HANDLE stdout_pipe[2] = { INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE };
254 HANDLE stderr_pipe[2] = { INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE };
255 DWORD pipe_mode = PIPE_NOWAIT;
256 bool result = false;
257
258 // Keep the malloc() before exec() so that an OOM won't run a process at all
259 envp = SDL_GetEnvironmentVariables(env);
260 if (!envp) {
261 return false;
262 }
263
264 SDL_ProcessData *data = SDL_calloc(1, sizeof(*data));
265 if (!data) {
266 SDL_free(envp);
267 return false;
268 }
269 process->internal = data;
270 data->process_information.hProcess = INVALID_HANDLE_VALUE;
271 data->process_information.hThread = INVALID_HANDLE_VALUE;
272
273 creation_flags = CREATE_UNICODE_ENVIRONMENT;
274
275 SDL_zero(startup_info);
276 startup_info.cb = sizeof(startup_info);
277 startup_info.dwFlags |= STARTF_USESTDHANDLES;
278 startup_info.hStdInput = INVALID_HANDLE_VALUE;
279 startup_info.hStdOutput = INVALID_HANDLE_VALUE;
280 startup_info.hStdError = INVALID_HANDLE_VALUE;
281
282 SDL_zero(security_attributes);
283 security_attributes.nLength = sizeof(SECURITY_ATTRIBUTES);
284 security_attributes.bInheritHandle = TRUE;
285 security_attributes.lpSecurityDescriptor = NULL;
286
287 if (!join_arguments(args, &createprocess_cmdline)) {
288 goto done;
289 }
290
291 if (!join_env(envp, &createprocess_env)) {
292 goto done;
293 }
294
295 // Background processes don't have access to the terminal
296 // This isn't necessary on Windows, but we keep the same behavior as the POSIX implementation.
297 if (process->background) {
298 if (stdin_option == SDL_PROCESS_STDIO_INHERITED) {
299 stdin_option = SDL_PROCESS_STDIO_NULL;
300 }
301 if (stdout_option == SDL_PROCESS_STDIO_INHERITED) {
302 stdout_option = SDL_PROCESS_STDIO_NULL;
303 }
304 if (stderr_option == SDL_PROCESS_STDIO_INHERITED) {
305 stderr_option = SDL_PROCESS_STDIO_NULL;
306 }
307 }
308
309 switch (stdin_option) {
310 case SDL_PROCESS_STDIO_REDIRECT:
311 if (!SetupRedirect(props, SDL_PROP_PROCESS_CREATE_STDIN_POINTER, &startup_info.hStdInput)) {
312 goto done;
313 }
314 break;
315 case SDL_PROCESS_STDIO_APP:
316 if (!CreatePipe(&stdin_pipe[READ_END], &stdin_pipe[WRITE_END], &security_attributes, 0)) {
317 stdin_pipe[READ_END] = INVALID_HANDLE_VALUE;
318 stdin_pipe[WRITE_END] = INVALID_HANDLE_VALUE;
319 goto done;
320 }
321 if (!SetNamedPipeHandleState(stdin_pipe[WRITE_END], &pipe_mode, NULL, NULL)) {
322 WIN_SetError("SetNamedPipeHandleState()");
323 goto done;
324 }
325 if (!SetHandleInformation(stdin_pipe[WRITE_END], HANDLE_FLAG_INHERIT, 0) ) {
326 WIN_SetError("SetHandleInformation()");
327 goto done;
328 }
329 startup_info.hStdInput = stdin_pipe[READ_END];
330 break;
331 case SDL_PROCESS_STDIO_NULL:
332 startup_info.hStdInput = CreateFile(TEXT("\\\\.\\NUL"), GENERIC_ALL, 0, &security_attributes, OPEN_EXISTING, 0, NULL);
333 break;
334 case SDL_PROCESS_STDIO_INHERITED:
335 default:
336 if (!DuplicateHandle(GetCurrentProcess(), GetStdHandle(STD_INPUT_HANDLE),
337 GetCurrentProcess(), &startup_info.hStdInput,
338 0, TRUE, DUPLICATE_SAME_ACCESS)) {
339 startup_info.hStdInput = INVALID_HANDLE_VALUE;
340 WIN_SetError("DuplicateHandle()");
341 goto done;
342 }
343 break;
344 }
345
346 switch (stdout_option) {
347 case SDL_PROCESS_STDIO_REDIRECT:
348 if (!SetupRedirect(props, SDL_PROP_PROCESS_CREATE_STDOUT_POINTER, &startup_info.hStdOutput)) {
349 goto done;
350 }
351 break;
352 case SDL_PROCESS_STDIO_APP:
353 if (!CreatePipe(&stdout_pipe[READ_END], &stdout_pipe[WRITE_END], &security_attributes, 0)) {
354 stdout_pipe[READ_END] = INVALID_HANDLE_VALUE;
355 stdout_pipe[WRITE_END] = INVALID_HANDLE_VALUE;
356 goto done;
357 }
358 if (!SetNamedPipeHandleState(stdout_pipe[READ_END], &pipe_mode, NULL, NULL)) {
359 WIN_SetError("SetNamedPipeHandleState()");
360 goto done;
361 }
362 if (!SetHandleInformation(stdout_pipe[READ_END], HANDLE_FLAG_INHERIT, 0) ) {
363 WIN_SetError("SetHandleInformation()");
364 goto done;
365 }
366 startup_info.hStdOutput = stdout_pipe[WRITE_END];
367 break;
368 case SDL_PROCESS_STDIO_NULL:
369 startup_info.hStdOutput = CreateFile(TEXT("\\\\.\\NUL"), GENERIC_ALL, 0, &security_attributes, OPEN_EXISTING, 0, NULL);
370 break;
371 case SDL_PROCESS_STDIO_INHERITED:
372 default:
373 if (!DuplicateHandle(GetCurrentProcess(), GetStdHandle(STD_OUTPUT_HANDLE),
374 GetCurrentProcess(), &startup_info.hStdOutput,
375 0, TRUE, DUPLICATE_SAME_ACCESS)) {
376 startup_info.hStdOutput = INVALID_HANDLE_VALUE;
377 WIN_SetError("DuplicateHandle()");
378 goto done;
379 }
380 break;
381 }
382
383 if (redirect_stderr) {
384 if (!DuplicateHandle(GetCurrentProcess(), startup_info.hStdOutput,
385 GetCurrentProcess(), &startup_info.hStdError,
386 0, TRUE, DUPLICATE_SAME_ACCESS)) {
387 startup_info.hStdError = INVALID_HANDLE_VALUE;
388 WIN_SetError("DuplicateHandle()");
389 goto done;
390 }
391 } else {
392 switch (stderr_option) {
393 case SDL_PROCESS_STDIO_REDIRECT:
394 if (!SetupRedirect(props, SDL_PROP_PROCESS_CREATE_STDERR_POINTER, &startup_info.hStdError)) {
395 goto done;
396 }
397 break;
398 case SDL_PROCESS_STDIO_APP:
399 if (!CreatePipe(&stderr_pipe[READ_END], &stderr_pipe[WRITE_END], &security_attributes, 0)) {
400 stderr_pipe[READ_END] = INVALID_HANDLE_VALUE;
401 stderr_pipe[WRITE_END] = INVALID_HANDLE_VALUE;
402 goto done;
403 }
404 if (!SetNamedPipeHandleState(stderr_pipe[READ_END], &pipe_mode, NULL, NULL)) {
405 WIN_SetError("SetNamedPipeHandleState()");
406 goto done;
407 }
408 if (!SetHandleInformation(stderr_pipe[READ_END], HANDLE_FLAG_INHERIT, 0) ) {
409 WIN_SetError("SetHandleInformation()");
410 goto done;
411 }
412 startup_info.hStdError = stderr_pipe[WRITE_END];
413 break;
414 case SDL_PROCESS_STDIO_NULL:
415 startup_info.hStdError = CreateFile(TEXT("\\\\.\\NUL"), GENERIC_ALL, 0, &security_attributes, OPEN_EXISTING, 0, NULL);
416 break;
417 case SDL_PROCESS_STDIO_INHERITED:
418 default:
419 if (!DuplicateHandle(GetCurrentProcess(), GetStdHandle(STD_ERROR_HANDLE),
420 GetCurrentProcess(), &startup_info.hStdError,
421 0, TRUE, DUPLICATE_SAME_ACCESS)) {
422 startup_info.hStdError = INVALID_HANDLE_VALUE;
423 WIN_SetError("DuplicateHandle()");
424 goto done;
425 }
426 break;
427 }
428 }
429
430 if (!CreateProcessW(NULL, createprocess_cmdline, NULL, NULL, TRUE, creation_flags, createprocess_env, NULL, &startup_info, &data->process_information)) {
431 WIN_SetError("CreateProcess");
432 goto done;
433 }
434
435 SDL_SetNumberProperty(process->props, SDL_PROP_PROCESS_PID_NUMBER, data->process_information.dwProcessId);
436
437 if (stdin_option == SDL_PROCESS_STDIO_APP) {
438 if (!SetupStream(process, stdin_pipe[WRITE_END], "wb", SDL_PROP_PROCESS_STDIN_POINTER)) {
439 CloseHandle(stdin_pipe[WRITE_END]);
440 stdin_pipe[WRITE_END] = INVALID_HANDLE_VALUE;
441 }
442 }
443 if (stdout_option == SDL_PROCESS_STDIO_APP) {
444 if (!SetupStream(process, stdout_pipe[READ_END], "rb", SDL_PROP_PROCESS_STDOUT_POINTER)) {
445 CloseHandle(stdout_pipe[READ_END]);
446 stdout_pipe[READ_END] = INVALID_HANDLE_VALUE;
447 }
448 }
449 if (stderr_option == SDL_PROCESS_STDIO_APP) {
450 if (!SetupStream(process, stderr_pipe[READ_END], "rb", SDL_PROP_PROCESS_STDERR_POINTER)) {
451 CloseHandle(stderr_pipe[READ_END]);
452 stderr_pipe[READ_END] = INVALID_HANDLE_VALUE;
453 }
454 }
455
456 result = true;
457
458done:
459 if (startup_info.hStdInput != INVALID_HANDLE_VALUE &&
460 startup_info.hStdInput != stdin_pipe[READ_END]) {
461 CloseHandle(startup_info.hStdInput);
462 }
463 if (startup_info.hStdOutput != INVALID_HANDLE_VALUE &&
464 startup_info.hStdOutput != stdout_pipe[WRITE_END]) {
465 CloseHandle(startup_info.hStdOutput);
466 }
467 if (startup_info.hStdError != INVALID_HANDLE_VALUE &&
468 startup_info.hStdError != stderr_pipe[WRITE_END]) {
469 CloseHandle(startup_info.hStdError);
470 }
471 if (stdin_pipe[READ_END] != INVALID_HANDLE_VALUE) {
472 CloseHandle(stdin_pipe[READ_END]);
473 }
474 if (stdout_pipe[WRITE_END] != INVALID_HANDLE_VALUE) {
475 CloseHandle(stdout_pipe[WRITE_END]);
476 }
477 if (stderr_pipe[WRITE_END] != INVALID_HANDLE_VALUE) {
478 CloseHandle(stderr_pipe[WRITE_END]);
479 }
480 SDL_free(createprocess_cmdline);
481 SDL_free(createprocess_env);
482 SDL_free(envp);
483
484 if (!result) {
485 if (stdin_pipe[WRITE_END] != INVALID_HANDLE_VALUE) {
486 CloseHandle(stdin_pipe[WRITE_END]);
487 }
488 if (stdout_pipe[READ_END] != INVALID_HANDLE_VALUE) {
489 CloseHandle(stdout_pipe[READ_END]);
490 }
491 if (stderr_pipe[READ_END] != INVALID_HANDLE_VALUE) {
492 CloseHandle(stderr_pipe[READ_END]);
493 }
494 }
495 return result;
496}
497
498bool SDL_SYS_KillProcess(SDL_Process *process, bool force)
499{
500 if (!TerminateProcess(process->internal->process_information.hProcess, 1)) {
501 return WIN_SetError("TerminateProcess failed");
502 }
503 return true;
504}
505
506bool SDL_SYS_WaitProcess(SDL_Process *process, bool block, int *exitcode)
507{
508 DWORD result;
509
510 result = WaitForSingleObject(process->internal->process_information.hProcess, block ? INFINITE : 0);
511
512 if (result == WAIT_OBJECT_0) {
513 DWORD rc;
514 if (!GetExitCodeProcess(process->internal->process_information.hProcess, &rc)) {
515 return WIN_SetError("GetExitCodeProcess");
516 }
517 if (exitcode) {
518 *exitcode = (int)rc;
519 }
520 return true;
521 } else if (result == WAIT_FAILED) {
522 return WIN_SetError("WaitForSingleObject(hProcess) returned WAIT_FAILED");
523 } else {
524 SDL_ClearError();
525 return false;
526 }
527}
528
529void SDL_SYS_DestroyProcess(SDL_Process *process)
530{
531 SDL_ProcessData *data = process->internal;
532 SDL_IOStream *io;
533
534 io = (SDL_IOStream *)SDL_GetPointerProperty(process->props, SDL_PROP_PROCESS_STDIN_POINTER, NULL);
535 if (io) {
536 SDL_CloseIO(io);
537 }
538 io = (SDL_IOStream *)SDL_GetPointerProperty(process->props, SDL_PROP_PROCESS_STDERR_POINTER, NULL);
539 if (io) {
540 SDL_CloseIO(io);
541 }
542 io = (SDL_IOStream *)SDL_GetPointerProperty(process->props, SDL_PROP_PROCESS_STDOUT_POINTER, NULL);
543 if (io) {
544 SDL_CloseIO(io);
545 }
546 if (data) {
547 if (data->process_information.hThread != INVALID_HANDLE_VALUE) {
548 CloseHandle(data->process_information.hThread);
549 }
550 if (data->process_information.hProcess != INVALID_HANDLE_VALUE) {
551 CloseHandle(data->process_information.hProcess);
552 }
553 }
554 SDL_free(data);
555}
556
557#endif // SDL_PROCESS_WINDOWS