/
githubmirror
/
fwupd
Обзор
Документация
Войти
/
githubmirror
/
fwupd
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
1.9.30
src/fu-spawn.c
201 строка
5 KB
Richard Hughes
trivial: Add the closure user_data to the introspection
21 фев 2023, 11:15
21 фев 2023, 11:15
cd9c81c
Код
Авторство
О чём код?
/* * 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 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->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) (closure user_data) (nullable): optional #FuSpawnOutputHandler * @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 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->user_data = 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; }