1 /* Auxiliary functions for the creation of subprocesses. Native Windows API.
2 Copyright (C) 2001, 2003-2025 Free Software Foundation, Inc.
3 Written by Bruno Haible <bruno@clisp.org>, 2003.
5 This file is free software: you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as
7 published by the Free Software Foundation; either version 2.1 of the
8 License, or (at your option) any later version.
10 This file is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public License
16 along with this program. If not, see <https://www.gnu.org/licenses/>. */
21 #include "windows-spawn.h"
23 /* Get declarations of the native Windows API functions. */
24 #define WIN32_LEAN_AND_MEAN
32 /* Get _get_osfhandle(). */
33 #if GNULIB_MSVC_NOTHROW
34 # include "msvc-nothrow.h"
41 #include "windows-path.h"
43 /* Don't assume that UNICODE is not defined. */
45 #define STARTUPINFO STARTUPINFOA
47 #define CreateProcess CreateProcessA
49 #define SHELL_SPECIAL_CHARS "\"\\ \001\002\003\004\005\006\007\010\011\012\013\014\015\016\017\020\021\022\023\024\025\026\027\030\031\032\033\034\035\036\037*?"
50 #define SHELL_SPACE_CHARS " \001\002\003\004\005\006\007\010\011\012\013\014\015\016\017\020\021\022\023\024\025\026\027\030\031\032\033\034\035\036\037"
52 /* Returns the length of a quoted argument string. */
54 quoted_arg_length (const char *string
)
56 bool quote_around
= (strpbrk (string
, SHELL_SPACE_CHARS
) != NULL
);
58 unsigned int backslashes
;
65 for (s
= string
; *s
!= '\0'; s
++)
69 length
+= backslashes
+ 1;
77 length
+= backslashes
+ 1;
82 /* Produces a quoted argument string.
83 Stores exactly quoted_arg_length (STRING) + 1 bytes, including the final
85 Returns a pointer past the stored quoted argument string. */
87 quoted_arg_string (const char *string
, char *mem
)
89 bool quote_around
= (strpbrk (string
, SHELL_SPACE_CHARS
) != NULL
);
91 unsigned int backslashes
;
98 for (s
= string
; *s
!= '\0'; s
++)
104 for (j
= backslashes
+ 1; j
> 0; j
--)
116 for (j
= backslashes
; j
> 0; j
--)
126 prepare_spawn (const char * const *argv
, char **mem_to_free
)
129 const char **new_argv
;
132 /* Count number of arguments. */
133 for (argc
= 0; argv
[argc
] != NULL
; argc
++)
136 /* Allocate new argument vector. */
137 new_argv
= (const char **) malloc ((1 + argc
+ 1) * sizeof (const char *));
139 /* Add an element upfront that can be used when argv[0] turns out to be a
140 script, not a program.
141 On Unix, this would be "/bin/sh". On native Windows, "sh" is actually
142 "sh.exe". We have to omit the directory part and rely on the search in
143 PATH, because the mingw "mount points" are not visible inside Windows
145 new_argv
[0] = "sh.exe";
147 /* Put quoted arguments into the new argument vector. */
148 size_t needed_size
= 0;
149 for (i
= 0; i
< argc
; i
++)
151 const char *string
= argv
[i
];
154 if (string
[0] == '\0')
155 length
= strlen ("\"\"");
156 else if (strpbrk (string
, SHELL_SPECIAL_CHARS
) != NULL
)
157 length
= quoted_arg_length (string
);
159 length
= strlen (string
);
160 needed_size
+= length
+ 1;
164 if (needed_size
== 0)
168 mem
= (char *) malloc (needed_size
);
171 /* Memory allocation failure. */
179 for (i
= 0; i
< argc
; i
++)
181 const char *string
= argv
[i
];
183 new_argv
[1 + i
] = mem
;
184 if (string
[0] == '\0')
186 size_t length
= strlen ("\"\"");
187 memcpy (mem
, "\"\"", length
+ 1);
190 else if (strpbrk (string
, SHELL_SPECIAL_CHARS
) != NULL
)
192 mem
= quoted_arg_string (string
, mem
);
196 size_t length
= strlen (string
);
197 memcpy (mem
, string
, length
+ 1);
201 new_argv
[1 + argc
] = NULL
;
207 compose_command (const char * const *argv
)
209 /* Just concatenate the argv[] strings, separated by spaces. */
212 /* Determine the size of the needed block of memory. */
213 size_t total_size
= 0;
214 const char * const *ap
;
216 for (ap
= argv
; (p
= *ap
) != NULL
; ap
++)
217 total_size
+= strlen (p
) + 1;
218 size_t command_size
= (total_size
> 0 ? total_size
: 1);
220 /* Allocate the block of memory. */
221 command
= (char *) malloc (command_size
);
232 for (ap
= argv
; (p
= *ap
) != NULL
; ap
++)
234 size_t size
= strlen (p
) + 1;
235 memcpy (cp
, p
, size
- 1);
248 compose_envblock (const char * const *envp
, const char *new_PATH
)
250 /* This is a bit hairy, because we don't have a lock that would prevent other
251 threads from making modifications in ENVP. So, just make sure we don't
252 crash; but if other threads are making modifications, part of the result
256 /* Guess the size of the needed block of memory.
257 The guess will be exact if other threads don't make modifications. */
258 size_t total_size
= 0;
259 const char * const *ep
;
261 if (new_PATH
!= NULL
)
262 total_size
+= strlen (new_PATH
) + 1;
263 for (ep
= envp
; (p
= *ep
) != NULL
; ep
++)
264 if (!(new_PATH
!= NULL
&& strncmp (p
, "PATH=", 5) == 0))
265 total_size
+= strlen (p
) + 1;
266 size_t envblock_size
= total_size
;
268 /* Allocate the block of memory. */
269 char *envblock
= (char *) malloc (envblock_size
+ 1);
270 if (envblock
== NULL
)
275 size_t envblock_used
= 0;
276 if (new_PATH
!= NULL
)
278 size_t size
= strlen (new_PATH
) + 1;
279 memcpy (envblock
+ envblock_used
, new_PATH
, size
);
280 envblock_used
+= size
;
282 for (ep
= envp
; (p
= *ep
) != NULL
; ep
++)
283 if (!(new_PATH
!= NULL
&& strncmp (p
, "PATH=", 5) == 0))
285 size_t size
= strlen (p
) + 1;
286 if (envblock_used
+ size
> envblock_size
)
288 /* Other threads did modifications. Need more memory. */
289 envblock_size
+= envblock_size
/ 2;
290 if (envblock_used
+ size
> envblock_size
)
291 envblock_size
= envblock_used
+ size
;
294 (char *) realloc (envblock
, envblock_size
+ 1);
295 if (new_envblock
== NULL
)
301 envblock
= new_envblock
;
303 memcpy (envblock
+ envblock_used
, p
, size
);
304 envblock_used
+= size
;
305 if (envblock
[envblock_used
- 1] != '\0')
307 /* Other threads did modifications. Restart. */
312 envblock
[envblock_used
] = '\0';
318 init_inheritable_handles (struct inheritable_handles
*inh_handles
,
321 /* Determine the minimal count of handles we need to care about. */
322 size_t handles_count
;
325 <https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/getmaxstdio>
326 Default value is 512. */
327 unsigned int fdmax
= _getmaxstdio ();
330 for (; fdmax
> 3; fdmax
--)
332 unsigned int fd
= fdmax
- 1;
334 <https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/get-osfhandle> */
335 HANDLE handle
= (HANDLE
) _get_osfhandle (fd
);
336 if (handle
!= INVALID_HANDLE_VALUE
)
339 /* We will add fd to the array, regardless of whether it is
340 inheritable or not. */
345 /* GetHandleInformation
346 <https://docs.microsoft.com/en-us/windows/win32/api/handleapi/nf-handleapi-gethandleinformation> */
347 if (GetHandleInformation (handle
, &hflags
))
349 if ((hflags
& HANDLE_FLAG_INHERIT
) != 0)
350 /* fd denotes an inheritable descriptor. */
356 handles_count
= fdmax
;
358 /* Note: handles_count >= 3. */
360 /* Allocate the array. */
361 size_t handles_allocated
= handles_count
;
363 (struct IHANDLE
*) malloc (handles_allocated
* sizeof (struct IHANDLE
));
370 /* Fill in the array. */
372 HANDLE curr_process
= (duplicate
? GetCurrentProcess () : INVALID_HANDLE_VALUE
);
374 for (fd
= 0; fd
< handles_count
; fd
++)
376 ih
[fd
].handle
= INVALID_HANDLE_VALUE
;
378 <https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/get-osfhandle> */
379 HANDLE handle
= (HANDLE
) _get_osfhandle (fd
);
380 if (handle
!= INVALID_HANDLE_VALUE
)
383 /* GetHandleInformation
384 <https://docs.microsoft.com/en-us/windows/win32/api/handleapi/nf-handleapi-gethandleinformation> */
385 if (GetHandleInformation (handle
, &hflags
))
389 /* Add fd to the array, regardless of whether it is
390 inheritable or not. */
391 if ((hflags
& HANDLE_FLAG_INHERIT
) != 0)
393 /* Instead of duplicating it, just mark it as shared. */
394 ih
[fd
].handle
= handle
;
395 ih
[fd
].flags
= KEEP_OPEN_IN_PARENT
| KEEP_OPEN_IN_CHILD
;
399 if (!DuplicateHandle (curr_process
, handle
,
400 curr_process
, &ih
[fd
].handle
,
401 0, TRUE
, DUPLICATE_SAME_ACCESS
))
404 for (i
= 0; i
< fd
; i
++)
405 if (ih
[i
].handle
!= INVALID_HANDLE_VALUE
406 && !(ih
[i
].flags
& KEEP_OPEN_IN_PARENT
))
407 CloseHandle (ih
[i
].handle
);
409 errno
= EBADF
; /* arbitrary */
417 if ((hflags
& HANDLE_FLAG_INHERIT
) != 0)
419 /* fd denotes an inheritable descriptor. */
420 ih
[fd
].handle
= handle
;
421 ih
[fd
].flags
= KEEP_OPEN_IN_CHILD
;
429 /* Return the result. */
430 inh_handles
->count
= handles_count
;
431 inh_handles
->allocated
= handles_allocated
;
432 inh_handles
->ih
= ih
;
437 compose_handles_block (const struct inheritable_handles
*inh_handles
,
441 <https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/ns-processthreadsapi-startupinfoa> */
442 sinfo
->dwFlags
= STARTF_USESTDHANDLES
;
443 sinfo
->hStdInput
= inh_handles
->ih
[0].handle
;
444 sinfo
->hStdOutput
= inh_handles
->ih
[1].handle
;
445 sinfo
->hStdError
= inh_handles
->ih
[2].handle
;
447 /* On newer versions of Windows, more file descriptors / handles than the
448 first three can be passed.
449 The format is as follows: Let N be an exclusive upper bound for the file
450 descriptors to be passed. Two arrays are constructed in memory:
451 - flags[0..N-1], of element type 'unsigned char',
452 - handles[0..N-1], of element type 'HANDLE' or 'intptr_t'.
453 For used entries, handles[i] is the handle, and flags[i] is a set of flags,
455 1 for open file descriptors,
456 64 for handles of type FILE_TYPE_CHAR,
457 8 for handles of type FILE_TYPE_PIPE,
459 For unused entries - this may include any of the first three, since they
460 are already passed above -, handles[i] is INVALID_HANDLE_VALUE and flags[i]
462 lpReserved2 now is a pointer to the concatenation (without padding) of:
463 - an 'unsigned int' whose value is N,
464 - the contents of the flags[0..N-1] array,
465 - the contents of the handles[0..N-1] array.
466 cbReserved2 is the size (in bytes) of the object at lpReserved2. */
468 size_t handles_count
= inh_handles
->count
;
471 sizeof (unsigned int)
472 + handles_count
* sizeof (unsigned char)
473 + handles_count
* sizeof (HANDLE
);
474 /* Add some padding, so that we can work with a properly aligned HANDLE
476 char *hblock
= (char *) malloc (sinfo
->cbReserved2
+ (sizeof (HANDLE
) - 1));
482 unsigned char *flags
= (unsigned char *) (hblock
+ sizeof (unsigned int));
483 char *handles
= (char *) (flags
+ handles_count
);
484 HANDLE
*handles_aligned
=
485 (HANDLE
*) (((uintptr_t) handles
+ (sizeof (HANDLE
) - 1))
486 & - (uintptr_t) sizeof (HANDLE
));
488 * (unsigned int *) hblock
= handles_count
;
491 for (fd
= 0; fd
< handles_count
; fd
++)
493 handles_aligned
[fd
] = INVALID_HANDLE_VALUE
;
496 HANDLE handle
= inh_handles
->ih
[fd
].handle
;
497 if (handle
!= INVALID_HANDLE_VALUE
498 /* The first three are possibly already passed above.
499 But they need to passed here as well, if they have some flags. */
500 && (fd
>= 3 || (unsigned char) inh_handles
->ih
[fd
].flags
!= 0))
503 /* GetHandleInformation
504 <https://docs.microsoft.com/en-us/windows/win32/api/handleapi/nf-handleapi-gethandleinformation> */
505 if (GetHandleInformation (handle
, &hflags
))
507 if ((hflags
& HANDLE_FLAG_INHERIT
) != 0)
509 /* fd denotes an inheritable descriptor. */
510 handles_aligned
[fd
] = handle
;
511 /* On Microsoft Windows, it would be sufficient to set
512 flags[fd] = 1. But on ReactOS or Wine, adding the bit
513 that indicates the handle type may be necessary. So,
514 just do it everywhere. */
515 flags
[fd
] = 1 | (unsigned char) inh_handles
->ih
[fd
].flags
;
516 switch (GetFileType (handle
))
529 /* We shouldn't have any non-inheritable handles in
530 inh_handles->handles. */
536 if (handles
!= (char *) handles_aligned
)
537 memmove (handles
, (char *) handles_aligned
, handles_count
* sizeof (HANDLE
));
539 sinfo
->lpReserved2
= (BYTE
*) hblock
;
545 free_inheritable_handles (struct inheritable_handles
*inh_handles
)
547 free (inh_handles
->ih
);
551 convert_CreateProcess_error (DWORD error
)
553 /* Some of these errors probably cannot happen. But who knows... */
556 case ERROR_FILE_NOT_FOUND
:
557 case ERROR_PATH_NOT_FOUND
:
558 case ERROR_BAD_PATHNAME
:
559 case ERROR_BAD_NET_NAME
:
560 case ERROR_INVALID_NAME
:
561 case ERROR_DIRECTORY
:
564 case ERROR_ACCESS_DENIED
:
565 case ERROR_SHARING_VIOLATION
:
568 case ERROR_OUTOFMEMORY
:
571 case ERROR_BUFFER_OVERFLOW
:
572 case ERROR_FILENAME_EXCED_RANGE
:
575 case ERROR_BAD_FORMAT
:
576 case ERROR_BAD_EXE_FORMAT
:
585 spawnpvech (int mode
,
586 const char *progname
, const char * const *argv
,
587 const char * const *envp
,
588 const char * const *dll_dirs
,
590 HANDLE stdin_handle
, HANDLE stdout_handle
, HANDLE stderr_handle
)
592 /* Validate the arguments. */
596 || mode
== P_OVERLAY
)
597 || progname
== NULL
|| argv
== NULL
)
603 /* Implement the 'p' letter: search for PROGNAME in getenv ("PATH"). */
604 const char *resolved_progname
=
605 find_in_given_path (progname
, getenv ("PATH"), NULL
, false);
606 if (resolved_progname
== NULL
)
609 /* Compose the command. */
610 char *command
= compose_command (argv
);
612 goto out_of_memory_1
;
614 /* Copy *ENVP into a contiguous block of memory. */
616 if (envp
== NULL
&& !(dll_dirs
!= NULL
&& dll_dirs
[0] != NULL
))
622 <https://learn.microsoft.com/en-us/cpp/c-runtime-library/environ-wenviron> */
623 envp
= (const char **) _environ
;
624 char *new_PATH
= NULL
;
625 if (dll_dirs
!= NULL
&& dll_dirs
[0] != NULL
)
627 new_PATH
= extended_PATH (dll_dirs
);
628 if (new_PATH
== NULL
)
629 goto out_of_memory_2
;
631 envblock
= compose_envblock (envp
, new_PATH
);
633 if (envblock
== NULL
)
634 goto out_of_memory_2
;
637 /* Collect the inheritable handles. */
638 struct inheritable_handles inh_handles
;
639 if (init_inheritable_handles (&inh_handles
, false) < 0)
641 int saved_errno
= errno
;
642 if (envblock
!= NULL
)
645 if (resolved_progname
!= progname
)
646 free ((char *) resolved_progname
);
650 inh_handles
.ih
[0].handle
= stdin_handle
;
651 inh_handles
.ih
[0].flags
= KEEP_OPEN_IN_CHILD
;
652 inh_handles
.ih
[1].handle
= stdout_handle
;
653 inh_handles
.ih
[1].flags
= KEEP_OPEN_IN_CHILD
;
654 inh_handles
.ih
[2].handle
= stderr_handle
;
655 inh_handles
.ih
[2].flags
= KEEP_OPEN_IN_CHILD
;
658 <https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createprocessa> */
659 /* <https://docs.microsoft.com/en-us/windows/win32/procthread/process-creation-flags> */
660 DWORD process_creation_flags
= (mode
== P_DETACH
? DETACHED_PROCESS
: 0);
662 <https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/ns-processthreadsapi-startupinfoa> */
664 sinfo
.cb
= sizeof (STARTUPINFO
);
665 sinfo
.lpReserved
= NULL
;
666 sinfo
.lpDesktop
= NULL
;
667 sinfo
.lpTitle
= NULL
;
668 if (compose_handles_block (&inh_handles
, &sinfo
) < 0)
670 int saved_errno
= errno
;
671 free_inheritable_handles (&inh_handles
);
672 if (envblock
!= NULL
)
675 if (resolved_progname
!= progname
)
676 free ((char *) resolved_progname
);
681 PROCESS_INFORMATION pinfo
;
682 if (!CreateProcess (resolved_progname
, command
, NULL
, NULL
, TRUE
,
683 process_creation_flags
, envblock
, currdir
, &sinfo
,
686 DWORD error
= GetLastError ();
688 free (sinfo
.lpReserved2
);
689 free_inheritable_handles (&inh_handles
);
690 if (envblock
!= NULL
)
693 if (resolved_progname
!= progname
)
694 free ((char *) resolved_progname
);
696 errno
= convert_CreateProcess_error (error
);
701 CloseHandle (pinfo
.hThread
);
702 free (sinfo
.lpReserved2
);
703 free_inheritable_handles (&inh_handles
);
704 if (envblock
!= NULL
)
707 if (resolved_progname
!= progname
)
708 free ((char *) resolved_progname
);
714 /* Wait until it terminates. Then get its exit status code. */
715 switch (WaitForSingleObject (pinfo
.hProcess
, INFINITE
))
727 if (!GetExitCodeProcess (pinfo
.hProcess
, &exit_code
))
732 CloseHandle (pinfo
.hProcess
);
737 /* Return pinfo.hProcess, not pinfo.dwProcessId. */
738 return (intptr_t) pinfo
.hProcess
;
742 CloseHandle (pinfo
.hProcess
);
746 /* Already checked above. */
754 if (resolved_progname
!= progname
)
755 free ((char *) resolved_progname
);