summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKjell Ahlstedt <kjellahlstedt@gmail.com>2023-04-17 16:01:10 +0200
committerKjell Ahlstedt <kjellahlstedt@gmail.com>2023-04-17 16:01:10 +0200
commit4a59730ba4b410ee1fb0080f5e4d6e8cc798a298 (patch)
tree7521c113a32c4b0329d8aa6765a29a91178c53d4
parent262c4da18c78c01ee15ce079bb85c80a8d9e7650 (diff)
downloadglibmm-4a59730ba4b410ee1fb0080f5e4d6e8cc798a298.tar.gz
Add Gio::Subprocess, SubprocessLauncher and examples/subprocess
Fixes #106
-rw-r--r--examples/Makefile.am4
-rw-r--r--examples/meson.build1
-rw-r--r--examples/subprocess/main.cc114
-rw-r--r--gio/giomm.h1
-rw-r--r--gio/giomm/meson.build2
-rw-r--r--gio/src/filelist.am2
-rw-r--r--gio/src/gio_docs_override.xml1
-rw-r--r--gio/src/gio_extra_objects.defs12
-rw-r--r--gio/src/subprocess.ccg89
-rw-r--r--gio/src/subprocess.hg253
-rw-r--r--gio/src/subprocesslauncher.ccg62
-rw-r--r--gio/src/subprocesslauncher.hg121
-rw-r--r--tools/m4/convert_gio.m45
13 files changed, 667 insertions, 0 deletions
diff --git a/examples/Makefile.am b/examples/Makefile.am
index a32cfb84..4de7e4e3 100644
--- a/examples/Makefile.am
+++ b/examples/Makefile.am
@@ -33,6 +33,7 @@ check_PROGRAMS = \
properties/example \
regex/example \
settings/settings \
+ subprocess/example \
thread/dispatcher \
thread/dispatcher2
@@ -85,6 +86,9 @@ network_socket_server_LDADD = $(giomm_ldadd)
settings_settings_SOURCES = settings/settings.cc
settings_settings_LDADD = $(giomm_ldadd)
+subprocess_example_SOURCES = subprocess/main.cc
+subprocess_example_LDADD = $(giomm_ldadd)
+
dist_noinst_DATA = settings/org.gtkmm.demo.gschema.xml
CLEANFILES = settings/gschemas.compiled
diff --git a/examples/meson.build b/examples/meson.build
index f2c47417..04a7608a 100644
--- a/examples/meson.build
+++ b/examples/meson.build
@@ -17,6 +17,7 @@ examples = [
[['properties'], 'example', ['properties_example.cc'], false],
[['regex'], 'example', ['main.cc'], false],
[['settings'], 'settings', ['settings.cc', 'org.gtkmm.demo.gschema.xml'], true],
+ [['subprocess'], 'example', ['main.cc'], true],
[['thread'], 'dispatcher', ['dispatcher.cc'], false],
[['thread'], 'dispatcher2', ['dispatcher2.cc'], false],
]
diff --git a/examples/subprocess/main.cc b/examples/subprocess/main.cc
new file mode 100644
index 00000000..f72526ee
--- /dev/null
+++ b/examples/subprocess/main.cc
@@ -0,0 +1,114 @@
+/* Copyright (C) 2023 The gtkmm Development Team
+ *
+ * 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.1 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <giomm.h>
+#include <iostream>
+
+namespace
+{
+int finish_pending = 0;
+
+void on_communicate_finished(Glib::RefPtr<Gio::AsyncResult>& result,
+ const Glib::RefPtr<Gio::Subprocess>& subprocess, const Glib::ustring& heading)
+{
+ try
+ {
+ std::cout << "\n" << heading << "\n";
+ auto [stdout_buf, stderr_buf] = subprocess->communicate_utf8_finish(result);
+ std::cout << "stdout_buf: " << stdout_buf << "\n"
+ << "stderr_buf: " << stderr_buf << "\n";
+ }
+ catch (const Glib::Error& error)
+ {
+ std::cerr << "on_communicate_finished(), Glib::Error: " << error.what() << std::endl;
+ }
+ catch (const std::exception& error)
+ {
+ std::cerr << "on_communicate_finished(), std::exception: " << error.what() << std::endl;
+ }
+ --finish_pending;
+}
+} // anonymous namespace
+
+int main(int argc, char** argv)
+{
+ Gio::init();
+
+ if (argc < 3)
+ {
+ std::cerr << "Usage: " << argv[0] << " input-data command [arguments]...\n";
+ return 1;
+ }
+
+ // Three character encodings can be involved:
+ // 1. The encoding in the user's preferred locale.
+ // 2. The filename encoding, used by GLib.
+ // 3. UTF-8.
+ // The encoding used in argv is determined by the operating system.
+ // It's assumed to be the encoding in the user's preferred locale,
+ // which is also the C and C++ global locale. See the documentation of
+ // Glib::set_init_to_users_preferred_locale().
+ try
+ {
+ const auto stdin_buf = Glib::locale_to_utf8(argv[1]);
+
+ std::vector<std::string> arg_vector;
+ for (int i = 2; i < argc; ++i)
+ arg_vector.push_back(Glib::filename_from_utf8(Glib::locale_to_utf8(argv[i])));
+
+ Gio::Subprocess::Flags flags =
+ Gio::Subprocess::Flags::STDOUT_PIPE | Gio::Subprocess::Flags::STDERR_PIPE;
+ if (!stdin_buf.empty())
+ flags |= Gio::Subprocess::Flags::STDIN_PIPE;
+
+ // This example would be easier with the synchronous communicate_utf8().
+
+ // Without SubprocessLauncher.
+ auto subprocess = Gio::Subprocess::create(arg_vector, flags);
+ ++finish_pending;
+ subprocess->communicate_utf8_async(stdin_buf,
+ [&subprocess](Glib::RefPtr<Gio::AsyncResult>& result)
+ {
+ on_communicate_finished(result, subprocess, "Without SubprocessLauncher");
+ });
+
+ // With SubprocessLauncher.
+ auto launcher = Gio::SubprocessLauncher::create(flags);
+ auto spawned_subprocess = launcher->spawn(arg_vector);
+ ++finish_pending;
+ spawned_subprocess->communicate_utf8_async(stdin_buf,
+ [&spawned_subprocess](Glib::RefPtr<Gio::AsyncResult>& result)
+ {
+ on_communicate_finished(result, spawned_subprocess, "With SubprocessLauncher");
+ });
+ }
+ catch (const Glib::Error& error)
+ {
+ std::cerr << "Glib::Error: " << error.what() << std::endl;
+ return 1;
+ }
+ catch (const std::exception& error)
+ {
+ std::cerr << "std::exception: " << error.what() << std::endl;
+ return 1;
+ }
+
+ // Wait for on_communicate_finished() to finish.
+ auto main_context = Glib::MainContext::get_thread_default();
+ while (finish_pending > 0)
+ main_context->iteration(true);
+ return 0;
+}
diff --git a/gio/giomm.h b/gio/giomm.h
index 6778a914..04f300bd 100644
--- a/gio/giomm.h
+++ b/gio/giomm.h
@@ -146,6 +146,7 @@
#include <giomm/socketservice.h>
#include <giomm/socketsource.h>
#include <giomm/srvtarget.h>
+#include <giomm/subprocesslauncher.h>
#include <giomm/tcpconnection.h>
#include <giomm/tcpwrapperconnection.h>
#include <giomm/themedicon.h>
diff --git a/gio/giomm/meson.build b/gio/giomm/meson.build
index 1fb44702..4aba4ae2 100644
--- a/gio/giomm/meson.build
+++ b/gio/giomm/meson.build
@@ -142,6 +142,8 @@ giomm_any_hg_ccg_basenames = [
'socketlistener',
'socketservice',
'srvtarget',
+ 'subprocess',
+ 'subprocesslauncher',
'tcpconnection',
'tcpwrapperconnection',
'threadedsocketservice',
diff --git a/gio/src/filelist.am b/gio/src/filelist.am
index 12929523..8138f469 100644
--- a/gio/src/filelist.am
+++ b/gio/src/filelist.am
@@ -127,6 +127,8 @@ giomm_files_any_hg = \
socketlistener.hg \
socketservice.hg \
srvtarget.hg \
+ subprocess.hg \
+ subprocesslauncher.hg \
tcpconnection.hg \
tcpwrapperconnection.hg \
threadedsocketservice.hg \
diff --git a/gio/src/gio_docs_override.xml b/gio/src/gio_docs_override.xml
index c889da8a..5c85a134 100644
--- a/gio/src/gio_docs_override.xml
+++ b/gio/src/gio_docs_override.xml
@@ -84,6 +84,7 @@
<substitute_enumerator_name from_prefix="G_SOCKET_FAMILY_" to_prefix="Gio::SocketFamily::" />
<substitute_enumerator_name from_prefix="G_IO_" to_prefix="Glib::IOCondition::" />
<substitute_enumerator_name from_prefix="G_SOCKET_CLIENT_" to_prefix="Gio::SocketClientEvent::" />
+<substitute_enumerator_name from_prefix="G_SUBPROCESS_FLAGS_" to_prefix="Gio::Subprocess::Flags::" />
<substitute_enumerator_name from_prefix="G_TLS_AUTHENTICATION_" to_prefix="Gio::GTlsAuthenticationMode::" />
<substitute_enumerator_name from_prefix="G_TLS_CERTIFICATE_" to_prefix="Gio::TlsCertificateFlags::" />
<substitute_enumerator_name from_prefix="G_TLS_REHANDSHAKE_" to_prefix="Gio::TlsRehandshakeMode::" />
diff --git a/gio/src/gio_extra_objects.defs b/gio/src/gio_extra_objects.defs
index 999ba38e..2123d47d 100644
--- a/gio/src/gio_extra_objects.defs
+++ b/gio/src/gio_extra_objects.defs
@@ -229,6 +229,18 @@
(gtype-id "G_TYPE_SIMPLE_ACTION")
)
+(define-object Subprocess
+ (in-module "Gio")
+ (c-name "GSubprocess")
+ (gtype-id "G_TYPE_SUBPROCESS")
+)
+
+(define-object SubprocessLauncher
+ (in-module "Gio")
+ (c-name "GSubprocessLauncher")
+ (gtype-id "G_TYPE_SUBPROCESS_LAUNCHER")
+)
+
(define-object TlsBackend
(in-module "Gio")
(c-name "GTlsBackend")
diff --git a/gio/src/subprocess.ccg b/gio/src/subprocess.ccg
new file mode 100644
index 00000000..73f1d9a3
--- /dev/null
+++ b/gio/src/subprocess.ccg
@@ -0,0 +1,89 @@
+/* Copyright (C) 2023 The gtkmm Development Team
+ *
+ * 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.1 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <giomm/slot_async.h>
+#include <gio/gio.h>
+
+namespace Gio
+{
+Subprocess::Subprocess(const std::vector<std::string>& argv, Flags flags)
+: _CONSTRUCT("argv", Glib::ArrayHandler<std::string>::vector_to_array(argv).data(), "flags", (GSubprocessFlags)flags)
+{
+ init();
+}
+
+std::pair<Glib::RefPtr<Glib::Bytes>, Glib::RefPtr<Glib::Bytes>>
+Subprocess::communicate(const Glib::RefPtr<const Glib::Bytes>& stdin_buf,
+ const Glib::RefPtr<Cancellable>& cancellable)
+{
+ GError* gerror = nullptr;
+ GBytes* gstdout_buf = nullptr;
+ GBytes* gstderr_buf = nullptr;
+ (void)g_subprocess_communicate(gobj(),
+ const_cast<GBytes*>(Glib::unwrap(stdin_buf)),
+ Glib::unwrap(cancellable), &gstdout_buf, &gstderr_buf, &gerror);
+ if (gerror)
+ ::Glib::Error::throw_exception(gerror);
+
+ return {Glib::wrap(gstdout_buf), Glib::wrap(gstderr_buf)};
+}
+
+std::pair<Glib::RefPtr<Glib::Bytes>, Glib::RefPtr<Glib::Bytes>>
+Subprocess::communicate_finish(const Glib::RefPtr<AsyncResult>& result)
+{
+ GError* gerror = nullptr;
+ GBytes* gstdout_buf = nullptr;
+ GBytes* gstderr_buf = nullptr;
+ (void)g_subprocess_communicate_finish(gobj(), Glib::unwrap(result),
+ &gstdout_buf, &gstderr_buf, &gerror);
+ if (gerror)
+ ::Glib::Error::throw_exception(gerror);
+
+ return {Glib::wrap(gstdout_buf), Glib::wrap(gstderr_buf)};
+}
+
+std::pair<Glib::ustring, Glib::ustring>
+Subprocess::communicate_utf8(const Glib::ustring& stdin_buf,
+ const Glib::RefPtr<Cancellable>& cancellable)
+{
+ GError* gerror = nullptr;
+ char* gstdout_buf = nullptr;
+ char* gstderr_buf = nullptr;
+ (void)g_subprocess_communicate_utf8(gobj(), Glib::c_str_or_nullptr(stdin_buf),
+ Glib::unwrap(cancellable), &gstdout_buf, &gstderr_buf, &gerror);
+ if (gerror)
+ ::Glib::Error::throw_exception(gerror);
+
+ return {Glib::convert_return_gchar_ptr_to_ustring(gstdout_buf),
+ Glib::convert_return_gchar_ptr_to_ustring(gstderr_buf)};
+}
+
+std::pair<Glib::ustring, Glib::ustring>
+Subprocess::communicate_utf8_finish(const Glib::RefPtr<AsyncResult>& result)
+{
+ GError* gerror = nullptr;
+ char* gstdout_buf = nullptr;
+ char* gstderr_buf = nullptr;
+ (void)g_subprocess_communicate_utf8_finish(gobj(), Glib::unwrap(result),
+ &gstdout_buf, &gstderr_buf, &gerror);
+ if (gerror)
+ ::Glib::Error::throw_exception(gerror);
+
+ return {Glib::convert_return_gchar_ptr_to_ustring(gstdout_buf),
+ Glib::convert_return_gchar_ptr_to_ustring(gstderr_buf)};
+}
+
+} // namespace Gio
diff --git a/gio/src/subprocess.hg b/gio/src/subprocess.hg
new file mode 100644
index 00000000..1b6322ca
--- /dev/null
+++ b/gio/src/subprocess.hg
@@ -0,0 +1,253 @@
+/* Copyright (C) 2023 The gtkmm Development Team
+ *
+ * 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.1 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, see <http://www.gnu.org/licenses/>.
+ */
+
+_CONFIGINCLUDE(giommconfig.h)
+
+#include <glibmm/object.h>
+#include <glibmm/bytes.h>
+#include <giomm/initable.h>
+#include <giomm/asyncresult.h>
+#include <giomm/cancellable.h>
+#include <giomm/inputstream.h>
+#include <giomm/outputstream.h>
+#include <utility> // std::pair
+
+_DEFS(giomm,gio)
+_PINCLUDE(glibmm/private/object_p.h)
+
+namespace Gio
+{
+
+/** Child processes.
+ *
+ * %Gio::Subprocess allows the creation of and interaction with child
+ * processes.
+ *
+ * Processes can be communicated with using standard GIO-style APIs
+ * (Gio::InputStream, Gio::OutputStream). There are GIO-style APIs to wait for
+ * process termination (cancellable and with an asynchronous variant).
+ *
+ * There is an API to force a process to terminate, as well as a
+ * race-free API for sending UNIX signals to a subprocess.
+ *
+ * One major advantage that GIO brings over the core GLib library is
+ * comprehensive API for asynchronous I/O, such as
+ * Gio::OutputStream::splice_async(). This makes %Gio::Subprocess
+ * significantly more powerful and flexible than equivalent APIs in
+ * some other languages such as the `subprocess.py`
+ * included with Python. For example, using %Gio::Subprocess one could
+ * create two child processes, reading standard output from the first,
+ * processing it, and writing to the input stream of the second, all
+ * without blocking the main loop.
+ *
+ * A powerful communicate() API is provided similar to the
+ * `%communicate()` method of `subprocess.py`. This enables very easy
+ * interaction with a subprocess that has been opened with pipes.
+ *
+ * %Gio::Subprocess defaults to tight control over the file descriptors open
+ * in the child process, avoiding dangling-fd issues that are caused by
+ * a simple fork()/exec(). The only open file descriptors in the
+ * spawned process are ones that were explicitly specified by the
+ * %Gio::Subprocess API (unless Gio::Subprocess::Flags::INHERIT_FDS was
+ * specified).
+ *
+ * %Gio::Subprocess will quickly reap all child processes as they exit,
+ * avoiding "zombie processes" remaining around for long periods of
+ * time. wait() can be used to wait for this to happen,
+ * but it will happen even without the call being explicitly made.
+ *
+ * As a matter of principle, %Gio::Subprocess has no API that accepts
+ * shell-style space-separated strings. It will, however, match the
+ * typical shell behaviour of searching the PATH for executables that do
+ * not contain a directory separator in their name. By default, the `PATH`
+ * of the current process is used. You can specify
+ * Gio::Subprocess::Flags::SEARCH_PATH_FROM_ENVP to use the `PATH` of the
+ * launcher environment instead.
+ *
+ * %Gio::Subprocess attempts to have a very simple API for most uses (ie:
+ * spawning a subprocess with arguments and support for most typical
+ * kinds of input and output redirection). See create(). The
+ * Gio::SubprocessLauncher API is provided for more complicated cases
+ * (advanced types of redirection, environment variable manipulation,
+ * change of working directory, child setup functions, etc).
+ *
+ * A typical use of %Gio::Subprocess will involve calling create(), followed by
+ * wait_async() or wait(). After the process exits, the status can be
+ * checked using functions such as get_if_exited() (which are similar to
+ * the familiar WIFEXITED-style POSIX macros).
+ *
+ * @see Gio::SubprocessLauncher
+ * @newin{2,78}
+ */
+class GIOMM_API Subprocess : public Glib::Object, public Initable
+{
+ _CLASS_GOBJECT(Subprocess, GSubprocess, G_SUBPROCESS, Glib::Object, GObject, , , GIOMM_API)
+ _IMPLEMENTS_INTERFACE(Initable)
+
+public:
+ _WRAP_ENUM(Flags, GSubprocessFlags, newin "2,78", decl_prefix GIOMM_API)
+
+protected:
+ // Handwritten to ignore the final GError** parameter in the g_subprocess_newv() function.
+ // It can throw, due to its call to Initable::init().
+ explicit Subprocess(const std::vector<std::string>& argv, Flags flags = Flags::NONE);
+ _IGNORE(g_subprocess_new, g_subprocess_newv)
+
+public:
+ /** Create a new process with the given flags and argument list.
+ *
+ * @newin{2,78}
+ *
+ * @param argv Commandline arguments for the subprocess.
+ * @param flags Flags that define the behaviour of the subprocess.
+ * @return A newly created Subprocess. On error, an exception is thrown.
+ *
+ * @throws Glib::Error
+ */
+ _WRAP_CREATE(const std::vector<std::string>& argv, Flags flags = Flags::NONE)
+
+ _WRAP_METHOD(Glib::RefPtr<OutputStream> get_stdin_pipe(), g_subprocess_get_stdin_pipe, refreturn, newin "2,78")
+ _WRAP_METHOD(Glib::RefPtr<const OutputStream> get_stdin_pipe() const, g_subprocess_get_stdin_pipe, refreturn, constversion, newin "2,78")
+ _WRAP_METHOD(Glib::RefPtr<InputStream> get_stdout_pipe(), g_subprocess_get_stdout_pipe, refreturn, newin "2,78")
+ _WRAP_METHOD(Glib::RefPtr<const InputStream> get_stdout_pipe() const, g_subprocess_get_stdout_pipe, refreturn, constversion, newin "2,78")
+ _WRAP_METHOD(Glib::RefPtr<InputStream> get_stderr_pipe(), g_subprocess_get_stderr_pipe, refreturn, newin "2,78")
+ _WRAP_METHOD(Glib::RefPtr<const InputStream> get_stderr_pipe() const, g_subprocess_get_stderr_pipe, refreturn, constversion, newin "2,78")
+
+ _WRAP_METHOD(Glib::ustring get_identifier() const, g_subprocess_get_identifier, newin "2,78")
+ _WRAP_METHOD(void send_signal(int signal_num), g_subprocess_send_signal, ifdef G_OS_UNIX, newin "2,78")
+ _WRAP_METHOD(void force_exit(), g_subprocess_force_exit, newin "2,78")
+
+ _WRAP_METHOD(void wait(const Glib::RefPtr<Cancellable>& cancellable = {}) const, g_subprocess_wait, errthrow, newin "2,78")
+ _WRAP_METHOD(void wait_async(const SlotAsyncReady& slot{callback}, const Glib::RefPtr<Cancellable>& cancellable{.} = {}) const,
+ g_subprocess_wait_async, slot_name slot, slot_callback giomm_SignalProxy_async_callback, newin "2,78")
+ _WRAP_METHOD(void wait_finish(const Glib::RefPtr<AsyncResult>& result) const, g_subprocess_wait_finish, errthrow, newin "2,78")
+
+ _WRAP_METHOD(void wait_check(const Glib::RefPtr<Cancellable>& cancellable = {}) const, g_subprocess_wait_check, errthrow, newin "2,78")
+ _WRAP_METHOD(void wait_check_async(const SlotAsyncReady& slot{callback}, const Glib::RefPtr<Cancellable>& cancellable{.} = {}) const,
+ g_subprocess_wait_check_async, slot_name slot, slot_callback giomm_SignalProxy_async_callback, newin "2,78")
+ _WRAP_METHOD(void wait_check_finish(const Glib::RefPtr<AsyncResult>& result) const, g_subprocess_wait_check_finish, errthrow, newin "2,78")
+
+ _WRAP_METHOD(int get_status() const, g_subprocess_get_status, newin "2,78")
+ _WRAP_METHOD(bool get_successful() const, g_subprocess_get_successful, newin "2,78")
+ _WRAP_METHOD(bool get_if_exited() const, g_subprocess_get_if_exited, newin "2,78")
+ _WRAP_METHOD(int get_exit_status() const, g_subprocess_get_exit_status, newin "2,78")
+ _WRAP_METHOD(bool get_if_signaled() const, g_subprocess_get_if_signaled, newin "2,78")
+ _WRAP_METHOD(int get_term_sig() const, g_subprocess_get_term_sig, newin "2,78")
+
+ /** Communicate with the subprocess until it terminates, and all input
+ * and output has been completed.
+ *
+ * If @a stdin_buf is given, the subprocess must have been created with
+ * Gio::Subprocess::Flags::STDIN_PIPE. The given data is fed to the
+ * stdin of the subprocess and the pipe is closed (ie: EOF).
+ *
+ * At the same time (as not to cause blocking when dealing with large
+ * amounts of data), if Gio::Subprocess::Flags::STDOUT_PIPE or
+ * Gio::Subprocess::Flags::STDERR_PIPE were used, reads from those
+ * streams. The data that was read is returned in @a stdout_buf and/or
+ * the @a stderr_buf.
+ *
+ * If the subprocess was created with Gio::Subprocess::Flags::STDOUT_PIPE,
+ * @a stdout_buf will contain the data read from stdout. Otherwise, for
+ * subprocesses not created with Gio::Subprocess::Flags::STDOUT_PIPE,
+ * @a stdout_buf will be set to an empty RefPtr. Similar provisions apply to
+ * @a stderr_buf and Gio::Subprocess::Flags::STDERR_PIPE.
+ *
+ * If you desire the stdout and stderr data to be interleaved, create
+ * the subprocess with Gio::Subprocess::Flags::STDOUT_PIPE and
+ * Gio::Subprocess::Flags::STDERR_MERGE. The merged result will be returned
+ * in @a stdout_buf, and @a stderr_buf will be set to an empty RefPtr.
+ *
+ * In case of any error (including cancellation), an exception will be thrown.
+ *
+ * After a normal return (no exception thrown), the subprocess has exited and the
+ * exit status inspection APIs (eg: get_if_exited(), get_exit_status()) may be used.
+ *
+ * You should not attempt to use any of the subprocess pipes after
+ * starting this function, since they may be left in strange states,
+ * even if the operation was cancelled. You should especially not
+ * attempt to interact with the pipes while the operation is in progress
+ * (either from another thread or if using the asynchronous version).
+ *
+ * @newin{2,78}
+ *
+ * @param stdin_buf Data to send to the stdin of the subprocess, or an empty RefPtr.
+ * @param cancellable A Cancellable.
+ * @return {stdout_buf, stderr_buf} stdout data and stderr data.
+ * Can be empty RefPtrs, if there are no data.
+ *
+ * @throws Glib::Error
+ */
+ std::pair<Glib::RefPtr<Glib::Bytes>, Glib::RefPtr<Glib::Bytes>>
+ communicate(const Glib::RefPtr<const Glib::Bytes>& stdin_buf,
+ const Glib::RefPtr<Cancellable>& cancellable = {});
+ _IGNORE(g_subprocess_communicate)
+
+ _WRAP_METHOD(void communicate_async(const Glib::RefPtr<const Glib::Bytes>& stdin_buf{.},
+ const SlotAsyncReady& slot{callback}, const Glib::RefPtr<Cancellable>& cancellable{.} = {}),
+ g_subprocess_communicate_async, slot_name slot, slot_callback giomm_SignalProxy_async_callback, newin "2,78")
+
+ /** Complete an invocation of communicate_async().
+ *
+ * @newin{2,78}
+ *
+ * @param result Result.
+ * @return {stdout_buf, stderr_buf} stdout data and stderr data.
+ * Can be empty RefPtrs, if there are no data.
+ *
+ * @throws Glib::Error
+ */
+ std::pair<Glib::RefPtr<Glib::Bytes>, Glib::RefPtr<Glib::Bytes>>
+ communicate_finish(const Glib::RefPtr<AsyncResult>& result);
+ _IGNORE(g_subprocess_communicate_finish)
+
+ /** Like communicate(), but validates the output of the
+ * process as UTF-8, and returns it as a regular Glib::ustring.
+ *
+ * On error, an exception is thrown.
+ *
+ * @newin{2,78}
+ *
+ * @param stdin_buf Data to send to the stdin of the subprocess, or an empty string.
+ * @param cancellable A Cancellable.
+ * @return {stdout_buf, stderr_buf} stdout data and stderr data.
+ *
+ * @throws Glib::Error
+ */
+ std::pair<Glib::ustring, Glib::ustring> communicate_utf8(const Glib::ustring& stdin_buf,
+ const Glib::RefPtr<Cancellable>& cancellable = {});
+ _IGNORE(g_subprocess_communicate_utf8)
+
+ _WRAP_METHOD(void communicate_utf8_async(const Glib::ustring& stdin_buf{. NULL},
+ const SlotAsyncReady& slot{callback}, const Glib::RefPtr<Cancellable>& cancellable{.} = {}),
+ g_subprocess_communicate_utf8_async, slot_name slot, slot_callback giomm_SignalProxy_async_callback, newin "2,78")
+
+ /** Complete an invocation of communicate_utf8_async().
+ *
+ * @newin{2,78}
+ *
+ * @param result Result.
+ * @return {stdout_buf, stderr_buf} stdout data and stderr data.
+ *
+ * @throws Glib::Error
+ */
+ std::pair<Glib::ustring, Glib::ustring> communicate_utf8_finish(const Glib::RefPtr<AsyncResult>& result);
+ _IGNORE(g_subprocess_communicate_utf8_finish)
+
+ // _IGNORE_PROPERTY("flags", "argv") // write-only, construct-only
+};
+
+} // namespace Gio
diff --git a/gio/src/subprocesslauncher.ccg b/gio/src/subprocesslauncher.ccg
new file mode 100644
index 00000000..702b22d0
--- /dev/null
+++ b/gio/src/subprocesslauncher.ccg
@@ -0,0 +1,62 @@
+/* Copyright (C) 2023 The gtkmm Development Team
+ *
+ * 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.1 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <gio/gio.h>
+
+namespace
+{
+extern "C"
+{
+#ifdef G_OS_UNIX
+static void SubprocessLauncher_child_setup_callback(void* user_data)
+{
+ try
+ {
+ (*static_cast<Glib::SlotSpawnChildSetup*>(user_data))();
+ }
+ catch (...)
+ {
+ Glib::exception_handlers_invoke();
+ }
+}
+
+static void SubprocessLauncher_child_setup_callback_destroy(void* user_data)
+{
+ delete static_cast<Glib::SlotSpawnChildSetup*>(user_data);
+}
+#endif // G_OS_UNIX
+} // extern "C"
+} // anonymous namespace
+
+namespace Gio
+{
+void SubprocessLauncher::inherit_environ()
+{
+ g_subprocess_launcher_set_environ(gobj(), nullptr);
+}
+
+#ifdef G_OS_UNIX
+void SubprocessLauncher::set_child_setup(const Glib::SlotSpawnChildSetup& child_setup)
+{
+ const bool setup_slot = !child_setup.empty();
+
+ g_subprocess_launcher_set_child_setup(gobj(),
+ setup_slot ? &SubprocessLauncher_child_setup_callback : nullptr,
+ setup_slot ? new Glib::SlotSpawnChildSetup(child_setup) : nullptr,
+ setup_slot ? &SubprocessLauncher_child_setup_callback_destroy : nullptr);
+}
+#endif // G_OS_UNIX
+} // namespace Gio
diff --git a/gio/src/subprocesslauncher.hg b/gio/src/subprocesslauncher.hg
new file mode 100644
index 00000000..e96a6ad0
--- /dev/null
+++ b/gio/src/subprocesslauncher.hg
@@ -0,0 +1,121 @@
+/* Copyright (C) 2023 The gtkmm Development Team
+ *
+ * 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.1 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, see <http://www.gnu.org/licenses/>.
+ */
+
+_CONFIGINCLUDE(giommconfig.h)
+
+#include <glibmm/object.h>
+#include <glibmm/spawn.h>
+#include <giomm/subprocess.h>
+
+_DEFS(giomm,gio)
+_PINCLUDE(glibmm/private/object_p.h)
+
+namespace Gio
+{
+
+/** Environment options for launching a child process.
+ *
+ * This class contains a set of options for launching child processes,
+ * such as where its standard input and output will be directed, the
+ * argument list, the environment, and more.
+ *
+ * While the Gio::Subprocess class has high level functions covering
+ * popular cases, use of this class allows access to more advanced
+ * options. It can also be used to launch multiple subprocesses with
+ * a similar configuration.
+ *
+ * @see Subprocess
+ * @newin{2,78}
+ */
+class GIOMM_API SubprocessLauncher : public Glib::Object
+{
+ _CLASS_GOBJECT(SubprocessLauncher, GSubprocessLauncher, G_SUBPROCESS_LAUNCHER, Glib::Object, GObject, , , GIOMM_API)
+
+public:
+ // g_subprocess_launcher_new() does more than call g_object_new().
+ _WRAP_METHOD(static Glib::RefPtr<SubprocessLauncher> create(
+ Subprocess::Flags flags = Subprocess::Flags::NONE), g_subprocess_launcher_new, newin "2,78")
+
+#m4 _CONVERSION(`const std::vector<std::string>&', `const gchar*-const*',`Glib::ArrayHandler<std::string>::vector_to_array($3).data()')
+ _WRAP_METHOD(Glib::RefPtr<Subprocess> spawn(const std::vector<std::string>& argv),
+ g_subprocess_launcher_spawnv, errthrow, newin "2,78")
+ _IGNORE(g_subprocess_launcher_spawn)
+
+ /** Inherit the parent process’ environment.
+ * @newin{2,78}
+ */
+ void inherit_environ();
+#m4 _CONVERSION(`const std::vector<std::string>&', `gchar**',`const_cast<char**>(Glib::ArrayHandler<std::string>::vector_to_array($3).data())')
+ _WRAP_METHOD(void set_environ(const std::vector<std::string>& env),
+ g_subprocess_launcher_set_environ, newin "2,78")
+ _WRAP_METHOD(void setenv(const std::string& variable, const std::string& value, bool overwrite),
+ g_subprocess_launcher_setenv, newin "2,78")
+ _WRAP_METHOD(void unsetenv(const std::string& variable),
+ g_subprocess_launcher_unsetenv, newin "2,78")
+ _WRAP_METHOD(std::string getenv(const std::string& variable),
+ g_subprocess_launcher_getenv, newin "2,78")
+
+ _WRAP_METHOD(void set_cwd(const std::string& cwd), g_subprocess_launcher_set_cwd, newin "2,78")
+ _WRAP_METHOD(void set_flags(Subprocess::Flags flags), g_subprocess_launcher_set_flags, newin "2,78")
+
+ // Extended I/O control, only available on UNIX.
+ _WRAP_METHOD(void set_stdin_file_path(const std::string& path{NULL}),
+ g_subprocess_launcher_set_stdin_file_path, ifdef G_OS_UNIX, newin "2,78")
+ _WRAP_METHOD(void take_stdin_fd(int fd), g_subprocess_launcher_take_stdin_fd,
+ ifdef G_OS_UNIX, newin "2,78")
+
+ _WRAP_METHOD(void set_stdout_file_path(const std::string& path{NULL}),
+ g_subprocess_launcher_set_stdout_file_path, ifdef G_OS_UNIX, newin "2,78")
+ _WRAP_METHOD(void take_stdout_fd(int fd), g_subprocess_launcher_take_stdout_fd,
+ ifdef G_OS_UNIX, newin "2,78")
+
+ _WRAP_METHOD(void set_stderr_file_path(const std::string& path{NULL}),
+ g_subprocess_launcher_set_stderr_file_path, ifdef G_OS_UNIX, newin "2,78")
+ _WRAP_METHOD(void take_stderr_fd(int fd), g_subprocess_launcher_take_stderr_fd,
+ ifdef G_OS_UNIX, newin "2,78")
+
+ _WRAP_METHOD(void take_fd(int source_fd, int target_fd), g_subprocess_launcher_take_fd,
+ ifdef G_OS_UNIX, newin "2,78")
+
+ _WRAP_METHOD(void close(), g_subprocess_launcher_close, ifdef G_OS_UNIX, newin "2,78")
+
+#ifdef G_OS_UNIX
+ /** Sets up a child setup function.
+ *
+ * The child setup function will be called after fork() but before
+ * exec() on the child's side.
+ *
+ * A copy of the @a child_setup slot is stored. The copy will not be
+ * automatically deleted on the child's side of the fork(). It will only be
+ * deleted when the last reference on the %SubprocessLauncher is dropped
+ * or when a new child setup slot is given.
+ *
+ * An empty slot can be given as @a child_setup to disable the functionality.
+ *
+ * Child setup functions are only available on UNIX.
+ *
+ * @newin{2,78}
+ *
+ * @param child_setup A Glib::SlotSpawnChildSetup to use as the child setup function.
+ */
+ void set_child_setup(const Glib::SlotSpawnChildSetup& child_setup = {});
+ _IGNORE(g_subprocess_launcher_set_child_setup)
+#endif // G_OS_UNIX
+
+ // _IGNORE_PROPERTY("flags") // write-only, construct-only
+};
+
+} // namespace Gio
diff --git a/tools/m4/convert_gio.m4 b/tools/m4/convert_gio.m4
index b75ffdfe..ab0785a7 100644
--- a/tools/m4/convert_gio.m4
+++ b/tools/m4/convert_gio.m4
@@ -75,6 +75,7 @@ _CONV_GIO_ENUM(SocketFamily)
_CONV_GIO_INCLASS_ENUM(Socket,MsgFlags)
_CONV_GIO_INCLASS_ENUM(Socket,Protocol)
_CONV_GIO_INCLASS_ENUM(Socket,Type)
+_CONV_GIO_INCLASS_ENUM(Subprocess,Flags)
_CONV_GIO_ENUM(TlsCertificateFlags)
_CONV_GIO_ENUM(TlsCertificateRequestFlags)
_CONV_GIO_INCLASS_ENUM(TlsDatabase,VerifyFlags)
@@ -334,6 +335,10 @@ _CONVERSION(`const Glib::RefPtr<SocketConnection>&',`GSocketConnection*',__CONVE
#SocketControlMessage
_CONVERSION(`GSocketControlMessage*',`Glib::RefPtr<SocketControlMessage>',`Glib::wrap($3)')
+#Subprocess, SubprocessLauncher
+_CONVERSION(`GSubprocess*',`Glib::RefPtr<Subprocess>',`Glib::wrap($3)')
+_CONVERSION(`GSubprocessLauncher*',`Glib::RefPtr<SubprocessLauncher>',`Glib::wrap($3)')
+
#TimeZoneMonitor
_CONVERSION(`GTimeZoneMonitor*',`Glib::RefPtr<TimeZoneMonitor>',`Glib::wrap($3)')