mirror of
https://git.proxmox.com/git/fwupd
synced 2025-08-13 21:35:02 +00:00
Unexport fu_common_spawn_sync()
This isn't something we want plugins to use.
This commit is contained in:
parent
425fedcca1
commit
25dccf09f7
@ -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
|
||||
|
@ -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!
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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 *
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
201
src/fu-spawn.c
Normal file
201
src/fu-spawn.c
Normal file
@ -0,0 +1,201 @@
|
||||
/*
|
||||
* Copyright (C) 2017 Richard Hughes <richard@hughsie.com>
|
||||
*
|
||||
* 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;
|
||||
}
|
26
src/fu-spawn.h
Normal file
26
src/fu-spawn.h
Normal file
@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright (C) 2017 Richard Hughes <richard@hughsie.com>
|
||||
*
|
||||
* SPDX-License-Identifier: LGPL-2.1+
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <gio/gio.h>
|
||||
|
||||
/**
|
||||
* 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;
|
@ -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,
|
||||
],
|
||||
|
Loading…
Reference in New Issue
Block a user