1/* GIO - GLib Input, Output and Streaming Library
2 *
3 * Copyright © 2012 Red Hat, Inc.
4 * Copyright © 2012-2013 Canonical Limited
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 * See the included COPYING file for more information.
12 *
13 * Authors: Colin Walters <walters@verbum.org>
14 * Ryan Lortie <desrt@desrt.ca>
15 */
16
17/**
18 * SECTION:gsubprocesslauncher
19 * @title: GSubprocess Launcher
20 * @short_description: Environment options for launching a child process
21 * @include: gio/gio.h
22 *
23 * This class contains a set of options for launching child processes,
24 * such as where its standard input and output will be directed, the
25 * argument list, the environment, and more.
26 *
27 * While the #GSubprocess class has high level functions covering
28 * popular cases, use of this class allows access to more advanced
29 * options. It can also be used to launch multiple subprocesses with
30 * a similar configuration.
31 *
32 * Since: 2.40
33 */
34
35#define ALL_STDIN_FLAGS (G_SUBPROCESS_FLAGS_STDIN_PIPE | \
36 G_SUBPROCESS_FLAGS_STDIN_INHERIT)
37#define ALL_STDOUT_FLAGS (G_SUBPROCESS_FLAGS_STDOUT_PIPE | \
38 G_SUBPROCESS_FLAGS_STDOUT_SILENCE)
39#define ALL_STDERR_FLAGS (G_SUBPROCESS_FLAGS_STDERR_PIPE | \
40 G_SUBPROCESS_FLAGS_STDERR_SILENCE | \
41 G_SUBPROCESS_FLAGS_STDERR_MERGE)
42
43#include "config.h"
44
45#include "gsubprocesslauncher-private.h"
46#include "gioenumtypes.h"
47#include "gsubprocess.h"
48#include "ginitable.h"
49#include "gioerror.h"
50
51#ifdef G_OS_UNIX
52#include <unistd.h>
53#include <fcntl.h>
54#endif
55
56typedef GObjectClass GSubprocessLauncherClass;
57
58G_DEFINE_TYPE (GSubprocessLauncher, g_subprocess_launcher, G_TYPE_OBJECT)
59
60static gboolean
61verify_disposition (const gchar *stream_name,
62 GSubprocessFlags filtered_flags,
63 gint fd,
64 const gchar *filename)
65{
66 guint n_bits;
67
68 if (!filtered_flags)
69 n_bits = 0;
70 else if (((filtered_flags - 1) & filtered_flags) == 0)
71 n_bits = 1;
72 else
73 n_bits = 2; /* ...or more */
74
75 if (n_bits + (fd >= 0) + (filename != NULL) > 1)
76 {
77 GString *err;
78
79 err = g_string_new (NULL);
80 if (n_bits)
81 {
82 GFlagsClass *class;
83 guint i;
84
85 class = g_type_class_peek (type: G_TYPE_SUBPROCESS_FLAGS);
86
87 for (i = 0; i < class->n_values; i++)
88 {
89 const GFlagsValue *value = &class->values[i];
90
91 if (filtered_flags & value->value)
92 g_string_append_printf (string: err, format: " %s", value->value_name);
93 }
94
95 g_type_class_unref (g_class: class);
96 }
97
98 if (fd >= 0)
99 g_string_append_printf (string: err, format: " g_subprocess_launcher_take_%s_fd()", stream_name);
100
101 if (filename)
102 g_string_append_printf (string: err, format: " g_subprocess_launcher_set_%s_file_path()", stream_name);
103
104 g_critical ("You may specify at most one disposition for the %s stream, but you specified:%s.",
105 stream_name, err->str);
106 g_string_free (string: err, TRUE);
107
108 return FALSE;
109 }
110
111 return TRUE;
112}
113
114static gboolean
115verify_flags (GSubprocessFlags flags)
116{
117 return verify_disposition (stream_name: "stdin", filtered_flags: flags & ALL_STDIN_FLAGS, fd: -1, NULL) &&
118 verify_disposition (stream_name: "stdout", filtered_flags: flags & ALL_STDOUT_FLAGS, fd: -1, NULL) &&
119 verify_disposition (stream_name: "stderr", filtered_flags: flags & ALL_STDERR_FLAGS, fd: -1, NULL);
120}
121
122static void
123g_subprocess_launcher_set_property (GObject *object, guint prop_id,
124 const GValue *value, GParamSpec *pspec)
125{
126 GSubprocessLauncher *launcher = G_SUBPROCESS_LAUNCHER (object);
127
128 g_assert (prop_id == 1);
129
130 if (verify_flags (flags: g_value_get_flags (value)))
131 launcher->flags = g_value_get_flags (value);
132}
133
134static void
135g_subprocess_launcher_dispose (GObject *object)
136{
137 GSubprocessLauncher *self = G_SUBPROCESS_LAUNCHER (object);
138
139#ifdef G_OS_UNIX
140 g_clear_pointer (&self->stdin_path, g_free);
141 g_clear_pointer (&self->stdout_path, g_free);
142 g_clear_pointer (&self->stderr_path, g_free);
143
144 g_subprocess_launcher_close (self);
145
146 if (self->child_setup_destroy_notify)
147 (* self->child_setup_destroy_notify) (self->child_setup_user_data);
148 self->child_setup_destroy_notify = NULL;
149 self->child_setup_user_data = NULL;
150#endif
151
152 g_clear_pointer (&self->envp, g_strfreev);
153 g_clear_pointer (&self->cwd, g_free);
154
155 G_OBJECT_CLASS (g_subprocess_launcher_parent_class)->dispose (object);
156}
157
158static void
159g_subprocess_launcher_init (GSubprocessLauncher *self)
160{
161 self->envp = g_get_environ ();
162
163#ifdef G_OS_UNIX
164 self->stdin_fd = -1;
165 self->stdout_fd = -1;
166 self->stderr_fd = -1;
167 self->source_fds = g_array_new (FALSE, clear_: 0, element_size: sizeof (int));
168 self->target_fds = g_array_new (FALSE, clear_: 0, element_size: sizeof (int));
169#endif
170}
171
172static void
173g_subprocess_launcher_class_init (GSubprocessLauncherClass *class)
174{
175 GObjectClass *gobject_class = G_OBJECT_CLASS (class);
176
177 gobject_class->set_property = g_subprocess_launcher_set_property;
178 gobject_class->dispose = g_subprocess_launcher_dispose;
179
180 g_object_class_install_property (oclass: gobject_class, property_id: 1,
181 pspec: g_param_spec_flags (name: "flags", nick: "Flags", blurb: "GSubprocessFlags for launched processes",
182 flags_type: G_TYPE_SUBPROCESS_FLAGS, default_value: 0, flags: G_PARAM_WRITABLE |
183 G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY));
184}
185
186/**
187 * g_subprocess_launcher_new:
188 * @flags: #GSubprocessFlags
189 *
190 * Creates a new #GSubprocessLauncher.
191 *
192 * The launcher is created with the default options. A copy of the
193 * environment of the calling process is made at the time of this call
194 * and will be used as the environment that the process is launched in.
195 *
196 * Since: 2.40
197 **/
198GSubprocessLauncher *
199g_subprocess_launcher_new (GSubprocessFlags flags)
200{
201 if (!verify_flags (flags))
202 return NULL;
203
204 return g_object_new (G_TYPE_SUBPROCESS_LAUNCHER,
205 first_property_name: "flags", flags,
206 NULL);
207}
208
209/**
210 * g_subprocess_launcher_set_environ:
211 * @self: a #GSubprocessLauncher
212 * @env: (array zero-terminated=1) (element-type filename) (transfer none):
213 * the replacement environment
214 *
215 * Replace the entire environment of processes launched from this
216 * launcher with the given 'environ' variable.
217 *
218 * Typically you will build this variable by using g_listenv() to copy
219 * the process 'environ' and using the functions g_environ_setenv(),
220 * g_environ_unsetenv(), etc.
221 *
222 * As an alternative, you can use g_subprocess_launcher_setenv(),
223 * g_subprocess_launcher_unsetenv(), etc.
224 *
225 * Pass an empty array to set an empty environment. Pass %NULL to inherit the
226 * parent process’ environment. As of GLib 2.54, the parent process’ environment
227 * will be copied when g_subprocess_launcher_set_environ() is called.
228 * Previously, it was copied when the subprocess was executed. This means the
229 * copied environment may now be modified (using g_subprocess_launcher_setenv(),
230 * etc.) before launching the subprocess.
231 *
232 * On UNIX, all strings in this array can be arbitrary byte strings.
233 * On Windows, they should be in UTF-8.
234 *
235 * Since: 2.40
236 **/
237void
238g_subprocess_launcher_set_environ (GSubprocessLauncher *self,
239 gchar **env)
240{
241 g_strfreev (str_array: self->envp);
242 self->envp = g_strdupv (str_array: env);
243
244 if (self->envp == NULL)
245 self->envp = g_get_environ ();
246}
247
248/**
249 * g_subprocess_launcher_setenv:
250 * @self: a #GSubprocessLauncher
251 * @variable: (type filename): the environment variable to set,
252 * must not contain '='
253 * @value: (type filename): the new value for the variable
254 * @overwrite: whether to change the variable if it already exists
255 *
256 * Sets the environment variable @variable in the environment of
257 * processes launched from this launcher.
258 *
259 * On UNIX, both the variable's name and value can be arbitrary byte
260 * strings, except that the variable's name cannot contain '='.
261 * On Windows, they should be in UTF-8.
262 *
263 * Since: 2.40
264 **/
265void
266g_subprocess_launcher_setenv (GSubprocessLauncher *self,
267 const gchar *variable,
268 const gchar *value,
269 gboolean overwrite)
270{
271 self->envp = g_environ_setenv (envp: self->envp, variable, value, overwrite);
272}
273
274/**
275 * g_subprocess_launcher_unsetenv:
276 * @self: a #GSubprocessLauncher
277 * @variable: (type filename): the environment variable to unset,
278 * must not contain '='
279 *
280 * Removes the environment variable @variable from the environment of
281 * processes launched from this launcher.
282 *
283 * On UNIX, the variable's name can be an arbitrary byte string not
284 * containing '='. On Windows, it should be in UTF-8.
285 *
286 * Since: 2.40
287 **/
288void
289g_subprocess_launcher_unsetenv (GSubprocessLauncher *self,
290 const gchar *variable)
291{
292 self->envp = g_environ_unsetenv (envp: self->envp, variable);
293}
294
295/**
296 * g_subprocess_launcher_getenv:
297 * @self: a #GSubprocessLauncher
298 * @variable: (type filename): the environment variable to get
299 *
300 * Returns the value of the environment variable @variable in the
301 * environment of processes launched from this launcher.
302 *
303 * On UNIX, the returned string can be an arbitrary byte string.
304 * On Windows, it will be UTF-8.
305 *
306 * Returns: (nullable) (type filename): the value of the environment variable,
307 * %NULL if unset
308 *
309 * Since: 2.40
310 **/
311const gchar *
312g_subprocess_launcher_getenv (GSubprocessLauncher *self,
313 const gchar *variable)
314{
315 return g_environ_getenv (envp: self->envp, variable);
316}
317
318/**
319 * g_subprocess_launcher_set_cwd:
320 * @self: a #GSubprocessLauncher
321 * @cwd: (type filename): the cwd for launched processes
322 *
323 * Sets the current working directory that processes will be launched
324 * with.
325 *
326 * By default processes are launched with the current working directory
327 * of the launching process at the time of launch.
328 *
329 * Since: 2.40
330 **/
331void
332g_subprocess_launcher_set_cwd (GSubprocessLauncher *self,
333 const gchar *cwd)
334{
335 g_free (mem: self->cwd);
336 self->cwd = g_strdup (str: cwd);
337}
338
339/**
340 * g_subprocess_launcher_set_flags:
341 * @self: a #GSubprocessLauncher
342 * @flags: #GSubprocessFlags
343 *
344 * Sets the flags on the launcher.
345 *
346 * The default flags are %G_SUBPROCESS_FLAGS_NONE.
347 *
348 * You may not set flags that specify conflicting options for how to
349 * handle a particular stdio stream (eg: specifying both
350 * %G_SUBPROCESS_FLAGS_STDIN_PIPE and
351 * %G_SUBPROCESS_FLAGS_STDIN_INHERIT).
352 *
353 * You may also not set a flag that conflicts with a previous call to a
354 * function like g_subprocess_launcher_set_stdin_file_path() or
355 * g_subprocess_launcher_take_stdout_fd().
356 *
357 * Since: 2.40
358 **/
359void
360g_subprocess_launcher_set_flags (GSubprocessLauncher *self,
361 GSubprocessFlags flags)
362{
363 const gchar *stdin_path = NULL, *stdout_path = NULL, *stderr_path = NULL;
364 gint stdin_fd = -1, stdout_fd = -1, stderr_fd = -1;
365
366#ifdef G_OS_UNIX
367 stdin_fd = self->stdin_fd;
368 stdout_fd = self->stdout_fd;
369 stderr_fd = self->stderr_fd;
370 stdin_path = self->stdin_path;
371 stdout_path = self->stdout_path;
372 stderr_path = self->stderr_path;
373#endif
374
375 if (verify_disposition (stream_name: "stdin", filtered_flags: flags & ALL_STDIN_FLAGS, fd: stdin_fd, filename: stdin_path) &&
376 verify_disposition (stream_name: "stdout", filtered_flags: flags & ALL_STDOUT_FLAGS, fd: stdout_fd, filename: stdout_path) &&
377 verify_disposition (stream_name: "stderr", filtered_flags: flags & ALL_STDERR_FLAGS, fd: stderr_fd, filename: stderr_path))
378 self->flags = flags;
379}
380
381#ifdef G_OS_UNIX
382static void
383assign_fd (gint *fd_ptr, gint fd)
384{
385 gint flags;
386
387 if (*fd_ptr != -1)
388 close (fd: *fd_ptr);
389
390 *fd_ptr = fd;
391
392 if (fd != -1)
393 {
394 /* best effort */
395 flags = fcntl (fd: fd, F_GETFD);
396 if (~flags & FD_CLOEXEC)
397 fcntl (fd: fd, F_SETFD, flags | FD_CLOEXEC);
398 }
399}
400
401/**
402 * g_subprocess_launcher_set_stdin_file_path:
403 * @self: a #GSubprocessLauncher
404 * @path: (type filename) (nullable: a filename or %NULL
405 *
406 * Sets the file path to use as the stdin for spawned processes.
407 *
408 * If @path is %NULL then any previously given path is unset.
409 *
410 * The file must exist or spawning the process will fail.
411 *
412 * You may not set a stdin file path if a stdin fd is already set or if
413 * the launcher flags contain any flags directing stdin elsewhere.
414 *
415 * This feature is only available on UNIX.
416 *
417 * Since: 2.40
418 **/
419void
420g_subprocess_launcher_set_stdin_file_path (GSubprocessLauncher *self,
421 const gchar *path)
422{
423 if (verify_disposition (stream_name: "stdin", filtered_flags: self->flags & ALL_STDIN_FLAGS, fd: self->stdin_fd, filename: path))
424 {
425 g_free (mem: self->stdin_path);
426 self->stdin_path = g_strdup (str: path);
427 }
428}
429
430/**
431 * g_subprocess_launcher_take_stdin_fd:
432 * @self: a #GSubprocessLauncher
433 * @fd: a file descriptor, or -1
434 *
435 * Sets the file descriptor to use as the stdin for spawned processes.
436 *
437 * If @fd is -1 then any previously given fd is unset.
438 *
439 * Note that if your intention is to have the stdin of the calling
440 * process inherited by the child then %G_SUBPROCESS_FLAGS_STDIN_INHERIT
441 * is a better way to go about doing that.
442 *
443 * The passed @fd is noted but will not be touched in the current
444 * process. It is therefore necessary that it be kept open by the
445 * caller until the subprocess is spawned. The file descriptor will
446 * also not be explicitly closed on the child side, so it must be marked
447 * O_CLOEXEC if that's what you want.
448 *
449 * You may not set a stdin fd if a stdin file path is already set or if
450 * the launcher flags contain any flags directing stdin elsewhere.
451 *
452 * This feature is only available on UNIX.
453 *
454 * Since: 2.40
455 **/
456void
457g_subprocess_launcher_take_stdin_fd (GSubprocessLauncher *self,
458 gint fd)
459{
460 if (verify_disposition (stream_name: "stdin", filtered_flags: self->flags & ALL_STDIN_FLAGS, fd, filename: self->stdin_path))
461 assign_fd (fd_ptr: &self->stdin_fd, fd);
462}
463
464/**
465 * g_subprocess_launcher_set_stdout_file_path:
466 * @self: a #GSubprocessLauncher
467 * @path: (type filename) (nullable): a filename or %NULL
468 *
469 * Sets the file path to use as the stdout for spawned processes.
470 *
471 * If @path is %NULL then any previously given path is unset.
472 *
473 * The file will be created or truncated when the process is spawned, as
474 * would be the case if using '>' at the shell.
475 *
476 * You may not set a stdout file path if a stdout fd is already set or
477 * if the launcher flags contain any flags directing stdout elsewhere.
478 *
479 * This feature is only available on UNIX.
480 *
481 * Since: 2.40
482 **/
483void
484g_subprocess_launcher_set_stdout_file_path (GSubprocessLauncher *self,
485 const gchar *path)
486{
487 if (verify_disposition (stream_name: "stdout", filtered_flags: self->flags & ALL_STDOUT_FLAGS, fd: self->stdout_fd, filename: path))
488 {
489 g_free (mem: self->stdout_path);
490 self->stdout_path = g_strdup (str: path);
491 }
492}
493
494/**
495 * g_subprocess_launcher_take_stdout_fd:
496 * @self: a #GSubprocessLauncher
497 * @fd: a file descriptor, or -1
498 *
499 * Sets the file descriptor to use as the stdout for spawned processes.
500 *
501 * If @fd is -1 then any previously given fd is unset.
502 *
503 * Note that the default behaviour is to pass stdout through to the
504 * stdout of the parent process.
505 *
506 * The passed @fd is noted but will not be touched in the current
507 * process. It is therefore necessary that it be kept open by the
508 * caller until the subprocess is spawned. The file descriptor will
509 * also not be explicitly closed on the child side, so it must be marked
510 * O_CLOEXEC if that's what you want.
511 *
512 * You may not set a stdout fd if a stdout file path is already set or
513 * if the launcher flags contain any flags directing stdout elsewhere.
514 *
515 * This feature is only available on UNIX.
516 *
517 * Since: 2.40
518 **/
519void
520g_subprocess_launcher_take_stdout_fd (GSubprocessLauncher *self,
521 gint fd)
522{
523 if (verify_disposition (stream_name: "stdout", filtered_flags: self->flags & ALL_STDOUT_FLAGS, fd, filename: self->stdout_path))
524 assign_fd (fd_ptr: &self->stdout_fd, fd);
525}
526
527/**
528 * g_subprocess_launcher_set_stderr_file_path:
529 * @self: a #GSubprocessLauncher
530 * @path: (type filename) (nullable): a filename or %NULL
531 *
532 * Sets the file path to use as the stderr for spawned processes.
533 *
534 * If @path is %NULL then any previously given path is unset.
535 *
536 * The file will be created or truncated when the process is spawned, as
537 * would be the case if using '2>' at the shell.
538 *
539 * If you want to send both stdout and stderr to the same file then use
540 * %G_SUBPROCESS_FLAGS_STDERR_MERGE.
541 *
542 * You may not set a stderr file path if a stderr fd is already set or
543 * if the launcher flags contain any flags directing stderr elsewhere.
544 *
545 * This feature is only available on UNIX.
546 *
547 * Since: 2.40
548 **/
549void
550g_subprocess_launcher_set_stderr_file_path (GSubprocessLauncher *self,
551 const gchar *path)
552{
553 if (verify_disposition (stream_name: "stderr", filtered_flags: self->flags & ALL_STDERR_FLAGS, fd: self->stderr_fd, filename: path))
554 {
555 g_free (mem: self->stderr_path);
556 self->stderr_path = g_strdup (str: path);
557 }
558}
559
560/**
561 * g_subprocess_launcher_take_stderr_fd:
562 * @self: a #GSubprocessLauncher
563 * @fd: a file descriptor, or -1
564 *
565 * Sets the file descriptor to use as the stderr for spawned processes.
566 *
567 * If @fd is -1 then any previously given fd is unset.
568 *
569 * Note that the default behaviour is to pass stderr through to the
570 * stderr of the parent process.
571 *
572 * The passed @fd belongs to the #GSubprocessLauncher. It will be
573 * automatically closed when the launcher is finalized. The file
574 * descriptor will also be closed on the child side when executing the
575 * spawned process.
576 *
577 * You may not set a stderr fd if a stderr file path is already set or
578 * if the launcher flags contain any flags directing stderr elsewhere.
579 *
580 * This feature is only available on UNIX.
581 *
582 * Since: 2.40
583 **/
584void
585g_subprocess_launcher_take_stderr_fd (GSubprocessLauncher *self,
586 gint fd)
587{
588 if (verify_disposition (stream_name: "stderr", filtered_flags: self->flags & ALL_STDERR_FLAGS, fd, filename: self->stderr_path))
589 assign_fd (fd_ptr: &self->stderr_fd, fd);
590}
591
592/**
593 * g_subprocess_launcher_take_fd:
594 * @self: a #GSubprocessLauncher
595 * @source_fd: File descriptor in parent process
596 * @target_fd: Target descriptor for child process
597 *
598 * Transfer an arbitrary file descriptor from parent process to the
599 * child. This function takes ownership of the @source_fd; it will be closed
600 * in the parent when @self is freed.
601 *
602 * By default, all file descriptors from the parent will be closed.
603 * This function allows you to create (for example) a custom `pipe()` or
604 * `socketpair()` before launching the process, and choose the target
605 * descriptor in the child.
606 *
607 * An example use case is GNUPG, which has a command line argument
608 * `--passphrase-fd` providing a file descriptor number where it expects
609 * the passphrase to be written.
610 */
611void
612g_subprocess_launcher_take_fd (GSubprocessLauncher *self,
613 gint source_fd,
614 gint target_fd)
615{
616 if (self->source_fds != NULL && self->target_fds != NULL)
617 {
618 g_array_append_val (self->source_fds, source_fd);
619 g_array_append_val (self->target_fds, target_fd);
620 }
621}
622
623/**
624 * g_subprocess_launcher_close:
625 * @self: a #GSubprocessLauncher
626 *
627 * Closes all the file descriptors previously passed to the object with
628 * g_subprocess_launcher_take_fd(), g_subprocess_launcher_take_stderr_fd(), etc.
629 *
630 * After calling this method, any subsequent calls to g_subprocess_launcher_spawn() or g_subprocess_launcher_spawnv() will
631 * return %G_IO_ERROR_CLOSED. This method is idempotent if
632 * called more than once.
633 *
634 * This function is called automatically when the #GSubprocessLauncher
635 * is disposed, but is provided separately so that garbage collected
636 * language bindings can call it earlier to guarantee when FDs are closed.
637 *
638 * Since: 2.68
639 */
640void
641g_subprocess_launcher_close (GSubprocessLauncher *self)
642{
643 guint i;
644
645 g_return_if_fail (G_IS_SUBPROCESS_LAUNCHER (self));
646
647 if (self->stdin_fd != -1)
648 close (fd: self->stdin_fd);
649 self->stdin_fd = -1;
650
651 if (self->stdout_fd != -1)
652 close (fd: self->stdout_fd);
653 self->stdout_fd = -1;
654
655 if (self->stderr_fd != -1)
656 close (fd: self->stderr_fd);
657 self->stderr_fd = -1;
658
659 if (self->source_fds)
660 {
661 g_assert (self->target_fds != NULL);
662 g_assert (self->source_fds->len == self->target_fds->len);
663
664 /* Note: Don’t close the target_fds, as they’re only valid FDs in the
665 * child process. This code never executes in the child process. */
666 for (i = 0; i < self->source_fds->len; i++)
667 (void) close (g_array_index (self->source_fds, int, i));
668
669 g_clear_pointer (&self->source_fds, g_array_unref);
670 g_clear_pointer (&self->target_fds, g_array_unref);
671 }
672
673 self->closed_fd = TRUE;
674}
675
676/**
677 * g_subprocess_launcher_set_child_setup: (skip)
678 * @self: a #GSubprocessLauncher
679 * @child_setup: a #GSpawnChildSetupFunc to use as the child setup function
680 * @user_data: user data for @child_setup
681 * @destroy_notify: a #GDestroyNotify for @user_data
682 *
683 * Sets up a child setup function.
684 *
685 * The child setup function will be called after fork() but before
686 * exec() on the child's side.
687 *
688 * @destroy_notify will not be automatically called on the child's side
689 * of the fork(). It will only be called when the last reference on the
690 * #GSubprocessLauncher is dropped or when a new child setup function is
691 * given.
692 *
693 * %NULL can be given as @child_setup to disable the functionality.
694 *
695 * Child setup functions are only available on UNIX.
696 *
697 * Since: 2.40
698 **/
699void
700g_subprocess_launcher_set_child_setup (GSubprocessLauncher *self,
701 GSpawnChildSetupFunc child_setup,
702 gpointer user_data,
703 GDestroyNotify destroy_notify)
704{
705 if (self->child_setup_destroy_notify)
706 (* self->child_setup_destroy_notify) (self->child_setup_user_data);
707
708 self->child_setup_func = child_setup;
709 self->child_setup_user_data = user_data;
710 self->child_setup_destroy_notify = destroy_notify;
711}
712#endif
713
714/**
715 * g_subprocess_launcher_spawn:
716 * @self: a #GSubprocessLauncher
717 * @error: Error
718 * @argv0: Command line arguments
719 * @...: Continued arguments, %NULL terminated
720 *
721 * Creates a #GSubprocess given a provided varargs list of arguments.
722 *
723 * Since: 2.40
724 * Returns: (transfer full): A new #GSubprocess, or %NULL on error (and @error will be set)
725 **/
726GSubprocess *
727g_subprocess_launcher_spawn (GSubprocessLauncher *launcher,
728 GError **error,
729 const gchar *argv0,
730 ...)
731{
732 GSubprocess *result;
733 GPtrArray *args;
734 const gchar *arg;
735 va_list ap;
736
737 g_return_val_if_fail (argv0 != NULL && argv0[0] != '\0', NULL);
738 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
739
740 args = g_ptr_array_new ();
741
742 va_start (ap, argv0);
743 g_ptr_array_add (array: args, data: (gchar *) argv0);
744 while ((arg = va_arg (ap, const gchar *)))
745 g_ptr_array_add (array: args, data: (gchar *) arg);
746
747 g_ptr_array_add (array: args, NULL);
748 va_end (ap);
749
750 result = g_subprocess_launcher_spawnv (self: launcher, argv: (const gchar * const *) args->pdata, error);
751
752 g_ptr_array_free (array: args, TRUE);
753
754 return result;
755
756}
757
758/**
759 * g_subprocess_launcher_spawnv:
760 * @self: a #GSubprocessLauncher
761 * @argv: (array zero-terminated=1) (element-type filename): Command line arguments
762 * @error: Error
763 *
764 * Creates a #GSubprocess given a provided array of arguments.
765 *
766 * Since: 2.40
767 * Returns: (transfer full): A new #GSubprocess, or %NULL on error (and @error will be set)
768 **/
769GSubprocess *
770g_subprocess_launcher_spawnv (GSubprocessLauncher *launcher,
771 const gchar * const *argv,
772 GError **error)
773{
774 GSubprocess *subprocess;
775
776 g_return_val_if_fail (argv != NULL && argv[0] != NULL && argv[0][0] != '\0', NULL);
777
778#ifdef G_OS_UNIX
779 if (launcher->closed_fd)
780 {
781 g_set_error (err: error,
782 G_IO_ERROR,
783 code: G_IO_ERROR_CLOSED,
784 format: "Can't spawn a new child because a passed file descriptor has been closed.");
785 return NULL;
786 }
787#endif
788
789 subprocess = g_object_new (G_TYPE_SUBPROCESS,
790 first_property_name: "argv", argv,
791 "flags", launcher->flags,
792 NULL);
793 g_subprocess_set_launcher (subprocess, launcher);
794
795 if (!g_initable_init (G_INITABLE (subprocess), NULL, error))
796 {
797 g_object_unref (object: subprocess);
798 return NULL;
799 }
800
801 return subprocess;
802}
803

source code of gtk/subprojects/glib/gio/gsubprocesslauncher.c