1 /* Auxiliary functions for the creation of subprocesses. Native Windows API.
2 Copyright (C) 2001, 2003-2020 Free Software Foundation, Inc.
3 Written by Bruno Haible <bruno@clisp.org>, 2003.
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 3 of the License, or
8 (at your option) any later version.
10 This program 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 General Public License for more details.
15 You should have received a copy of the GNU 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
33 /* Get _get_osfhandle(). */
34 #if GNULIB_MSVC_NOTHROW
35 # include "msvc-nothrow.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 spawnpvech (int mode
,
208 const char *progname
, const char * const *argv
,
209 const char * const *envp
,
211 HANDLE stdin_handle
, HANDLE stdout_handle
, HANDLE stderr_handle
)
213 /* Validate the arguments. */
217 || mode
== P_OVERLAY
)
218 || progname
== NULL
|| argv
== NULL
)
224 /* Implement the 'p' letter: search for PROGNAME in getenv ("PATH"). */
225 const char *resolved_progname
=
226 find_in_given_path (progname
, getenv ("PATH"), false);
227 if (resolved_progname
== NULL
)
230 /* Compose the command.
231 Just concatenate the argv[] strings, separated by spaces. */
234 /* Determine the size of the needed block of memory. */
235 size_t total_size
= 0;
236 const char * const *ap
;
238 for (ap
= argv
; (p
= *ap
) != NULL
; ap
++)
239 total_size
+= strlen (p
) + 1;
240 size_t command_size
= (total_size
> 0 ? total_size
: 1);
241 command
= (char *) malloc (command_size
);
243 goto out_of_memory_1
;
247 for (ap
= argv
; (p
= *ap
) != NULL
; ap
++)
249 size_t size
= strlen (p
) + 1;
250 memcpy (cp
, p
, size
- 1);
260 /* Copy *ENVP into a contiguous block of memory. */
267 /* Guess the size of the needed block of memory.
268 The guess will be exact if other threads don't make modifications. */
269 size_t total_size
= 0;
270 const char * const *ep
;
272 for (ep
= envp
; (p
= *ep
) != NULL
; ep
++)
273 total_size
+= strlen (p
) + 1;
274 size_t envblock_size
= total_size
;
275 envblock
= (char *) malloc (envblock_size
+ 1);
276 if (envblock
== NULL
)
277 goto out_of_memory_2
;
278 size_t envblock_used
= 0;
279 for (ep
= envp
; (p
= *ep
) != NULL
; ep
++)
281 size_t size
= strlen (p
) + 1;
282 if (envblock_used
+ size
> envblock_size
)
284 /* Other threads did modifications. Need more memory. */
285 envblock_size
+= envblock_size
/ 2;
286 if (envblock_used
+ size
> envblock_size
)
287 envblock_size
= envblock_used
+ size
;
289 char *new_envblock
= (char *) realloc (envblock
, envblock_size
+ 1);
290 if (new_envblock
== NULL
)
291 goto out_of_memory_3
;
292 envblock
= new_envblock
;
294 memcpy (envblock
+ envblock_used
, p
, size
);
295 envblock_used
+= size
;
296 if (envblock
[envblock_used
- 1] != '\0')
298 /* Other threads did modifications. Restart. */
303 envblock
[envblock_used
] = '\0';
307 <https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createprocessa> */
308 /* Regarding handle inheritance, see
309 <https://docs.microsoft.com/en-us/windows/win32/sysinfo/handle-inheritance> */
310 /* <https://docs.microsoft.com/en-us/windows/win32/procthread/process-creation-flags> */
311 DWORD flags
= (mode
== P_DETACH
? DETACHED_PROCESS
: 0);
313 <https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/ns-processthreadsapi-startupinfoa> */
315 sinfo
.cb
= sizeof (STARTUPINFO
);
316 sinfo
.lpReserved
= NULL
;
317 sinfo
.lpDesktop
= NULL
;
318 sinfo
.lpTitle
= NULL
;
319 sinfo
.dwFlags
= STARTF_USESTDHANDLES
;
320 sinfo
.hStdInput
= stdin_handle
;
321 sinfo
.hStdOutput
= stdout_handle
;
322 sinfo
.hStdError
= stderr_handle
;
326 sinfo
.cbReserved2
= 0;
327 sinfo
.lpReserved2
= NULL
;
329 /* On newer versions of Windows, more file descriptors / handles than the
330 first three can be passed.
331 The format is as follows: Let N be an exclusive upper bound for the file
332 descriptors to be passed. Two arrays are constructed in memory:
333 - flags[0..N-1], of element type 'unsigned char',
334 - handles[0..N-1], of element type 'HANDLE' or 'intptr_t'.
335 For used entries, handles[i] is the handle, and flags[i] is a set of flags,
337 1 for open file descriptors,
338 64 for handles of type FILE_TYPE_CHAR,
339 8 for handles of type FILE_TYPE_PIPE.
340 For unused entries - this includes the first three, since they are already
341 passed above -, handles[i] is INVALID_HANDLE_VALUE and flags[i] is zero.
342 lpReserved2 now is a pointer to the concatenation (without padding) of:
343 - an 'unsigned int' whose value is N,
344 - the contents of the flags[0..N-1] array,
345 - the contents of the handles[0..N-1] array.
346 cbReserved2 is the size (in bytes) of the object at lpReserved2. */
349 <https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/getmaxstdio>
350 Default value is 512. */
352 for (fdmax
= _getmaxstdio (); fdmax
> 0; fdmax
--)
354 unsigned int fd
= fdmax
- 1;
356 <https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/get-osfhandle> */
357 HANDLE handle
= (HANDLE
) _get_osfhandle (fd
);
358 if (handle
!= INVALID_HANDLE_VALUE
)
361 /* GetHandleInformation
362 <https://docs.microsoft.com/en-us/windows/win32/api/handleapi/nf-handleapi-gethandleinformation> */
363 if (GetHandleInformation (handle
, &hflags
))
365 if ((hflags
& HANDLE_FLAG_INHERIT
) != 0)
366 /* fd denotes an inheritable descriptor. */
374 sizeof (unsigned int)
375 + fdmax
* sizeof (unsigned char)
376 + fdmax
* sizeof (HANDLE
);
377 /* Add some padding, so that we can work with a properly HANDLE array. */
378 hblock
= (char *) malloc (sinfo
.cbReserved2
+ (sizeof (HANDLE
) - 1));
380 goto out_of_memory_3
;
381 * (unsigned int *) hblock
= fdmax
;
382 unsigned char *flags
= (unsigned char *) (hblock
+ sizeof (unsigned int));
383 char *handles
= (char *) (flags
+ fdmax
);
384 HANDLE
*handles_aligned
=
385 (HANDLE
*) (((uintptr_t) handles
+ (sizeof (HANDLE
) - 1))
386 & - (uintptr_t) sizeof (HANDLE
));
389 for (fd
= 0; fd
< fdmax
; fd
++)
392 handles_aligned
[fd
] = INVALID_HANDLE_VALUE
;
393 /* The first three are already passed above. */
397 <https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/get-osfhandle> */
398 HANDLE handle
= (HANDLE
) _get_osfhandle (fd
);
399 if (handle
!= INVALID_HANDLE_VALUE
)
402 /* GetHandleInformation
403 <https://docs.microsoft.com/en-us/windows/win32/api/handleapi/nf-handleapi-gethandleinformation> */
404 if (GetHandleInformation (handle
, &hflags
))
406 if ((hflags
& HANDLE_FLAG_INHERIT
) != 0)
408 /* fd denotes an inheritable descriptor. */
409 /* On Microsoft Windows, it would be sufficient to
410 set flags[fd] = 1. But on ReactOS or Wine,
411 adding the bit that indicates the handle type
412 may be necessary. So, just do it everywhere. */
413 switch (GetFileType (handle
))
425 handles_aligned
[fd
] = handle
;
432 if (handles
!= (char *) handles_aligned
)
433 memmove (handles
, (char *) handles_aligned
, fdmax
* sizeof (HANDLE
));
434 sinfo
.lpReserved2
= (BYTE
*) hblock
;
438 sinfo
.cbReserved2
= 0;
439 sinfo
.lpReserved2
= NULL
;
444 PROCESS_INFORMATION pinfo
;
445 if (!CreateProcess (resolved_progname
, command
, NULL
, NULL
, TRUE
,
446 flags
, envblock
, currdir
, &sinfo
, &pinfo
))
448 DWORD error
= GetLastError ();
452 if (envblock
!= NULL
)
455 if (resolved_progname
!= progname
)
456 free ((char *) resolved_progname
);
458 /* Some of these errors probably cannot happen. But who knows... */
461 case ERROR_FILE_NOT_FOUND
:
462 case ERROR_PATH_NOT_FOUND
:
463 case ERROR_BAD_PATHNAME
:
464 case ERROR_BAD_NET_NAME
:
465 case ERROR_INVALID_NAME
:
466 case ERROR_DIRECTORY
:
470 case ERROR_ACCESS_DENIED
:
471 case ERROR_SHARING_VIOLATION
:
475 case ERROR_OUTOFMEMORY
:
479 case ERROR_BUFFER_OVERFLOW
:
480 case ERROR_FILENAME_EXCED_RANGE
:
481 errno
= ENAMETOOLONG
;
493 CloseHandle (pinfo
.hThread
);
496 if (envblock
!= NULL
)
499 if (resolved_progname
!= progname
)
500 free ((char *) resolved_progname
);
506 /* Wait until it terminates. Then get its exit status code. */
507 switch (WaitForSingleObject (pinfo
.hProcess
, INFINITE
))
519 if (!GetExitCodeProcess (pinfo
.hProcess
, &exit_code
))
524 CloseHandle (pinfo
.hProcess
);
529 /* Return pinfo.hProcess, not pinfo.dwProcessId. */
530 return (intptr_t) pinfo
.hProcess
;
534 CloseHandle (pinfo
.hProcess
);
538 /* Already checked above. */
549 if (envblock
!= NULL
)
554 if (resolved_progname
!= progname
)
555 free ((char *) resolved_progname
);