Commit 5b48dc40 authored by Colin Walters's avatar Colin Walters Committed by Allison Karlitskaya

GSubprocess: New class for spawning child processes

There are a number of nice things this class brings:

0) Has a race-free termination API on all platforms (on UNIX, calls to
   kill() and waitpid() are coordinated as not to cause problems).
1) Operates in terms of G{Input,Output}Stream, not file descriptors
2) Standard GIO-style async API for wait() with cancellation
3) Makes some simple cases easy, like synchronously spawning a
   process with an argument list
4) Makes hard cases possible, like asynchronously running a process
   with stdout/stderr merged, output directly to a file path

Much rewriting and code review from Ryan Lortie <desrt@desrt.ca>

https://bugzilla.gnome.org/show_bug.cgi?id=672102
parent e30bbca6
......@@ -108,6 +108,10 @@
<xi:include href="xml/ginitable.xml"/>
<xi:include href="xml/gasyncinitable.xml"/>
</chapter>
<chapter id="subprocesses">
<title>Subprocesses</title>
<xi:include href="xml/gsubprocess.xml"/>
</chapter>
<chapter id="networking">
<title>Low-level network support</title>
<xi:include href="xml/gsocket.xml"/>
......
......@@ -4077,4 +4077,37 @@ G_SIMPLE_PROXY_RESOLVER_CLASS
G_IS_SIMPLE_PROXY_RESOLVER_CLASS
G_SIMPLE_PROXY_RESOLVER_GET_CLASS
g_simple_proxy_resolver_get_type
<FILE>gsubprocess</FILE>
<TITLE>GSubprocess</TITLE>
GSubprocess
g_subprocess_new
g_subprocess_newv
<SUBSECTION IO>
g_subprocess_get_stdin_pipe
g_subprocess_get_stdout_pipe
g_subprocess_get_stderr_pipe
<SUBSECTION Waiting>
g_subprocess_wait
g_subprocess_wait_sync
g_subprocess_wait_finish
g_subprocess_wait_check
g_subprocess_wait_check_sync
g_subprocess_wait_check_finish
<SUBSECTION Status>
g_subprocess_get_successful
g_subprocess_get_if_exited
g_subprocess_get_exit_status
g_subprocess_get_if_signaled
g_subprocess_get_term_sig
g_subprocess_get_status
<SUBSECTION Control>
g_subprocess_send_signal
g_subprocess_force_exit
<SUBSECTION Standard>
G_IS_SUBPROCESS
G_TYPE_SUBPROCESS
G_SUBPROCESS
<SUBSECTION Private>
g_subprocess_get_type
</SECTION>
......@@ -137,3 +137,4 @@ g_test_dbus_get_type
g_test_dbus_flags_get_type
g_task_get_type
g_simple_proxy_resolver_get_type
g_subprocess_get_type
......@@ -428,6 +428,9 @@ libgio_2_0_la_SOURCES = \
gsocketlistener.c \
gsocketoutputstream.c \
gsocketoutputstream.h \
gsubprocesslauncher.c \
gsubprocess.c \
gsubprocesslauncher-private.h \
gsocketservice.c \
gsrvtarget.c \
gsimpleproxyresolver.c \
......@@ -591,6 +594,8 @@ gio_headers = \
gsrvtarget.h \
gsimpleproxyresolver.h \
gtask.h \
gsubprocess.h \
gsubprocesslauncher.h \
gtcpconnection.h \
gtcpwrapperconnection.h \
gthreadedsocketservice.h\
......
......@@ -126,6 +126,8 @@
#include <gio/gsrvtarget.h>
#include <gio/gsimpleproxyresolver.h>
#include <gio/gtask.h>
#include <gio/gsubprocess.h>
#include <gio/gsubprocesslauncher.h>
#include <gio/gtcpconnection.h>
#include <gio/gtcpwrapperconnection.h>
#include <gio/gtestdbus.h>
......
......@@ -1696,6 +1696,56 @@ typedef enum /*< flags >*/ {
G_TEST_DBUS_NONE = 0
} GTestDBusFlags;
/**
* GSubprocessFlags:
* @G_SUBPROCESS_FLAGS_NONE: No flags.
* @G_SUBPROCESS_FLAGS_STDIN_PIPE: create a pipe for the stdin of the
* spawned process that can be accessed with
* g_subprocess_get_stdin_pipe().
* @G_SUBPROCESS_FLAGS_STDIN_INHERIT: stdin is inherited from the
* calling process.
* @G_SUBPROCESS_FLAGS_STDOUT_PIPE: create a pipe for the stdout of the
* spawned process that can be accessed with
* g_subprocess_get_stdout_pipe().
* @G_SUBPROCESS_FLAGS_STDOUT_SILENCE: silence the stdout of the spawned
* process (ie: redirect to /dev/null).
* @G_SUBPROCESS_FLAGS_STDERR_PIPE: create a pipe for the stderr of the
* spawned process that can be accessed with
* g_subprocess_get_stderr_pipe().
* @G_SUBPROCESS_FLAGS_STDERR_SILENCE: silence the stderr of the spawned
* process (ie: redirect to /dev/null).
* @G_SUBPROCESS_FLAGS_STDERR_MERGE: merge the stderr of the spawned
* process with whatever the stdout happens to be. This is a good way
* of directing both streams to a common log file, for example.
* @G_SUBPROCESS_FLAGS_INHERIT_FDS: spawned processes will inherit the
* file descriptors of their parent, unless those descriptors have
* been explicitly marked as close-on-exec. This flag has no effect
* over the "standard" file descriptors (stdin, stdout, stderr).
*
* Flags to define the behaviour of a #GSubprocess.
*
* Note that the default for stdin is to redirect from /dev/null. For
* stdout and stderr the default are for them to inherit the
* corresponding descriptor from the calling process.
*
* Note that it is a programmer error to mix 'incompatible' flags. For
* example, you may not request both %G_SUBPROCESS_FLAGS_STDOUT_PIPE and
* %G_SUBPROCESS_FLAGS_STDOUT_SILENCE.
*
* Since: 2.36
**/
typedef enum {
G_SUBPROCESS_FLAGS_NONE = 0,
G_SUBPROCESS_FLAGS_STDIN_PIPE = (1u << 0),
G_SUBPROCESS_FLAGS_STDIN_INHERIT = (1u << 1),
G_SUBPROCESS_FLAGS_STDOUT_PIPE = (1u << 2),
G_SUBPROCESS_FLAGS_STDOUT_SILENCE = (1u << 3),
G_SUBPROCESS_FLAGS_STDERR_PIPE = (1u << 4),
G_SUBPROCESS_FLAGS_STDERR_SILENCE = (1u << 5),
G_SUBPROCESS_FLAGS_STDERR_MERGE = (1u << 6),
G_SUBPROCESS_FLAGS_INHERIT_FDS = (1u << 7)
} GSubprocessFlags;
G_END_DECLS
#endif /* __GIO_ENUMS_H__ */
......@@ -137,6 +137,7 @@ typedef struct _GIOStream GIOStream;
typedef struct _GPollableInputStream GPollableInputStream; /* Dummy typedef */
typedef struct _GPollableOutputStream GPollableOutputStream; /* Dummy typedef */
typedef struct _GResolver GResolver;
/**
* GResource:
*
......@@ -513,6 +514,23 @@ typedef GType (*GDBusProxyTypeFunc) (GDBusObjectManagerClient *manager,
typedef struct _GTestDBus GTestDBus;
/**
* GSubprocess:
*
* A child process.
*
* Since: 2.36
*/
typedef struct _GSubprocess GSubprocess;
/**
* GSubprocessLauncher:
*
* Options for launching a child process.
*
* Since: 2.36
*/
typedef struct _GSubprocessLauncher GSubprocessLauncher;
G_END_DECLS
#endif /* __GIO_TYPES_H__ */
......@@ -297,9 +297,8 @@ end_element (GMarkupParseContext *context,
if (xml_stripblanks && xmllint != NULL)
{
gchar *argv[8];
int status, fd, argc;
gchar *stderr_child = NULL;
int fd;
GSubprocess *proc;
tmp_file = g_strdup ("resource-XXXXXXXX");
if ((fd = g_mkstemp (tmp_file)) == -1)
......@@ -315,43 +314,29 @@ end_element (GMarkupParseContext *context,
}
close (fd);
argc = 0;
argv[argc++] = (gchar *) xmllint;
argv[argc++] = "--nonet";
argv[argc++] = "--noblanks";
argv[argc++] = "--output";
argv[argc++] = tmp_file;
argv[argc++] = real_file;
argv[argc++] = NULL;
g_assert (argc <= G_N_ELEMENTS (argv));
if (!g_spawn_sync (NULL /* cwd */, argv, NULL /* envv */,
G_SPAWN_STDOUT_TO_DEV_NULL,
NULL, NULL, NULL, &stderr_child, &status, &my_error))
{
g_propagate_error (error, my_error);
goto cleanup;
}
/* Ugly...we shoud probably just let stderr be inherited */
if (!g_spawn_check_exit_status (status, NULL))
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
_("Error processing input file with xmllint:\n%s"), stderr_child);
g_free (stderr_child);
proc = g_subprocess_new (G_SUBPROCESS_FLAGS_STDOUT_SILENCE, error,
xmllint, "--nonet", "--noblanks", "--output", tmp_file, real_file, NULL);
g_free (real_file);
real_file = NULL;
if (!proc)
goto cleanup;
if (!g_subprocess_wait_check (proc, NULL, error))
{
g_object_unref (proc);
goto cleanup;
}
g_free (stderr_child);
g_free (real_file);
g_object_unref (proc);
real_file = g_strdup (tmp_file);
}
if (to_pixdata)
{
gchar *argv[4];
gchar *stderr_child = NULL;
int status, fd, argc;
int fd;
GSubprocess *proc;
if (gdk_pixbuf_pixdata == NULL)
{
......@@ -375,31 +360,19 @@ end_element (GMarkupParseContext *context,
}
close (fd);
argc = 0;
argv[argc++] = (gchar *) gdk_pixbuf_pixdata;
argv[argc++] = real_file;
argv[argc++] = tmp_file2;
argv[argc++] = NULL;
g_assert (argc <= G_N_ELEMENTS (argv));
proc = g_subprocess_new (G_SUBPROCESS_FLAGS_STDOUT_SILENCE, error,
gdk_pixbuf_pixdata, real_file, tmp_file2, NULL);
g_free (real_file);
real_file = NULL;
if (!g_spawn_sync (NULL /* cwd */, argv, NULL /* envv */,
G_SPAWN_STDOUT_TO_DEV_NULL,
NULL, NULL, NULL, &stderr_child, &status, &my_error))
{
g_propagate_error (error, my_error);
goto cleanup;
}
if (!g_spawn_check_exit_status (status, NULL))
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
_("Error processing input file with to-pixdata:\n%s"), stderr_child);
g_free (stderr_child);
if (!g_subprocess_wait_check (proc, NULL, error))
{
g_object_unref (proc);
goto cleanup;
}
}
g_object_unref (proc);
g_free (stderr_child);
g_free (real_file);
real_file = g_strdup (tmp_file2);
}
}
......
This diff is collapsed.
/* GIO - GLib Input, Output and Streaming Library
*
* Copyright (C) 2012 Colin Walters <walters@verbum.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, MA 02111-1307, USA.
*
* Author: Colin Walters <walters@verbum.org>
*/
#if !defined (__GIO_GIO_H_INSIDE__) && !defined (GIO_COMPILATION)
#error "Only <gio/gio.h> can be included directly."
#endif
#ifndef __G_SUBPROCESS_H__
#define __G_SUBPROCESS_H__
#include <gio/giotypes.h>
G_BEGIN_DECLS
#define G_TYPE_SUBPROCESS (g_subprocess_get_type ())
#define G_SUBPROCESS(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_SUBPROCESS, GSubprocess))
#define G_IS_SUBPROCESS(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_SUBPROCESS))
GLIB_AVAILABLE_IN_2_40
GType g_subprocess_get_type (void) G_GNUC_CONST;
/**** Core API ****/
GLIB_AVAILABLE_IN_2_40
GSubprocess * g_subprocess_new (GSubprocessFlags flags,
GError **error,
const gchar *argv0,
...) G_GNUC_NULL_TERMINATED;
GLIB_AVAILABLE_IN_2_40
GSubprocess * g_subprocess_newv (const gchar * const *argv,
GSubprocessFlags flags,
GError **error);
GLIB_AVAILABLE_IN_2_40
GOutputStream * g_subprocess_get_stdin_pipe (GSubprocess *self);
GLIB_AVAILABLE_IN_2_40
GInputStream * g_subprocess_get_stdout_pipe (GSubprocess *self);
GLIB_AVAILABLE_IN_2_40
GInputStream * g_subprocess_get_stderr_pipe (GSubprocess *self);
GLIB_AVAILABLE_IN_2_40
const gchar * g_subprocess_get_identifier (GSubprocess *self);
#ifdef G_OS_UNIX
GLIB_AVAILABLE_IN_2_40
void g_subprocess_send_signal (GSubprocess *self,
gint signal_num);
#endif
GLIB_AVAILABLE_IN_2_40
void g_subprocess_force_exit (GSubprocess *self);
GLIB_AVAILABLE_IN_2_40
gboolean g_subprocess_wait (GSubprocess *self,
GCancellable *cancellable,
GError **error);
GLIB_AVAILABLE_IN_2_40
void g_subprocess_wait_async (GSubprocess *self,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
GLIB_AVAILABLE_IN_2_40
gboolean g_subprocess_wait_finish (GSubprocess *self,
GAsyncResult *result,
GError **error);
GLIB_AVAILABLE_IN_2_40
gboolean g_subprocess_wait_check (GSubprocess *self,
GCancellable *cancellable,
GError **error);
GLIB_AVAILABLE_IN_2_40
void g_subprocess_wait_check_async (GSubprocess *self,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
GLIB_AVAILABLE_IN_2_40
gboolean g_subprocess_wait_check_finish (GSubprocess *self,
GAsyncResult *result,
GError **error);
GLIB_AVAILABLE_IN_2_40
gint g_subprocess_get_status (GSubprocess *self);
GLIB_AVAILABLE_IN_2_40
gboolean g_subprocess_get_successful (GSubprocess *self);
GLIB_AVAILABLE_IN_2_40
gboolean g_subprocess_get_if_exited (GSubprocess *self);
GLIB_AVAILABLE_IN_2_40
gint g_subprocess_get_exit_status (GSubprocess *self);
GLIB_AVAILABLE_IN_2_40
gboolean g_subprocess_get_if_signaled (GSubprocess *self);
GLIB_AVAILABLE_IN_2_40
gint g_subprocess_get_term_sig (GSubprocess *self);
GLIB_AVAILABLE_IN_2_40
gboolean g_subprocess_communicate (GSubprocess *subprocess,
const gchar *stdin_data,
gssize stdin_length,
GCancellable *cancellable,
gchar **stdout_data,
gsize *stdout_length,
gchar **stderr_data,
gsize *stderr_length,
GError **error);
GLIB_AVAILABLE_IN_2_40
void g_subprocess_communicate_async (GSubprocess *subprocess,
const gchar *stdin_data,
gssize stdin_length,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
GLIB_AVAILABLE_IN_2_40
gboolean g_subprocess_communicate_finish (GSubprocess *subprocess,
GAsyncResult *result,
gchar **stdout_data,
gsize *stdout_length,
gchar **stderr_data,
gsize *stderr_length,
GError **error);
GLIB_AVAILABLE_IN_2_40
gboolean g_subprocess_communicate_bytes (GSubprocess *subprocess,
GBytes *stdin_bytes,
GCancellable *cancellable,
GBytes **stdout_bytes,
GBytes **stderr_bytes,
GError **error);
GLIB_AVAILABLE_IN_2_40
void g_subprocess_communicate_bytes_async (GSubprocess *subprocess,
GBytes *stdin_bytes,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
GLIB_AVAILABLE_IN_2_40
gboolean g_subprocess_communicate_bytes_finish (GSubprocess *subprocess,
GAsyncResult *result,
GBytes **stdout_bytes,
GBytes **stderr_bytes,
GError **error);
G_END_DECLS
#endif /* __G_SUBPROCESS_H__ */
/* GIO - GLib Input, Output and Streaming Library
*
* Copyright (C) 2012 Colin Walters <walters@verbum.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, MA 02111-1307, USA.
*/
#ifndef __G_SUBPROCESS_CONTEXT_PRIVATE_H__
#define __G_SUBPROCESS_CONTEXT_PRIVATE_H__
#include "gsubprocesslauncher.h"
G_BEGIN_DECLS
struct _GSubprocessLauncher
{
GObject parent;
GSubprocessFlags flags;
gboolean path_from_envp;
char **envp;
char *cwd;
#ifdef G_OS_UNIX
gint stdin_fd;
gchar *stdin_path;
gint stdout_fd;
gchar *stdout_path;
gint stderr_fd;
gchar *stderr_path;
GArray *basic_fd_assignments;
GArray *needdup_fd_assignments;
GSpawnChildSetupFunc child_setup_func;
gpointer child_setup_user_data;
GDestroyNotify child_setup_destroy_notify;
#endif
};
void g_subprocess_set_launcher (GSubprocess *subprocess,
GSubprocessLauncher *launcher);
G_END_DECLS
#endif
This diff is collapsed.
/* GIO - GLib Input, Output and Streaming Library
*
* Copyright © 2012,2013 Colin Walters <walters@verbum.org>
* Copyright © 2012,2013 Canonical Limited
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, MA 02111-1307, USA.
*
* Author: Ryan Lortie <desrt@desrt.ca>
* Author: Colin Walters <walters@verbum.org>
*/
#if !defined (__GIO_GIO_H_INSIDE__) && !defined (GIO_COMPILATION)
#error "Only <gio/gio.h> can be included directly."
#endif
#ifndef __G_SUBPROCESS_LAUNCHER_H__
#define __G_SUBPROCESS_LAUNCHER_H__
#include <gio/giotypes.h>
G_BEGIN_DECLS
#define G_TYPE_SUBPROCESS_LAUNCHER (g_subprocess_launcher_get_type ())
#define G_SUBPROCESS_LAUNCHER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_SUBPROCESS_LAUNCHER, GSubprocessLauncher))
#define G_IS_SUBPROCESS_LAUNCHER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_SUBPROCESS_LAUNCHER))
GLIB_AVAILABLE_IN_2_40
GType g_subprocess_launcher_get_type (void) G_GNUC_CONST;
GLIB_AVAILABLE_IN_2_40
GSubprocessLauncher * g_subprocess_launcher_new (GSubprocessFlags flags);
GLIB_AVAILABLE_IN_2_40
GSubprocess * g_subprocess_launcher_spawn (GSubprocessLauncher *self,
GError **error,
const gchar *argv0,
...);
GLIB_AVAILABLE_IN_2_40
GSubprocess * g_subprocess_launcher_spawnv (GSubprocessLauncher *self,
const gchar * const *argv,
GError **error);
GLIB_AVAILABLE_IN_2_40
void g_subprocess_launcher_set_environ (GSubprocessLauncher *self,
gchar **environ);
GLIB_AVAILABLE_IN_2_40
void g_subprocess_launcher_setenv (GSubprocessLauncher *self,
const gchar *variable,
const gchar *value,
gboolean overwrite);
GLIB_AVAILABLE_IN_2_40
void g_subprocess_launcher_unsetenv (GSubprocessLauncher *self,
const gchar *variable);
GLIB_AVAILABLE_IN_2_40
const gchar * g_subprocess_launcher_getenv (GSubprocessLauncher *self,
const gchar *variable);
GLIB_AVAILABLE_IN_2_40
void g_subprocess_launcher_set_cwd (GSubprocessLauncher *self,
const gchar *cwd);
GLIB_AVAILABLE_IN_2_40
void g_subprocess_launcher_set_flags (GSubprocessLauncher *self,
GSubprocessFlags flags);
/* Extended I/O control, only available on UNIX */
#ifdef G_OS_UNIX
GLIB_AVAILABLE_IN_2_40
void g_subprocess_launcher_set_stdin_file_path (GSubprocessLauncher *self,
const gchar *path);
GLIB_AVAILABLE_IN_2_40
void g_subprocess_launcher_take_stdin_fd (GSubprocessLauncher *self,
gint fd);
GLIB_AVAILABLE_IN_2_40
void g_subprocess_launcher_set_stdout_file_path (GSubprocessLauncher *self,
const gchar *path);
GLIB_AVAILABLE_IN_2_40
void g_subprocess_launcher_take_stdout_fd (GSubprocessLauncher *self,
gint fd);
GLIB_AVAILABLE_IN_2_40
void g_subprocess_launcher_set_stderr_file_path (GSubprocessLauncher *self,
const gchar *path);
GLIB_AVAILABLE_IN_2_40
void g_subprocess_launcher_take_stderr_fd (GSubprocessLauncher *self,
gint fd);
GLIB_AVAILABLE_IN_2_40
void g_subprocess_launcher_pass_fd (GSubprocessLauncher *self,
gint source_fd,
gint target_fd);
/* Child setup, only available on UNIX */
GLIB_AVAILABLE_IN_2_40
void g_subprocess_launcher_set_child_setup (GSubprocessLauncher *self,
GSpawnChildSetupFunc child_setup,
gpointer user_data,
GDestroyNotify destroy_notify);
#endif
G_END_DECLS
#endif /* __G_SUBPROCESS_H__ */
......@@ -71,6 +71,8 @@ gdbus-test-fixture
gdbus-testserver
gdbus-threading
gio-du
gsubprocess
gsubprocess-testprog
g-file
g-file-info
g-icon
......
......@@ -30,6 +30,7 @@ test_programs = \
data-output-stream \
fileattributematcher \
filter-streams \
gsubprocess \
g-file \
g-file-info \
g-icon \
......@@ -87,6 +88,7 @@ uninstalled_test_extra_programs = \
gdbus-example-subtree \
gdbus-example-watch-name \
gdbus-example-watch-proxy \
gsubprocess-testprog \
httpd \
proxy \
resolver \
......@@ -294,8 +296,7 @@ gdbus_non_socket_SOURCES = \
uninstalled_test_extra_programs += gdbus-example-objectmanager-client
gdbus_example_objectmanager_client_LDADD = gdbus-object-manager-example/libgdbus-example-objectmanager.la $(LDADD)
uninstalled_test_extra_programs += gdbus-example-objectmanager-server
gdbus_example_objectmanager_server_LDADD = gdbus-object-manager-example/libgdbus-example-objectmanager.la $(LDADD)
test_extra_programs += gsubprocess-testprog
uninstalled_test_extra_programs += gdbus-test-fixture
gdbus_test_fixture_LDADD = gdbus-object-manager-example/libgdbus-example-objectmanager.la $(LDADD)
......
#include <gio/gio.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#ifdef G_OS_UNIX
#include <gio/gunixinputstream.h>
#include <gio/gunixoutputstream.h>
#endif
static GOptionEntry options[] = {
{NULL}
};
static void
write_all (int fd,
const guint8* buf,
gsize len)
{
while (len > 0)
{
ssize_t bytes_written = write (fd, buf, len);
if (bytes_written < 0)
g_error ("Failed to write to fd %d: %s",
fd, strerror (errno));
buf += bytes_written;
len -= bytes_written;
}
}
static int
echo_mode (int argc,
char **argv)
{
int i;
for (i = 2; i < argc; i++)
{
write_all (1, (guint8*)argv[i], strlen (argv[i]));
write_all (1, (guint8*)"\n", 1);
}
return 0;
}
static int
echo_stdout_and_stderr_mode (int argc,
char **argv)
{
int i;
for (i = 2; i < argc; i++)
{
write_all (1, (guint8*)argv[i], strlen (argv[i]));
write_all (1, (guint8*)"\n", 1);
write_all (2, (guint8*)argv[i], strlen (argv[i]));
write_all (2, (guint8*)"\n", 1);
}
return 0;
}
static int
cat_mode (int argc,
char **argv)