From 25dccf09f7113e21bc4e1cb8e47b41c096d3b997 Mon Sep 17 00:00:00 2001 From: Richard Hughes Date: Mon, 6 Jun 2022 14:43:59 +0100 Subject: [PATCH] Unexport fu_common_spawn_sync() This isn't something we want plugins to use. --- contrib/migrate.py | 2 + libfwupdplugin/README.md | 1 + libfwupdplugin/fu-common.c | 190 ----------------------- libfwupdplugin/fu-common.h | 17 --- libfwupdplugin/fu-self-test.c | 54 ------- libfwupdplugin/fwupdplugin.map | 1 - src/fu-offline.c | 11 +- src/fu-self-test.c | 55 +++++++ src/fu-spawn.c | 201 +++++++++++++++++++++++++ src/fu-spawn.h | 26 ++++ src/meson.build | 2 + {libfwupdplugin => src}/tests/spawn.sh | 0 12 files changed, 293 insertions(+), 267 deletions(-) create mode 100644 src/fu-spawn.c create mode 100644 src/fu-spawn.h rename {libfwupdplugin => src}/tests/spawn.sh (100%) diff --git a/contrib/migrate.py b/contrib/migrate.py index fa1acbba5..9d6185cc3 100755 --- a/contrib/migrate.py +++ b/contrib/migrate.py @@ -90,6 +90,8 @@ if __name__ == "__main__": "fu_common_read_uint32": "fu_memread_uint32", "fu_common_read_uint64": "fu_memread_uint64", "fu_common_bytes_compare_raw": "fu_memcmp_safe", + "FuOutputHandler": "FuSpawnOutputHandler", + "fu_common_spawn_sync": "fu_spawn_sync", }.items(): if buf.find(old) == -1: continue diff --git a/libfwupdplugin/README.md b/libfwupdplugin/README.md index 544c45efe..be221c597 100644 --- a/libfwupdplugin/README.md +++ b/libfwupdplugin/README.md @@ -70,3 +70,4 @@ Remember: Plugins should be upstream! * `fu_common_read*`: Use `fu_memread` prefix, i.e. replace the `_common` with `_mem` * `fu_common_write*`: Use `fu_memwrite` prefix, i.e. replace the `_common` with `_mem` * `fu_common_bytes_compare_raw()`: Use `fu_memcmp_safe()` instead +* `fu_common_spawn_sync()`: Use `g_spawn_sync()` instead, or ideally not at all! diff --git a/libfwupdplugin/fu-common.c b/libfwupdplugin/fu-common.c index 5791c9695..4f435d277 100644 --- a/libfwupdplugin/fu-common.c +++ b/libfwupdplugin/fu-common.c @@ -503,196 +503,6 @@ fu_common_firmware_builder(GBytes *bytes, return g_steal_pointer(&firmware_blob); } -typedef struct { - FuOutputHandler handler_cb; - gpointer handler_user_data; - GMainLoop *loop; - GSource *source; - GInputStream *stream; - GCancellable *cancellable; - guint timeout_id; -} FuCommonSpawnHelper; - -static void -fu_common_spawn_create_pollable_source(FuCommonSpawnHelper *helper); - -static gboolean -fu_common_spawn_source_pollable_cb(GObject *stream, gpointer user_data) -{ - FuCommonSpawnHelper *helper = (FuCommonSpawnHelper *)user_data; - gchar buffer[1024]; - gssize sz; - g_auto(GStrv) split = NULL; - g_autoptr(GError) error = NULL; - - /* read from stream */ - sz = g_pollable_input_stream_read_nonblocking(G_POLLABLE_INPUT_STREAM(stream), - buffer, - sizeof(buffer) - 1, - NULL, - &error); - if (sz < 0) { - if (!g_error_matches(error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK)) { - g_warning("failed to get read from nonblocking fd: %s", error->message); - } - return G_SOURCE_REMOVE; - } - - /* no read possible */ - if (sz == 0) - g_main_loop_quit(helper->loop); - - /* emit lines */ - if (helper->handler_cb != NULL) { - buffer[sz] = '\0'; - split = g_strsplit(buffer, "\n", -1); - for (guint i = 0; split[i] != NULL; i++) { - if (split[i][0] == '\0') - continue; - helper->handler_cb(split[i], helper->handler_user_data); - } - } - - /* set up the source for the next read */ - fu_common_spawn_create_pollable_source(helper); - return G_SOURCE_REMOVE; -} - -static void -fu_common_spawn_create_pollable_source(FuCommonSpawnHelper *helper) -{ - if (helper->source != NULL) - g_source_destroy(helper->source); - helper->source = - g_pollable_input_stream_create_source(G_POLLABLE_INPUT_STREAM(helper->stream), - helper->cancellable); - g_source_attach(helper->source, NULL); - g_source_set_callback(helper->source, - (GSourceFunc)fu_common_spawn_source_pollable_cb, - helper, - NULL); -} - -static void -fu_common_spawn_helper_free(FuCommonSpawnHelper *helper) -{ - g_object_unref(helper->cancellable); - if (helper->stream != NULL) - g_object_unref(helper->stream); - if (helper->source != NULL) - g_source_destroy(helper->source); - if (helper->loop != NULL) - g_main_loop_unref(helper->loop); - if (helper->timeout_id != 0) - g_source_remove(helper->timeout_id); - g_free(helper); -} - -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wunused-function" -G_DEFINE_AUTOPTR_CLEANUP_FUNC(FuCommonSpawnHelper, fu_common_spawn_helper_free) -#pragma clang diagnostic pop - -#ifndef _WIN32 -static gboolean -fu_common_spawn_timeout_cb(gpointer user_data) -{ - FuCommonSpawnHelper *helper = (FuCommonSpawnHelper *)user_data; - g_cancellable_cancel(helper->cancellable); - g_main_loop_quit(helper->loop); - helper->timeout_id = 0; - return G_SOURCE_REMOVE; -} - -static void -fu_common_spawn_cancelled_cb(GCancellable *cancellable, FuCommonSpawnHelper *helper) -{ - /* just propagate */ - g_cancellable_cancel(helper->cancellable); -} -#endif - -/** - * fu_common_spawn_sync: - * @argv: the argument list to run - * @handler_cb: (scope call) (nullable): optional #FuOutputHandler - * @handler_user_data: (nullable): the user data to pass to @handler_cb - * @timeout_ms: a timeout in ms, or 0 for no limit - * @cancellable: (nullable): optional #GCancellable - * @error: (nullable): optional return location for an error - * - * Runs a subprocess and waits for it to exit. Any output on standard out or - * standard error will be forwarded to @handler_cb as whole lines. - * - * Returns: %TRUE for success - * - * Since: 0.9.7 - **/ -gboolean -fu_common_spawn_sync(const gchar *const *argv, - FuOutputHandler handler_cb, - gpointer handler_user_data, - guint timeout_ms, - GCancellable *cancellable, - GError **error) -{ - g_autoptr(FuCommonSpawnHelper) helper = NULL; - g_autoptr(GSubprocess) subprocess = NULL; - g_autofree gchar *argv_str = NULL; -#ifndef _WIN32 - gulong cancellable_id = 0; -#endif - - g_return_val_if_fail(argv != NULL, FALSE); - g_return_val_if_fail(cancellable == NULL || G_IS_CANCELLABLE(cancellable), FALSE); - g_return_val_if_fail(error == NULL || *error == NULL, FALSE); - - /* create subprocess */ - argv_str = g_strjoinv(" ", (gchar **)argv); - g_debug("running '%s'", argv_str); - subprocess = - g_subprocess_newv(argv, - G_SUBPROCESS_FLAGS_STDOUT_PIPE | G_SUBPROCESS_FLAGS_STDERR_MERGE, - error); - if (subprocess == NULL) - return FALSE; - -#ifndef _WIN32 - /* watch for process to exit */ - helper = g_new0(FuCommonSpawnHelper, 1); - helper->handler_cb = handler_cb; - helper->handler_user_data = handler_user_data; - helper->loop = g_main_loop_new(NULL, FALSE); - helper->stream = g_subprocess_get_stdout_pipe(subprocess); - - /* always create a cancellable, and connect up the parent */ - helper->cancellable = g_cancellable_new(); - if (cancellable != NULL) { - cancellable_id = g_cancellable_connect(cancellable, - G_CALLBACK(fu_common_spawn_cancelled_cb), - helper, - NULL); - } - - /* allow timeout */ - if (timeout_ms > 0) { - helper->timeout_id = g_timeout_add(timeout_ms, fu_common_spawn_timeout_cb, helper); - } - fu_common_spawn_create_pollable_source(helper); - g_main_loop_run(helper->loop); - g_cancellable_disconnect(cancellable, cancellable_id); -#endif - if (!g_subprocess_wait_check(subprocess, cancellable, error)) - return FALSE; -#ifndef _WIN32 - if (g_cancellable_set_error_if_cancelled(helper->cancellable, error)) - return FALSE; -#endif - - /* success */ - return TRUE; -} - static const GError * fu_common_error_array_find(GPtrArray *errors, FwupdError error_code) { diff --git a/libfwupdplugin/fu-common.h b/libfwupdplugin/fu-common.h index 3ddff6150..a0a95fd8c 100644 --- a/libfwupdplugin/fu-common.h +++ b/libfwupdplugin/fu-common.h @@ -169,23 +169,6 @@ typedef enum { FU_LID_STATE_LAST } FuLidState; -/** - * FuOutputHandler: - * @line: text data - * @user_data: user data - * - * The process spawn iteration callback. - */ -typedef void (*FuOutputHandler)(const gchar *line, gpointer user_data); - -gboolean -fu_common_spawn_sync(const gchar *const *argv, - FuOutputHandler handler_cb, - gpointer handler_user_data, - guint timeout_ms, - GCancellable *cancellable, - GError **error) G_GNUC_WARN_UNUSED_RESULT; - gchar * fu_common_get_path(FuPathKind path_kind); gchar * diff --git a/libfwupdplugin/fu-self-test.c b/libfwupdplugin/fu-self-test.c index 093f43b52..3540f79f0 100644 --- a/libfwupdplugin/fu-self-test.c +++ b/libfwupdplugin/fu-self-test.c @@ -948,14 +948,6 @@ fu_common_firmware_builder_func(void) g_assert_cmpstr(data, ==, "xobdnas eht ni gninnur"); } -static void -fu_test_stdout_cb(const gchar *line, gpointer user_data) -{ - guint *lines = (guint *)user_data; - g_debug("got '%s'", line); - (*lines)++; -} - static gboolean _open_cb(GObject *device, GError **error) { @@ -1020,50 +1012,6 @@ fu_device_locker_fail_func(void) g_assert_false(fu_device_has_internal_flag(device, FU_DEVICE_INTERNAL_FLAG_IS_OPEN)); } -static void -fu_common_spawn_func(void) -{ - gboolean ret; - guint lines = 0; - g_autoptr(GError) error = NULL; - g_autofree gchar *fn = NULL; - const gchar *argv[4] = {"/bin/sh", "replace", "test", NULL}; - -#ifdef _WIN32 - g_test_skip("Known failures on Windows right now, skipping spawn func test"); - return; -#endif - - fn = g_test_build_filename(G_TEST_DIST, "tests", "spawn.sh", NULL); - argv[1] = fn; - ret = fu_common_spawn_sync(argv, fu_test_stdout_cb, &lines, 0, NULL, &error); - g_assert_no_error(error); - g_assert_true(ret); - g_assert_cmpint(lines, ==, 6); -} - -static void -fu_common_spawn_timeout_func(void) -{ - gboolean ret; - guint lines = 0; - g_autoptr(GError) error = NULL; - g_autofree gchar *fn = NULL; - const gchar *argv[4] = {"/bin/sh", "replace", "test", NULL}; - -#ifdef _WIN32 - g_test_skip("Known failures on Windows right now, skipping spawn timeout test"); - return; -#endif - - fn = g_test_build_filename(G_TEST_DIST, "tests", "spawn.sh", NULL); - argv[1] = fn; - ret = fu_common_spawn_sync(argv, fu_test_stdout_cb, &lines, 500, NULL, &error); - g_assert_error(error, G_IO_ERROR, G_IO_ERROR_CANCELLED); - g_assert_false(ret); - g_assert_cmpint(lines, ==, 1); -} - static void fu_common_endian_func(void) { @@ -4070,8 +4018,6 @@ main(int argc, char **argv) fu_common_store_cab_error_missing_file_func); g_test_add_func("/fwupd/common{cab-error-size}", fu_common_store_cab_error_size_func); g_test_add_func("/fwupd/common{bytes-get-data}", fu_common_bytes_get_data_func); - g_test_add_func("/fwupd/common{spawn)", fu_common_spawn_func); - g_test_add_func("/fwupd/common{spawn-timeout)", fu_common_spawn_timeout_func); g_test_add_func("/fwupd/common{firmware-builder}", fu_common_firmware_builder_func); g_test_add_func("/fwupd/common{kernel-lockdown}", fu_common_kernel_lockdown_func); g_test_add_func("/fwupd/common{strsafe}", fu_strsafe_func); diff --git a/libfwupdplugin/fwupdplugin.map b/libfwupdplugin/fwupdplugin.map index 0062077cb..ba66af63c 100644 --- a/libfwupdplugin/fwupdplugin.map +++ b/libfwupdplugin/fwupdplugin.map @@ -74,7 +74,6 @@ LIBFWUPDPLUGIN_0.9.7 { fu_common_firmware_builder; fu_common_mkdir_parent; fu_common_rmtree; - fu_common_spawn_sync; fu_device_get_metadata_boolean; fu_device_get_metadata_integer; fu_device_set_metadata_boolean; diff --git a/src/fu-offline.c b/src/fu-offline.c index 15f3b087e..744346019 100644 --- a/src/fu-offline.c +++ b/src/fu-offline.c @@ -14,6 +14,7 @@ #include "fu-history.h" #include "fu-plugin-private.h" +#include "fu-spawn.h" #include "fu-util-common.h" typedef enum { @@ -46,7 +47,7 @@ fu_offline_set_splash_progress(FuUtilPrivate *priv, guint percentage, GError **e argv[1] = "display-message"; argv[2] = "--text"; } - return fu_common_spawn_sync(argv, NULL, NULL, 200, NULL, error); + return fu_spawn_sync(argv, NULL, NULL, 200, NULL, error); } static gboolean @@ -63,9 +64,9 @@ fu_offline_set_splash_mode(FuUtilPrivate *priv, GError **error) } /* try the new fancy mode, then fall back to really old mode */ - if (!fu_common_spawn_sync(argv, NULL, NULL, 1500, NULL, &error_local)) { + if (!fu_spawn_sync(argv, NULL, NULL, 1500, NULL, &error_local)) { argv[2] = "--updates"; - if (!fu_common_spawn_sync(argv, NULL, NULL, 1500, NULL, error)) { + if (!fu_spawn_sync(argv, NULL, NULL, 1500, NULL, error)) { g_prefix_error(error, "%s: ", error_local->message); return FALSE; } @@ -92,10 +93,10 @@ fu_offline_set_splash_reboot(FuUtilPrivate *priv, GError **error) } /* try the new fancy mode, then fall back to really old mode */ - if (!fu_common_spawn_sync(argv, NULL, NULL, 200, NULL, &error_local)) { + if (!fu_spawn_sync(argv, NULL, NULL, 200, NULL, &error_local)) { /* fall back to really old mode that should be supported */ argv[2] = "--shutdown"; - if (!fu_common_spawn_sync(argv, NULL, NULL, 200, NULL, error)) { + if (!fu_spawn_sync(argv, NULL, NULL, 200, NULL, error)) { g_prefix_error(error, "%s: ", error_local->message); return FALSE; } diff --git a/src/fu-self-test.c b/src/fu-self-test.c index 6554fe29c..c02ec8fc2 100644 --- a/src/fu-self-test.c +++ b/src/fu-self-test.c @@ -29,6 +29,7 @@ #include "fu-progressbar.h" #include "fu-security-attr.h" #include "fu-smbios-private.h" +#include "fu-spawn.h" typedef struct { FuPlugin *plugin; @@ -3802,6 +3803,58 @@ fu_release_compare_func_cb(gconstpointer a, gconstpointer b) return fu_release_compare(release1, release2); } +static void +fu_spawn_stdout_cb(const gchar *line, gpointer user_data) +{ + guint *lines = (guint *)user_data; + g_debug("got '%s'", line); + (*lines)++; +} + +static void +fu_spawn_func(void) +{ + gboolean ret; + guint lines = 0; + g_autoptr(GError) error = NULL; + g_autofree gchar *fn = NULL; + const gchar *argv[4] = {"/bin/sh", "replace", "test", NULL}; + +#ifdef _WIN32 + g_test_skip("Known failures on Windows right now, skipping spawn func test"); + return; +#endif + + fn = g_test_build_filename(G_TEST_DIST, "tests", "spawn.sh", NULL); + argv[1] = fn; + ret = fu_spawn_sync(argv, fu_spawn_stdout_cb, &lines, 0, NULL, &error); + g_assert_no_error(error); + g_assert_true(ret); + g_assert_cmpint(lines, ==, 6); +} + +static void +fu_spawn_timeout_func(void) +{ + gboolean ret; + guint lines = 0; + g_autoptr(GError) error = NULL; + g_autofree gchar *fn = NULL; + const gchar *argv[4] = {"/bin/sh", "replace", "test", NULL}; + +#ifdef _WIN32 + g_test_skip("Known failures on Windows right now, skipping spawn timeout test"); + return; +#endif + + fn = g_test_build_filename(G_TEST_DIST, "tests", "spawn.sh", NULL); + argv[1] = fn; + ret = fu_spawn_sync(argv, fu_spawn_stdout_cb, &lines, 500, NULL, &error); + g_assert_error(error, G_IO_ERROR, G_IO_ERROR_CANCELLED); + g_assert_false(ret); + g_assert_cmpint(lines, ==, 1); +} + static void fu_release_compare_func(gconstpointer user_data) { @@ -3994,5 +4047,7 @@ main(int argc, char **argv) g_test_add_data_func("/fwupd/history{migrate}", self, fu_history_migrate_func); g_test_add_data_func("/fwupd/plugin-list", self, fu_plugin_list_func); g_test_add_data_func("/fwupd/plugin-list{depsolve}", self, fu_plugin_list_depsolve_func); + g_test_add_func("/fwupd/spawn", fu_spawn_func); + g_test_add_func("/fwupd/spawn-timeou)", fu_spawn_timeout_func); return g_test_run(); } diff --git a/src/fu-spawn.c b/src/fu-spawn.c new file mode 100644 index 000000000..8b332bf2d --- /dev/null +++ b/src/fu-spawn.c @@ -0,0 +1,201 @@ +/* + * Copyright (C) 2017 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#define G_LOG_DOMAIN "FuSpawn" + +#include "config.h" + +#include "fu-spawn.h" + +typedef struct { + FuSpawnOutputHandler handler_cb; + gpointer handler_user_data; + GMainLoop *loop; + GSource *source; + GInputStream *stream; + GCancellable *cancellable; + guint timeout_id; +} FuSpawnHelper; + +static void +fu_spawn_create_pollable_source(FuSpawnHelper *helper); + +static gboolean +fu_spawn_source_pollable_cb(GObject *stream, gpointer user_data) +{ + FuSpawnHelper *helper = (FuSpawnHelper *)user_data; + gchar buffer[1024]; + gssize sz; + g_auto(GStrv) split = NULL; + g_autoptr(GError) error = NULL; + + /* read from stream */ + sz = g_pollable_input_stream_read_nonblocking(G_POLLABLE_INPUT_STREAM(stream), + buffer, + sizeof(buffer) - 1, + NULL, + &error); + if (sz < 0) { + if (!g_error_matches(error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK)) { + g_warning("failed to get read from nonblocking fd: %s", error->message); + } + return G_SOURCE_REMOVE; + } + + /* no read possible */ + if (sz == 0) + g_main_loop_quit(helper->loop); + + /* emit lines */ + if (helper->handler_cb != NULL) { + buffer[sz] = '\0'; + split = g_strsplit(buffer, "\n", -1); + for (guint i = 0; split[i] != NULL; i++) { + if (split[i][0] == '\0') + continue; + helper->handler_cb(split[i], helper->handler_user_data); + } + } + + /* set up the source for the next read */ + fu_spawn_create_pollable_source(helper); + return G_SOURCE_REMOVE; +} + +static void +fu_spawn_create_pollable_source(FuSpawnHelper *helper) +{ + if (helper->source != NULL) + g_source_destroy(helper->source); + helper->source = + g_pollable_input_stream_create_source(G_POLLABLE_INPUT_STREAM(helper->stream), + helper->cancellable); + g_source_attach(helper->source, NULL); + g_source_set_callback(helper->source, + (GSourceFunc)fu_spawn_source_pollable_cb, + helper, + NULL); +} + +static void +fu_spawn_helper_free(FuSpawnHelper *helper) +{ + g_object_unref(helper->cancellable); + if (helper->stream != NULL) + g_object_unref(helper->stream); + if (helper->source != NULL) + g_source_destroy(helper->source); + if (helper->loop != NULL) + g_main_loop_unref(helper->loop); + if (helper->timeout_id != 0) + g_source_remove(helper->timeout_id); + g_free(helper); +} + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-function" +G_DEFINE_AUTOPTR_CLEANUP_FUNC(FuSpawnHelper, fu_spawn_helper_free) +#pragma clang diagnostic pop + +#ifndef _WIN32 +static gboolean +fu_spawn_timeout_cb(gpointer user_data) +{ + FuSpawnHelper *helper = (FuSpawnHelper *)user_data; + g_cancellable_cancel(helper->cancellable); + g_main_loop_quit(helper->loop); + helper->timeout_id = 0; + return G_SOURCE_REMOVE; +} + +static void +fu_spawn_cancelled_cb(GCancellable *cancellable, FuSpawnHelper *helper) +{ + /* just propagate */ + g_cancellable_cancel(helper->cancellable); +} +#endif + +/** + * fu_spawn_sync: + * @argv: the argument list to run + * @handler_cb: (scope call) (nullable): optional #FuSpawnOutputHandler + * @handler_user_data: (nullable): the user data to pass to @handler_cb + * @timeout_ms: a timeout in ms, or 0 for no limit + * @cancellable: (nullable): optional #GCancellable + * @error: (nullable): optional return location for an error + * + * Runs a subprocess and waits for it to exit. Any output on standard out or + * standard error will be forwarded to @handler_cb as whole lines. + * + * Returns: %TRUE for success + * + * Since: 0.9.7 + **/ +gboolean +fu_spawn_sync(const gchar *const *argv, + FuSpawnOutputHandler handler_cb, + gpointer handler_user_data, + guint timeout_ms, + GCancellable *cancellable, + GError **error) +{ + g_autoptr(FuSpawnHelper) helper = NULL; + g_autoptr(GSubprocess) subprocess = NULL; + g_autofree gchar *argv_str = NULL; +#ifndef _WIN32 + gulong cancellable_id = 0; +#endif + + g_return_val_if_fail(argv != NULL, FALSE); + g_return_val_if_fail(cancellable == NULL || G_IS_CANCELLABLE(cancellable), FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + /* create subprocess */ + argv_str = g_strjoinv(" ", (gchar **)argv); + g_debug("running '%s'", argv_str); + subprocess = + g_subprocess_newv(argv, + G_SUBPROCESS_FLAGS_STDOUT_PIPE | G_SUBPROCESS_FLAGS_STDERR_MERGE, + error); + if (subprocess == NULL) + return FALSE; + +#ifndef _WIN32 + /* watch for process to exit */ + helper = g_new0(FuSpawnHelper, 1); + helper->handler_cb = handler_cb; + helper->handler_user_data = handler_user_data; + helper->loop = g_main_loop_new(NULL, FALSE); + helper->stream = g_subprocess_get_stdout_pipe(subprocess); + + /* always create a cancellable, and connect up the parent */ + helper->cancellable = g_cancellable_new(); + if (cancellable != NULL) { + cancellable_id = g_cancellable_connect(cancellable, + G_CALLBACK(fu_spawn_cancelled_cb), + helper, + NULL); + } + + /* allow timeout */ + if (timeout_ms > 0) { + helper->timeout_id = g_timeout_add(timeout_ms, fu_spawn_timeout_cb, helper); + } + fu_spawn_create_pollable_source(helper); + g_main_loop_run(helper->loop); + g_cancellable_disconnect(cancellable, cancellable_id); +#endif + if (!g_subprocess_wait_check(subprocess, cancellable, error)) + return FALSE; +#ifndef _WIN32 + if (g_cancellable_set_error_if_cancelled(helper->cancellable, error)) + return FALSE; +#endif + + /* success */ + return TRUE; +} diff --git a/src/fu-spawn.h b/src/fu-spawn.h new file mode 100644 index 000000000..cb9992b55 --- /dev/null +++ b/src/fu-spawn.h @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2017 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +/** + * FuSpawnOutputHandler: + * @line: text data + * @user_data: user data + * + * The process spawn iteration callback. + */ +typedef void (*FuSpawnOutputHandler)(const gchar *line, gpointer user_data); + +gboolean +fu_spawn_sync(const gchar *const *argv, + FuSpawnOutputHandler handler_cb, + gpointer handler_user_data, + guint timeout_ms, + GCancellable *cancellable, + GError **error) G_GNUC_WARN_UNUSED_RESULT; diff --git a/src/meson.build b/src/meson.build index 1a5567f66..fe026ec03 100644 --- a/src/meson.build +++ b/src/meson.build @@ -122,6 +122,7 @@ fwupdoffline = executable( sources : [ 'fu-history.c', 'fu-offline.c', + 'fu-spawn.c', 'fu-security-attr.c', 'fu-util-common.c', systemd_src @@ -269,6 +270,7 @@ if get_option('tests') fu_hash, sources : [ 'fu-progressbar.c', + 'fu-spawn.c', 'fu-self-test.c', daemon_src, ], diff --git a/libfwupdplugin/tests/spawn.sh b/src/tests/spawn.sh similarity index 100% rename from libfwupdplugin/tests/spawn.sh rename to src/tests/spawn.sh