summaryrefslogtreecommitdiff
path: root/contrib/SDL-3.2.8/src/process/windows
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/SDL-3.2.8/src/process/windows')
-rw-r--r--contrib/SDL-3.2.8/src/process/windows/SDL_windowsprocess.c557
1 files changed, 557 insertions, 0 deletions
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