Branch data Line data Source code
1 : : /* gspawn-win32.c - Process launching on Win32
2 : : *
3 : : * Copyright 2000 Red Hat, Inc.
4 : : * Copyright 2003 Tor Lillqvist
5 : : *
6 : : * SPDX-License-Identifier: LGPL-2.1-or-later
7 : : *
8 : : * This library is free software; you can redistribute it and/or
9 : : * modify it under the terms of the GNU Lesser General Public
10 : : * License as published by the Free Software Foundation; either
11 : : * version 2.1 of the License, or (at your option) any later version.
12 : : *
13 : : * This library is distributed in the hope that it will be useful,
14 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 : : * Lesser General Public License for more details.
17 : : *
18 : : * You should have received a copy of the GNU Lesser General Public License
19 : : * along with this library; if not, see <http://www.gnu.org/licenses/>.
20 : : */
21 : :
22 : : /*
23 : : * Implementation details on Win32.
24 : : *
25 : : * - There is no way to set the no-inherit flag for
26 : : * a "file descriptor" in the MS C runtime. The flag is there,
27 : : * and the dospawn() function uses it, but unfortunately
28 : : * this flag can only be set when opening the file.
29 : : * - As there is no fork(), we cannot reliably change directory
30 : : * before starting the child process. (There might be several threads
31 : : * running, and the current directory is common for all threads.)
32 : : *
33 : : * Thus, we must in many cases use a helper program to handle closing
34 : : * of (inherited) file descriptors and changing of directory. The
35 : : * helper process is also needed if the standard input, standard
36 : : * output, or standard error of the process to be run are supposed to
37 : : * be redirected somewhere.
38 : : *
39 : : * The structure of the source code in this file is a mess, I know.
40 : : */
41 : :
42 : : /* Define this to get some logging all the time */
43 : : /* #define G_SPAWN_WIN32_DEBUG */
44 : :
45 : : #include "config.h"
46 : :
47 : : #include "glib-init.h"
48 : : #include "glib-private.h"
49 : : #include "glib.h"
50 : : #include "glibintl.h"
51 : : #include "gprintfint.h"
52 : : #include "gspawn-private.h"
53 : : #include "gthread.h"
54 : :
55 : : #include <string.h>
56 : : #include <stdlib.h>
57 : : #include <stdio.h>
58 : :
59 : : #include <windows.h>
60 : : #include <errno.h>
61 : : #include <fcntl.h>
62 : : #include <io.h>
63 : : #include <process.h>
64 : : #include <direct.h>
65 : : #include <wchar.h>
66 : :
67 : : #ifdef _MSC_VER
68 : : #ifdef HAVE_VCRUNTIME_H
69 : : #include <vcruntime.h> /* for _UCRT */
70 : : #endif
71 : : #endif
72 : :
73 : : #ifndef GSPAWN_HELPER
74 : : #ifdef G_SPAWN_WIN32_DEBUG
75 : : static int debug = 1;
76 : : #define SETUP_DEBUG() /* empty */
77 : : #else
78 : : static int debug = -1;
79 : : #define SETUP_DEBUG() \
80 : : G_STMT_START \
81 : : { \
82 : : if (debug == -1) \
83 : : { \
84 : : if (g_getenv ("G_SPAWN_WIN32_DEBUG") != NULL) \
85 : : debug = 1; \
86 : : else \
87 : : debug = 0; \
88 : : } \
89 : : } \
90 : : G_STMT_END
91 : : #endif
92 : : #endif
93 : :
94 : : enum
95 : : {
96 : : CHILD_NO_ERROR,
97 : : CHILD_CHDIR_FAILED,
98 : : CHILD_SPAWN_FAILED,
99 : : CHILD_SPAWN_NOENT,
100 : : CHILD_DUP_FAILED,
101 : : };
102 : :
103 : : enum {
104 : : ARG_CHILD_ERR_REPORT = 1,
105 : : ARG_HELPER_SYNC,
106 : : ARG_STDIN,
107 : : ARG_STDOUT,
108 : : ARG_STDERR,
109 : : ARG_WORKING_DIRECTORY,
110 : : ARG_CLOSE_DESCRIPTORS,
111 : : ARG_USE_PATH,
112 : : ARG_WAIT,
113 : : ARG_FDS,
114 : : ARG_PROGRAM,
115 : : ARG_COUNT = ARG_PROGRAM
116 : : };
117 : :
118 : : #ifndef GSPAWN_HELPER
119 : :
120 : : #ifdef _WIN64
121 : : #define HELPER_PROCESS "gspawn-win64-helper"
122 : : #else
123 : : #define HELPER_PROCESS "gspawn-win32-helper"
124 : : #endif
125 : :
126 : : #ifndef _UCRT
127 : :
128 : : /* The wspawn*e functions are thread-safe only in the Universal
129 : : * CRT (UCRT). If we are linking against the MSVCRT.dll or the
130 : : * pre-2015 MSVC runtime (MSVCRXXX.dll), then we have to use a
131 : : * mutex.
132 : : */
133 : :
134 : : static GMutex safe_wspawn_e_mutex;
135 : :
136 : : static intptr_t
137 : : safe_wspawnve (int _Mode,
138 : : const wchar_t *_Filename,
139 : : const wchar_t *const *_ArgList,
140 : : const wchar_t *const *_Env)
141 : : {
142 : : intptr_t ret_val = -1;
143 : :
144 : : g_mutex_lock (&safe_wspawn_e_mutex);
145 : : ret_val = _wspawnve (_Mode, _Filename, _ArgList, _Env);
146 : : g_mutex_unlock (&safe_wspawn_e_mutex);
147 : :
148 : : return ret_val;
149 : : }
150 : :
151 : : static intptr_t
152 : : safe_wspawnvpe (int _Mode,
153 : : const wchar_t *_Filename,
154 : : const wchar_t *const *_ArgList,
155 : : const wchar_t *const *_Env)
156 : : {
157 : : intptr_t ret_val = -1;
158 : :
159 : : g_mutex_lock (&safe_wspawn_e_mutex);
160 : : ret_val = _wspawnvpe (_Mode, _Filename, _ArgList, _Env);
161 : : g_mutex_unlock (&safe_wspawn_e_mutex);
162 : :
163 : : return ret_val;
164 : : }
165 : :
166 : : #else
167 : :
168 : : /**< private >
169 : : * ensure_cmd_environment:
170 : : *
171 : : * Workaround for an issue in the universal C Runtime library (UCRT). This adds
172 : : * a custom environment variable to this process's environment block that looks
173 : : * like the cmd.exe's shell-related environment variables, i.e the name starts
174 : : * with an equal sign character: '='. This is needed because the UCRT may crash
175 : : * if those environment variables are missing from the calling process's block.
176 : : *
177 : : * Reference:
178 : : *
179 : : * https://developercommunity.visualstudio.com/t/UCRT-Crash-in-_wspawne-functions/10262748
180 : : */
181 : : static void
182 : 65 : ensure_cmd_environment (void)
183 : : {
184 : : static gsize initialization_value = 0;
185 : :
186 : 65 : if (g_once_init_enter (&initialization_value))
187 : : {
188 : 11 : wchar_t *block = GetEnvironmentStringsW ();
189 : 11 : gboolean have_cmd_environment = FALSE;
190 : :
191 : 11 : if (block)
192 : : {
193 : 11 : const wchar_t *p = block;
194 : :
195 : 2865 : while (*p != L'\0')
196 : : {
197 : 2854 : if (*p == L'=')
198 : : {
199 : 0 : have_cmd_environment = TRUE;
200 : 0 : break;
201 : : }
202 : :
203 : 2854 : p += wcslen (p) + 1;
204 : : }
205 : :
206 : 11 : if (!FreeEnvironmentStringsW (block))
207 : 0 : g_warning ("%s failed with error code %u",
208 : : "FreeEnvironmentStrings",
209 : 0 : (guint) GetLastError ());
210 : 11 : }
211 : :
212 : 11 : if (!have_cmd_environment)
213 : : {
214 : 11 : if (!SetEnvironmentVariableW (L"=GLIB", L"GLIB"))
215 : : {
216 : 0 : g_critical ("%s failed with error code %u",
217 : : "SetEnvironmentVariable",
218 : 0 : (guint) GetLastError ());
219 : 0 : }
220 : 11 : }
221 : :
222 : 11 : g_once_init_leave (&initialization_value, 1);
223 : 11 : }
224 : 65 : }
225 : :
226 : : static intptr_t
227 : 0 : safe_wspawnve (int _mode,
228 : : const wchar_t * _filename,
229 : : const wchar_t *const *_args,
230 : : const wchar_t *const *_env)
231 : : {
232 : 0 : ensure_cmd_environment ();
233 : :
234 : 0 : return _wspawnve (_mode, _filename, _args, _env);;
235 : : }
236 : :
237 : : static intptr_t
238 : 65 : safe_wspawnvpe (int _mode,
239 : : const wchar_t * _filename,
240 : : const wchar_t *const *_args,
241 : : const wchar_t *const *_env)
242 : : {
243 : 65 : ensure_cmd_environment ();
244 : :
245 : 65 : return _wspawnvpe (_mode, _filename, _args, _env);
246 : : }
247 : :
248 : : #endif /* _UCRT */
249 : :
250 : : /* This logic has a copy for wchar_t in gspawn-win32-helper.c, protect_wargv() */
251 : : static gchar *
252 : 2534 : protect_argv_string (const gchar *string)
253 : : {
254 : 2534 : const gchar *p = string;
255 : : gchar *retval, *q;
256 : 2534 : size_t len = 0;
257 : 2534 : size_t pre_bslash = 0;
258 : 2534 : gboolean need_dblquotes = FALSE;
259 : 95656 : while (*p)
260 : : {
261 : 93122 : if (*p == ' ' || *p == '\t')
262 : 42 : need_dblquotes = TRUE;
263 : : /* estimate max len, assuming that all escapable characters will be escaped */
264 : 93122 : if (*p == '"' || *p == '\\')
265 : 5829 : len += 2;
266 : : else
267 : 87293 : len += 1;
268 : 93122 : p++;
269 : : }
270 : :
271 : 2534 : q = retval = g_malloc (len + need_dblquotes*2 + 1);
272 : 2534 : p = string;
273 : :
274 : 2534 : if (need_dblquotes)
275 : 34 : *q++ = '"';
276 : : /* Only quotes and backslashes preceding quotes are escaped:
277 : : * see "Parsing C Command-Line Arguments" at
278 : : * https://docs.microsoft.com/en-us/cpp/c-language/parsing-c-command-line-arguments
279 : : */
280 : 95656 : while (*p)
281 : : {
282 : 93122 : if (*p == '"')
283 : : {
284 : : /* Add backslash for escaping quote itself */
285 : 11 : *q++ = '\\';
286 : : /* Add backslash for every preceding backslash for escaping it */
287 : 17 : for (;pre_bslash > 0; --pre_bslash)
288 : 6 : *q++ = '\\';
289 : 11 : }
290 : :
291 : : /* Count length of continuous sequence of preceding backslashes. */
292 : 93122 : if (*p == '\\')
293 : 5818 : ++pre_bslash;
294 : : else
295 : 87304 : pre_bslash = 0;
296 : :
297 : 93122 : *q++ = *p;
298 : 93122 : p++;
299 : : }
300 : :
301 : 2534 : if (need_dblquotes)
302 : : {
303 : : /* Add backslash for every preceding backslash for escaping it,
304 : : * do NOT escape quote itself.
305 : : */
306 : 35 : for (;pre_bslash > 0; --pre_bslash)
307 : 1 : *q++ = '\\';
308 : 34 : *q++ = '"';
309 : 34 : }
310 : 2534 : *q++ = '\0';
311 : :
312 : 2534 : return retval;
313 : : }
314 : :
315 : : static gint
316 : 428 : protect_argv (const gchar * const *argv,
317 : : gchar ***new_argv)
318 : : {
319 : : gint i;
320 : 428 : gint argc = 0;
321 : :
322 : 2277 : while (argv[argc])
323 : 1849 : ++argc;
324 : 428 : *new_argv = g_new (gchar *, argc+1);
325 : :
326 : : /* Quote each argv element if necessary, so that it will get
327 : : * reconstructed correctly in the C runtime startup code. Note that
328 : : * the unquoting algorithm in the C runtime is really weird, and
329 : : * rather different than what Unix shells do. See stdargv.c in the C
330 : : * runtime sources (in the Platform SDK, in src/crt).
331 : : *
332 : : * Note that a new_argv[0] constructed by this function should
333 : : * *not* be passed as the filename argument to a spawn* or exec*
334 : : * family function. That argument should be the real file name
335 : : * without any quoting.
336 : : */
337 : 2277 : for (i = 0; i < argc; i++)
338 : 1849 : (*new_argv)[i] = protect_argv_string (argv[i]);
339 : :
340 : 428 : (*new_argv)[argc] = NULL;
341 : :
342 : 428 : return argc;
343 : : }
344 : :
345 : 13 : G_DEFINE_QUARK (g-exec-error-quark, g_spawn_error)
346 : 41 : G_DEFINE_QUARK (g-spawn-exit-error-quark, g_spawn_exit_error)
347 : :
348 : : /* Avoids a danger in threaded situations (calling close()
349 : : * on a file descriptor twice, and another thread has
350 : : * re-opened it since the first close)
351 : : */
352 : : static void
353 : 3044 : close_and_invalidate (gint *fd)
354 : : {
355 : 3044 : if (*fd < 0)
356 : 580 : return;
357 : :
358 : 2464 : close (*fd);
359 : 2464 : *fd = -1;
360 : 3044 : }
361 : :
362 : : typedef enum
363 : : {
364 : : READ_FAILED = 0, /* FALSE */
365 : : READ_OK,
366 : : READ_EOF
367 : : } ReadResult;
368 : :
369 : : static ReadResult
370 : 253 : read_data (GString *str,
371 : : GIOChannel *iochannel,
372 : : GError **error)
373 : : {
374 : : GIOStatus giostatus;
375 : : gsize bytes;
376 : 253 : gchar buf[4096];
377 : :
378 : : again:
379 : :
380 : 253 : giostatus = g_io_channel_read_chars (iochannel, buf, sizeof (buf), &bytes, NULL);
381 : :
382 : 253 : if (bytes == 0)
383 : 64 : return READ_EOF;
384 : 189 : else if (bytes > 0)
385 : : {
386 : 189 : g_string_append_len (str, buf, bytes);
387 : 189 : return READ_OK;
388 : : }
389 : 0 : else if (giostatus == G_IO_STATUS_AGAIN)
390 : 0 : goto again;
391 : 0 : else if (giostatus == G_IO_STATUS_ERROR)
392 : : {
393 : 0 : g_set_error_literal (error, G_SPAWN_ERROR, G_SPAWN_ERROR_READ,
394 : 0 : _("Failed to read data from child process"));
395 : :
396 : 0 : return READ_FAILED;
397 : : }
398 : : else
399 : 0 : return READ_OK;
400 : 253 : }
401 : :
402 : : static gboolean
403 : 1547 : make_pipe (gint p[2],
404 : : GError **error)
405 : : {
406 : 1547 : if (_pipe (p, 4096, _O_BINARY) < 0)
407 : : {
408 : 0 : int errsv = errno;
409 : :
410 : 0 : g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED,
411 : 0 : _("Failed to create pipe for communicating with child process (%s)"),
412 : 0 : g_strerror (errsv));
413 : 0 : return FALSE;
414 : : }
415 : : else
416 : 1547 : return TRUE;
417 : 1547 : }
418 : :
419 : : /* The helper process writes a status report back to us, through a
420 : : * pipe, consisting of two ints.
421 : : */
422 : : static gboolean
423 : 427 : read_helper_report (int fd,
424 : : gintptr report[2],
425 : : GError **error)
426 : : {
427 : 427 : gsize bytes = 0;
428 : :
429 : 1219 : while (bytes < sizeof(gintptr)*2)
430 : : {
431 : : gint chunk;
432 : : int errsv;
433 : :
434 : 792 : if (debug)
435 : 0 : g_print ("%s:read_helper_report: read %" G_GSIZE_FORMAT "...\n",
436 : : __FILE__,
437 : 0 : sizeof(gintptr)*2 - bytes);
438 : :
439 : 1584 : chunk = read (fd, ((gchar*)report) + bytes,
440 : 792 : sizeof(gintptr)*2 - bytes);
441 : 792 : errsv = errno;
442 : :
443 : 792 : if (debug)
444 : 0 : g_print ("...got %d bytes\n", chunk);
445 : :
446 : 792 : if (chunk < 0)
447 : : {
448 : : /* Some weird shit happened, bail out */
449 : 0 : g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED,
450 : 0 : _("Failed to read from child pipe (%s)"),
451 : 0 : g_strerror (errsv));
452 : :
453 : 0 : return FALSE;
454 : : }
455 : 792 : else if (chunk == 0)
456 : : {
457 : 0 : g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED,
458 : 0 : _("Failed to read from child pipe (%s)"),
459 : : "EOF");
460 : 0 : break; /* EOF */
461 : : }
462 : : else
463 : 792 : bytes += chunk;
464 : : }
465 : :
466 : 427 : if (bytes < sizeof(gintptr)*2)
467 : 0 : return FALSE;
468 : :
469 : 427 : return TRUE;
470 : 427 : }
471 : :
472 : : static void
473 : 5 : set_child_error (gintptr report[2],
474 : : const gchar *working_directory,
475 : : GError **error)
476 : : {
477 : 5 : switch (report[0])
478 : : {
479 : : case CHILD_CHDIR_FAILED:
480 : 0 : g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_CHDIR,
481 : 0 : _("Failed to change to directory “%s” (%s)"),
482 : 0 : working_directory,
483 : 0 : g_strerror (report[1]));
484 : 0 : break;
485 : : case CHILD_SPAWN_FAILED:
486 : 0 : g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED,
487 : 0 : _("Failed to execute child process (%s)"),
488 : 0 : g_strerror (report[1]));
489 : 0 : break;
490 : : case CHILD_SPAWN_NOENT:
491 : 10 : g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_NOENT,
492 : 5 : _("Failed to execute child process (%s)"),
493 : 5 : g_strerror (report[1]));
494 : 5 : break;
495 : : case CHILD_DUP_FAILED:
496 : 0 : g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED,
497 : 0 : _("Failed to dup() in child process (%s)"),
498 : 0 : g_strerror (report[1]));
499 : 0 : break;
500 : : default:
501 : : g_assert_not_reached ();
502 : 0 : }
503 : 5 : }
504 : :
505 : : static gboolean
506 : 854 : utf8_charv_to_wcharv (const gchar * const *utf8_charv,
507 : : wchar_t ***wcharv,
508 : : int *error_index,
509 : : GError **error)
510 : : {
511 : 854 : wchar_t **retval = NULL;
512 : :
513 : 854 : *wcharv = NULL;
514 : 854 : if (utf8_charv != NULL)
515 : : {
516 : 492 : int n = 0, i;
517 : :
518 : 23410 : while (utf8_charv[n])
519 : 22918 : n++;
520 : 492 : retval = g_new (wchar_t *, n + 1);
521 : :
522 : 23410 : for (i = 0; i < n; i++)
523 : : {
524 : 22918 : retval[i] = g_utf8_to_utf16 (utf8_charv[i], -1, NULL, NULL, error);
525 : 22918 : if (retval[i] == NULL)
526 : : {
527 : 0 : if (error_index)
528 : 0 : *error_index = i;
529 : 0 : while (i)
530 : 0 : g_free (retval[--i]);
531 : 0 : g_free (retval);
532 : 0 : return FALSE;
533 : : }
534 : 22918 : }
535 : :
536 : 492 : retval[n] = NULL;
537 : 492 : }
538 : 854 : *wcharv = retval;
539 : 854 : return TRUE;
540 : 854 : }
541 : :
542 : : static gboolean
543 : 0 : do_spawn_directly (gint *exit_status,
544 : : gboolean do_return_handle,
545 : : GSpawnFlags flags,
546 : : const gchar * const *argv,
547 : : const gchar * const *envp,
548 : : const gchar * const *protected_argv,
549 : : GPid *child_pid,
550 : : GError **error)
551 : : {
552 : 0 : const int mode = (exit_status == NULL) ? P_NOWAIT : P_WAIT;
553 : : const gchar * const *new_argv;
554 : 0 : gintptr rc = -1;
555 : : int errsv;
556 : 0 : GError *conv_error = NULL;
557 : : gint conv_error_index;
558 : : wchar_t *wargv0, **wargv, **wenvp;
559 : :
560 : 0 : g_assert (argv != NULL && argv[0] != NULL);
561 : :
562 : 0 : new_argv = (flags & G_SPAWN_FILE_AND_ARGV_ZERO) ? protected_argv + 1 : protected_argv;
563 : :
564 : 0 : wargv0 = g_utf8_to_utf16 (argv[0], -1, NULL, NULL, &conv_error);
565 : 0 : if (wargv0 == NULL)
566 : : {
567 : 0 : g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED,
568 : 0 : _("Invalid program name: %s"),
569 : 0 : conv_error->message);
570 : 0 : g_error_free (conv_error);
571 : :
572 : 0 : return FALSE;
573 : : }
574 : :
575 : 0 : if (!utf8_charv_to_wcharv (new_argv, &wargv, &conv_error_index, &conv_error))
576 : : {
577 : 0 : g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED,
578 : 0 : _("Invalid string in argument vector at %d: %s"),
579 : 0 : conv_error_index, conv_error->message);
580 : 0 : g_error_free (conv_error);
581 : 0 : g_free (wargv0);
582 : :
583 : 0 : return FALSE;
584 : : }
585 : :
586 : 0 : if (!utf8_charv_to_wcharv (envp, &wenvp, NULL, &conv_error))
587 : : {
588 : 0 : g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED,
589 : 0 : _("Invalid string in environment: %s"),
590 : 0 : conv_error->message);
591 : 0 : g_error_free (conv_error);
592 : 0 : g_free (wargv0);
593 : 0 : g_strfreev ((gchar **) wargv);
594 : :
595 : 0 : return FALSE;
596 : : }
597 : :
598 : 0 : if (flags & G_SPAWN_SEARCH_PATH)
599 : 0 : if (wenvp != NULL)
600 : 0 : rc = safe_wspawnvpe (mode, wargv0, (const wchar_t **) wargv, (const wchar_t **) wenvp);
601 : : else
602 : 0 : rc = _wspawnvp (mode, wargv0, (const wchar_t **) wargv);
603 : : else
604 : 0 : if (wenvp != NULL)
605 : 0 : rc = safe_wspawnve (mode, wargv0, (const wchar_t **) wargv, (const wchar_t **) wenvp);
606 : : else
607 : 0 : rc = _wspawnv (mode, wargv0, (const wchar_t **) wargv);
608 : :
609 : 0 : errsv = errno;
610 : :
611 : 0 : g_free (wargv0);
612 : 0 : g_strfreev ((gchar **) wargv);
613 : 0 : g_strfreev ((gchar **) wenvp);
614 : :
615 : 0 : if (rc == -1 && errsv != 0)
616 : : {
617 : 0 : g_set_error (error, G_SPAWN_ERROR, _g_spawn_exec_err_to_g_error (errsv),
618 : 0 : _("Failed to execute child process (%s)"),
619 : 0 : g_strerror (errsv));
620 : 0 : return FALSE;
621 : : }
622 : :
623 : 0 : if (exit_status == NULL)
624 : : {
625 : 0 : if (child_pid && do_return_handle)
626 : 0 : *child_pid = (GPid) rc;
627 : : else
628 : : {
629 : 0 : CloseHandle ((HANDLE) rc);
630 : 0 : if (child_pid)
631 : 0 : *child_pid = 0;
632 : : }
633 : 0 : }
634 : : else
635 : 0 : *exit_status = rc;
636 : :
637 : 0 : return TRUE;
638 : 0 : }
639 : :
640 : : static gboolean
641 : 427 : might_be_console_process (void)
642 : : {
643 : : // we should always fail to attach ourself to a console (because we're
644 : : // either already attached, or we do not have a console)
645 : 427 : gboolean attached_to_self = AttachConsole (GetCurrentProcessId ());
646 : 427 : g_return_val_if_fail (!attached_to_self, TRUE);
647 : :
648 : 427 : switch (GetLastError ())
649 : : {
650 : : // current process is already attached to a console
651 : : case ERROR_ACCESS_DENIED:
652 : 427 : return TRUE;
653 : : // current process does not have a console
654 : : case ERROR_INVALID_HANDLE:
655 : 0 : return FALSE;
656 : : // we should not get ERROR_INVALID_PARAMETER
657 : : }
658 : :
659 : : g_return_val_if_reached (FALSE);
660 : 427 : }
661 : :
662 : : static gboolean
663 : 428 : fork_exec (gint *exit_status,
664 : : gboolean do_return_handle,
665 : : const gchar *working_directory,
666 : : const gchar * const *argv,
667 : : const gchar * const *envp,
668 : : GSpawnFlags flags,
669 : : GSpawnChildSetupFunc child_setup,
670 : : gpointer user_data,
671 : : GPid *child_pid,
672 : : gint *stdin_pipe_out,
673 : : gint *stdout_pipe_out,
674 : : gint *stderr_pipe_out,
675 : : gint stdin_fd,
676 : : gint stdout_fd,
677 : : gint stderr_fd,
678 : : const gint *source_fds,
679 : : const gint *target_fds,
680 : : gsize n_fds,
681 : : gint *err_report,
682 : : GError **error)
683 : : {
684 : : char **protected_argv;
685 : : char args[ARG_COUNT][10];
686 : : char **new_argv;
687 : : int i;
688 : 428 : gintptr rc = -1;
689 : : int errsv;
690 : : int argc;
691 : 428 : int child_err_report_pipe[2] = { -1, -1 };
692 : 428 : int helper_sync_pipe[2] = { -1, -1 };
693 : : gintptr helper_report[2];
694 : : static gboolean warned_about_child_setup = FALSE;
695 : 428 : GError *conv_error = NULL;
696 : : gint conv_error_index;
697 : : gchar *helper_process;
698 : : wchar_t *whelper, **wargv, **wenvp;
699 : 428 : int stdin_pipe[2] = { -1, -1 };
700 : 428 : int stdout_pipe[2] = { -1, -1 };
701 : 428 : int stderr_pipe[2] = { -1, -1 };
702 : :
703 : 428 : g_assert (argv != NULL && argv[0] != NULL);
704 : 428 : g_assert (stdin_pipe_out == NULL || stdin_fd < 0);
705 : 428 : g_assert (stdout_pipe_out == NULL || stdout_fd < 0);
706 : 428 : g_assert (stderr_pipe_out == NULL || stderr_fd < 0);
707 : :
708 : 428 : if (child_setup && !warned_about_child_setup)
709 : : {
710 : 0 : warned_about_child_setup = TRUE;
711 : 0 : g_warning ("passing a child setup function to the g_spawn functions is pointless on Windows and it is ignored");
712 : 0 : }
713 : :
714 : 428 : if (stdin_pipe_out != NULL)
715 : : {
716 : 55 : if (!make_pipe (stdin_pipe, error))
717 : 0 : goto cleanup_and_fail;
718 : 55 : if (_g_spawn_invalid_source_fd (stdin_pipe[0], source_fds, n_fds, error) ||
719 : 55 : _g_spawn_invalid_source_fd (stdin_pipe[1], source_fds, n_fds, error))
720 : 0 : goto cleanup_and_fail;
721 : 55 : stdin_fd = stdin_pipe[0];
722 : 55 : }
723 : :
724 : 428 : if (stdout_pipe_out != NULL)
725 : : {
726 : 362 : if (!make_pipe (stdout_pipe, error))
727 : 0 : goto cleanup_and_fail;
728 : 362 : if (_g_spawn_invalid_source_fd (stdout_pipe[0], source_fds, n_fds, error) ||
729 : 362 : _g_spawn_invalid_source_fd (stdout_pipe[1], source_fds, n_fds, error))
730 : 0 : goto cleanup_and_fail;
731 : 362 : stdout_fd = stdout_pipe[1];
732 : 362 : }
733 : :
734 : 428 : if (stderr_pipe_out != NULL)
735 : : {
736 : 275 : if (!make_pipe (stderr_pipe, error))
737 : 0 : goto cleanup_and_fail;
738 : 275 : if (_g_spawn_invalid_source_fd (stderr_pipe[0], source_fds, n_fds, error) ||
739 : 275 : _g_spawn_invalid_source_fd (stderr_pipe[1], source_fds, n_fds, error))
740 : 0 : goto cleanup_and_fail;
741 : 275 : stderr_fd = stderr_pipe[1];
742 : 275 : }
743 : :
744 : 428 : argc = protect_argv (argv, &protected_argv);
745 : :
746 : 428 : if (stdin_fd == -1 && stdout_fd == -1 && stderr_fd == -1 &&
747 : 38 : (flags & G_SPAWN_CHILD_INHERITS_STDIN) &&
748 : 1 : !(flags & G_SPAWN_STDOUT_TO_DEV_NULL) &&
749 : 1 : !(flags & G_SPAWN_STDERR_TO_DEV_NULL) &&
750 : 1 : (working_directory == NULL || !*working_directory) &&
751 : 1 : (flags & G_SPAWN_LEAVE_DESCRIPTORS_OPEN) &&
752 : 0 : n_fds == 0)
753 : : {
754 : : /* We can do without the helper process */
755 : 0 : gboolean retval =
756 : 0 : do_spawn_directly (exit_status, do_return_handle, flags,
757 : 0 : argv, envp, (const gchar * const *) protected_argv,
758 : 0 : child_pid, error);
759 : 0 : g_strfreev (protected_argv);
760 : 0 : return retval;
761 : : }
762 : :
763 : 428 : if (!make_pipe (child_err_report_pipe, error))
764 : 0 : goto cleanup_and_fail;
765 : 428 : if (_g_spawn_invalid_source_fd (child_err_report_pipe[0], source_fds, n_fds, error) ||
766 : 427 : _g_spawn_invalid_source_fd (child_err_report_pipe[1], source_fds, n_fds, error))
767 : 1 : goto cleanup_and_fail;
768 : :
769 : 427 : if (!make_pipe (helper_sync_pipe, error))
770 : 0 : goto cleanup_and_fail;
771 : 427 : if (_g_spawn_invalid_source_fd (helper_sync_pipe[0], source_fds, n_fds, error) ||
772 : 427 : _g_spawn_invalid_source_fd (helper_sync_pipe[1], source_fds, n_fds, error))
773 : 0 : goto cleanup_and_fail;
774 : :
775 : 427 : new_argv = g_new (char *, argc + 1 + ARG_COUNT);
776 : 427 : if (might_be_console_process ())
777 : 427 : helper_process = HELPER_PROCESS "-console.exe";
778 : : else
779 : 0 : helper_process = HELPER_PROCESS ".exe";
780 : :
781 : 427 : helper_process = g_win32_find_helper_executable_path (helper_process, glib_dll);
782 : 427 : new_argv[0] = protect_argv_string (helper_process);
783 : :
784 : 427 : _g_sprintf (args[ARG_CHILD_ERR_REPORT], "%d", child_err_report_pipe[1]);
785 : 427 : new_argv[ARG_CHILD_ERR_REPORT] = args[ARG_CHILD_ERR_REPORT];
786 : :
787 : : /* Make the read end of the child error report pipe
788 : : * noninherited. Otherwise it will needlessly be inherited by the
789 : : * helper process, and the started actual user process. As such that
790 : : * shouldn't harm, but it is unnecessary.
791 : : */
792 : 427 : child_err_report_pipe[0] = g_win32_reopen_noninherited (
793 : 427 : child_err_report_pipe[0], _O_RDONLY, error);
794 : 427 : if (child_err_report_pipe[0] == -1)
795 : 0 : goto cleanup_and_fail;
796 : :
797 : 427 : if (flags & G_SPAWN_FILE_AND_ARGV_ZERO)
798 : : {
799 : : /* Overload ARG_CHILD_ERR_REPORT to also encode the
800 : : * G_SPAWN_FILE_AND_ARGV_ZERO functionality.
801 : : */
802 : 1 : strcat (args[ARG_CHILD_ERR_REPORT], "#");
803 : 1 : }
804 : :
805 : 427 : _g_sprintf (args[ARG_HELPER_SYNC], "%d", helper_sync_pipe[0]);
806 : 427 : new_argv[ARG_HELPER_SYNC] = args[ARG_HELPER_SYNC];
807 : :
808 : : /* Make the write end of the sync pipe noninherited. Otherwise the
809 : : * helper process will inherit it, and thus if this process happens
810 : : * to crash before writing the sync byte to the pipe, the helper
811 : : * process won't read but won't get any EOF either, as it has the
812 : : * write end open itself.
813 : : */
814 : 427 : helper_sync_pipe[1] = g_win32_reopen_noninherited (
815 : 427 : helper_sync_pipe[1], _O_WRONLY, error);
816 : 427 : if (helper_sync_pipe[1] == -1)
817 : 0 : goto cleanup_and_fail;
818 : :
819 : 427 : if (stdin_fd != -1)
820 : : {
821 : 56 : _g_sprintf (args[ARG_STDIN], "%d", stdin_fd);
822 : 56 : new_argv[ARG_STDIN] = args[ARG_STDIN];
823 : 56 : }
824 : 371 : else if (flags & G_SPAWN_CHILD_INHERITS_STDIN)
825 : : {
826 : : /* Let stdin be alone */
827 : 3 : new_argv[ARG_STDIN] = "-";
828 : 3 : }
829 : : else
830 : : {
831 : : /* Keep process from blocking on a read of stdin */
832 : 368 : new_argv[ARG_STDIN] = "z";
833 : : }
834 : :
835 : 427 : if (stdout_fd != -1)
836 : : {
837 : 365 : _g_sprintf (args[ARG_STDOUT], "%d", stdout_fd);
838 : 365 : new_argv[ARG_STDOUT] = args[ARG_STDOUT];
839 : 365 : }
840 : 62 : else if (flags & G_SPAWN_STDOUT_TO_DEV_NULL)
841 : : {
842 : 28 : new_argv[ARG_STDOUT] = "z";
843 : 28 : }
844 : : else
845 : : {
846 : 34 : new_argv[ARG_STDOUT] = "-";
847 : : }
848 : :
849 : 427 : if (stderr_fd != -1)
850 : : {
851 : 277 : _g_sprintf (args[ARG_STDERR], "%d", stderr_fd);
852 : 277 : new_argv[ARG_STDERR] = args[ARG_STDERR];
853 : 277 : }
854 : 150 : else if (flags & G_SPAWN_STDERR_TO_DEV_NULL)
855 : : {
856 : 65 : new_argv[ARG_STDERR] = "z";
857 : 65 : }
858 : : else
859 : : {
860 : 85 : new_argv[ARG_STDERR] = "-";
861 : : }
862 : :
863 : 427 : if (working_directory && *working_directory)
864 : 258 : new_argv[ARG_WORKING_DIRECTORY] = protect_argv_string (working_directory);
865 : : else
866 : 169 : new_argv[ARG_WORKING_DIRECTORY] = g_strdup ("-");
867 : :
868 : 427 : if (!(flags & G_SPAWN_LEAVE_DESCRIPTORS_OPEN))
869 : 420 : new_argv[ARG_CLOSE_DESCRIPTORS] = "y";
870 : : else
871 : 7 : new_argv[ARG_CLOSE_DESCRIPTORS] = "-";
872 : :
873 : 427 : if (flags & G_SPAWN_SEARCH_PATH)
874 : 7 : new_argv[ARG_USE_PATH] = "y";
875 : : else
876 : 420 : new_argv[ARG_USE_PATH] = "-";
877 : :
878 : 427 : if (exit_status == NULL)
879 : 349 : new_argv[ARG_WAIT] = "-";
880 : : else
881 : 78 : new_argv[ARG_WAIT] = "w";
882 : :
883 : 427 : if (n_fds == 0)
884 : 426 : new_argv[ARG_FDS] = g_strdup ("-");
885 : : else
886 : : {
887 : 1 : GString *fds = g_string_new ("");
888 : : gsize n;
889 : :
890 : 11 : for (n = 0; n < n_fds; n++)
891 : 10 : g_string_append_printf (fds, "%d:%d,", source_fds[n], target_fds[n]);
892 : :
893 : : /* remove the trailing , */
894 : 1 : g_string_truncate (fds, fds->len - 1);
895 : 1 : new_argv[ARG_FDS] = g_string_free (fds, FALSE);
896 : : }
897 : :
898 : 2701 : for (i = 0; i <= argc; i++)
899 : 2274 : new_argv[ARG_PROGRAM + i] = protected_argv[i];
900 : :
901 : 427 : SETUP_DEBUG();
902 : :
903 : 427 : if (debug)
904 : : {
905 : 0 : g_print ("calling %s with argv:\n", helper_process);
906 : 0 : for (i = 0; i < argc + 1 + ARG_COUNT; i++)
907 : 0 : g_print ("argv[%d]: %s\n", i, (new_argv[i] ? new_argv[i] : "NULL"));
908 : 0 : }
909 : :
910 : 427 : if (!utf8_charv_to_wcharv ((const gchar * const *) new_argv, &wargv, &conv_error_index, &conv_error))
911 : : {
912 : 0 : if (conv_error_index == ARG_WORKING_DIRECTORY)
913 : 0 : g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_CHDIR,
914 : 0 : _("Invalid working directory: %s"),
915 : 0 : conv_error->message);
916 : : else
917 : 0 : g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED,
918 : 0 : _("Invalid string in argument vector at %d: %s"),
919 : 0 : conv_error_index - ARG_PROGRAM, conv_error->message);
920 : 0 : g_error_free (conv_error);
921 : 0 : g_strfreev (protected_argv);
922 : 0 : g_free (new_argv[0]);
923 : 0 : g_free (new_argv[ARG_WORKING_DIRECTORY]);
924 : 0 : g_free (new_argv[ARG_FDS]);
925 : 0 : g_free (new_argv);
926 : 0 : g_free (helper_process);
927 : :
928 : 0 : goto cleanup_and_fail;
929 : : }
930 : :
931 : 427 : if (!utf8_charv_to_wcharv (envp, &wenvp, NULL, &conv_error))
932 : : {
933 : 0 : g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED,
934 : 0 : _("Invalid string in environment: %s"),
935 : 0 : conv_error->message);
936 : 0 : g_error_free (conv_error);
937 : 0 : g_strfreev (protected_argv);
938 : 0 : g_free (new_argv[0]);
939 : 0 : g_free (new_argv[ARG_WORKING_DIRECTORY]);
940 : 0 : g_free (new_argv[ARG_FDS]);
941 : 0 : g_free (new_argv);
942 : 0 : g_free (helper_process);
943 : 0 : g_strfreev ((gchar **) wargv);
944 : :
945 : 0 : goto cleanup_and_fail;
946 : : }
947 : :
948 : 427 : whelper = g_utf8_to_utf16 (helper_process, -1, NULL, NULL, NULL);
949 : 427 : g_free (helper_process);
950 : :
951 : 427 : if (wenvp != NULL)
952 : 65 : rc = safe_wspawnvpe (P_NOWAIT, whelper, (const wchar_t **) wargv, (const wchar_t **) wenvp);
953 : : else
954 : 362 : rc = _wspawnvp (P_NOWAIT, whelper, (const wchar_t **) wargv);
955 : :
956 : 427 : errsv = errno;
957 : :
958 : 427 : g_free (whelper);
959 : 427 : g_strfreev ((gchar **) wargv);
960 : 427 : g_strfreev ((gchar **) wenvp);
961 : :
962 : : /* Close the other process's ends of the pipes in this process,
963 : : * otherwise the reader will never get EOF.
964 : : */
965 : 427 : close_and_invalidate (&child_err_report_pipe[1]);
966 : 427 : close_and_invalidate (&helper_sync_pipe[0]);
967 : :
968 : 427 : g_strfreev (protected_argv);
969 : :
970 : 427 : g_free (new_argv[0]);
971 : 427 : g_free (new_argv[ARG_WORKING_DIRECTORY]);
972 : 427 : g_free (new_argv[ARG_FDS]);
973 : 427 : g_free (new_argv);
974 : :
975 : : /* Check if gspawn-win32-helper couldn't be run */
976 : 427 : if (rc == -1 && errsv != 0)
977 : : {
978 : 0 : g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED,
979 : 0 : _("Failed to execute helper program (%s)"),
980 : 0 : g_strerror (errsv));
981 : 0 : goto cleanup_and_fail;
982 : : }
983 : :
984 : 427 : if (exit_status != NULL)
985 : : {
986 : : /* Synchronous case. Pass helper's report pipe back to caller,
987 : : * which takes care of reading it after the grandchild has
988 : : * finished.
989 : : */
990 : 78 : g_assert (err_report != NULL);
991 : 78 : *err_report = child_err_report_pipe[0];
992 : 78 : write (helper_sync_pipe[1], " ", 1);
993 : 78 : close_and_invalidate (&helper_sync_pipe[1]);
994 : 78 : }
995 : : else
996 : : {
997 : : /* Asynchronous case. We read the helper's report right away. */
998 : 349 : if (!read_helper_report (child_err_report_pipe[0], helper_report, error))
999 : 0 : goto cleanup_and_fail;
1000 : :
1001 : 349 : close_and_invalidate (&child_err_report_pipe[0]);
1002 : :
1003 : 349 : switch (helper_report[0])
1004 : : {
1005 : : case CHILD_NO_ERROR:
1006 : 346 : if (child_pid && do_return_handle)
1007 : : {
1008 : : /* rc is our HANDLE for gspawn-win32-helper. It has
1009 : : * told us the HANDLE of its child. Duplicate that into
1010 : : * a HANDLE valid in this process.
1011 : : */
1012 : 686 : if (!DuplicateHandle ((HANDLE) rc, (HANDLE) helper_report[1],
1013 : 343 : GetCurrentProcess (), (LPHANDLE) child_pid,
1014 : : 0, TRUE, DUPLICATE_SAME_ACCESS))
1015 : : {
1016 : 0 : char *emsg = g_win32_error_message (GetLastError ());
1017 : 0 : g_print ("%s\n", emsg);
1018 : 0 : g_free (emsg);
1019 : 0 : *child_pid = 0;
1020 : 0 : }
1021 : 343 : }
1022 : 3 : else if (child_pid)
1023 : 1 : *child_pid = 0;
1024 : 346 : write (helper_sync_pipe[1], " ", 1);
1025 : 346 : close_and_invalidate (&helper_sync_pipe[1]);
1026 : 346 : break;
1027 : :
1028 : : default:
1029 : 3 : write (helper_sync_pipe[1], " ", 1);
1030 : 3 : close_and_invalidate (&helper_sync_pipe[1]);
1031 : 3 : set_child_error (helper_report, working_directory, error);
1032 : 3 : goto cleanup_and_fail;
1033 : : }
1034 : : }
1035 : :
1036 : : /* Success against all odds! return the information */
1037 : :
1038 : 424 : if (rc != -1)
1039 : 424 : CloseHandle ((HANDLE) rc);
1040 : :
1041 : : /* Close the other process's ends of the pipes in this process,
1042 : : * otherwise the reader will never get EOF.
1043 : : */
1044 : 424 : close_and_invalidate (&stdin_pipe[0]);
1045 : 424 : close_and_invalidate (&stdout_pipe[1]);
1046 : 424 : close_and_invalidate (&stderr_pipe[1]);
1047 : :
1048 : 424 : if (stdin_pipe_out != NULL)
1049 : 55 : *stdin_pipe_out = stdin_pipe[1];
1050 : 424 : if (stdout_pipe_out != NULL)
1051 : 362 : *stdout_pipe_out = stdout_pipe[0];
1052 : 424 : if (stderr_pipe_out != NULL)
1053 : 275 : *stderr_pipe_out = stderr_pipe[0];
1054 : :
1055 : 424 : return TRUE;
1056 : :
1057 : : cleanup_and_fail:
1058 : :
1059 : 4 : if (rc != -1)
1060 : 3 : CloseHandle ((HANDLE) rc);
1061 : 4 : if (child_err_report_pipe[0] != -1)
1062 : 1 : close (child_err_report_pipe[0]);
1063 : 4 : if (child_err_report_pipe[1] != -1)
1064 : 1 : close (child_err_report_pipe[1]);
1065 : 4 : if (helper_sync_pipe[0] != -1)
1066 : 0 : close (helper_sync_pipe[0]);
1067 : 4 : if (helper_sync_pipe[1] != -1)
1068 : 0 : close (helper_sync_pipe[1]);
1069 : :
1070 : 4 : if (stdin_pipe[0] != -1)
1071 : 0 : close (stdin_pipe[0]);
1072 : 4 : if (stdin_pipe[1] != -1)
1073 : 0 : close (stdin_pipe[1]);
1074 : 4 : if (stdout_pipe[0] != -1)
1075 : 0 : close (stdout_pipe[0]);
1076 : 4 : if (stdout_pipe[1] != -1)
1077 : 0 : close (stdout_pipe[1]);
1078 : 4 : if (stderr_pipe[0] != -1)
1079 : 0 : close (stderr_pipe[0]);
1080 : 4 : if (stderr_pipe[1] != -1)
1081 : 0 : close (stderr_pipe[1]);
1082 : :
1083 : 4 : return FALSE;
1084 : 428 : }
1085 : :
1086 : : gboolean
1087 : 78 : g_spawn_sync_impl (const gchar *working_directory,
1088 : : gchar **argv,
1089 : : gchar **envp,
1090 : : GSpawnFlags flags,
1091 : : GSpawnChildSetupFunc child_setup,
1092 : : gpointer user_data,
1093 : : gchar **standard_output,
1094 : : gchar **standard_error,
1095 : : gint *wait_status,
1096 : : GError **error)
1097 : : {
1098 : 78 : gint outpipe = -1;
1099 : 78 : gint errpipe = -1;
1100 : 78 : gint reportpipe = -1;
1101 : 78 : GIOChannel *outchannel = NULL;
1102 : 78 : GIOChannel *errchannel = NULL;
1103 : 78 : GPollFD outfd = { -1, 0, 0 }, errfd = { -1, 0, 0 };
1104 : : GPollFD fds[2];
1105 : : gint nfds;
1106 : 78 : gint outindex = -1;
1107 : 78 : gint errindex = -1;
1108 : : gint ret;
1109 : 78 : GString *outstr = NULL;
1110 : 78 : GString *errstr = NULL;
1111 : : gboolean failed;
1112 : : gint status;
1113 : :
1114 : 78 : g_return_val_if_fail (argv != NULL && argv[0] != NULL, FALSE);
1115 : 78 : g_return_val_if_fail (!(flags & G_SPAWN_DO_NOT_REAP_CHILD), FALSE);
1116 : 78 : g_return_val_if_fail (standard_output == NULL ||
1117 : 61 : !(flags & G_SPAWN_STDOUT_TO_DEV_NULL), FALSE);
1118 : 78 : g_return_val_if_fail (standard_error == NULL ||
1119 : 3 : !(flags & G_SPAWN_STDERR_TO_DEV_NULL), FALSE);
1120 : :
1121 : : /* Just to ensure segfaults if callers try to use
1122 : : * these when an error is reported.
1123 : : */
1124 : 78 : if (standard_output)
1125 : 61 : *standard_output = NULL;
1126 : :
1127 : 78 : if (standard_error)
1128 : 3 : *standard_error = NULL;
1129 : :
1130 : 78 : if (!fork_exec (&status,
1131 : : FALSE,
1132 : 78 : working_directory,
1133 : 78 : (const gchar * const *) argv,
1134 : 78 : (const gchar * const *) envp,
1135 : 78 : flags,
1136 : 78 : child_setup,
1137 : 78 : user_data,
1138 : : NULL,
1139 : : NULL,
1140 : 78 : standard_output ? &outpipe : NULL,
1141 : 78 : standard_error ? &errpipe : NULL,
1142 : : -1,
1143 : : -1,
1144 : : -1,
1145 : : NULL, NULL, 0,
1146 : : &reportpipe,
1147 : 78 : error))
1148 : 0 : return FALSE;
1149 : :
1150 : : /* Read data from child. */
1151 : :
1152 : 78 : failed = FALSE;
1153 : :
1154 : 78 : if (outpipe >= 0)
1155 : : {
1156 : 61 : outstr = g_string_new (NULL);
1157 : 61 : outchannel = g_io_channel_win32_new_fd (outpipe);
1158 : 61 : g_io_channel_set_encoding (outchannel, NULL, NULL);
1159 : 61 : g_io_channel_set_buffered (outchannel, FALSE);
1160 : 61 : g_io_channel_win32_make_pollfd (outchannel,
1161 : : G_IO_IN | G_IO_ERR | G_IO_HUP,
1162 : : &outfd);
1163 : 61 : if (debug)
1164 : 0 : g_print ("outfd=%p\n", (HANDLE) outfd.fd);
1165 : 61 : }
1166 : :
1167 : 78 : if (errpipe >= 0)
1168 : : {
1169 : 3 : errstr = g_string_new (NULL);
1170 : 3 : errchannel = g_io_channel_win32_new_fd (errpipe);
1171 : 3 : g_io_channel_set_encoding (errchannel, NULL, NULL);
1172 : 3 : g_io_channel_set_buffered (errchannel, FALSE);
1173 : 3 : g_io_channel_win32_make_pollfd (errchannel,
1174 : : G_IO_IN | G_IO_ERR | G_IO_HUP,
1175 : : &errfd);
1176 : 3 : if (debug)
1177 : 0 : g_print ("errfd=%p\n", (HANDLE) errfd.fd);
1178 : 3 : }
1179 : :
1180 : : /* Read data until we get EOF on all pipes. */
1181 : 662 : while (!failed && (outpipe >= 0 || errpipe >= 0))
1182 : : {
1183 : 253 : nfds = 0;
1184 : 253 : if (outpipe >= 0)
1185 : : {
1186 : 251 : fds[nfds] = outfd;
1187 : 251 : outindex = nfds;
1188 : 251 : nfds++;
1189 : 251 : }
1190 : 253 : if (errpipe >= 0)
1191 : : {
1192 : 7 : fds[nfds] = errfd;
1193 : 7 : errindex = nfds;
1194 : 7 : nfds++;
1195 : 7 : }
1196 : :
1197 : 253 : if (debug)
1198 : 0 : g_print ("g_spawn_sync: calling g_io_channel_win32_poll, nfds=%d\n",
1199 : 0 : nfds);
1200 : :
1201 : 253 : ret = g_io_channel_win32_poll (fds, nfds, -1);
1202 : :
1203 : 253 : if (ret < 0)
1204 : : {
1205 : 0 : failed = TRUE;
1206 : :
1207 : 0 : g_set_error_literal (error, G_SPAWN_ERROR, G_SPAWN_ERROR_READ,
1208 : 0 : _("Unexpected error in g_io_channel_win32_poll() reading data from a child process"));
1209 : :
1210 : 0 : break;
1211 : : }
1212 : :
1213 : 253 : if (outpipe >= 0 && (fds[outindex].revents & G_IO_IN))
1214 : : {
1215 : 248 : switch (read_data (outstr, outchannel, error))
1216 : : {
1217 : : case READ_FAILED:
1218 : 0 : if (debug)
1219 : 0 : g_print ("g_spawn_sync: outchannel: READ_FAILED\n");
1220 : 0 : failed = TRUE;
1221 : 0 : break;
1222 : : case READ_EOF:
1223 : 61 : if (debug)
1224 : 0 : g_print ("g_spawn_sync: outchannel: READ_EOF\n");
1225 : 61 : g_io_channel_unref (outchannel);
1226 : 61 : outchannel = NULL;
1227 : 61 : close_and_invalidate (&outpipe);
1228 : 61 : break;
1229 : : default:
1230 : 187 : if (debug)
1231 : 0 : g_print ("g_spawn_sync: outchannel: OK\n");
1232 : 187 : break;
1233 : : }
1234 : :
1235 : 248 : if (failed)
1236 : 0 : break;
1237 : 248 : }
1238 : :
1239 : 253 : if (errpipe >= 0 && (fds[errindex].revents & G_IO_IN))
1240 : : {
1241 : 5 : switch (read_data (errstr, errchannel, error))
1242 : : {
1243 : : case READ_FAILED:
1244 : 0 : if (debug)
1245 : 0 : g_print ("g_spawn_sync: errchannel: READ_FAILED\n");
1246 : 0 : failed = TRUE;
1247 : 0 : break;
1248 : : case READ_EOF:
1249 : 3 : if (debug)
1250 : 0 : g_print ("g_spawn_sync: errchannel: READ_EOF\n");
1251 : 3 : g_io_channel_unref (errchannel);
1252 : 3 : errchannel = NULL;
1253 : 3 : close_and_invalidate (&errpipe);
1254 : 3 : break;
1255 : : default:
1256 : 2 : if (debug)
1257 : 0 : g_print ("g_spawn_sync: errchannel: OK\n");
1258 : 2 : break;
1259 : : }
1260 : :
1261 : 5 : if (failed)
1262 : 0 : break;
1263 : 5 : }
1264 : : }
1265 : :
1266 : 78 : if (reportpipe == -1)
1267 : : {
1268 : : /* No helper process, exit status of actual spawned process
1269 : : * already available.
1270 : : */
1271 : 0 : if (wait_status)
1272 : 0 : *wait_status = status;
1273 : 0 : }
1274 : : else
1275 : : {
1276 : : /* Helper process was involved. Read its report now after the
1277 : : * grandchild has finished.
1278 : : */
1279 : : gintptr helper_report[2];
1280 : :
1281 : 78 : if (!read_helper_report (reportpipe, helper_report, error))
1282 : 0 : failed = TRUE;
1283 : : else
1284 : : {
1285 : 78 : switch (helper_report[0])
1286 : : {
1287 : : case CHILD_NO_ERROR:
1288 : 76 : if (wait_status)
1289 : 70 : *wait_status = helper_report[1];
1290 : 76 : break;
1291 : : default:
1292 : 2 : set_child_error (helper_report, working_directory, error);
1293 : 2 : failed = TRUE;
1294 : 2 : break;
1295 : : }
1296 : : }
1297 : 78 : close_and_invalidate (&reportpipe);
1298 : : }
1299 : :
1300 : :
1301 : : /* These should only be open still if we had an error. */
1302 : :
1303 : 78 : if (outchannel != NULL)
1304 : 0 : g_io_channel_unref (outchannel);
1305 : 78 : if (errchannel != NULL)
1306 : 0 : g_io_channel_unref (errchannel);
1307 : 78 : if (outpipe >= 0)
1308 : 0 : close_and_invalidate (&outpipe);
1309 : 78 : if (errpipe >= 0)
1310 : 0 : close_and_invalidate (&errpipe);
1311 : :
1312 : 78 : if (failed)
1313 : : {
1314 : 2 : if (outstr)
1315 : 1 : g_string_free (outstr, TRUE);
1316 : 2 : if (errstr)
1317 : 0 : g_string_free (errstr, TRUE);
1318 : :
1319 : 2 : return FALSE;
1320 : : }
1321 : : else
1322 : : {
1323 : 76 : if (standard_output)
1324 : 60 : *standard_output = g_string_free (outstr, FALSE);
1325 : :
1326 : 76 : if (standard_error)
1327 : 3 : *standard_error = g_string_free (errstr, FALSE);
1328 : :
1329 : 76 : return TRUE;
1330 : : }
1331 : 78 : }
1332 : :
1333 : : gboolean
1334 : 350 : g_spawn_async_with_pipes_and_fds_impl (const gchar *working_directory,
1335 : : const gchar * const *argv,
1336 : : const gchar * const *envp,
1337 : : GSpawnFlags flags,
1338 : : GSpawnChildSetupFunc child_setup,
1339 : : gpointer user_data,
1340 : : gint stdin_fd,
1341 : : gint stdout_fd,
1342 : : gint stderr_fd,
1343 : : const gint *source_fds,
1344 : : const gint *target_fds,
1345 : : gsize n_fds,
1346 : : GPid *child_pid_out,
1347 : : gint *stdin_pipe_out,
1348 : : gint *stdout_pipe_out,
1349 : : gint *stderr_pipe_out,
1350 : : GError **error)
1351 : : {
1352 : 350 : g_return_val_if_fail (argv != NULL && argv[0] != NULL, FALSE);
1353 : 350 : g_return_val_if_fail (stdout_pipe_out == NULL ||
1354 : 301 : !(flags & G_SPAWN_STDOUT_TO_DEV_NULL), FALSE);
1355 : 350 : g_return_val_if_fail (stderr_pipe_out == NULL ||
1356 : 272 : !(flags & G_SPAWN_STDERR_TO_DEV_NULL), FALSE);
1357 : : /* can't inherit stdin if we have an input pipe. */
1358 : 350 : g_return_val_if_fail (stdin_pipe_out == NULL ||
1359 : 55 : !(flags & G_SPAWN_CHILD_INHERITS_STDIN), FALSE);
1360 : : /* can’t use pipes and stdin/stdout/stderr FDs */
1361 : 350 : g_return_val_if_fail (stdin_pipe_out == NULL || stdin_fd < 0, FALSE);
1362 : 350 : g_return_val_if_fail (stdout_pipe_out == NULL || stdout_fd < 0, FALSE);
1363 : 350 : g_return_val_if_fail (stderr_pipe_out == NULL || stderr_fd < 0, FALSE);
1364 : :
1365 : 350 : return fork_exec (NULL,
1366 : 350 : (flags & G_SPAWN_DO_NOT_REAP_CHILD),
1367 : 350 : working_directory,
1368 : 350 : argv,
1369 : 350 : envp,
1370 : 350 : flags,
1371 : 350 : child_setup,
1372 : 350 : user_data,
1373 : 350 : child_pid_out,
1374 : 350 : stdin_pipe_out,
1375 : 350 : stdout_pipe_out,
1376 : 350 : stderr_pipe_out,
1377 : 350 : stdin_fd,
1378 : 350 : stdout_fd,
1379 : 350 : stderr_fd,
1380 : 350 : source_fds,
1381 : 350 : target_fds,
1382 : 350 : n_fds,
1383 : : NULL,
1384 : 350 : error);
1385 : 350 : }
1386 : :
1387 : : void
1388 : 77 : g_spawn_close_pid_impl (GPid pid)
1389 : : {
1390 : : /* CRT functions such as _wspawn* return (HANDLE)-1
1391 : : * on failure, so check also for that value. */
1392 : 77 : if (pid != NULL && pid != (HANDLE) -1)
1393 : 77 : CloseHandle (pid);
1394 : 77 : }
1395 : :
1396 : : gboolean
1397 : 72 : g_spawn_check_wait_status_impl (gint wait_status,
1398 : : GError **error)
1399 : : {
1400 : 72 : gboolean ret = FALSE;
1401 : :
1402 : 72 : if (wait_status != 0)
1403 : : {
1404 : : /* On Windows, the wait status is just the exit status: the
1405 : : * difference between the two that exists on Unix is not relevant */
1406 : 48 : g_set_error (error, G_SPAWN_EXIT_ERROR, wait_status,
1407 : 24 : _("Child process exited with code %ld"),
1408 : 24 : (long) wait_status);
1409 : 24 : goto out;
1410 : : }
1411 : :
1412 : 48 : ret = TRUE;
1413 : : out:
1414 : 72 : return ret;
1415 : : }
1416 : :
1417 : : #ifdef G_OS_WIN32
1418 : :
1419 : : /* Binary compatibility versions. Not for newly compiled code. */
1420 : :
1421 : : _GLIB_EXTERN gboolean g_spawn_async_utf8 (const gchar *working_directory,
1422 : : gchar **argv,
1423 : : gchar **envp,
1424 : : GSpawnFlags flags,
1425 : : GSpawnChildSetupFunc child_setup,
1426 : : gpointer user_data,
1427 : : GPid *child_pid,
1428 : : GError **error);
1429 : : _GLIB_EXTERN gboolean g_spawn_async_with_pipes_utf8 (const gchar *working_directory,
1430 : : gchar **argv,
1431 : : gchar **envp,
1432 : : GSpawnFlags flags,
1433 : : GSpawnChildSetupFunc child_setup,
1434 : : gpointer user_data,
1435 : : GPid *child_pid,
1436 : : gint *standard_input,
1437 : : gint *standard_output,
1438 : : gint *standard_error,
1439 : : GError **error);
1440 : : _GLIB_EXTERN gboolean g_spawn_sync_utf8 (const gchar *working_directory,
1441 : : gchar **argv,
1442 : : gchar **envp,
1443 : : GSpawnFlags flags,
1444 : : GSpawnChildSetupFunc child_setup,
1445 : : gpointer user_data,
1446 : : gchar **standard_output,
1447 : : gchar **standard_error,
1448 : : gint *wait_status,
1449 : : GError **error);
1450 : : _GLIB_EXTERN gboolean g_spawn_command_line_sync_utf8 (const gchar *command_line,
1451 : : gchar **standard_output,
1452 : : gchar **standard_error,
1453 : : gint *wait_status,
1454 : : GError **error);
1455 : : _GLIB_EXTERN gboolean g_spawn_command_line_async_utf8 (const gchar *command_line,
1456 : : GError **error);
1457 : :
1458 : : gboolean
1459 : 0 : g_spawn_async_utf8 (const gchar *working_directory,
1460 : : gchar **argv,
1461 : : gchar **envp,
1462 : : GSpawnFlags flags,
1463 : : GSpawnChildSetupFunc child_setup,
1464 : : gpointer user_data,
1465 : : GPid *child_pid,
1466 : : GError **error)
1467 : : {
1468 : 0 : return g_spawn_async (working_directory,
1469 : 0 : argv,
1470 : 0 : envp,
1471 : 0 : flags,
1472 : 0 : child_setup,
1473 : 0 : user_data,
1474 : 0 : child_pid,
1475 : 0 : error);
1476 : : }
1477 : :
1478 : : gboolean
1479 : 0 : g_spawn_async_with_pipes_utf8 (const gchar *working_directory,
1480 : : gchar **argv,
1481 : : gchar **envp,
1482 : : GSpawnFlags flags,
1483 : : GSpawnChildSetupFunc child_setup,
1484 : : gpointer user_data,
1485 : : GPid *child_pid,
1486 : : gint *standard_input,
1487 : : gint *standard_output,
1488 : : gint *standard_error,
1489 : : GError **error)
1490 : : {
1491 : 0 : return g_spawn_async_with_pipes (working_directory,
1492 : 0 : argv,
1493 : 0 : envp,
1494 : 0 : flags,
1495 : 0 : child_setup,
1496 : 0 : user_data,
1497 : 0 : child_pid,
1498 : 0 : standard_input,
1499 : 0 : standard_output,
1500 : 0 : standard_error,
1501 : 0 : error);
1502 : : }
1503 : :
1504 : : gboolean
1505 : 0 : g_spawn_sync_utf8 (const gchar *working_directory,
1506 : : gchar **argv,
1507 : : gchar **envp,
1508 : : GSpawnFlags flags,
1509 : : GSpawnChildSetupFunc child_setup,
1510 : : gpointer user_data,
1511 : : gchar **standard_output,
1512 : : gchar **standard_error,
1513 : : gint *wait_status,
1514 : : GError **error)
1515 : : {
1516 : 0 : return g_spawn_sync (working_directory,
1517 : 0 : argv,
1518 : 0 : envp,
1519 : 0 : flags,
1520 : 0 : child_setup,
1521 : 0 : user_data,
1522 : 0 : standard_output,
1523 : 0 : standard_error,
1524 : 0 : wait_status,
1525 : 0 : error);
1526 : : }
1527 : :
1528 : : gboolean
1529 : 0 : g_spawn_command_line_sync_utf8 (const gchar *command_line,
1530 : : gchar **standard_output,
1531 : : gchar **standard_error,
1532 : : gint *wait_status,
1533 : : GError **error)
1534 : : {
1535 : 0 : return g_spawn_command_line_sync (command_line,
1536 : 0 : standard_output,
1537 : 0 : standard_error,
1538 : 0 : wait_status,
1539 : 0 : error);
1540 : : }
1541 : :
1542 : : gboolean
1543 : 0 : g_spawn_command_line_async_utf8 (const gchar *command_line,
1544 : : GError **error)
1545 : : {
1546 : 0 : return g_spawn_command_line_async (command_line, error);
1547 : : }
1548 : :
1549 : : #endif /* G_OS_WIN32 */
1550 : :
1551 : : #endif /* !GSPAWN_HELPER */
|