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