1 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
2 *
3 * Copyright (C) 2012 Colin Walters <walters@verbum.org>
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 * Boston, MA 02111-1307, USA.
19 */
20
21 #include "config.h"
22
23 #include "libgsystem.h"
24
25 #if GLIB_CHECK_VERSION(2,34,0)
26
27 #ifdef G_OS_UNIX
28 #include <gio/gunixoutputstream.h>
29 #include <gio/gfiledescriptorbased.h>
30 #include <gio/gunixinputstream.h>
31 #include <glib-unix.h>
32 #endif
33
34 /**
35 * SECTION:gssubprocesscontext
36 * @title: GSSubprocess Context
37 * @short_description: Environment options for launching a child process
38 *
39 * This class contains a set of options for launching child processes,
40 * such as where its standard input and output will be directed, the
41 * argument list, the environment, and more.
42 *
43 * While the #GSSubprocess class has high level functions covering
44 * popular cases, use of this class allows access to more advanced
45 * options. It can also be used to launch multiple subprocesses with
46 * a similar configuration.
47 *
48 * Since: 2.36
49 */
50
51 #include "config.h"
52
53 #include "gsystem-subprocess-context-private.h"
54 #include "gsystem-subprocess.h"
55
56 #include <string.h>
57
58 typedef GObjectClass GSSubprocessContextClass;
59
60 G_DEFINE_TYPE (GSSubprocessContext, gs_subprocess_context, G_TYPE_OBJECT);
61
62 enum
63 {
64 PROP_0,
65 PROP_ARGV,
66 N_PROPS
67 };
68
69 static GParamSpec *gs_subprocess_context_pspecs[N_PROPS];
70
71 /**
72 * gs_subprocess_context_new:
73 * @argv: Argument list
74 *
75 * Returns: (transfer full): A new instance of a #GSSubprocessContext.
76 */
77 GSSubprocessContext *
78 gs_subprocess_context_new (gchar **argv)
79 {
80 g_return_val_if_fail (argv != NULL && argv[0] != NULL, NULL);
81
82 return g_object_new (GS_TYPE_SUBPROCESS_CONTEXT,
83 "argv", argv,
84 NULL);
85 }
86
87 GSSubprocessContext *
88 gs_subprocess_context_newv (const gchar *first_arg,
89 ...)
90 {
91 GSSubprocessContext *result;
92 va_list args;
93
94 g_return_val_if_fail (first_arg != NULL, NULL);
95
96 va_start (args, first_arg);
97 result = gs_subprocess_context_newa (first_arg, args);
98 va_end (args);
99
100 return result;
101 }
102
103 /**
104 * gs_subprocess_context_newa:
105 * @first_arg: First argument
106 * @args: a va_list
107 *
108 * Returns: (transfer full): A new instance of a #GSSubprocessContext.
109 */
110 GSSubprocessContext *
111 gs_subprocess_context_newa (const gchar *first_arg,
112 va_list args)
113 {
114 GSSubprocessContext *result;
115 GPtrArray *argv;
116
117 g_return_val_if_fail (first_arg != NULL, NULL);
118
119 argv = g_ptr_array_new ();
120 do
121 g_ptr_array_add (argv, (gchar*)first_arg);
122 while ((first_arg = va_arg (args, const gchar *)) != NULL);
123 g_ptr_array_add (argv, NULL);
124
125 result = gs_subprocess_context_new ((gchar**)argv->pdata);
126
127 return result;
128 }
129
130 #ifdef G_OS_UNIX
131 GSSubprocessContext *
132 gs_subprocess_context_new_argv0 (const gchar *argv0,
133 gchar **argv)
134 {
135 GSSubprocessContext *result;
136 GPtrArray *real_argv;
137 gchar **iter;
138
139 g_return_val_if_fail (argv0 != NULL, NULL);
140 g_return_val_if_fail (argv != NULL && argv[0] != NULL, NULL);
141
142 real_argv = g_ptr_array_new ();
143 g_ptr_array_add (real_argv, (gchar*)argv0);
144 for (iter = argv; *iter; iter++)
145 g_ptr_array_add (real_argv, (gchar*) *iter);
146 g_ptr_array_add (real_argv, NULL);
147
148 result = g_object_new (GS_TYPE_SUBPROCESS_CONTEXT,
149 "argv", real_argv->pdata,
150 NULL);
151 result->has_argv0 = TRUE;
152
153 return result;
154 }
155 #endif
156
157 static void
158 gs_subprocess_context_init (GSSubprocessContext *self)
159 {
160 self->stdin_fd = -1;
161 self->stdout_fd = -1;
162 self->stderr_fd = -1;
163 self->stdout_disposition = GS_SUBPROCESS_STREAM_DISPOSITION_INHERIT;
164 self->stderr_disposition = GS_SUBPROCESS_STREAM_DISPOSITION_INHERIT;
165 self->postfork_close_fds = g_array_new (FALSE, FALSE, sizeof (int));
166 self->inherit_fds = g_array_new (FALSE, FALSE, sizeof (int));
167 }
168
169 static void
170 gs_subprocess_context_finalize (GObject *object)
171 {
172 GSSubprocessContext *self = GS_SUBPROCESS_CONTEXT (object);
173
174 g_strfreev (self->argv);
175 g_strfreev (self->envp);
176 g_free (self->cwd);
177
178 g_free (self->stdin_path);
179 g_free (self->stdout_path);
180 g_free (self->stderr_path);
181
182 g_array_unref (self->postfork_close_fds);
183 g_array_unref (self->inherit_fds);
184
185 if (G_OBJECT_CLASS (gs_subprocess_context_parent_class)->finalize != NULL)
186 G_OBJECT_CLASS (gs_subprocess_context_parent_class)->finalize (object);
187 }
188
189 static void
190 gs_subprocess_context_set_property (GObject *object,
191 guint prop_id,
192 const GValue *value,
193 GParamSpec *pspec)
194 {
195 GSSubprocessContext *self = GS_SUBPROCESS_CONTEXT (object);
196
197 switch (prop_id)
198 {
199 case PROP_ARGV:
200 self->argv = (gchar**) g_value_dup_boxed (value);
201 break;
202
203 default:
204 g_assert_not_reached ();
205 }
206 }
207
208 static void
209 gs_subprocess_context_get_property (GObject *object,
210 guint prop_id,
211 GValue *value,
212 GParamSpec *pspec)
213 {
214 GSSubprocessContext *self = GS_SUBPROCESS_CONTEXT (object);
215
216 switch (prop_id)
217 {
218 case PROP_ARGV:
219 g_value_set_boxed (value, self->argv);
220 break;
221
222 default:
223 g_assert_not_reached ();
224 }
225 }
226
227 static void
228 gs_subprocess_context_class_init (GSSubprocessContextClass *class)
229 {
230 GObjectClass *gobject_class = G_OBJECT_CLASS (class);
231
232 gobject_class->finalize = gs_subprocess_context_finalize;
233 gobject_class->get_property = gs_subprocess_context_get_property;
234 gobject_class->set_property = gs_subprocess_context_set_property;
235
236 /**
237 * GSSubprocessContext:argv:
238 *
239 * Array of arguments passed to child process; must have at least
240 * one element. The first element has special handling - if it is
241 * an not absolute path ( as determined by g_path_is_absolute() ),
242 * then the system search path will be used. See
243 * %G_SPAWN_SEARCH_PATH.
244 *
245 * Note that in order to use the Unix-specific argv0 functionality,
246 * you must use the setter function
247 * gs_subprocess_context_set_args_and_argv0(). For more information
248 * about this, see %G_SPAWN_FILE_AND_ARGV_ZERO.
249 *
250 * Since: 2.36
251 */
252 gs_subprocess_context_pspecs[PROP_ARGV] = g_param_spec_boxed ("argv", "Arguments", "Arguments for child process", G_TYPE_STRV,
253 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
254
255 g_object_class_install_properties (gobject_class, N_PROPS, gs_subprocess_context_pspecs);
256 }
257
258 /**
259 * gs_subprocess_context_argv_append:
260 * @self:
261 * @arg: An argument
262 *
263 * Append an argument to the child's argument vector.
264 */
265 void
266 gs_subprocess_context_argv_append (GSSubprocessContext *self,
267 const gchar *arg)
268 {
269 GPtrArray *new_argv = g_ptr_array_new ();
270 gchar **iter;
271
272 for (iter = self->argv; *iter; iter++)
273 g_ptr_array_add (new_argv, *iter);
274 g_ptr_array_add (new_argv, g_strdup (arg));
275 g_ptr_array_add (new_argv, NULL);
276
277 /* Don't free elements */
278 g_free (self->argv);
279 self->argv = (char**)g_ptr_array_free (new_argv, FALSE);
280 }
281
282 /* Environment */
283
284 /**
285 * gs_subprocess_context_set_environment:
286 * @self:
287 * @environ: (array zero-terminated=1) (element-type utf8): Environment KEY=VALUE pairs
288 *
289 * Replace the environment that will be used for the child process.
290 * The default is to inherit the current process.
291 */
292 void
293 gs_subprocess_context_set_environment (GSSubprocessContext *self,
294 gchar **env)
295 {
296 g_strfreev (self->envp);
297 self->envp = g_strdupv (env);
298 }
299
300 void
301 gs_subprocess_context_set_cwd (GSSubprocessContext *self,
302 const gchar *cwd)
303 {
304 g_free (self->cwd);
305 self->cwd = g_strdup (cwd);
306 }
307
308 void
309 gs_subprocess_context_set_keep_descriptors (GSSubprocessContext *self,
310 gboolean keep_descriptors)
311
312 {
313 self->keep_descriptors = keep_descriptors ? 1 : 0;
314 }
315
316 void
317 gs_subprocess_context_set_search_path (GSSubprocessContext *self,
318 gboolean search_path,
319 gboolean search_path_from_envp)
320 {
321 self->search_path = search_path ? 1 : 0;
322 self->search_path_from_envp = search_path_from_envp ? 1 : 0;
323 }
324
325 void
326 gs_subprocess_context_set_stdin_disposition (GSSubprocessContext *self,
327 GSSubprocessStreamDisposition disposition)
328 {
(1) Event cond_true: |
Condition "disposition != GS_SUBPROCESS_STREAM_DISPOSITION_STDERR_MERGE", taking true branch |
(2) Event if_fallthrough: |
Falling through to end of if statement |
(3) Event if_end: |
End of if statement |
(4) Event cond_true: |
Condition "({...})", taking true branch |
(5) Event if_fallthrough: |
Falling through to end of if statement |
(6) Event if_end: |
End of if statement |
329 g_return_if_fail (disposition != GS_SUBPROCESS_STREAM_DISPOSITION_STDERR_MERGE);
(7) Event deref_parm: |
Directly dereferencing parameter "self". |
330 self->stdin_disposition = disposition;
331 }
332
333 void
334 gs_subprocess_context_set_stdout_disposition (GSSubprocessContext *self,
335 GSSubprocessStreamDisposition disposition)
336 {
337 g_return_if_fail (disposition != GS_SUBPROCESS_STREAM_DISPOSITION_STDERR_MERGE);
338 self->stdout_disposition = disposition;
339 }
340
341 void
342 gs_subprocess_context_set_stderr_disposition (GSSubprocessContext *self,
343 GSSubprocessStreamDisposition disposition)
344 {
345 self->stderr_disposition = disposition;
346 }
347
348 #ifdef G_OS_UNIX
349 void
350 gs_subprocess_context_set_stdin_file_path (GSSubprocessContext *self,
351 const gchar *path)
352 {
353 self->stdin_disposition = GS_SUBPROCESS_STREAM_DISPOSITION_NULL;
354 g_free (self->stdin_path);
355 self->stdin_path = g_strdup (path);
356 }
357
358 void
359 gs_subprocess_context_set_stdin_fd (GSSubprocessContext *self,
360 gint fd)
361 {
362 self->stdin_disposition = GS_SUBPROCESS_STREAM_DISPOSITION_NULL;
363 self->stdin_fd = fd;
364 }
365
366 void
367 gs_subprocess_context_set_stdout_file_path (GSSubprocessContext *self,
368 const gchar *path)
369 {
370 self->stdout_disposition = GS_SUBPROCESS_STREAM_DISPOSITION_NULL;
371 g_free (self->stdout_path);
372 self->stdout_path = g_strdup (path);
373 }
374
375 void
376 gs_subprocess_context_set_stdout_fd (GSSubprocessContext *self,
377 gint fd)
378 {
379 self->stdout_disposition = GS_SUBPROCESS_STREAM_DISPOSITION_NULL;
380 self->stdout_fd = fd;
381 }
382
383 void
384 gs_subprocess_context_set_stderr_file_path (GSSubprocessContext *self,
385 const gchar *path)
386 {
387 self->stderr_disposition = GS_SUBPROCESS_STREAM_DISPOSITION_NULL;
388 g_free (self->stderr_path);
389 self->stderr_path = g_strdup (path);
390 }
391
392 void
393 gs_subprocess_context_set_stderr_fd (GSSubprocessContext *self,
394 gint fd)
395 {
396 self->stderr_disposition = GS_SUBPROCESS_STREAM_DISPOSITION_NULL;
397 self->stderr_fd = fd;
398 }
399 #endif
400
401 #ifdef G_OS_UNIX
402 /**
403 * gs_subprocess_context_set_child_setup: (skip)
404 * @self:
405 * @child_setup: Function to call in the newly forked child, before execve()
406 * @user_data: Data passed to child
407 *
408 * FIXME - note extensive restricitons on GSpawnChildSetupFunc here
409 */
410 void
411 gs_subprocess_context_set_child_setup (GSSubprocessContext *self,
412 GSpawnChildSetupFunc child_setup,
413 gpointer user_data)
414 {
415 self->child_setup_func = child_setup;
416 self->child_setup_data = user_data;
417 }
418
419 static gboolean
420 open_pipe_internal (GSSubprocessContext *self,
421 gboolean for_read,
422 void **out_stream,
423 gint *out_fdno,
424 GError **error)
425 {
426 int pipefds[2];
427
428 g_return_val_if_fail (out_stream != NULL, FALSE);
429 g_return_val_if_fail (out_fdno != NULL, FALSE);
430
431 if (!g_unix_open_pipe (pipefds, FD_CLOEXEC, error))
432 return FALSE;
433
434 if (for_read)
435 {
436 *out_stream = g_unix_input_stream_new (pipefds[0], TRUE);
437 *out_fdno = pipefds[1];
438 }
439 else
440 {
441 *out_stream = g_unix_output_stream_new (pipefds[1], TRUE);
442 *out_fdno = pipefds[0];
443 }
444 g_array_append_val (self->inherit_fds, *out_fdno);
445 g_array_append_val (self->postfork_close_fds, *out_fdno);
446
447 return TRUE;
448 }
449
450 /**
451 * gs_subprocess_context_open_pipe_read:
452 * @self:
453 * @out_stream: (out) (transfer full): A newly referenced output stream
454 * @out_fdno: (out): File descriptor number for the subprocess side of the pipe
455 *
456 * This allows you to open a pipe between the parent and child
457 * processes, independent of the standard streams. For this function,
458 * the pipe is set up so that the parent can read, and the child can
459 * write. For the opposite version, see
460 * gs_subprocess_context_open_pipe_write().
461 *
462 * The returned @out_fdno is the file descriptor number that the child
463 * will see; you need to communicate this number via a separate
464 * channel, such as the argument list. For example, if you're using
465 * this pipe to send a password, provide
466 * <literal>--password-fd=<fdno string></literal>.
467 *
468 * Returns: %TRUE on success, %FALSE on error (and @error will be set)
469 */
470 gboolean
471 gs_subprocess_context_open_pipe_read (GSSubprocessContext *self,
472 GInputStream **out_stream,
473 gint *out_fdno,
474 GError **error)
475 {
476 return open_pipe_internal (self, TRUE, (void**)out_stream, out_fdno, error);
477 }
478
479 /**
480 * gs_subprocess_context_open_pipe_write:
481 * @self:
482 * @out_stream: (out) (transfer full): A newly referenced stream
483 * @out_fdno: (out): File descriptor number for the subprocess side of the pipe
484 *
485 * Like gs_subprocess_context_open_pipe_read(), but returns a writable
486 * channel from which the child process can read.
487 *
488 * Returns: %TRUE on success, %FALSE on error (and @error will be set)
489 */
490 gboolean
491 gs_subprocess_context_open_pipe_write (GSSubprocessContext *self,
492 GOutputStream **out_stream,
493 gint *out_fdno,
494 GError **error)
495 {
496 return open_pipe_internal (self, FALSE, (void**)out_stream, out_fdno, error);
497 }
498
499 #endif
500
501 #endif
502