mirror of
https://git.proxmox.com/git/fwupd
synced 2025-05-22 03:47:33 +00:00
5207 lines
148 KiB
C
5207 lines
148 KiB
C
/*
|
|
* Copyright (C) 2016 Richard Hughes <richard@hughsie.com>
|
|
*
|
|
* SPDX-License-Identifier: LGPL-2.1+
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include <gio/gio.h>
|
|
#include <glib-object.h>
|
|
#include <gmodule.h>
|
|
#ifdef HAVE_LIBCURL
|
|
#include <curl/curl.h>
|
|
#endif
|
|
#ifdef HAVE_GIO_UNIX
|
|
#include <gio/gunixfdlist.h>
|
|
#endif
|
|
|
|
#include <fcntl.h>
|
|
#include <string.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/types.h>
|
|
|
|
#include "fwupd-client-private.h"
|
|
#include "fwupd-client-sync.h"
|
|
#include "fwupd-common-private.h"
|
|
#include "fwupd-deprecated.h"
|
|
#include "fwupd-device-private.h"
|
|
#include "fwupd-enums.h"
|
|
#include "fwupd-error.h"
|
|
#include "fwupd-plugin-private.h"
|
|
#include "fwupd-release-private.h"
|
|
#include "fwupd-remote-private.h"
|
|
#include "fwupd-request-private.h"
|
|
#include "fwupd-security-attr-private.h"
|
|
|
|
typedef GObject *(*FwupdClientObjectNewFunc)(void);
|
|
|
|
/**
|
|
* FwupdClient:
|
|
*
|
|
* Allow client code to call the daemon methods.
|
|
*
|
|
* See also: [class@FwupdDevice]
|
|
*/
|
|
|
|
static void
|
|
fwupd_client_finalize(GObject *object);
|
|
|
|
typedef struct {
|
|
GMainContext *main_ctx;
|
|
FwupdStatus status;
|
|
gboolean tainted;
|
|
gboolean interactive;
|
|
guint percentage;
|
|
GMutex idle_mutex; /* for @idle_id and @idle_sources */
|
|
guint idle_id;
|
|
GPtrArray *idle_sources; /* element-type FwupdClientContextHelper */
|
|
gchar *daemon_version;
|
|
gchar *host_product;
|
|
gchar *host_machine_id;
|
|
gchar *host_security_id;
|
|
GMutex proxy_mutex; /* for @proxy */
|
|
GDBusProxy *proxy;
|
|
GProxyResolver *proxy_resolver;
|
|
gchar *user_agent;
|
|
#ifdef SOUP_SESSION_COMPAT
|
|
GObject *soup_session;
|
|
GModule *soup_module; /* we leak this */
|
|
#endif
|
|
} FwupdClientPrivate;
|
|
|
|
#ifdef HAVE_LIBCURL
|
|
typedef struct {
|
|
GPtrArray *urls;
|
|
CURL *curl;
|
|
curl_mime *mime;
|
|
struct curl_slist *headers;
|
|
} FwupdCurlHelper;
|
|
#endif
|
|
|
|
enum {
|
|
SIGNAL_CHANGED,
|
|
SIGNAL_STATUS_CHANGED,
|
|
SIGNAL_DEVICE_ADDED,
|
|
SIGNAL_DEVICE_REMOVED,
|
|
SIGNAL_DEVICE_CHANGED,
|
|
SIGNAL_DEVICE_REQUEST,
|
|
SIGNAL_LAST
|
|
};
|
|
|
|
enum {
|
|
PROP_0,
|
|
PROP_STATUS,
|
|
PROP_PERCENTAGE,
|
|
PROP_DAEMON_VERSION,
|
|
PROP_TAINTED,
|
|
PROP_SOUP_SESSION, /* compat ABI, do not use! */
|
|
PROP_HOST_PRODUCT,
|
|
PROP_HOST_MACHINE_ID,
|
|
PROP_HOST_SECURITY_ID,
|
|
PROP_INTERACTIVE,
|
|
PROP_LAST
|
|
};
|
|
|
|
static guint signals[SIGNAL_LAST] = {0};
|
|
|
|
G_DEFINE_TYPE_WITH_PRIVATE(FwupdClient, fwupd_client, G_TYPE_OBJECT)
|
|
#define GET_PRIVATE(o) (fwupd_client_get_instance_private(o))
|
|
|
|
#ifdef HAVE_LIBCURL_7_62_0
|
|
G_DEFINE_AUTOPTR_CLEANUP_FUNC(CURLU, curl_url_cleanup)
|
|
#endif
|
|
|
|
#ifdef HAVE_LIBCURL
|
|
static void
|
|
fwupd_client_curl_helper_free(FwupdCurlHelper *helper)
|
|
{
|
|
if (helper->curl != NULL)
|
|
curl_easy_cleanup(helper->curl);
|
|
if (helper->mime != NULL)
|
|
curl_mime_free(helper->mime);
|
|
if (helper->headers != NULL)
|
|
curl_slist_free_all(helper->headers);
|
|
if (helper->urls != NULL)
|
|
g_ptr_array_unref(helper->urls);
|
|
g_free(helper);
|
|
}
|
|
|
|
G_DEFINE_AUTOPTR_CLEANUP_FUNC(FwupdCurlHelper, fwupd_client_curl_helper_free)
|
|
#endif
|
|
|
|
typedef struct {
|
|
FwupdClient *self;
|
|
gchar *property_name;
|
|
guint signal_id;
|
|
GObject *payload;
|
|
} FwupdClientContextHelper;
|
|
|
|
static void
|
|
fwupd_client_context_helper_free(FwupdClientContextHelper *helper)
|
|
{
|
|
g_clear_object(&helper->payload);
|
|
g_object_unref(helper->self);
|
|
g_free(helper->property_name);
|
|
g_free(helper);
|
|
}
|
|
|
|
/* always executed in the main context given by priv->main_ctx */
|
|
static void
|
|
fwupd_client_context_object_notify(FwupdClient *self, const gchar *property_name)
|
|
{
|
|
FwupdClientPrivate *priv = GET_PRIVATE(self);
|
|
|
|
g_return_if_fail(g_main_context_is_owner(priv->main_ctx));
|
|
|
|
/* property */
|
|
g_object_notify(G_OBJECT(self), property_name);
|
|
|
|
/* legacy signal name */
|
|
if (g_strcmp0(property_name, "status") == 0)
|
|
g_signal_emit(self, signals[SIGNAL_STATUS_CHANGED], 0, priv->status);
|
|
}
|
|
|
|
/* emits all pending context helpers in the correct GMainContext */
|
|
static gboolean
|
|
fwupd_client_context_idle_cb(gpointer user_data)
|
|
{
|
|
FwupdClient *self = FWUPD_CLIENT(user_data);
|
|
FwupdClientPrivate *priv = GET_PRIVATE(self);
|
|
g_autoptr(GMutexLocker) locker = g_mutex_locker_new(&priv->idle_mutex);
|
|
|
|
g_assert(locker != NULL);
|
|
|
|
for (guint i = 0; i < priv->idle_sources->len; i++) {
|
|
FwupdClientContextHelper *helper = g_ptr_array_index(priv->idle_sources, i);
|
|
|
|
/* property */
|
|
if (helper->property_name != NULL)
|
|
fwupd_client_context_object_notify(self, helper->property_name);
|
|
|
|
/* payload signal */
|
|
if (helper->signal_id != 0 && helper->payload != NULL)
|
|
g_signal_emit(self, signals[helper->signal_id], 0, helper->payload);
|
|
}
|
|
|
|
/* all done */
|
|
g_ptr_array_set_size(priv->idle_sources, 0);
|
|
priv->idle_id = 0;
|
|
return G_SOURCE_REMOVE;
|
|
}
|
|
|
|
static void
|
|
fwupd_client_context_helper(FwupdClient *self, FwupdClientContextHelper *helper)
|
|
{
|
|
FwupdClientPrivate *priv = GET_PRIVATE(self);
|
|
g_autoptr(GMutexLocker) locker = g_mutex_locker_new(&priv->idle_mutex);
|
|
|
|
g_assert(locker != NULL);
|
|
|
|
/* no source already attached to the context */
|
|
if (priv->idle_id == 0) {
|
|
g_autoptr(GSource) source = g_idle_source_new();
|
|
g_source_set_callback(source, fwupd_client_context_idle_cb, self, NULL);
|
|
priv->idle_id = g_source_attach(g_steal_pointer(&source), priv->main_ctx);
|
|
}
|
|
|
|
/* run in the correct GMainContext and thread */
|
|
g_ptr_array_add(priv->idle_sources, helper);
|
|
}
|
|
|
|
/* run callback in the correct thread */
|
|
static void
|
|
fwupd_client_object_notify(FwupdClient *self, const gchar *property_name)
|
|
{
|
|
FwupdClientPrivate *priv = GET_PRIVATE(self);
|
|
FwupdClientContextHelper *helper = NULL;
|
|
|
|
/* shortcut */
|
|
if (g_main_context_is_owner(priv->main_ctx)) {
|
|
fwupd_client_context_object_notify(self, property_name);
|
|
return;
|
|
}
|
|
|
|
/* run in the correct GMainContext and thread */
|
|
helper = g_new0(FwupdClientContextHelper, 1);
|
|
helper->self = g_object_ref(self);
|
|
helper->property_name = g_strdup(property_name);
|
|
fwupd_client_context_helper(self, helper);
|
|
}
|
|
|
|
/* run callback in the correct thread */
|
|
static void
|
|
fwupd_client_signal_emit_object(FwupdClient *self, guint signal_id, GObject *payload)
|
|
{
|
|
FwupdClientPrivate *priv = GET_PRIVATE(self);
|
|
FwupdClientContextHelper *helper = NULL;
|
|
|
|
/* shortcut */
|
|
if (g_main_context_is_owner(priv->main_ctx)) {
|
|
g_signal_emit(self, signals[signal_id], 0, payload);
|
|
return;
|
|
}
|
|
|
|
/* run in the correct GMainContext and thread */
|
|
helper = g_new0(FwupdClientContextHelper, 1);
|
|
helper->self = g_object_ref(self);
|
|
helper->signal_id = signal_id;
|
|
helper->payload = g_object_ref(payload);
|
|
fwupd_client_context_helper(self, helper);
|
|
}
|
|
|
|
static void
|
|
fwupd_client_set_host_product(FwupdClient *self, const gchar *host_product)
|
|
{
|
|
FwupdClientPrivate *priv = GET_PRIVATE(self);
|
|
|
|
/* not changed */
|
|
if (g_strcmp0(priv->host_product, host_product) == 0)
|
|
return;
|
|
|
|
g_free(priv->host_product);
|
|
priv->host_product = g_strdup(host_product);
|
|
fwupd_client_object_notify(self, "host-product");
|
|
}
|
|
|
|
static void
|
|
fwupd_client_set_host_machine_id(FwupdClient *self, const gchar *host_machine_id)
|
|
{
|
|
FwupdClientPrivate *priv = GET_PRIVATE(self);
|
|
|
|
/* not changed */
|
|
if (g_strcmp0(priv->host_machine_id, host_machine_id) == 0)
|
|
return;
|
|
|
|
g_free(priv->host_machine_id);
|
|
priv->host_machine_id = g_strdup(host_machine_id);
|
|
fwupd_client_object_notify(self, "host-machine-id");
|
|
}
|
|
|
|
static void
|
|
fwupd_client_set_host_security_id(FwupdClient *self, const gchar *host_security_id)
|
|
{
|
|
FwupdClientPrivate *priv = GET_PRIVATE(self);
|
|
|
|
/* not changed */
|
|
if (g_strcmp0(priv->host_security_id, host_security_id) == 0)
|
|
return;
|
|
|
|
g_free(priv->host_security_id);
|
|
priv->host_security_id = g_strdup(host_security_id);
|
|
fwupd_client_object_notify(self, "host-security-id");
|
|
}
|
|
|
|
static void
|
|
fwupd_client_set_daemon_version(FwupdClient *self, const gchar *daemon_version)
|
|
{
|
|
FwupdClientPrivate *priv = GET_PRIVATE(self);
|
|
|
|
/* not changed */
|
|
if (g_strcmp0(priv->daemon_version, daemon_version) == 0)
|
|
return;
|
|
|
|
g_free(priv->daemon_version);
|
|
priv->daemon_version = g_strdup(daemon_version);
|
|
fwupd_client_object_notify(self, "daemon-version");
|
|
}
|
|
|
|
static void
|
|
fwupd_client_set_status(FwupdClient *self, FwupdStatus status)
|
|
{
|
|
FwupdClientPrivate *priv = GET_PRIVATE(self);
|
|
if (priv->status == status)
|
|
return;
|
|
priv->status = status;
|
|
g_debug("Emitting ::status-changed() [%s]", fwupd_status_to_string(priv->status));
|
|
fwupd_client_object_notify(self, "status");
|
|
}
|
|
|
|
static void
|
|
fwupd_client_set_percentage(FwupdClient *self, guint percentage)
|
|
{
|
|
FwupdClientPrivate *priv = GET_PRIVATE(self);
|
|
if (priv->percentage == percentage)
|
|
return;
|
|
priv->percentage = percentage;
|
|
fwupd_client_object_notify(self, "percentage");
|
|
}
|
|
|
|
static void
|
|
fwupd_client_properties_changed_cb(GDBusProxy *proxy,
|
|
GVariant *changed_properties,
|
|
GStrv invalidated_properties,
|
|
FwupdClient *self)
|
|
{
|
|
FwupdClientPrivate *priv = GET_PRIVATE(self);
|
|
g_autoptr(GVariantDict) dict = NULL;
|
|
|
|
/* print to the console */
|
|
dict = g_variant_dict_new(changed_properties);
|
|
if (g_variant_dict_contains(dict, "Status")) {
|
|
g_autoptr(GVariant) val = NULL;
|
|
val = g_dbus_proxy_get_cached_property(proxy, "Status");
|
|
if (val != NULL)
|
|
fwupd_client_set_status(self, g_variant_get_uint32(val));
|
|
}
|
|
if (g_variant_dict_contains(dict, "Tainted")) {
|
|
g_autoptr(GVariant) val = NULL;
|
|
val = g_dbus_proxy_get_cached_property(proxy, "Tainted");
|
|
if (val != NULL) {
|
|
priv->tainted = g_variant_get_boolean(val);
|
|
fwupd_client_object_notify(self, "tainted");
|
|
}
|
|
}
|
|
if (g_variant_dict_contains(dict, "Interactive")) {
|
|
g_autoptr(GVariant) val = NULL;
|
|
val = g_dbus_proxy_get_cached_property(proxy, "Interactive");
|
|
if (val != NULL) {
|
|
priv->interactive = g_variant_get_boolean(val);
|
|
fwupd_client_object_notify(self, "interactive");
|
|
}
|
|
}
|
|
if (g_variant_dict_contains(dict, "Percentage")) {
|
|
g_autoptr(GVariant) val = NULL;
|
|
val = g_dbus_proxy_get_cached_property(proxy, "Percentage");
|
|
if (val != NULL)
|
|
fwupd_client_set_percentage(self, g_variant_get_uint32(val));
|
|
}
|
|
if (g_variant_dict_contains(dict, "DaemonVersion")) {
|
|
g_autoptr(GVariant) val = NULL;
|
|
val = g_dbus_proxy_get_cached_property(proxy, "DaemonVersion");
|
|
if (val != NULL)
|
|
fwupd_client_set_daemon_version(self, g_variant_get_string(val, NULL));
|
|
}
|
|
if (g_variant_dict_contains(dict, "HostProduct")) {
|
|
g_autoptr(GVariant) val = NULL;
|
|
val = g_dbus_proxy_get_cached_property(proxy, "HostProduct");
|
|
if (val != NULL)
|
|
fwupd_client_set_host_product(self, g_variant_get_string(val, NULL));
|
|
}
|
|
if (g_variant_dict_contains(dict, "HostMachineId")) {
|
|
g_autoptr(GVariant) val = NULL;
|
|
val = g_dbus_proxy_get_cached_property(proxy, "HostMachineId");
|
|
if (val != NULL)
|
|
fwupd_client_set_host_machine_id(self, g_variant_get_string(val, NULL));
|
|
}
|
|
if (g_variant_dict_contains(dict, "HostSecurityId")) {
|
|
g_autoptr(GVariant) val = NULL;
|
|
val = g_dbus_proxy_get_cached_property(proxy, "HostSecurityId");
|
|
if (val != NULL)
|
|
fwupd_client_set_host_security_id(self, g_variant_get_string(val, NULL));
|
|
}
|
|
}
|
|
|
|
static void
|
|
fwupd_client_signal_cb(GDBusProxy *proxy,
|
|
const gchar *sender_name,
|
|
const gchar *signal_name,
|
|
GVariant *parameters,
|
|
FwupdClient *self)
|
|
{
|
|
g_autoptr(FwupdDevice) dev = NULL;
|
|
if (g_strcmp0(signal_name, "Changed") == 0) {
|
|
g_debug("Emitting ::changed()");
|
|
g_signal_emit(self, signals[SIGNAL_CHANGED], 0);
|
|
return;
|
|
}
|
|
if (g_strcmp0(signal_name, "DeviceAdded") == 0) {
|
|
dev = fwupd_device_from_variant(parameters);
|
|
g_debug("Emitting ::device-added(%s)", fwupd_device_get_id(dev));
|
|
fwupd_client_signal_emit_object(self, SIGNAL_DEVICE_ADDED, G_OBJECT(dev));
|
|
return;
|
|
}
|
|
if (g_strcmp0(signal_name, "DeviceRemoved") == 0) {
|
|
dev = fwupd_device_from_variant(parameters);
|
|
g_debug("Emitting ::device-removed(%s)", fwupd_device_get_id(dev));
|
|
fwupd_client_signal_emit_object(self, SIGNAL_DEVICE_REMOVED, G_OBJECT(dev));
|
|
return;
|
|
}
|
|
if (g_strcmp0(signal_name, "DeviceChanged") == 0) {
|
|
dev = fwupd_device_from_variant(parameters);
|
|
g_debug("Emitting ::device-changed(%s)", fwupd_device_get_id(dev));
|
|
fwupd_client_signal_emit_object(self, SIGNAL_DEVICE_CHANGED, G_OBJECT(dev));
|
|
return;
|
|
}
|
|
if (g_strcmp0(signal_name, "DeviceRequest") == 0) {
|
|
g_autoptr(FwupdRequest) req = fwupd_request_from_variant(parameters);
|
|
g_debug("Emitting ::device-request(%s)", fwupd_request_get_id(req));
|
|
fwupd_client_signal_emit_object(self, SIGNAL_DEVICE_REQUEST, G_OBJECT(req));
|
|
return;
|
|
}
|
|
g_debug("Unknown signal name '%s' from %s", signal_name, sender_name);
|
|
}
|
|
|
|
/**
|
|
* fwupd_client_get_main_context:
|
|
* @self: a #FwupdClient
|
|
*
|
|
* Gets the internal #GMainContext to use for synchronous methods.
|
|
* By default the value is set a new #GMainContext.
|
|
*
|
|
* Returns: (transfer full): the main context
|
|
*
|
|
* Since: 1.5.3
|
|
**/
|
|
GMainContext *
|
|
fwupd_client_get_main_context(FwupdClient *self)
|
|
{
|
|
FwupdClientPrivate *priv = GET_PRIVATE(self);
|
|
if (priv->main_ctx != NULL)
|
|
return g_main_context_ref(priv->main_ctx);
|
|
return g_main_context_new();
|
|
}
|
|
|
|
/**
|
|
* fwupd_client_set_main_context:
|
|
* @self: a #FwupdClient
|
|
* @main_ctx: (nullable): the global default main context to use
|
|
*
|
|
* Sets the internal main context to use for returning progress signals.
|
|
*
|
|
* Since: 1.5.3
|
|
**/
|
|
void
|
|
fwupd_client_set_main_context(FwupdClient *self, GMainContext *main_ctx)
|
|
{
|
|
FwupdClientPrivate *priv = GET_PRIVATE(self);
|
|
if (main_ctx == priv->main_ctx)
|
|
return;
|
|
g_clear_pointer(&priv->main_ctx, g_main_context_unref);
|
|
if (main_ctx != NULL)
|
|
priv->main_ctx = g_main_context_ref(main_ctx);
|
|
}
|
|
|
|
/**
|
|
* fwupd_client_ensure_networking:
|
|
* @self: a #FwupdClient
|
|
* @error: (nullable): optional return location for an error
|
|
*
|
|
* Sets up the client networking support ready for use. Most other download and
|
|
* upload methods call this automatically, and do you only need to call this if
|
|
* the session is being used outside the [class@FwupdClient].
|
|
*
|
|
* Returns: %TRUE for success
|
|
*
|
|
* Since: 1.4.5
|
|
**/
|
|
gboolean
|
|
fwupd_client_ensure_networking(FwupdClient *self, GError **error)
|
|
{
|
|
FwupdClientPrivate *priv = GET_PRIVATE(self);
|
|
|
|
g_return_val_if_fail(FWUPD_IS_CLIENT(self), FALSE);
|
|
g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
|
|
|
|
/* check the user agent is sane */
|
|
if (priv->user_agent == NULL) {
|
|
g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "user agent unset");
|
|
return FALSE;
|
|
}
|
|
if (g_strstr_len(priv->user_agent, -1, "fwupd/") == NULL) {
|
|
g_set_error_literal(error,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_INTERNAL,
|
|
"user agent unsuitable; fwupd version required");
|
|
return FALSE;
|
|
}
|
|
#ifdef SOUP_SESSION_COMPAT
|
|
if (priv->soup_session != NULL) {
|
|
g_object_set(priv->soup_session, "user-agent", priv->user_agent, NULL);
|
|
}
|
|
#endif
|
|
return TRUE;
|
|
}
|
|
|
|
#ifdef HAVE_LIBCURL
|
|
static int
|
|
fwupd_client_progress_callback_cb(void *clientp,
|
|
curl_off_t dltotal,
|
|
curl_off_t dlnow,
|
|
curl_off_t ultotal,
|
|
curl_off_t ulnow)
|
|
{
|
|
FwupdClient *self = FWUPD_CLIENT(clientp);
|
|
|
|
/* calculate percentage */
|
|
if (dltotal > 0 && dlnow >= 0 && dlnow <= dltotal) {
|
|
guint percentage = (guint)((100 * dlnow) / dltotal);
|
|
g_debug("download progress: %u%%", percentage);
|
|
fwupd_client_set_percentage(self, percentage);
|
|
} else if (ultotal > 0 && ulnow >= 0 && ulnow <= ultotal) {
|
|
guint percentage = (guint)((100 * ulnow) / ultotal);
|
|
g_debug("upload progress: %u%%", percentage);
|
|
fwupd_client_set_percentage(self, percentage);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
fwupd_client_curl_helper_set_proxy(FwupdClient *self, FwupdCurlHelper *helper, const gchar *url)
|
|
{
|
|
FwupdClientPrivate *priv = GET_PRIVATE(self);
|
|
g_auto(GStrv) proxies = NULL;
|
|
g_autoptr(GError) error_local = NULL;
|
|
|
|
proxies = g_proxy_resolver_lookup(priv->proxy_resolver, url, NULL, &error_local);
|
|
if (proxies == NULL) {
|
|
g_warning("failed to lookup proxy for %s: %s", url, error_local->message);
|
|
return;
|
|
}
|
|
if (g_strcmp0(proxies[0], "direct://") != 0)
|
|
curl_easy_setopt(helper->curl, CURLOPT_PROXY, proxies[0]);
|
|
}
|
|
|
|
static FwupdCurlHelper *
|
|
fwupd_client_curl_new(FwupdClient *self, GError **error)
|
|
{
|
|
FwupdClientPrivate *priv = GET_PRIVATE(self);
|
|
g_autoptr(FwupdCurlHelper) helper = g_new0(FwupdCurlHelper, 1);
|
|
|
|
/* check the user agent is sane */
|
|
if (!fwupd_client_ensure_networking(self, error))
|
|
return NULL;
|
|
|
|
/* create the session */
|
|
helper->curl = curl_easy_init();
|
|
if (helper->curl == NULL) {
|
|
g_set_error_literal(error,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_INTERNAL,
|
|
"failed to setup networking");
|
|
return NULL;
|
|
}
|
|
if (g_getenv("FWUPD_CURL_VERBOSE") != NULL)
|
|
curl_easy_setopt(helper->curl, CURLOPT_VERBOSE, 1L);
|
|
curl_easy_setopt(helper->curl, CURLOPT_XFERINFOFUNCTION, fwupd_client_progress_callback_cb);
|
|
curl_easy_setopt(helper->curl, CURLOPT_XFERINFODATA, self);
|
|
curl_easy_setopt(helper->curl, CURLOPT_USERAGENT, priv->user_agent);
|
|
curl_easy_setopt(helper->curl, CURLOPT_CONNECTTIMEOUT, 60L);
|
|
curl_easy_setopt(helper->curl, CURLOPT_NOPROGRESS, 0L);
|
|
curl_easy_setopt(helper->curl, CURLOPT_FOLLOWLOCATION, 1L);
|
|
curl_easy_setopt(helper->curl, CURLOPT_MAXREDIRS, 5L);
|
|
|
|
/* relax the SSL checks for broken corporate proxies */
|
|
if (g_getenv("DISABLE_SSL_STRICT") != NULL)
|
|
curl_easy_setopt(helper->curl, CURLOPT_SSL_VERIFYPEER, 0L);
|
|
|
|
/* this disables the double-compression of the firmware.xml.gz file */
|
|
curl_easy_setopt(helper->curl, CURLOPT_HTTP_CONTENT_DECODING, 0L);
|
|
return g_steal_pointer(&helper);
|
|
}
|
|
#endif
|
|
|
|
static void
|
|
fwupd_client_connect_get_proxy_cb(GObject *source, GAsyncResult *res, gpointer user_data)
|
|
{
|
|
g_autoptr(GTask) task = G_TASK(user_data);
|
|
FwupdClient *self = g_task_get_source_object(task);
|
|
FwupdClientPrivate *priv = GET_PRIVATE(self);
|
|
g_autoptr(GDBusProxy) proxy = NULL;
|
|
g_autoptr(GError) error = NULL;
|
|
g_autoptr(GVariant) val = NULL;
|
|
g_autoptr(GVariant) val2 = NULL;
|
|
g_autoptr(GVariant) val3 = NULL;
|
|
g_autoptr(GVariant) val4 = NULL;
|
|
g_autoptr(GVariant) val5 = NULL;
|
|
g_autoptr(GVariant) val6 = NULL;
|
|
g_autoptr(GVariant) val7 = NULL;
|
|
g_autoptr(GMutexLocker) locker = NULL;
|
|
|
|
proxy = g_dbus_proxy_new_finish(res, &error);
|
|
if (proxy == NULL) {
|
|
g_task_return_error(task, g_steal_pointer(&error));
|
|
return;
|
|
}
|
|
|
|
/* another thread did this for us */
|
|
locker = g_mutex_locker_new(&priv->proxy_mutex);
|
|
if (priv->proxy != NULL) {
|
|
g_task_return_boolean(task, TRUE);
|
|
return;
|
|
}
|
|
priv->proxy = g_steal_pointer(&proxy);
|
|
|
|
/* connect signals, etc. */
|
|
g_signal_connect(priv->proxy,
|
|
"g-properties-changed",
|
|
G_CALLBACK(fwupd_client_properties_changed_cb),
|
|
self);
|
|
g_signal_connect(priv->proxy, "g-signal", G_CALLBACK(fwupd_client_signal_cb), self);
|
|
val = g_dbus_proxy_get_cached_property(priv->proxy, "DaemonVersion");
|
|
if (val != NULL)
|
|
fwupd_client_set_daemon_version(self, g_variant_get_string(val, NULL));
|
|
val2 = g_dbus_proxy_get_cached_property(priv->proxy, "Tainted");
|
|
if (val2 != NULL)
|
|
priv->tainted = g_variant_get_boolean(val2);
|
|
val3 = g_dbus_proxy_get_cached_property(priv->proxy, "Status");
|
|
if (val3 != NULL)
|
|
fwupd_client_set_status(self, g_variant_get_uint32(val3));
|
|
val4 = g_dbus_proxy_get_cached_property(priv->proxy, "Interactive");
|
|
if (val4 != NULL)
|
|
priv->interactive = g_variant_get_boolean(val4);
|
|
val5 = g_dbus_proxy_get_cached_property(priv->proxy, "HostProduct");
|
|
if (val5 != NULL)
|
|
fwupd_client_set_host_product(self, g_variant_get_string(val5, NULL));
|
|
val6 = g_dbus_proxy_get_cached_property(priv->proxy, "HostMachineId");
|
|
if (val6 != NULL)
|
|
fwupd_client_set_host_machine_id(self, g_variant_get_string(val6, NULL));
|
|
val7 = g_dbus_proxy_get_cached_property(priv->proxy, "HostSecurityId");
|
|
if (val7 != NULL)
|
|
fwupd_client_set_host_security_id(self, g_variant_get_string(val7, NULL));
|
|
|
|
/* success */
|
|
g_task_return_boolean(task, TRUE);
|
|
}
|
|
|
|
/**
|
|
* fwupd_client_connect_async:
|
|
* @self: a #FwupdClient
|
|
* @cancellable: (nullable): optional #GCancellable
|
|
* @callback: the function to run on completion
|
|
* @callback_data: the data to pass to @callback
|
|
*
|
|
* Sets up the client ready for use. This is probably the first method you call
|
|
* when wanting to use libfwupd in an asynchronous manner.
|
|
*
|
|
* Other methods such as fwupd_client_get_devices_async() should only be called
|
|
* after fwupd_client_connect_finish() has been called without an error.
|
|
*
|
|
* Since: 1.5.0
|
|
**/
|
|
void
|
|
fwupd_client_connect_async(FwupdClient *self,
|
|
GCancellable *cancellable,
|
|
GAsyncReadyCallback callback,
|
|
gpointer callback_data)
|
|
{
|
|
FwupdClientPrivate *priv = GET_PRIVATE(self);
|
|
g_autoptr(GTask) task = g_task_new(self, cancellable, callback, callback_data);
|
|
g_autoptr(GMutexLocker) locker = g_mutex_locker_new(&priv->proxy_mutex);
|
|
|
|
g_return_if_fail(FWUPD_IS_CLIENT(self));
|
|
g_return_if_fail(cancellable == NULL || G_IS_CANCELLABLE(cancellable));
|
|
|
|
g_assert(locker != NULL);
|
|
|
|
/* nothing to do */
|
|
if (priv->proxy != NULL) {
|
|
g_task_return_boolean(task, TRUE);
|
|
return;
|
|
}
|
|
|
|
g_dbus_proxy_new_for_bus(G_BUS_TYPE_SYSTEM,
|
|
G_DBUS_PROXY_FLAGS_NONE,
|
|
NULL,
|
|
FWUPD_DBUS_SERVICE,
|
|
FWUPD_DBUS_PATH,
|
|
FWUPD_DBUS_INTERFACE,
|
|
cancellable,
|
|
fwupd_client_connect_get_proxy_cb,
|
|
g_steal_pointer(&task));
|
|
}
|
|
|
|
/**
|
|
* fwupd_client_connect_finish:
|
|
* @self: a #FwupdClient
|
|
* @res: the asynchronous result
|
|
* @error: (nullable): optional return location for an error
|
|
*
|
|
* Gets the result of [method@Client.connect_async].
|
|
*
|
|
* Returns: %TRUE for success
|
|
*
|
|
* Since: 1.5.0
|
|
**/
|
|
gboolean
|
|
fwupd_client_connect_finish(FwupdClient *self, GAsyncResult *res, GError **error)
|
|
{
|
|
g_return_val_if_fail(FWUPD_IS_CLIENT(self), FALSE);
|
|
g_return_val_if_fail(g_task_is_valid(res, self), FALSE);
|
|
g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
|
|
return g_task_propagate_boolean(G_TASK(res), error);
|
|
}
|
|
|
|
static void
|
|
fwupd_client_fixup_dbus_error(GError *error)
|
|
{
|
|
g_autofree gchar *name = NULL;
|
|
|
|
g_return_if_fail(error != NULL);
|
|
|
|
/* is a remote error? */
|
|
if (!g_dbus_error_is_remote_error(error))
|
|
return;
|
|
|
|
/* parse the remote error */
|
|
name = g_dbus_error_get_remote_error(error);
|
|
if (g_str_has_prefix(name, FWUPD_DBUS_INTERFACE)) {
|
|
error->domain = FWUPD_ERROR;
|
|
error->code = fwupd_error_from_string(name);
|
|
} else if (g_error_matches(error, G_DBUS_ERROR, G_DBUS_ERROR_SERVICE_UNKNOWN)) {
|
|
error->domain = FWUPD_ERROR;
|
|
error->code = FWUPD_ERROR_NOT_SUPPORTED;
|
|
} else if (g_error_matches(error, G_IO_ERROR, G_IO_ERROR_DBUS_ERROR)) {
|
|
error->domain = FWUPD_ERROR;
|
|
error->code = FWUPD_ERROR_NOT_SUPPORTED;
|
|
} else {
|
|
error->domain = FWUPD_ERROR;
|
|
error->code = FWUPD_ERROR_INTERNAL;
|
|
}
|
|
g_dbus_error_strip_remote_error(error);
|
|
}
|
|
|
|
static void
|
|
fwupd_client_get_host_security_attrs_cb(GObject *source, GAsyncResult *res, gpointer user_data)
|
|
{
|
|
g_autoptr(GTask) task = G_TASK(user_data);
|
|
g_autoptr(GError) error = NULL;
|
|
g_autoptr(GVariant) val = NULL;
|
|
|
|
val = g_dbus_proxy_call_finish(G_DBUS_PROXY(source), res, &error);
|
|
if (val == NULL) {
|
|
fwupd_client_fixup_dbus_error(error);
|
|
g_task_return_error(task, g_steal_pointer(&error));
|
|
return;
|
|
}
|
|
|
|
/* success */
|
|
g_task_return_pointer(task,
|
|
fwupd_security_attr_array_from_variant(val),
|
|
(GDestroyNotify)g_ptr_array_unref);
|
|
}
|
|
|
|
/**
|
|
* fwupd_client_get_host_security_attrs_async:
|
|
* @self: a #FwupdClient
|
|
* @cancellable: (nullable): optional #GCancellable
|
|
* @callback: the function to run on completion
|
|
* @callback_data: the data to pass to @callback
|
|
*
|
|
* Gets all the host security attributes from the daemon.
|
|
*
|
|
* You must have called [method@Client.connect_async] on @self before using
|
|
* this method.
|
|
*
|
|
* Since: 1.5.0
|
|
**/
|
|
void
|
|
fwupd_client_get_host_security_attrs_async(FwupdClient *self,
|
|
GCancellable *cancellable,
|
|
GAsyncReadyCallback callback,
|
|
gpointer callback_data)
|
|
{
|
|
FwupdClientPrivate *priv = GET_PRIVATE(self);
|
|
g_autoptr(GTask) task = NULL;
|
|
|
|
g_return_if_fail(FWUPD_IS_CLIENT(self));
|
|
g_return_if_fail(cancellable == NULL || G_IS_CANCELLABLE(cancellable));
|
|
g_return_if_fail(priv->proxy != NULL);
|
|
|
|
/* call into daemon */
|
|
task = g_task_new(self, cancellable, callback, callback_data);
|
|
g_dbus_proxy_call(priv->proxy,
|
|
"GetHostSecurityAttrs",
|
|
NULL,
|
|
G_DBUS_CALL_FLAGS_NONE,
|
|
-1,
|
|
cancellable,
|
|
fwupd_client_get_host_security_attrs_cb,
|
|
g_steal_pointer(&task));
|
|
}
|
|
|
|
/**
|
|
* fwupd_client_get_host_security_attrs_finish:
|
|
* @self: a #FwupdClient
|
|
* @res: the asynchronous result
|
|
* @error: (nullable): optional return location for an error
|
|
*
|
|
* Gets the result of fwupd_client_get_host_security_attrs_async().
|
|
*
|
|
* Returns: (element-type FwupdSecurityAttr) (transfer container): attributes
|
|
*
|
|
* Since: 1.5.0
|
|
**/
|
|
GPtrArray *
|
|
fwupd_client_get_host_security_attrs_finish(FwupdClient *self, GAsyncResult *res, GError **error)
|
|
{
|
|
g_return_val_if_fail(FWUPD_IS_CLIENT(self), NULL);
|
|
g_return_val_if_fail(g_task_is_valid(res, self), NULL);
|
|
g_return_val_if_fail(error == NULL || *error == NULL, NULL);
|
|
return g_task_propagate_pointer(G_TASK(res), error);
|
|
}
|
|
|
|
static GHashTable *
|
|
fwupd_report_metadata_hash_from_variant(GVariant *value)
|
|
{
|
|
GHashTable *hash;
|
|
gsize sz;
|
|
g_autoptr(GVariant) untuple = NULL;
|
|
|
|
hash = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
|
|
untuple = g_variant_get_child_value(value, 0);
|
|
sz = g_variant_n_children(untuple);
|
|
for (guint i = 0; i < sz; i++) {
|
|
g_autoptr(GVariant) data = NULL;
|
|
const gchar *key = NULL;
|
|
const gchar *val = NULL;
|
|
data = g_variant_get_child_value(untuple, i);
|
|
g_variant_get(data, "{&s&s}", &key, &val);
|
|
g_hash_table_insert(hash, g_strdup(key), g_strdup(val));
|
|
}
|
|
return hash;
|
|
}
|
|
|
|
static void
|
|
fwupd_client_get_report_metadata_cb(GObject *source, GAsyncResult *res, gpointer user_data)
|
|
{
|
|
g_autoptr(GTask) task = G_TASK(user_data);
|
|
g_autoptr(GError) error = NULL;
|
|
g_autoptr(GVariant) val = NULL;
|
|
|
|
val = g_dbus_proxy_call_finish(G_DBUS_PROXY(source), res, &error);
|
|
if (val == NULL) {
|
|
fwupd_client_fixup_dbus_error(error);
|
|
g_task_return_error(task, g_steal_pointer(&error));
|
|
return;
|
|
}
|
|
|
|
/* success */
|
|
g_task_return_pointer(task,
|
|
fwupd_report_metadata_hash_from_variant(val),
|
|
(GDestroyNotify)g_hash_table_unref);
|
|
}
|
|
|
|
/**
|
|
* fwupd_client_get_report_metadata_async:
|
|
* @self: a #FwupdClient
|
|
* @cancellable: (nullable): optional #GCancellable
|
|
* @callback: the function to run on completion
|
|
* @callback_data: the data to pass to @callback
|
|
*
|
|
* Gets all the report metadata from the daemon.
|
|
*
|
|
* You must have called [method@Client.connect_async] on @self before using
|
|
* this method.
|
|
*
|
|
* Since: 1.5.0
|
|
**/
|
|
void
|
|
fwupd_client_get_report_metadata_async(FwupdClient *self,
|
|
GCancellable *cancellable,
|
|
GAsyncReadyCallback callback,
|
|
gpointer callback_data)
|
|
{
|
|
FwupdClientPrivate *priv = GET_PRIVATE(self);
|
|
g_autoptr(GTask) task = NULL;
|
|
|
|
g_return_if_fail(FWUPD_IS_CLIENT(self));
|
|
g_return_if_fail(cancellable == NULL || G_IS_CANCELLABLE(cancellable));
|
|
g_return_if_fail(priv->proxy != NULL);
|
|
|
|
/* call into daemon */
|
|
task = g_task_new(self, cancellable, callback, callback_data);
|
|
g_dbus_proxy_call(priv->proxy,
|
|
"GetReportMetadata",
|
|
NULL,
|
|
G_DBUS_CALL_FLAGS_NONE,
|
|
-1,
|
|
cancellable,
|
|
fwupd_client_get_report_metadata_cb,
|
|
g_steal_pointer(&task));
|
|
}
|
|
|
|
/**
|
|
* fwupd_client_get_report_metadata_finish:
|
|
* @self: a #FwupdClient
|
|
* @res: the asynchronous result
|
|
* @error: (nullable): optional return location for an error
|
|
*
|
|
* Gets the result of fwupd_client_get_report_metadata_async().
|
|
*
|
|
* Returns: (transfer container): attributes
|
|
*
|
|
* Since: 1.5.0
|
|
**/
|
|
GHashTable *
|
|
fwupd_client_get_report_metadata_finish(FwupdClient *self, GAsyncResult *res, GError **error)
|
|
{
|
|
g_return_val_if_fail(FWUPD_IS_CLIENT(self), NULL);
|
|
g_return_val_if_fail(g_task_is_valid(res, self), NULL);
|
|
g_return_val_if_fail(error == NULL || *error == NULL, NULL);
|
|
return g_task_propagate_pointer(G_TASK(res), error);
|
|
}
|
|
|
|
static void
|
|
fwupd_client_get_devices_cb(GObject *source, GAsyncResult *res, gpointer user_data)
|
|
{
|
|
g_autoptr(GTask) task = G_TASK(user_data);
|
|
g_autoptr(GError) error = NULL;
|
|
g_autoptr(GVariant) val = NULL;
|
|
|
|
val = g_dbus_proxy_call_finish(G_DBUS_PROXY(source), res, &error);
|
|
if (val == NULL) {
|
|
fwupd_client_fixup_dbus_error(error);
|
|
g_task_return_error(task, g_steal_pointer(&error));
|
|
return;
|
|
}
|
|
|
|
/* success */
|
|
g_task_return_pointer(task,
|
|
fwupd_device_array_from_variant(val),
|
|
(GDestroyNotify)g_ptr_array_unref);
|
|
}
|
|
|
|
/**
|
|
* fwupd_client_get_devices_async:
|
|
* @self: a #FwupdClient
|
|
* @cancellable: (nullable): optional #GCancellable
|
|
* @callback: the function to run on completion
|
|
* @callback_data: the data to pass to @callback
|
|
*
|
|
* Gets all the devices registered with the daemon.
|
|
*
|
|
* You must have called [method@Client.connect_async] on @self before using
|
|
* this method.
|
|
*
|
|
* Since: 1.5.0
|
|
**/
|
|
void
|
|
fwupd_client_get_devices_async(FwupdClient *self,
|
|
GCancellable *cancellable,
|
|
GAsyncReadyCallback callback,
|
|
gpointer callback_data)
|
|
{
|
|
FwupdClientPrivate *priv = GET_PRIVATE(self);
|
|
g_autoptr(GTask) task = NULL;
|
|
|
|
g_return_if_fail(FWUPD_IS_CLIENT(self));
|
|
g_return_if_fail(cancellable == NULL || G_IS_CANCELLABLE(cancellable));
|
|
g_return_if_fail(priv->proxy != NULL);
|
|
|
|
/* call into daemon */
|
|
task = g_task_new(self, cancellable, callback, callback_data);
|
|
g_dbus_proxy_call(priv->proxy,
|
|
"GetDevices",
|
|
NULL,
|
|
G_DBUS_CALL_FLAGS_NONE,
|
|
-1,
|
|
cancellable,
|
|
fwupd_client_get_devices_cb,
|
|
g_steal_pointer(&task));
|
|
}
|
|
|
|
/**
|
|
* fwupd_client_get_devices_finish:
|
|
* @self: a #FwupdClient
|
|
* @res: the asynchronous result
|
|
* @error: (nullable): optional return location for an error
|
|
*
|
|
* Gets the result of fwupd_client_get_devices_async().
|
|
*
|
|
* Returns: (element-type FwupdDevice) (transfer container): results
|
|
*
|
|
* Since: 1.5.0
|
|
**/
|
|
GPtrArray *
|
|
fwupd_client_get_devices_finish(FwupdClient *self, GAsyncResult *res, GError **error)
|
|
{
|
|
g_return_val_if_fail(FWUPD_IS_CLIENT(self), NULL);
|
|
g_return_val_if_fail(g_task_is_valid(res, self), NULL);
|
|
g_return_val_if_fail(error == NULL || *error == NULL, NULL);
|
|
return g_task_propagate_pointer(G_TASK(res), error);
|
|
}
|
|
|
|
static void
|
|
fwupd_client_get_plugins_cb(GObject *source, GAsyncResult *res, gpointer user_data)
|
|
{
|
|
g_autoptr(GTask) task = G_TASK(user_data);
|
|
g_autoptr(GError) error = NULL;
|
|
g_autoptr(GVariant) val = NULL;
|
|
|
|
val = g_dbus_proxy_call_finish(G_DBUS_PROXY(source), res, &error);
|
|
if (val == NULL) {
|
|
fwupd_client_fixup_dbus_error(error);
|
|
g_task_return_error(task, g_steal_pointer(&error));
|
|
return;
|
|
}
|
|
|
|
/* success */
|
|
g_task_return_pointer(task,
|
|
fwupd_plugin_array_from_variant(val),
|
|
(GDestroyNotify)g_ptr_array_unref);
|
|
}
|
|
|
|
/**
|
|
* fwupd_client_get_plugins_async:
|
|
* @self: a #FwupdClient
|
|
* @cancellable: (nullable): optional #GCancellable
|
|
* @callback: the function to run on completion
|
|
* @callback_data: the data to pass to @callback
|
|
*
|
|
* Gets all the plugins being used by the daemon.
|
|
*
|
|
* You must have called [method@Client.connect_async] on @self before using
|
|
* this method.
|
|
*
|
|
* Since: 1.5.0
|
|
**/
|
|
void
|
|
fwupd_client_get_plugins_async(FwupdClient *self,
|
|
GCancellable *cancellable,
|
|
GAsyncReadyCallback callback,
|
|
gpointer callback_data)
|
|
{
|
|
FwupdClientPrivate *priv = GET_PRIVATE(self);
|
|
g_autoptr(GTask) task = NULL;
|
|
|
|
g_return_if_fail(FWUPD_IS_CLIENT(self));
|
|
g_return_if_fail(cancellable == NULL || G_IS_CANCELLABLE(cancellable));
|
|
g_return_if_fail(priv->proxy != NULL);
|
|
|
|
/* call into daemon */
|
|
task = g_task_new(self, cancellable, callback, callback_data);
|
|
g_dbus_proxy_call(priv->proxy,
|
|
"GetPlugins",
|
|
NULL,
|
|
G_DBUS_CALL_FLAGS_NONE,
|
|
-1,
|
|
cancellable,
|
|
fwupd_client_get_plugins_cb,
|
|
g_steal_pointer(&task));
|
|
}
|
|
|
|
/**
|
|
* fwupd_client_get_plugins_finish:
|
|
* @self: a #FwupdClient
|
|
* @res: the asynchronous result
|
|
* @error: (nullable): optional return location for an error
|
|
*
|
|
* Gets the result of fwupd_client_get_plugins_async().
|
|
*
|
|
* Returns: (element-type FwupdDevice) (transfer container): results
|
|
*
|
|
* Since: 1.5.0
|
|
**/
|
|
GPtrArray *
|
|
fwupd_client_get_plugins_finish(FwupdClient *self, GAsyncResult *res, GError **error)
|
|
{
|
|
g_return_val_if_fail(FWUPD_IS_CLIENT(self), NULL);
|
|
g_return_val_if_fail(g_task_is_valid(res, self), NULL);
|
|
g_return_val_if_fail(error == NULL || *error == NULL, NULL);
|
|
return g_task_propagate_pointer(G_TASK(res), error);
|
|
}
|
|
|
|
static void
|
|
fwupd_client_get_history_cb(GObject *source, GAsyncResult *res, gpointer user_data)
|
|
{
|
|
g_autoptr(GTask) task = G_TASK(user_data);
|
|
g_autoptr(GError) error = NULL;
|
|
g_autoptr(GVariant) val = NULL;
|
|
|
|
val = g_dbus_proxy_call_finish(G_DBUS_PROXY(source), res, &error);
|
|
if (val == NULL) {
|
|
fwupd_client_fixup_dbus_error(error);
|
|
g_task_return_error(task, g_steal_pointer(&error));
|
|
return;
|
|
}
|
|
|
|
/* success */
|
|
g_task_return_pointer(task,
|
|
fwupd_device_array_from_variant(val),
|
|
(GDestroyNotify)g_ptr_array_unref);
|
|
}
|
|
|
|
/**
|
|
* fwupd_client_get_history_async:
|
|
* @self: a #FwupdClient
|
|
* @cancellable: (nullable): optional #GCancellable
|
|
* @callback: the function to run on completion
|
|
* @callback_data: the data to pass to @callback
|
|
*
|
|
* Gets all the history.
|
|
*
|
|
* You must have called [method@Client.connect_async] on @self before using
|
|
* this method.
|
|
*
|
|
* Since: 1.5.0
|
|
**/
|
|
void
|
|
fwupd_client_get_history_async(FwupdClient *self,
|
|
GCancellable *cancellable,
|
|
GAsyncReadyCallback callback,
|
|
gpointer callback_data)
|
|
{
|
|
FwupdClientPrivate *priv = GET_PRIVATE(self);
|
|
g_autoptr(GTask) task = NULL;
|
|
|
|
g_return_if_fail(FWUPD_IS_CLIENT(self));
|
|
g_return_if_fail(cancellable == NULL || G_IS_CANCELLABLE(cancellable));
|
|
g_return_if_fail(priv->proxy != NULL);
|
|
|
|
/* call into daemon */
|
|
task = g_task_new(self, cancellable, callback, callback_data);
|
|
g_dbus_proxy_call(priv->proxy,
|
|
"GetHistory",
|
|
NULL,
|
|
G_DBUS_CALL_FLAGS_NONE,
|
|
-1,
|
|
cancellable,
|
|
fwupd_client_get_history_cb,
|
|
g_steal_pointer(&task));
|
|
}
|
|
|
|
/**
|
|
* fwupd_client_get_history_finish:
|
|
* @self: a #FwupdClient
|
|
* @res: the asynchronous result
|
|
* @error: (nullable): optional return location for an error
|
|
*
|
|
* Gets the result of fwupd_client_get_history_async().
|
|
*
|
|
* Returns: (element-type FwupdDevice) (transfer container): results
|
|
*
|
|
* Since: 1.5.0
|
|
**/
|
|
GPtrArray *
|
|
fwupd_client_get_history_finish(FwupdClient *self, GAsyncResult *res, GError **error)
|
|
{
|
|
g_return_val_if_fail(FWUPD_IS_CLIENT(self), NULL);
|
|
g_return_val_if_fail(g_task_is_valid(res, self), NULL);
|
|
g_return_val_if_fail(error == NULL || *error == NULL, NULL);
|
|
return g_task_propagate_pointer(G_TASK(res), error);
|
|
}
|
|
|
|
static void
|
|
fwupd_client_get_device_by_id_cb(GObject *source, GAsyncResult *res, gpointer user_data)
|
|
{
|
|
g_autoptr(GTask) task = G_TASK(user_data);
|
|
g_autoptr(GError) error = NULL;
|
|
g_autoptr(GPtrArray) devices = NULL;
|
|
const gchar *device_id = g_task_get_task_data(task);
|
|
|
|
devices = fwupd_client_get_devices_finish(FWUPD_CLIENT(source), res, &error);
|
|
if (devices == NULL) {
|
|
g_task_return_error(task, g_steal_pointer(&error));
|
|
return;
|
|
}
|
|
|
|
/* find the device by ID (client side) */
|
|
for (guint i = 0; i < devices->len; i++) {
|
|
FwupdDevice *dev = g_ptr_array_index(devices, i);
|
|
if (g_strcmp0(fwupd_device_get_id(dev), device_id) == 0) {
|
|
g_task_return_pointer(task,
|
|
g_object_ref(dev),
|
|
(GDestroyNotify)g_object_unref);
|
|
return;
|
|
}
|
|
}
|
|
|
|
/* failed */
|
|
g_task_return_new_error(task,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_NOT_FOUND,
|
|
"failed to find %s",
|
|
device_id);
|
|
}
|
|
|
|
/**
|
|
* fwupd_client_get_device_by_id_async:
|
|
* @self: a #FwupdClient
|
|
* @device_id: the device ID
|
|
* @cancellable: (nullable): optional #GCancellable
|
|
* @callback: the function to run on completion
|
|
* @callback_data: the data to pass to @callback
|
|
*
|
|
* Gets a device by it's device ID.
|
|
*
|
|
* You must have called [method@Client.connect_async] on @self before using
|
|
* this method.
|
|
*
|
|
* Since: 1.5.0
|
|
**/
|
|
void
|
|
fwupd_client_get_device_by_id_async(FwupdClient *self,
|
|
const gchar *device_id,
|
|
GCancellable *cancellable,
|
|
GAsyncReadyCallback callback,
|
|
gpointer callback_data)
|
|
{
|
|
FwupdClientPrivate *priv = GET_PRIVATE(self);
|
|
g_autoptr(GTask) task = NULL;
|
|
|
|
g_return_if_fail(FWUPD_IS_CLIENT(self));
|
|
g_return_if_fail(device_id != NULL);
|
|
g_return_if_fail(cancellable == NULL || G_IS_CANCELLABLE(cancellable));
|
|
g_return_if_fail(priv->proxy != NULL);
|
|
|
|
/* call into daemon */
|
|
task = g_task_new(self, cancellable, callback, callback_data);
|
|
g_task_set_task_data(task, g_strdup(device_id), g_free);
|
|
fwupd_client_get_devices_async(self,
|
|
cancellable,
|
|
fwupd_client_get_device_by_id_cb,
|
|
g_steal_pointer(&task));
|
|
}
|
|
|
|
/**
|
|
* fwupd_client_get_device_by_id_finish:
|
|
* @self: a #FwupdClient
|
|
* @res: the asynchronous result
|
|
* @error: (nullable): optional return location for an error
|
|
*
|
|
* Gets the result of fwupd_client_get_device_by_id_async().
|
|
*
|
|
* Returns: (transfer full): a device, or %NULL for failure
|
|
*
|
|
* Since: 1.5.0
|
|
**/
|
|
FwupdDevice *
|
|
fwupd_client_get_device_by_id_finish(FwupdClient *self, GAsyncResult *res, GError **error)
|
|
{
|
|
g_return_val_if_fail(FWUPD_IS_CLIENT(self), NULL);
|
|
g_return_val_if_fail(g_task_is_valid(res, self), NULL);
|
|
g_return_val_if_fail(error == NULL || *error == NULL, NULL);
|
|
return g_task_propagate_pointer(G_TASK(res), error);
|
|
}
|
|
|
|
static void
|
|
fwupd_client_get_devices_by_guid_cb(GObject *source, GAsyncResult *res, gpointer user_data)
|
|
{
|
|
g_autoptr(GTask) task = G_TASK(user_data);
|
|
g_autoptr(GError) error = NULL;
|
|
g_autoptr(GPtrArray) devices = NULL;
|
|
g_autoptr(GPtrArray) devices_tmp = NULL;
|
|
const gchar *guid = g_task_get_task_data(task);
|
|
|
|
/* get all the devices */
|
|
devices_tmp = fwupd_client_get_devices_finish(FWUPD_CLIENT(source), res, &error);
|
|
if (devices_tmp == NULL) {
|
|
g_task_return_error(task, g_steal_pointer(&error));
|
|
return;
|
|
}
|
|
|
|
/* find the devices by GUID (client side) */
|
|
devices = g_ptr_array_new_with_free_func((GDestroyNotify)g_object_unref);
|
|
for (guint i = 0; i < devices_tmp->len; i++) {
|
|
FwupdDevice *dev_tmp = g_ptr_array_index(devices_tmp, i);
|
|
if (fwupd_device_has_guid(dev_tmp, guid))
|
|
g_ptr_array_add(devices, g_object_ref(dev_tmp));
|
|
}
|
|
|
|
/* nothing */
|
|
if (devices->len == 0) {
|
|
g_task_return_new_error(task,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_NOT_FOUND,
|
|
"failed to find any device providing %s",
|
|
guid);
|
|
return;
|
|
}
|
|
|
|
/* success */
|
|
g_task_return_pointer(task, g_steal_pointer(&devices), (GDestroyNotify)g_ptr_array_unref);
|
|
}
|
|
|
|
/**
|
|
* fwupd_client_get_devices_by_guid_async:
|
|
* @self: a #FwupdClient
|
|
* @guid: the GUID, e.g. `e22c4520-43dc-5bb3-8245-5787fead9b63`
|
|
* @cancellable: (nullable): optional #GCancellable
|
|
* @callback: the function to run on completion
|
|
* @callback_data: the data to pass to @callback
|
|
*
|
|
* Gets any devices that provide a specific GUID. An error is returned if no
|
|
* devices contains this GUID.
|
|
*
|
|
* You must have called [method@Client.connect_async] on @self before using
|
|
* this method.
|
|
*
|
|
* Since: 1.5.0
|
|
**/
|
|
void
|
|
fwupd_client_get_devices_by_guid_async(FwupdClient *self,
|
|
const gchar *guid,
|
|
GCancellable *cancellable,
|
|
GAsyncReadyCallback callback,
|
|
gpointer callback_data)
|
|
{
|
|
FwupdClientPrivate *priv = GET_PRIVATE(self);
|
|
g_autoptr(GTask) task = NULL;
|
|
|
|
g_return_if_fail(FWUPD_IS_CLIENT(self));
|
|
g_return_if_fail(guid != NULL);
|
|
g_return_if_fail(cancellable == NULL || G_IS_CANCELLABLE(cancellable));
|
|
g_return_if_fail(priv->proxy != NULL);
|
|
|
|
/* call into daemon */
|
|
task = g_task_new(self, cancellable, callback, callback_data);
|
|
g_task_set_task_data(task, g_strdup(guid), g_free);
|
|
fwupd_client_get_devices_async(self,
|
|
cancellable,
|
|
fwupd_client_get_devices_by_guid_cb,
|
|
g_steal_pointer(&task));
|
|
}
|
|
|
|
/**
|
|
* fwupd_client_get_devices_by_guid_finish:
|
|
* @self: a #FwupdClient
|
|
* @res: the asynchronous result
|
|
* @error: (nullable): optional return location for an error
|
|
*
|
|
* Gets the result of fwupd_client_get_devices_by_guid_async().
|
|
*
|
|
* Returns: (element-type FwupdRelease) (transfer container): results
|
|
*
|
|
* Since: 1.5.0
|
|
**/
|
|
GPtrArray *
|
|
fwupd_client_get_devices_by_guid_finish(FwupdClient *self, GAsyncResult *res, GError **error)
|
|
{
|
|
g_return_val_if_fail(FWUPD_IS_CLIENT(self), NULL);
|
|
g_return_val_if_fail(g_task_is_valid(res, self), NULL);
|
|
g_return_val_if_fail(error == NULL || *error == NULL, NULL);
|
|
return g_task_propagate_pointer(G_TASK(res), error);
|
|
}
|
|
|
|
static void
|
|
fwupd_client_get_releases_cb(GObject *source, GAsyncResult *res, gpointer user_data)
|
|
{
|
|
g_autoptr(GTask) task = G_TASK(user_data);
|
|
g_autoptr(GError) error = NULL;
|
|
g_autoptr(GVariant) val = NULL;
|
|
|
|
val = g_dbus_proxy_call_finish(G_DBUS_PROXY(source), res, &error);
|
|
if (val == NULL) {
|
|
fwupd_client_fixup_dbus_error(error);
|
|
g_task_return_error(task, g_steal_pointer(&error));
|
|
return;
|
|
}
|
|
|
|
/* success */
|
|
g_task_return_pointer(task,
|
|
fwupd_release_array_from_variant(val),
|
|
(GDestroyNotify)g_ptr_array_unref);
|
|
}
|
|
|
|
/**
|
|
* fwupd_client_get_releases_async:
|
|
* @self: a #FwupdClient
|
|
* @device_id: the device ID
|
|
* @cancellable: (nullable): optional #GCancellable
|
|
* @callback: the function to run on completion
|
|
* @callback_data: the data to pass to @callback
|
|
*
|
|
* Gets all the releases for a specific device
|
|
*
|
|
* You must have called [method@Client.connect_async] on @self before using
|
|
* this method.
|
|
*
|
|
* Since: 1.5.0
|
|
**/
|
|
void
|
|
fwupd_client_get_releases_async(FwupdClient *self,
|
|
const gchar *device_id,
|
|
GCancellable *cancellable,
|
|
GAsyncReadyCallback callback,
|
|
gpointer callback_data)
|
|
{
|
|
FwupdClientPrivate *priv = GET_PRIVATE(self);
|
|
g_autoptr(GTask) task = NULL;
|
|
|
|
g_return_if_fail(FWUPD_IS_CLIENT(self));
|
|
g_return_if_fail(device_id != NULL);
|
|
g_return_if_fail(cancellable == NULL || G_IS_CANCELLABLE(cancellable));
|
|
g_return_if_fail(priv->proxy != NULL);
|
|
|
|
/* call into daemon */
|
|
task = g_task_new(self, cancellable, callback, callback_data);
|
|
g_dbus_proxy_call(priv->proxy,
|
|
"GetReleases",
|
|
g_variant_new("(s)", device_id),
|
|
G_DBUS_CALL_FLAGS_NONE,
|
|
-1,
|
|
cancellable,
|
|
fwupd_client_get_releases_cb,
|
|
g_steal_pointer(&task));
|
|
}
|
|
|
|
/**
|
|
* fwupd_client_get_releases_finish:
|
|
* @self: a #FwupdClient
|
|
* @res: the asynchronous result
|
|
* @error: (nullable): optional return location for an error
|
|
*
|
|
* Gets the result of fwupd_client_get_releases_async().
|
|
*
|
|
* Returns: (element-type FwupdRelease) (transfer container): results
|
|
*
|
|
* Since: 1.5.0
|
|
**/
|
|
GPtrArray *
|
|
fwupd_client_get_releases_finish(FwupdClient *self, GAsyncResult *res, GError **error)
|
|
{
|
|
g_return_val_if_fail(FWUPD_IS_CLIENT(self), NULL);
|
|
g_return_val_if_fail(g_task_is_valid(res, self), NULL);
|
|
g_return_val_if_fail(error == NULL || *error == NULL, NULL);
|
|
return g_task_propagate_pointer(G_TASK(res), error);
|
|
}
|
|
|
|
static void
|
|
fwupd_client_get_downgrades_cb(GObject *source, GAsyncResult *res, gpointer user_data)
|
|
{
|
|
g_autoptr(GTask) task = G_TASK(user_data);
|
|
g_autoptr(GError) error = NULL;
|
|
g_autoptr(GVariant) val = NULL;
|
|
|
|
val = g_dbus_proxy_call_finish(G_DBUS_PROXY(source), res, &error);
|
|
if (val == NULL) {
|
|
fwupd_client_fixup_dbus_error(error);
|
|
g_task_return_error(task, g_steal_pointer(&error));
|
|
return;
|
|
}
|
|
|
|
/* success */
|
|
g_task_return_pointer(task,
|
|
fwupd_release_array_from_variant(val),
|
|
(GDestroyNotify)g_ptr_array_unref);
|
|
}
|
|
|
|
/**
|
|
* fwupd_client_get_downgrades_async:
|
|
* @self: a #FwupdClient
|
|
* @device_id: the device ID
|
|
* @cancellable: (nullable): optional #GCancellable
|
|
* @callback: the function to run on completion
|
|
* @callback_data: the data to pass to @callback
|
|
*
|
|
* Gets all the downgrades for a specific device.
|
|
*
|
|
* You must have called [method@Client.connect_async] on @self before using
|
|
* this method.
|
|
*
|
|
* Since: 1.5.0
|
|
**/
|
|
void
|
|
fwupd_client_get_downgrades_async(FwupdClient *self,
|
|
const gchar *device_id,
|
|
GCancellable *cancellable,
|
|
GAsyncReadyCallback callback,
|
|
gpointer callback_data)
|
|
{
|
|
FwupdClientPrivate *priv = GET_PRIVATE(self);
|
|
g_autoptr(GTask) task = NULL;
|
|
|
|
g_return_if_fail(FWUPD_IS_CLIENT(self));
|
|
g_return_if_fail(device_id != NULL);
|
|
g_return_if_fail(cancellable == NULL || G_IS_CANCELLABLE(cancellable));
|
|
g_return_if_fail(priv->proxy != NULL);
|
|
|
|
/* call into daemon */
|
|
task = g_task_new(self, cancellable, callback, callback_data);
|
|
g_dbus_proxy_call(priv->proxy,
|
|
"GetDowngrades",
|
|
g_variant_new("(s)", device_id),
|
|
G_DBUS_CALL_FLAGS_NONE,
|
|
-1,
|
|
cancellable,
|
|
fwupd_client_get_downgrades_cb,
|
|
g_steal_pointer(&task));
|
|
}
|
|
|
|
/**
|
|
* fwupd_client_get_downgrades_finish:
|
|
* @self: a #FwupdClient
|
|
* @res: the asynchronous result
|
|
* @error: (nullable): optional return location for an error
|
|
*
|
|
* Gets the result of fwupd_client_get_downgrades_async().
|
|
*
|
|
* Returns: (element-type FwupdRelease) (transfer container): results
|
|
*
|
|
* Since: 1.5.0
|
|
**/
|
|
GPtrArray *
|
|
fwupd_client_get_downgrades_finish(FwupdClient *self, GAsyncResult *res, GError **error)
|
|
{
|
|
g_return_val_if_fail(FWUPD_IS_CLIENT(self), NULL);
|
|
g_return_val_if_fail(g_task_is_valid(res, self), NULL);
|
|
g_return_val_if_fail(error == NULL || *error == NULL, NULL);
|
|
return g_task_propagate_pointer(G_TASK(res), error);
|
|
}
|
|
|
|
static void
|
|
fwupd_client_get_upgrades_cb(GObject *source, GAsyncResult *res, gpointer user_data)
|
|
{
|
|
g_autoptr(GTask) task = G_TASK(user_data);
|
|
g_autoptr(GError) error = NULL;
|
|
g_autoptr(GVariant) val = NULL;
|
|
|
|
val = g_dbus_proxy_call_finish(G_DBUS_PROXY(source), res, &error);
|
|
if (val == NULL) {
|
|
fwupd_client_fixup_dbus_error(error);
|
|
g_task_return_error(task, g_steal_pointer(&error));
|
|
return;
|
|
}
|
|
|
|
/* success */
|
|
g_task_return_pointer(task,
|
|
fwupd_release_array_from_variant(val),
|
|
(GDestroyNotify)g_ptr_array_unref);
|
|
}
|
|
|
|
/**
|
|
* fwupd_client_get_upgrades_async:
|
|
* @self: a #FwupdClient
|
|
* @device_id: the device ID
|
|
* @cancellable: (nullable): optional #GCancellable
|
|
* @callback: the function to run on completion
|
|
* @callback_data: the data to pass to @callback
|
|
*
|
|
* Gets all the upgrades for a specific device.
|
|
*
|
|
* You must have called [method@Client.connect_async] on @self before using
|
|
* this method.
|
|
*
|
|
* Since: 1.5.0
|
|
**/
|
|
void
|
|
fwupd_client_get_upgrades_async(FwupdClient *self,
|
|
const gchar *device_id,
|
|
GCancellable *cancellable,
|
|
GAsyncReadyCallback callback,
|
|
gpointer callback_data)
|
|
{
|
|
FwupdClientPrivate *priv = GET_PRIVATE(self);
|
|
g_autoptr(GTask) task = NULL;
|
|
|
|
g_return_if_fail(FWUPD_IS_CLIENT(self));
|
|
g_return_if_fail(device_id != NULL);
|
|
g_return_if_fail(cancellable == NULL || G_IS_CANCELLABLE(cancellable));
|
|
g_return_if_fail(priv->proxy != NULL);
|
|
|
|
/* call into daemon */
|
|
task = g_task_new(self, cancellable, callback, callback_data);
|
|
g_dbus_proxy_call(priv->proxy,
|
|
"GetUpgrades",
|
|
g_variant_new("(s)", device_id),
|
|
G_DBUS_CALL_FLAGS_NONE,
|
|
-1,
|
|
cancellable,
|
|
fwupd_client_get_upgrades_cb,
|
|
g_steal_pointer(&task));
|
|
}
|
|
|
|
/**
|
|
* fwupd_client_get_upgrades_finish:
|
|
* @self: a #FwupdClient
|
|
* @res: the asynchronous result
|
|
* @error: (nullable): optional return location for an error
|
|
*
|
|
* Gets the result of fwupd_client_get_upgrades_async().
|
|
*
|
|
* Returns: (element-type FwupdRelease) (transfer container): results
|
|
*
|
|
* Since: 1.5.0
|
|
**/
|
|
GPtrArray *
|
|
fwupd_client_get_upgrades_finish(FwupdClient *self, GAsyncResult *res, GError **error)
|
|
{
|
|
g_return_val_if_fail(FWUPD_IS_CLIENT(self), NULL);
|
|
g_return_val_if_fail(g_task_is_valid(res, self), NULL);
|
|
g_return_val_if_fail(error == NULL || *error == NULL, NULL);
|
|
return g_task_propagate_pointer(G_TASK(res), error);
|
|
}
|
|
|
|
static void
|
|
fwupd_client_modify_config_cb(GObject *source, GAsyncResult *res, gpointer user_data)
|
|
{
|
|
g_autoptr(GTask) task = G_TASK(user_data);
|
|
g_autoptr(GError) error = NULL;
|
|
g_autoptr(GVariant) val = NULL;
|
|
|
|
val = g_dbus_proxy_call_finish(G_DBUS_PROXY(source), res, &error);
|
|
if (val == NULL) {
|
|
fwupd_client_fixup_dbus_error(error);
|
|
g_task_return_error(task, g_steal_pointer(&error));
|
|
return;
|
|
}
|
|
|
|
/* success */
|
|
g_task_return_boolean(task, TRUE);
|
|
}
|
|
|
|
/**
|
|
* fwupd_client_modify_config_async:
|
|
* @self: a #FwupdClient
|
|
* @key: config key, e.g. `DisabledPlugins`
|
|
* @value: config value, e.g. `*`
|
|
* @cancellable: (nullable): optional #GCancellable
|
|
* @callback: the function to run on completion
|
|
* @callback_data: the data to pass to @callback
|
|
*
|
|
* Modifies a daemon config option.
|
|
* The daemon will only respond to this request with proper permissions.
|
|
*
|
|
* Since: 1.5.0
|
|
**/
|
|
void
|
|
fwupd_client_modify_config_async(FwupdClient *self,
|
|
const gchar *key,
|
|
const gchar *value,
|
|
GCancellable *cancellable,
|
|
GAsyncReadyCallback callback,
|
|
gpointer callback_data)
|
|
{
|
|
FwupdClientPrivate *priv = GET_PRIVATE(self);
|
|
g_autoptr(GTask) task = NULL;
|
|
|
|
g_return_if_fail(FWUPD_IS_CLIENT(self));
|
|
g_return_if_fail(key != NULL);
|
|
g_return_if_fail(value != NULL);
|
|
g_return_if_fail(cancellable == NULL || G_IS_CANCELLABLE(cancellable));
|
|
g_return_if_fail(priv->proxy != NULL);
|
|
|
|
/* call into daemon */
|
|
task = g_task_new(self, cancellable, callback, callback_data);
|
|
g_dbus_proxy_call(priv->proxy,
|
|
"ModifyConfig",
|
|
g_variant_new("(ss)", key, value),
|
|
G_DBUS_CALL_FLAGS_NONE,
|
|
-1,
|
|
cancellable,
|
|
fwupd_client_modify_config_cb,
|
|
g_steal_pointer(&task));
|
|
}
|
|
|
|
/**
|
|
* fwupd_client_modify_config_finish:
|
|
* @self: a #FwupdClient
|
|
* @res: the asynchronous result
|
|
* @error: (nullable): optional return location for an error
|
|
*
|
|
* Gets the result of fwupd_client_modify_config_async().
|
|
*
|
|
* Returns: %TRUE for success
|
|
*
|
|
* Since: 1.5.0
|
|
**/
|
|
gboolean
|
|
fwupd_client_modify_config_finish(FwupdClient *self, GAsyncResult *res, GError **error)
|
|
{
|
|
g_return_val_if_fail(FWUPD_IS_CLIENT(self), FALSE);
|
|
g_return_val_if_fail(g_task_is_valid(res, self), FALSE);
|
|
g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
|
|
return g_task_propagate_boolean(G_TASK(res), error);
|
|
}
|
|
|
|
static void
|
|
fwupd_client_activate_cb(GObject *source, GAsyncResult *res, gpointer user_data)
|
|
{
|
|
g_autoptr(GTask) task = G_TASK(user_data);
|
|
g_autoptr(GError) error = NULL;
|
|
g_autoptr(GVariant) val = NULL;
|
|
|
|
val = g_dbus_proxy_call_finish(G_DBUS_PROXY(source), res, &error);
|
|
if (val == NULL) {
|
|
fwupd_client_fixup_dbus_error(error);
|
|
g_task_return_error(task, g_steal_pointer(&error));
|
|
return;
|
|
}
|
|
|
|
/* success */
|
|
g_task_return_boolean(task, TRUE);
|
|
}
|
|
|
|
/**
|
|
* fwupd_client_activate_async:
|
|
* @self: a #FwupdClient
|
|
* @device_id: a device
|
|
* @cancellable: (nullable): optional #GCancellable
|
|
* @callback: the function to run on completion
|
|
* @callback_data: the data to pass to @callback
|
|
*
|
|
* Activates up a device, which normally means the device switches to a new
|
|
* firmware version. This should only be called when data loss cannot occur.
|
|
*
|
|
* Since: 1.5.0
|
|
**/
|
|
void
|
|
fwupd_client_activate_async(FwupdClient *self,
|
|
const gchar *device_id,
|
|
GCancellable *cancellable,
|
|
GAsyncReadyCallback callback,
|
|
gpointer callback_data)
|
|
{
|
|
FwupdClientPrivate *priv = GET_PRIVATE(self);
|
|
g_autoptr(GTask) task = NULL;
|
|
|
|
g_return_if_fail(FWUPD_IS_CLIENT(self));
|
|
g_return_if_fail(device_id != NULL);
|
|
g_return_if_fail(cancellable == NULL || G_IS_CANCELLABLE(cancellable));
|
|
g_return_if_fail(priv->proxy != NULL);
|
|
|
|
/* call into daemon */
|
|
task = g_task_new(self, cancellable, callback, callback_data);
|
|
g_dbus_proxy_call(priv->proxy,
|
|
"Activate",
|
|
g_variant_new("(s)", device_id),
|
|
G_DBUS_CALL_FLAGS_NONE,
|
|
-1,
|
|
cancellable,
|
|
fwupd_client_activate_cb,
|
|
g_steal_pointer(&task));
|
|
}
|
|
|
|
/**
|
|
* fwupd_client_activate_finish:
|
|
* @self: a #FwupdClient
|
|
* @res: the asynchronous result
|
|
* @error: (nullable): optional return location for an error
|
|
*
|
|
* Gets the result of fwupd_client_activate_async().
|
|
*
|
|
* Returns: %TRUE for success
|
|
*
|
|
* Since: 1.5.0
|
|
**/
|
|
gboolean
|
|
fwupd_client_activate_finish(FwupdClient *self, GAsyncResult *res, GError **error)
|
|
{
|
|
g_return_val_if_fail(FWUPD_IS_CLIENT(self), FALSE);
|
|
g_return_val_if_fail(g_task_is_valid(res, self), FALSE);
|
|
g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
|
|
return g_task_propagate_boolean(G_TASK(res), error);
|
|
}
|
|
|
|
static void
|
|
fwupd_client_verify_cb(GObject *source, GAsyncResult *res, gpointer user_data)
|
|
{
|
|
g_autoptr(GTask) task = G_TASK(user_data);
|
|
g_autoptr(GError) error = NULL;
|
|
g_autoptr(GVariant) val = NULL;
|
|
|
|
val = g_dbus_proxy_call_finish(G_DBUS_PROXY(source), res, &error);
|
|
if (val == NULL) {
|
|
fwupd_client_fixup_dbus_error(error);
|
|
g_task_return_error(task, g_steal_pointer(&error));
|
|
return;
|
|
}
|
|
|
|
/* success */
|
|
g_task_return_boolean(task, TRUE);
|
|
}
|
|
|
|
/**
|
|
* fwupd_client_verify_async:
|
|
* @self: a #FwupdClient
|
|
* @device_id: the device ID
|
|
* @cancellable: (nullable): optional #GCancellable
|
|
* @callback: the function to run on completion
|
|
* @callback_data: the data to pass to @callback
|
|
*
|
|
* Verify a specific device.
|
|
*
|
|
* Since: 1.5.0
|
|
**/
|
|
void
|
|
fwupd_client_verify_async(FwupdClient *self,
|
|
const gchar *device_id,
|
|
GCancellable *cancellable,
|
|
GAsyncReadyCallback callback,
|
|
gpointer callback_data)
|
|
{
|
|
FwupdClientPrivate *priv = GET_PRIVATE(self);
|
|
g_autoptr(GTask) task = NULL;
|
|
|
|
g_return_if_fail(FWUPD_IS_CLIENT(self));
|
|
g_return_if_fail(device_id != NULL);
|
|
g_return_if_fail(cancellable == NULL || G_IS_CANCELLABLE(cancellable));
|
|
g_return_if_fail(priv->proxy != NULL);
|
|
|
|
/* call into daemon */
|
|
task = g_task_new(self, cancellable, callback, callback_data);
|
|
g_dbus_proxy_call(priv->proxy,
|
|
"Verify",
|
|
g_variant_new("(s)", device_id),
|
|
G_DBUS_CALL_FLAGS_NONE,
|
|
-1,
|
|
cancellable,
|
|
fwupd_client_verify_cb,
|
|
g_steal_pointer(&task));
|
|
}
|
|
|
|
/**
|
|
* fwupd_client_verify_finish:
|
|
* @self: a #FwupdClient
|
|
* @res: the asynchronous result
|
|
* @error: (nullable): optional return location for an error
|
|
*
|
|
* Gets the result of fwupd_client_verify_async().
|
|
*
|
|
* Returns: %TRUE for success
|
|
*
|
|
* Since: 1.5.0
|
|
**/
|
|
gboolean
|
|
fwupd_client_verify_finish(FwupdClient *self, GAsyncResult *res, GError **error)
|
|
{
|
|
g_return_val_if_fail(FWUPD_IS_CLIENT(self), FALSE);
|
|
g_return_val_if_fail(g_task_is_valid(res, self), FALSE);
|
|
g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
|
|
return g_task_propagate_boolean(G_TASK(res), error);
|
|
}
|
|
|
|
static void
|
|
fwupd_client_verify_update_cb(GObject *source, GAsyncResult *res, gpointer user_data)
|
|
{
|
|
g_autoptr(GTask) task = G_TASK(user_data);
|
|
g_autoptr(GError) error = NULL;
|
|
g_autoptr(GVariant) val = NULL;
|
|
|
|
val = g_dbus_proxy_call_finish(G_DBUS_PROXY(source), res, &error);
|
|
if (val == NULL) {
|
|
fwupd_client_fixup_dbus_error(error);
|
|
g_task_return_error(task, g_steal_pointer(&error));
|
|
return;
|
|
}
|
|
|
|
/* success */
|
|
g_task_return_boolean(task, TRUE);
|
|
}
|
|
|
|
/**
|
|
* fwupd_client_verify_update_async:
|
|
* @self: a #FwupdClient
|
|
* @device_id: the device ID
|
|
* @cancellable: (nullable): optional #GCancellable
|
|
* @callback: the function to run on completion
|
|
* @callback_data: the data to pass to @callback
|
|
*
|
|
* Update the verification record for a specific device.
|
|
*
|
|
* Since: 1.5.0
|
|
**/
|
|
void
|
|
fwupd_client_verify_update_async(FwupdClient *self,
|
|
const gchar *device_id,
|
|
GCancellable *cancellable,
|
|
GAsyncReadyCallback callback,
|
|
gpointer callback_data)
|
|
{
|
|
FwupdClientPrivate *priv = GET_PRIVATE(self);
|
|
g_autoptr(GTask) task = NULL;
|
|
|
|
g_return_if_fail(FWUPD_IS_CLIENT(self));
|
|
g_return_if_fail(device_id != NULL);
|
|
g_return_if_fail(cancellable == NULL || G_IS_CANCELLABLE(cancellable));
|
|
g_return_if_fail(priv->proxy != NULL);
|
|
|
|
/* call into daemon */
|
|
task = g_task_new(self, cancellable, callback, callback_data);
|
|
g_dbus_proxy_call(priv->proxy,
|
|
"VerifyUpdate",
|
|
g_variant_new("(s)", device_id),
|
|
G_DBUS_CALL_FLAGS_NONE,
|
|
-1,
|
|
cancellable,
|
|
fwupd_client_verify_update_cb,
|
|
g_steal_pointer(&task));
|
|
}
|
|
|
|
/**
|
|
* fwupd_client_verify_update_finish:
|
|
* @self: a #FwupdClient
|
|
* @res: the asynchronous result
|
|
* @error: (nullable): optional return location for an error
|
|
*
|
|
* Gets the result of fwupd_client_verify_update_async().
|
|
*
|
|
* Returns: %TRUE for success
|
|
*
|
|
* Since: 1.5.0
|
|
**/
|
|
gboolean
|
|
fwupd_client_verify_update_finish(FwupdClient *self, GAsyncResult *res, GError **error)
|
|
{
|
|
g_return_val_if_fail(FWUPD_IS_CLIENT(self), FALSE);
|
|
g_return_val_if_fail(g_task_is_valid(res, self), FALSE);
|
|
g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
|
|
return g_task_propagate_boolean(G_TASK(res), error);
|
|
}
|
|
|
|
static void
|
|
fwupd_client_unlock_cb(GObject *source, GAsyncResult *res, gpointer user_data)
|
|
{
|
|
g_autoptr(GTask) task = G_TASK(user_data);
|
|
g_autoptr(GError) error = NULL;
|
|
g_autoptr(GVariant) val = NULL;
|
|
|
|
val = g_dbus_proxy_call_finish(G_DBUS_PROXY(source), res, &error);
|
|
if (val == NULL) {
|
|
fwupd_client_fixup_dbus_error(error);
|
|
g_task_return_error(task, g_steal_pointer(&error));
|
|
return;
|
|
}
|
|
|
|
/* success */
|
|
g_task_return_boolean(task, TRUE);
|
|
}
|
|
|
|
/**
|
|
* fwupd_client_unlock_async:
|
|
* @self: a #FwupdClient
|
|
* @device_id: the device ID
|
|
* @cancellable: (nullable): optional #GCancellable
|
|
* @callback: the function to run on completion
|
|
* @callback_data: the data to pass to @callback
|
|
*
|
|
* Unlocks a specific device so firmware can be read or wrote.
|
|
*
|
|
* Since: 1.5.0
|
|
**/
|
|
void
|
|
fwupd_client_unlock_async(FwupdClient *self,
|
|
const gchar *device_id,
|
|
GCancellable *cancellable,
|
|
GAsyncReadyCallback callback,
|
|
gpointer callback_data)
|
|
{
|
|
FwupdClientPrivate *priv = GET_PRIVATE(self);
|
|
g_autoptr(GTask) task = NULL;
|
|
|
|
g_return_if_fail(FWUPD_IS_CLIENT(self));
|
|
g_return_if_fail(device_id != NULL);
|
|
g_return_if_fail(cancellable == NULL || G_IS_CANCELLABLE(cancellable));
|
|
g_return_if_fail(priv->proxy != NULL);
|
|
|
|
/* call into daemon */
|
|
task = g_task_new(self, cancellable, callback, callback_data);
|
|
g_dbus_proxy_call(priv->proxy,
|
|
"Unlock",
|
|
g_variant_new("(s)", device_id),
|
|
G_DBUS_CALL_FLAGS_NONE,
|
|
-1,
|
|
cancellable,
|
|
fwupd_client_unlock_cb,
|
|
g_steal_pointer(&task));
|
|
}
|
|
|
|
/**
|
|
* fwupd_client_unlock_finish:
|
|
* @self: a #FwupdClient
|
|
* @res: the asynchronous result
|
|
* @error: (nullable): optional return location for an error
|
|
*
|
|
* Gets the result of fwupd_client_unlock_async().
|
|
*
|
|
* Returns: %TRUE for success
|
|
*
|
|
* Since: 1.5.0
|
|
**/
|
|
gboolean
|
|
fwupd_client_unlock_finish(FwupdClient *self, GAsyncResult *res, GError **error)
|
|
{
|
|
g_return_val_if_fail(FWUPD_IS_CLIENT(self), FALSE);
|
|
g_return_val_if_fail(g_task_is_valid(res, self), FALSE);
|
|
g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
|
|
return g_task_propagate_boolean(G_TASK(res), error);
|
|
}
|
|
|
|
static void
|
|
fwupd_client_clear_results_cb(GObject *source, GAsyncResult *res, gpointer user_data)
|
|
{
|
|
g_autoptr(GTask) task = G_TASK(user_data);
|
|
g_autoptr(GError) error = NULL;
|
|
g_autoptr(GVariant) val = NULL;
|
|
|
|
val = g_dbus_proxy_call_finish(G_DBUS_PROXY(source), res, &error);
|
|
if (val == NULL) {
|
|
fwupd_client_fixup_dbus_error(error);
|
|
g_task_return_error(task, g_steal_pointer(&error));
|
|
return;
|
|
}
|
|
|
|
/* success */
|
|
g_task_return_boolean(task, TRUE);
|
|
}
|
|
|
|
/**
|
|
* fwupd_client_clear_results_async:
|
|
* @self: a #FwupdClient
|
|
* @device_id: a device
|
|
* @cancellable: (nullable): optional #GCancellable
|
|
* @callback: the function to run on completion
|
|
* @callback_data: the data to pass to @callback
|
|
*
|
|
* Clears the results for a specific device.
|
|
*
|
|
* Since: 1.5.0
|
|
**/
|
|
void
|
|
fwupd_client_clear_results_async(FwupdClient *self,
|
|
const gchar *device_id,
|
|
GCancellable *cancellable,
|
|
GAsyncReadyCallback callback,
|
|
gpointer callback_data)
|
|
{
|
|
FwupdClientPrivate *priv = GET_PRIVATE(self);
|
|
g_autoptr(GTask) task = NULL;
|
|
|
|
g_return_if_fail(FWUPD_IS_CLIENT(self));
|
|
g_return_if_fail(device_id != NULL);
|
|
g_return_if_fail(cancellable == NULL || G_IS_CANCELLABLE(cancellable));
|
|
g_return_if_fail(priv->proxy != NULL);
|
|
|
|
/* call into daemon */
|
|
task = g_task_new(self, cancellable, callback, callback_data);
|
|
g_dbus_proxy_call(priv->proxy,
|
|
"ClearResults",
|
|
g_variant_new("(s)", device_id),
|
|
G_DBUS_CALL_FLAGS_NONE,
|
|
-1,
|
|
cancellable,
|
|
fwupd_client_clear_results_cb,
|
|
g_steal_pointer(&task));
|
|
}
|
|
|
|
/**
|
|
* fwupd_client_clear_results_finish:
|
|
* @self: a #FwupdClient
|
|
* @res: the asynchronous result
|
|
* @error: (nullable): optional return location for an error
|
|
*
|
|
* Gets the result of fwupd_client_clear_results_async().
|
|
*
|
|
* Returns: %TRUE for success
|
|
*
|
|
* Since: 1.5.0
|
|
**/
|
|
gboolean
|
|
fwupd_client_clear_results_finish(FwupdClient *self, GAsyncResult *res, GError **error)
|
|
{
|
|
g_return_val_if_fail(FWUPD_IS_CLIENT(self), FALSE);
|
|
g_return_val_if_fail(g_task_is_valid(res, self), FALSE);
|
|
g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
|
|
return g_task_propagate_boolean(G_TASK(res), error);
|
|
}
|
|
|
|
static void
|
|
fwupd_client_get_results_cb(GObject *source, GAsyncResult *res, gpointer user_data)
|
|
{
|
|
g_autoptr(GTask) task = G_TASK(user_data);
|
|
g_autoptr(GError) error = NULL;
|
|
g_autoptr(GVariant) val = NULL;
|
|
|
|
val = g_dbus_proxy_call_finish(G_DBUS_PROXY(source), res, &error);
|
|
if (val == NULL) {
|
|
fwupd_client_fixup_dbus_error(error);
|
|
g_task_return_error(task, g_steal_pointer(&error));
|
|
return;
|
|
}
|
|
|
|
/* success */
|
|
g_task_return_pointer(task,
|
|
fwupd_device_from_variant(val),
|
|
(GDestroyNotify)g_ptr_array_unref);
|
|
}
|
|
|
|
/**
|
|
* fwupd_client_get_results_async:
|
|
* @self: a #FwupdClient
|
|
* @device_id: the device ID
|
|
* @cancellable: (nullable): optional #GCancellable
|
|
* @callback: the function to run on completion
|
|
* @callback_data: the data to pass to @callback
|
|
*
|
|
* Gets the results of a previous firmware update for a specific device.
|
|
*
|
|
* You must have called [method@Client.connect_async] on @self before using
|
|
* this method.
|
|
*
|
|
* Since: 1.5.0
|
|
**/
|
|
void
|
|
fwupd_client_get_results_async(FwupdClient *self,
|
|
const gchar *device_id,
|
|
GCancellable *cancellable,
|
|
GAsyncReadyCallback callback,
|
|
gpointer callback_data)
|
|
{
|
|
FwupdClientPrivate *priv = GET_PRIVATE(self);
|
|
g_autoptr(GTask) task = NULL;
|
|
|
|
g_return_if_fail(FWUPD_IS_CLIENT(self));
|
|
g_return_if_fail(device_id != NULL);
|
|
g_return_if_fail(cancellable == NULL || G_IS_CANCELLABLE(cancellable));
|
|
g_return_if_fail(priv->proxy != NULL);
|
|
|
|
/* call into daemon */
|
|
task = g_task_new(self, cancellable, callback, callback_data);
|
|
g_dbus_proxy_call(priv->proxy,
|
|
"GetResults",
|
|
g_variant_new("(s)", device_id),
|
|
G_DBUS_CALL_FLAGS_NONE,
|
|
-1,
|
|
cancellable,
|
|
fwupd_client_get_results_cb,
|
|
g_steal_pointer(&task));
|
|
}
|
|
|
|
/**
|
|
* fwupd_client_get_results_finish:
|
|
* @self: a #FwupdClient
|
|
* @res: the asynchronous result
|
|
* @error: (nullable): optional return location for an error
|
|
*
|
|
* Gets the result of fwupd_client_get_results_async().
|
|
*
|
|
* Returns: (transfer full): a device, or %NULL for failure
|
|
*
|
|
* Since: 1.5.0
|
|
**/
|
|
FwupdDevice *
|
|
fwupd_client_get_results_finish(FwupdClient *self, GAsyncResult *res, GError **error)
|
|
{
|
|
g_return_val_if_fail(FWUPD_IS_CLIENT(self), NULL);
|
|
g_return_val_if_fail(g_task_is_valid(res, self), NULL);
|
|
g_return_val_if_fail(error == NULL || *error == NULL, NULL);
|
|
return g_task_propagate_pointer(G_TASK(res), error);
|
|
}
|
|
|
|
#ifdef HAVE_GIO_UNIX
|
|
|
|
static void
|
|
fwupd_client_install_stream_cb(GObject *source, GAsyncResult *res, gpointer user_data)
|
|
{
|
|
g_autoptr(GDBusMessage) msg = NULL;
|
|
g_autoptr(GError) error = NULL;
|
|
g_autoptr(GTask) task = G_TASK(user_data);
|
|
|
|
msg = g_dbus_connection_send_message_with_reply_finish(G_DBUS_CONNECTION(source),
|
|
res,
|
|
&error);
|
|
if (msg == NULL) {
|
|
fwupd_client_fixup_dbus_error(error);
|
|
g_task_return_error(task, g_steal_pointer(&error));
|
|
return;
|
|
}
|
|
if (g_dbus_message_to_gerror(msg, &error)) {
|
|
fwupd_client_fixup_dbus_error(error);
|
|
g_task_return_error(task, g_steal_pointer(&error));
|
|
return;
|
|
}
|
|
|
|
/* success */
|
|
g_task_return_boolean(task, TRUE);
|
|
}
|
|
|
|
void
|
|
fwupd_client_install_stream_async(FwupdClient *self,
|
|
const gchar *device_id,
|
|
GUnixInputStream *istr,
|
|
const gchar *filename_hint,
|
|
FwupdInstallFlags install_flags,
|
|
GCancellable *cancellable,
|
|
GAsyncReadyCallback callback,
|
|
gpointer callback_data)
|
|
{
|
|
FwupdClientPrivate *priv = GET_PRIVATE(self);
|
|
GVariantBuilder builder;
|
|
g_autoptr(GDBusMessage) request = NULL;
|
|
g_autoptr(GUnixFDList) fd_list = NULL;
|
|
g_autoptr(GTask) task = g_task_new(self, cancellable, callback, callback_data);
|
|
|
|
/* set options */
|
|
g_variant_builder_init(&builder, G_VARIANT_TYPE_VARDICT);
|
|
g_variant_builder_add(&builder, "{sv}", "reason", g_variant_new_string("user-action"));
|
|
if (filename_hint != NULL) {
|
|
g_variant_builder_add(&builder,
|
|
"{sv}",
|
|
"filename",
|
|
g_variant_new_string(filename_hint));
|
|
}
|
|
if (install_flags & FWUPD_INSTALL_FLAG_OFFLINE) {
|
|
g_variant_builder_add(&builder, "{sv}", "offline", g_variant_new_boolean(TRUE));
|
|
}
|
|
if (install_flags & FWUPD_INSTALL_FLAG_ALLOW_OLDER) {
|
|
g_variant_builder_add(&builder, "{sv}", "allow-older", g_variant_new_boolean(TRUE));
|
|
}
|
|
if (install_flags & FWUPD_INSTALL_FLAG_ALLOW_REINSTALL) {
|
|
g_variant_builder_add(&builder,
|
|
"{sv}",
|
|
"allow-reinstall",
|
|
g_variant_new_boolean(TRUE));
|
|
}
|
|
if (install_flags & FWUPD_INSTALL_FLAG_ALLOW_BRANCH_SWITCH) {
|
|
g_variant_builder_add(&builder,
|
|
"{sv}",
|
|
"allow-branch-switch",
|
|
g_variant_new_boolean(TRUE));
|
|
}
|
|
if (install_flags & FWUPD_INSTALL_FLAG_FORCE) {
|
|
g_variant_builder_add(&builder, "{sv}", "force", g_variant_new_boolean(TRUE));
|
|
}
|
|
if (install_flags & FWUPD_INSTALL_FLAG_IGNORE_POWER) {
|
|
g_variant_builder_add(&builder,
|
|
"{sv}",
|
|
"ignore-power",
|
|
g_variant_new_boolean(TRUE));
|
|
}
|
|
if (install_flags & FWUPD_INSTALL_FLAG_NO_HISTORY) {
|
|
g_variant_builder_add(&builder, "{sv}", "no-history", g_variant_new_boolean(TRUE));
|
|
}
|
|
|
|
/* set out of band file descriptor */
|
|
fd_list = g_unix_fd_list_new();
|
|
g_unix_fd_list_append(fd_list, g_unix_input_stream_get_fd(istr), NULL);
|
|
request = g_dbus_message_new_method_call(FWUPD_DBUS_SERVICE,
|
|
FWUPD_DBUS_PATH,
|
|
FWUPD_DBUS_INTERFACE,
|
|
"Install");
|
|
g_dbus_message_set_unix_fd_list(request, fd_list);
|
|
|
|
/* call into daemon */
|
|
g_dbus_message_set_body(
|
|
request,
|
|
g_variant_new("(sha{sv})", device_id, g_unix_input_stream_get_fd(istr), &builder));
|
|
g_dbus_connection_send_message_with_reply(g_dbus_proxy_get_connection(priv->proxy),
|
|
request,
|
|
G_DBUS_SEND_MESSAGE_FLAGS_NONE,
|
|
G_MAXINT,
|
|
NULL,
|
|
cancellable,
|
|
fwupd_client_install_stream_cb,
|
|
g_steal_pointer(&task));
|
|
}
|
|
#endif
|
|
|
|
/**
|
|
* fwupd_client_install_bytes_async:
|
|
* @self: a #FwupdClient
|
|
* @device_id: the device ID
|
|
* @bytes: cabinet archive
|
|
* @install_flags: install flags, e.g. %FWUPD_INSTALL_FLAG_ALLOW_REINSTALL
|
|
* @cancellable: (nullable): optional #GCancellable
|
|
* @callback: the function to run on completion
|
|
* @callback_data: the data to pass to @callback
|
|
*
|
|
* Install firmware onto a specific device.
|
|
*
|
|
* NOTE: This method is thread-safe, but progress signals will be
|
|
* emitted in the global default main context, if not explicitly set with
|
|
* [method@Client.set_main_context].
|
|
*
|
|
* Since: 1.5.0
|
|
**/
|
|
void
|
|
fwupd_client_install_bytes_async(FwupdClient *self,
|
|
const gchar *device_id,
|
|
GBytes *bytes,
|
|
FwupdInstallFlags install_flags,
|
|
GCancellable *cancellable,
|
|
GAsyncReadyCallback callback,
|
|
gpointer callback_data)
|
|
{
|
|
#ifdef HAVE_GIO_UNIX
|
|
FwupdClientPrivate *priv = GET_PRIVATE(self);
|
|
g_autoptr(GError) error = NULL;
|
|
g_autoptr(GUnixInputStream) istr = NULL;
|
|
|
|
g_return_if_fail(FWUPD_IS_CLIENT(self));
|
|
g_return_if_fail(cancellable == NULL || G_IS_CANCELLABLE(cancellable));
|
|
g_return_if_fail(priv->proxy != NULL);
|
|
|
|
/* move to a thread if this ever takes more than a few ms */
|
|
istr = fwupd_unix_input_stream_from_bytes(bytes, &error);
|
|
if (istr == NULL) {
|
|
g_autoptr(GTask) task = g_task_new(self, cancellable, callback, callback_data);
|
|
g_task_return_error(task, g_steal_pointer(&error));
|
|
return;
|
|
}
|
|
|
|
/* call into daemon */
|
|
fwupd_client_install_stream_async(self,
|
|
device_id,
|
|
istr,
|
|
NULL,
|
|
install_flags,
|
|
cancellable,
|
|
callback,
|
|
callback_data);
|
|
#else
|
|
g_autoptr(GTask) task = g_task_new(self, cancellable, callback, callback_data);
|
|
g_task_return_new_error(task,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_NOT_SUPPORTED,
|
|
"Not supported as <glib-unix.h> is unavailable");
|
|
#endif
|
|
}
|
|
|
|
/**
|
|
* fwupd_client_install_bytes_finish:
|
|
* @self: a #FwupdClient
|
|
* @res: the asynchronous result
|
|
* @error: (nullable): optional return location for an error
|
|
*
|
|
* Gets the result of fwupd_client_install_bytes_async().
|
|
*
|
|
* Returns: %TRUE for success
|
|
*
|
|
* Since: 1.5.0
|
|
**/
|
|
gboolean
|
|
fwupd_client_install_bytes_finish(FwupdClient *self, GAsyncResult *res, GError **error)
|
|
{
|
|
g_return_val_if_fail(FWUPD_IS_CLIENT(self), FALSE);
|
|
g_return_val_if_fail(g_task_is_valid(res, self), FALSE);
|
|
g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
|
|
return g_task_propagate_boolean(G_TASK(res), error);
|
|
}
|
|
|
|
/**
|
|
* fwupd_client_install_async:
|
|
* @self: a #FwupdClient
|
|
* @device_id: the device ID
|
|
* @filename: the filename to install
|
|
* @install_flags: install flags, e.g. %FWUPD_INSTALL_FLAG_ALLOW_REINSTALL
|
|
* @cancellable: (nullable): optional #GCancellable
|
|
* @callback: the function to run on completion
|
|
* @callback_data: the data to pass to @callback
|
|
*
|
|
* Install firmware onto a specific device.
|
|
*
|
|
* NOTE: This method is thread-safe, but progress signals will be
|
|
* emitted in the global default main context, if not explicitly set with
|
|
* [method@Client.set_main_context].
|
|
*
|
|
* Since: 1.5.0
|
|
**/
|
|
void
|
|
fwupd_client_install_async(FwupdClient *self,
|
|
const gchar *device_id,
|
|
const gchar *filename,
|
|
FwupdInstallFlags install_flags,
|
|
GCancellable *cancellable,
|
|
GAsyncReadyCallback callback,
|
|
gpointer callback_data)
|
|
{
|
|
#ifdef HAVE_GIO_UNIX
|
|
FwupdClientPrivate *priv = GET_PRIVATE(self);
|
|
g_autoptr(GError) error = NULL;
|
|
g_autoptr(GUnixInputStream) istr = NULL;
|
|
|
|
g_return_if_fail(FWUPD_IS_CLIENT(self));
|
|
g_return_if_fail(device_id != NULL);
|
|
g_return_if_fail(filename != NULL);
|
|
g_return_if_fail(cancellable == NULL || G_IS_CANCELLABLE(cancellable));
|
|
g_return_if_fail(priv->proxy != NULL);
|
|
|
|
/* move to a thread if this ever takes more than a few ms */
|
|
istr = fwupd_unix_input_stream_from_fn(filename, &error);
|
|
if (istr == NULL) {
|
|
g_autoptr(GTask) task = g_task_new(self, cancellable, callback, callback_data);
|
|
g_task_return_error(task, g_steal_pointer(&error));
|
|
return;
|
|
}
|
|
|
|
/* call into daemon */
|
|
fwupd_client_install_stream_async(self,
|
|
device_id,
|
|
istr,
|
|
NULL,
|
|
install_flags,
|
|
cancellable,
|
|
callback,
|
|
callback_data);
|
|
#else
|
|
g_autoptr(GTask) task = g_task_new(self, cancellable, callback, callback_data);
|
|
g_task_return_new_error(task,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_NOT_SUPPORTED,
|
|
"Not supported as <glib-unix.h> is unavailable");
|
|
#endif
|
|
}
|
|
|
|
/**
|
|
* fwupd_client_install_finish:
|
|
* @self: a #FwupdClient
|
|
* @res: the asynchronous result
|
|
* @error: (nullable): optional return location for an error
|
|
*
|
|
* Gets the result of fwupd_client_install_async().
|
|
*
|
|
* Returns: %TRUE for success
|
|
*
|
|
* Since: 1.5.0
|
|
**/
|
|
gboolean
|
|
fwupd_client_install_finish(FwupdClient *self, GAsyncResult *res, GError **error)
|
|
{
|
|
g_return_val_if_fail(FWUPD_IS_CLIENT(self), FALSE);
|
|
g_return_val_if_fail(g_task_is_valid(res, self), FALSE);
|
|
g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
|
|
return g_task_propagate_boolean(G_TASK(res), error);
|
|
}
|
|
|
|
typedef struct {
|
|
FwupdDevice *device;
|
|
FwupdRelease *release;
|
|
FwupdInstallFlags install_flags;
|
|
FwupdClientDownloadFlags download_flags;
|
|
} FwupdClientInstallReleaseData;
|
|
|
|
static void
|
|
fwupd_client_install_release_data_free(FwupdClientInstallReleaseData *data)
|
|
{
|
|
g_object_unref(data->device);
|
|
g_object_unref(data->release);
|
|
g_free(data);
|
|
}
|
|
|
|
static void
|
|
fwupd_client_install_release_cb(GObject *source, GAsyncResult *res, gpointer user_data)
|
|
{
|
|
g_autoptr(GError) error = NULL;
|
|
g_autoptr(GTask) task = G_TASK(user_data);
|
|
|
|
if (!fwupd_client_install_release_finish(FWUPD_CLIENT(source), res, &error)) {
|
|
g_task_return_error(task, g_steal_pointer(&error));
|
|
return;
|
|
}
|
|
|
|
/* success */
|
|
g_task_return_boolean(task, TRUE);
|
|
}
|
|
|
|
static void
|
|
fwupd_client_install_release_bytes_cb(GObject *source, GAsyncResult *res, gpointer user_data)
|
|
{
|
|
g_autoptr(GError) error = NULL;
|
|
g_autoptr(GTask) task = G_TASK(user_data);
|
|
|
|
if (!fwupd_client_install_bytes_finish(FWUPD_CLIENT(source), res, &error)) {
|
|
g_task_return_error(task, g_steal_pointer(&error));
|
|
return;
|
|
}
|
|
|
|
/* success */
|
|
g_task_return_boolean(task, TRUE);
|
|
}
|
|
|
|
static void
|
|
fwupd_client_install_release_download_cb(GObject *source, GAsyncResult *res, gpointer user_data)
|
|
{
|
|
g_autoptr(GBytes) blob = NULL;
|
|
g_autoptr(GError) error = NULL;
|
|
g_autoptr(GTask) task = G_TASK(user_data);
|
|
FwupdClientInstallReleaseData *data = g_task_get_task_data(task);
|
|
GChecksumType checksum_type;
|
|
GCancellable *cancellable = g_task_get_cancellable(task);
|
|
const gchar *checksum_expected;
|
|
g_autofree gchar *checksum_actual = NULL;
|
|
|
|
blob = fwupd_client_download_bytes_finish(FWUPD_CLIENT(source), res, &error);
|
|
if (blob == NULL) {
|
|
g_task_return_error(task, g_steal_pointer(&error));
|
|
return;
|
|
}
|
|
|
|
/* verify checksum */
|
|
checksum_expected = fwupd_checksum_get_best(fwupd_release_get_checksums(data->release));
|
|
checksum_type = fwupd_checksum_guess_kind(checksum_expected);
|
|
checksum_actual = g_compute_checksum_for_bytes(checksum_type, blob);
|
|
if (g_strcmp0(checksum_expected, checksum_actual) != 0) {
|
|
g_task_return_new_error(task,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_INVALID_FILE,
|
|
"checksum invalid, expected %s got %s",
|
|
checksum_expected,
|
|
checksum_actual);
|
|
return;
|
|
}
|
|
|
|
/* if the device specifies ONLY_OFFLINE automatically set this flag */
|
|
if (fwupd_device_has_flag(data->device, FWUPD_DEVICE_FLAG_ONLY_OFFLINE))
|
|
data->install_flags |= FWUPD_INSTALL_FLAG_OFFLINE;
|
|
fwupd_client_install_bytes_async(FWUPD_CLIENT(source),
|
|
fwupd_device_get_id(data->device),
|
|
blob,
|
|
data->install_flags,
|
|
cancellable,
|
|
fwupd_client_install_release_bytes_cb,
|
|
g_steal_pointer(&task));
|
|
}
|
|
|
|
static gboolean
|
|
fwupd_client_is_url_http(const gchar *perhaps_url)
|
|
{
|
|
#ifdef HAVE_LIBCURL_7_62_0
|
|
g_autoptr(CURLU) h = curl_url();
|
|
return curl_url_set(h, CURLUPART_URL, perhaps_url, 0) == CURLUE_OK;
|
|
#else
|
|
return g_str_has_prefix(perhaps_url, "http://") ||
|
|
g_str_has_prefix(perhaps_url, "https://");
|
|
#endif
|
|
}
|
|
|
|
static gboolean
|
|
fwupd_client_is_url_ipfs(const gchar *perhaps_url)
|
|
{
|
|
return g_str_has_prefix(perhaps_url, "ipfs://") || g_str_has_prefix(perhaps_url, "ipns://");
|
|
}
|
|
|
|
static void
|
|
fwupd_client_install_release_remote_cb(GObject *source, GAsyncResult *res, gpointer user_data)
|
|
{
|
|
GPtrArray *locations;
|
|
const gchar *uri_tmp;
|
|
g_autofree gchar *fn = NULL;
|
|
g_autoptr(FwupdRemote) remote = NULL;
|
|
g_autoptr(GError) error = NULL;
|
|
g_autoptr(GTask) task = G_TASK(user_data);
|
|
g_autoptr(GPtrArray) uris_built = g_ptr_array_new_with_free_func(g_free);
|
|
FwupdClientInstallReleaseData *data = g_task_get_task_data(task);
|
|
GCancellable *cancellable = g_task_get_cancellable(task);
|
|
|
|
/* if a remote-id was specified, the remote has to exist */
|
|
remote = fwupd_client_get_remote_by_id_finish(FWUPD_CLIENT(source), res, &error);
|
|
if (remote == NULL) {
|
|
g_task_return_error(task, g_steal_pointer(&error));
|
|
return;
|
|
}
|
|
|
|
/* get the default release only until other parts of fwupd can cope */
|
|
locations = fwupd_release_get_locations(data->release);
|
|
if (locations->len == 0) {
|
|
g_task_return_new_error(task,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_INVALID_FILE,
|
|
"release missing URI");
|
|
return;
|
|
}
|
|
uri_tmp = g_ptr_array_index(locations, 0);
|
|
|
|
/* local and directory remotes may have the firmware already */
|
|
if (fwupd_remote_get_kind(remote) == FWUPD_REMOTE_KIND_LOCAL &&
|
|
!fwupd_client_is_url_http(uri_tmp)) {
|
|
const gchar *fn_cache = fwupd_remote_get_filename_cache(remote);
|
|
g_autofree gchar *path = g_path_get_dirname(fn_cache);
|
|
fn = g_build_filename(path, uri_tmp, NULL);
|
|
} else if (fwupd_remote_get_kind(remote) == FWUPD_REMOTE_KIND_DIRECTORY) {
|
|
fn = g_strdup(uri_tmp + 7);
|
|
}
|
|
|
|
/* install with flags chosen by the user */
|
|
if (fn != NULL) {
|
|
fwupd_client_install_async(FWUPD_CLIENT(source),
|
|
fwupd_device_get_id(data->device),
|
|
fn,
|
|
data->install_flags,
|
|
cancellable,
|
|
fwupd_client_install_release_cb,
|
|
g_steal_pointer(&task));
|
|
return;
|
|
}
|
|
|
|
/* remote file */
|
|
for (guint i = 0; i < locations->len; i++) {
|
|
uri_tmp = g_ptr_array_index(locations, i);
|
|
if (fwupd_client_is_url_ipfs(uri_tmp)) {
|
|
g_ptr_array_add(uris_built, g_strdup(uri_tmp));
|
|
} else if (fwupd_client_is_url_http(uri_tmp)) {
|
|
g_autofree gchar *uri_str = NULL;
|
|
uri_str = fwupd_remote_build_firmware_uri(remote, uri_tmp, &error);
|
|
if (uri_str == NULL) {
|
|
g_task_return_error(task, g_steal_pointer(&error));
|
|
return;
|
|
}
|
|
g_ptr_array_add(uris_built, g_steal_pointer(&uri_str));
|
|
}
|
|
}
|
|
|
|
/* download file */
|
|
fwupd_client_download_bytes2_async(FWUPD_CLIENT(source),
|
|
uris_built,
|
|
data->download_flags,
|
|
cancellable,
|
|
fwupd_client_install_release_download_cb,
|
|
g_steal_pointer(&task));
|
|
}
|
|
|
|
#ifdef HAVE_LIBCURL
|
|
static GPtrArray *
|
|
fwupd_client_filter_locations(GPtrArray *locations,
|
|
FwupdClientDownloadFlags download_flags,
|
|
GError **error)
|
|
{
|
|
g_autoptr(GPtrArray) uris_filtered = g_ptr_array_new_with_free_func(g_free);
|
|
|
|
g_return_val_if_fail(locations != NULL, NULL);
|
|
g_return_val_if_fail(error == NULL || *error == NULL, NULL);
|
|
|
|
for (guint i = 0; i < locations->len; i++) {
|
|
const gchar *uri = g_ptr_array_index(locations, i);
|
|
if ((download_flags & FWUPD_CLIENT_DOWNLOAD_FLAG_ONLY_IPFS) > 0 &&
|
|
!fwupd_client_is_url_ipfs(uri))
|
|
continue;
|
|
g_ptr_array_add(uris_filtered, g_strdup(uri));
|
|
}
|
|
if (uris_filtered->len == 0) {
|
|
g_set_error_literal(error,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_INVALID_FILE,
|
|
"no valid release URIs");
|
|
return NULL;
|
|
}
|
|
return g_steal_pointer(&uris_filtered);
|
|
}
|
|
#endif
|
|
|
|
/**
|
|
* fwupd_client_install_release2_async:
|
|
* @self: a #FwupdClient
|
|
* @device: a device
|
|
* @release: a release
|
|
* @install_flags: install flags, e.g. %FWUPD_INSTALL_FLAG_ALLOW_REINSTALL
|
|
* @download_flags: download flags, e.g. %FWUPD_CLIENT_DOWNLOAD_FLAG_DISABLE_IPFS
|
|
* @cancellable: (nullable): optional #GCancellable
|
|
* @callback: the function to run on completion
|
|
* @callback_data: the data to pass to @callback
|
|
*
|
|
* Installs a new release on a device, downloading the firmware if required.
|
|
*
|
|
* NOTE: This method is thread-safe, but progress signals will be
|
|
* emitted in the global default main context, if not explicitly set with
|
|
* [method@Client.set_main_context].
|
|
*
|
|
* Since: 1.5.6
|
|
**/
|
|
void
|
|
fwupd_client_install_release2_async(FwupdClient *self,
|
|
FwupdDevice *device,
|
|
FwupdRelease *release,
|
|
FwupdInstallFlags install_flags,
|
|
FwupdClientDownloadFlags download_flags,
|
|
GCancellable *cancellable,
|
|
GAsyncReadyCallback callback,
|
|
gpointer callback_data)
|
|
{
|
|
FwupdClientPrivate *priv = GET_PRIVATE(self);
|
|
g_autoptr(GTask) task = NULL;
|
|
FwupdClientInstallReleaseData *data;
|
|
const gchar *remote_id;
|
|
|
|
g_return_if_fail(FWUPD_IS_CLIENT(self));
|
|
g_return_if_fail(FWUPD_IS_DEVICE(device));
|
|
g_return_if_fail(FWUPD_IS_RELEASE(release));
|
|
g_return_if_fail(cancellable == NULL || G_IS_CANCELLABLE(cancellable));
|
|
g_return_if_fail(priv->proxy != NULL);
|
|
|
|
/* call into daemon */
|
|
task = g_task_new(self, cancellable, callback, callback_data);
|
|
data = g_new0(FwupdClientInstallReleaseData, 1);
|
|
data->device = g_object_ref(device);
|
|
data->release = g_object_ref(release);
|
|
data->download_flags = download_flags;
|
|
data->install_flags = install_flags;
|
|
g_task_set_task_data(task, data, (GDestroyNotify)fwupd_client_install_release_data_free);
|
|
|
|
/* work out what remote-specific URI fields this should use */
|
|
remote_id = fwupd_release_get_remote_id(release);
|
|
if (remote_id == NULL) {
|
|
fwupd_client_download_bytes2_async(self,
|
|
fwupd_release_get_locations(release),
|
|
download_flags,
|
|
cancellable,
|
|
fwupd_client_install_release_download_cb,
|
|
g_steal_pointer(&task));
|
|
return;
|
|
}
|
|
|
|
/* if a remote-id was specified, the remote has to exist */
|
|
fwupd_client_get_remote_by_id_async(self,
|
|
remote_id,
|
|
cancellable,
|
|
fwupd_client_install_release_remote_cb,
|
|
g_steal_pointer(&task));
|
|
}
|
|
|
|
/**
|
|
* fwupd_client_install_release_async:
|
|
* @self: a #FwupdClient
|
|
* @device: a device
|
|
* @release: a release
|
|
* @install_flags: install flags, e.g. %FWUPD_INSTALL_FLAG_ALLOW_REINSTALL
|
|
* @cancellable: (nullable): optional #GCancellable
|
|
* @callback: the function to run on completion
|
|
* @callback_data: the data to pass to @callback
|
|
*
|
|
* Installs a new release on a device, downloading the firmware if required.
|
|
*
|
|
* NOTE: This method is thread-safe, but progress signals will be
|
|
* emitted in the global default main context, if not explicitly set with
|
|
* [method@Client.set_main_context].
|
|
*
|
|
* Since: 1.5.0
|
|
* Deprecated: 1.5.6
|
|
**/
|
|
void
|
|
fwupd_client_install_release_async(FwupdClient *self,
|
|
FwupdDevice *device,
|
|
FwupdRelease *release,
|
|
FwupdInstallFlags install_flags,
|
|
GCancellable *cancellable,
|
|
GAsyncReadyCallback callback,
|
|
gpointer callback_data)
|
|
{
|
|
return fwupd_client_install_release2_async(self,
|
|
device,
|
|
release,
|
|
install_flags,
|
|
FWUPD_CLIENT_DOWNLOAD_FLAG_NONE,
|
|
cancellable,
|
|
callback,
|
|
callback_data);
|
|
}
|
|
|
|
/**
|
|
* fwupd_client_install_release_finish:
|
|
* @self: a #FwupdClient
|
|
* @res: the asynchronous result
|
|
* @error: (nullable): optional return location for an error
|
|
*
|
|
* Gets the result of fwupd_client_install_release_async().
|
|
*
|
|
* Returns: %TRUE for success
|
|
*
|
|
* Since: 1.5.0
|
|
**/
|
|
gboolean
|
|
fwupd_client_install_release_finish(FwupdClient *self, GAsyncResult *res, GError **error)
|
|
{
|
|
g_return_val_if_fail(FWUPD_IS_CLIENT(self), FALSE);
|
|
g_return_val_if_fail(g_task_is_valid(res, self), FALSE);
|
|
g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
|
|
return g_task_propagate_boolean(G_TASK(res), error);
|
|
}
|
|
|
|
#ifdef HAVE_GIO_UNIX
|
|
|
|
static void
|
|
fwupd_client_get_details_stream_cb(GObject *source, GAsyncResult *res, gpointer user_data)
|
|
{
|
|
g_autoptr(GDBusMessage) msg = NULL;
|
|
g_autoptr(GError) error = NULL;
|
|
g_autoptr(GTask) task = G_TASK(user_data);
|
|
|
|
msg = g_dbus_connection_send_message_with_reply_finish(G_DBUS_CONNECTION(source),
|
|
res,
|
|
&error);
|
|
if (msg == NULL) {
|
|
fwupd_client_fixup_dbus_error(error);
|
|
g_task_return_error(task, g_steal_pointer(&error));
|
|
return;
|
|
}
|
|
if (g_dbus_message_to_gerror(msg, &error)) {
|
|
fwupd_client_fixup_dbus_error(error);
|
|
g_task_return_error(task, g_steal_pointer(&error));
|
|
return;
|
|
}
|
|
|
|
/* success */
|
|
g_task_return_pointer(task,
|
|
fwupd_device_array_from_variant(g_dbus_message_get_body(msg)),
|
|
(GDestroyNotify)g_ptr_array_unref);
|
|
}
|
|
|
|
void
|
|
fwupd_client_get_details_stream_async(FwupdClient *self,
|
|
GUnixInputStream *istr,
|
|
GCancellable *cancellable,
|
|
GAsyncReadyCallback callback,
|
|
gpointer callback_data)
|
|
{
|
|
FwupdClientPrivate *priv = GET_PRIVATE(self);
|
|
gint fd = g_unix_input_stream_get_fd(istr);
|
|
g_autoptr(GDBusMessage) request = NULL;
|
|
g_autoptr(GUnixFDList) fd_list = NULL;
|
|
g_autoptr(GTask) task = g_task_new(self, cancellable, callback, callback_data);
|
|
|
|
/* set out of band file descriptor */
|
|
fd_list = g_unix_fd_list_new();
|
|
g_unix_fd_list_append(fd_list, fd, NULL);
|
|
request = g_dbus_message_new_method_call(FWUPD_DBUS_SERVICE,
|
|
FWUPD_DBUS_PATH,
|
|
FWUPD_DBUS_INTERFACE,
|
|
"GetDetails");
|
|
g_dbus_message_set_unix_fd_list(request, fd_list);
|
|
|
|
/* call into daemon */
|
|
g_dbus_message_set_body(request, g_variant_new("(h)", fd));
|
|
g_dbus_connection_send_message_with_reply(g_dbus_proxy_get_connection(priv->proxy),
|
|
request,
|
|
G_DBUS_SEND_MESSAGE_FLAGS_NONE,
|
|
G_MAXINT,
|
|
NULL,
|
|
cancellable,
|
|
fwupd_client_get_details_stream_cb,
|
|
g_steal_pointer(&task));
|
|
}
|
|
#endif
|
|
|
|
/**
|
|
* fwupd_client_get_details_bytes_async:
|
|
* @self: a #FwupdClient
|
|
* @bytes: firmware archive
|
|
* @cancellable: (nullable): optional #GCancellable
|
|
* @callback: the function to run on completion
|
|
* @callback_data: the data to pass to @callback
|
|
*
|
|
* Gets details about a specific firmware file.
|
|
*
|
|
* Since: 1.5.0
|
|
**/
|
|
void
|
|
fwupd_client_get_details_bytes_async(FwupdClient *self,
|
|
GBytes *bytes,
|
|
GCancellable *cancellable,
|
|
GAsyncReadyCallback callback,
|
|
gpointer callback_data)
|
|
{
|
|
#ifdef HAVE_GIO_UNIX
|
|
FwupdClientPrivate *priv = GET_PRIVATE(self);
|
|
g_autoptr(GError) error = NULL;
|
|
g_autoptr(GUnixInputStream) istr = NULL;
|
|
|
|
g_return_if_fail(FWUPD_IS_CLIENT(self));
|
|
g_return_if_fail(cancellable == NULL || G_IS_CANCELLABLE(cancellable));
|
|
g_return_if_fail(priv->proxy != NULL);
|
|
|
|
/* move to a thread if this ever takes more than a few ms */
|
|
istr = fwupd_unix_input_stream_from_bytes(bytes, &error);
|
|
if (istr == NULL) {
|
|
g_autoptr(GTask) task = g_task_new(self, cancellable, callback, callback_data);
|
|
g_task_return_error(task, g_steal_pointer(&error));
|
|
return;
|
|
}
|
|
|
|
/* call into daemon */
|
|
fwupd_client_get_details_stream_async(self, istr, cancellable, callback, callback_data);
|
|
#else
|
|
g_autoptr(GTask) task = g_task_new(self, cancellable, callback, callback_data);
|
|
g_task_return_new_error(task,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_NOT_SUPPORTED,
|
|
"Not supported as <glib-unix.h> is unavailable");
|
|
#endif
|
|
}
|
|
|
|
/**
|
|
* fwupd_client_get_details_bytes_finish:
|
|
* @self: a #FwupdClient
|
|
* @res: the asynchronous result
|
|
* @error: (nullable): optional return location for an error
|
|
*
|
|
* Gets the result of fwupd_client_get_details_bytes_async().
|
|
*
|
|
* Returns: (transfer container) (element-type FwupdDevice): an array of results
|
|
*
|
|
* Since: 1.5.0
|
|
**/
|
|
GPtrArray *
|
|
fwupd_client_get_details_bytes_finish(FwupdClient *self, GAsyncResult *res, GError **error)
|
|
{
|
|
g_return_val_if_fail(FWUPD_IS_CLIENT(self), NULL);
|
|
g_return_val_if_fail(g_task_is_valid(res, self), NULL);
|
|
g_return_val_if_fail(error == NULL || *error == NULL, NULL);
|
|
return g_task_propagate_pointer(G_TASK(res), error);
|
|
}
|
|
|
|
/**
|
|
* fwupd_client_get_percentage:
|
|
* @self: a #FwupdClient
|
|
*
|
|
* Gets the last returned percentage value.
|
|
*
|
|
* Returns: a percentage, or 0 for unknown.
|
|
*
|
|
* Since: 0.7.3
|
|
**/
|
|
guint
|
|
fwupd_client_get_percentage(FwupdClient *self)
|
|
{
|
|
FwupdClientPrivate *priv = GET_PRIVATE(self);
|
|
g_return_val_if_fail(FWUPD_IS_CLIENT(self), 0);
|
|
return priv->percentage;
|
|
}
|
|
|
|
/**
|
|
* fwupd_client_get_daemon_version:
|
|
* @self: a #FwupdClient
|
|
*
|
|
* Gets the daemon version number.
|
|
*
|
|
* Returns: a string, or %NULL for unknown.
|
|
*
|
|
* Since: 0.9.6
|
|
**/
|
|
const gchar *
|
|
fwupd_client_get_daemon_version(FwupdClient *self)
|
|
{
|
|
FwupdClientPrivate *priv = GET_PRIVATE(self);
|
|
g_return_val_if_fail(FWUPD_IS_CLIENT(self), NULL);
|
|
return priv->daemon_version;
|
|
}
|
|
|
|
/**
|
|
* fwupd_client_get_host_product:
|
|
* @self: a #FwupdClient
|
|
*
|
|
* Gets the string that represents the host running fwupd
|
|
*
|
|
* Returns: a string, or %NULL for unknown.
|
|
*
|
|
* Since: 1.3.1
|
|
**/
|
|
const gchar *
|
|
fwupd_client_get_host_product(FwupdClient *self)
|
|
{
|
|
FwupdClientPrivate *priv = GET_PRIVATE(self);
|
|
g_return_val_if_fail(FWUPD_IS_CLIENT(self), NULL);
|
|
return priv->host_product;
|
|
}
|
|
|
|
/**
|
|
* fwupd_client_get_host_machine_id:
|
|
* @self: a #FwupdClient
|
|
*
|
|
* Gets the string that represents the host machine ID
|
|
*
|
|
* Returns: a string, or %NULL for unknown.
|
|
*
|
|
* Since: 1.3.2
|
|
**/
|
|
const gchar *
|
|
fwupd_client_get_host_machine_id(FwupdClient *self)
|
|
{
|
|
FwupdClientPrivate *priv = GET_PRIVATE(self);
|
|
g_return_val_if_fail(FWUPD_IS_CLIENT(self), NULL);
|
|
return priv->host_machine_id;
|
|
}
|
|
|
|
/**
|
|
* fwupd_client_get_host_security_id:
|
|
* @self: a #FwupdClient
|
|
*
|
|
* Gets the string that represents the host machine ID
|
|
*
|
|
* Returns: a string, or %NULL for unknown.
|
|
*
|
|
* Since: 1.5.0
|
|
**/
|
|
const gchar *
|
|
fwupd_client_get_host_security_id(FwupdClient *self)
|
|
{
|
|
FwupdClientPrivate *priv = GET_PRIVATE(self);
|
|
g_return_val_if_fail(FWUPD_IS_CLIENT(self), NULL);
|
|
return priv->host_security_id;
|
|
}
|
|
|
|
/**
|
|
* fwupd_client_get_status:
|
|
* @self: a #FwupdClient
|
|
*
|
|
* Gets the last returned status value.
|
|
*
|
|
* Returns: a #FwupdStatus, or %FWUPD_STATUS_UNKNOWN for unknown.
|
|
*
|
|
* Since: 0.7.3
|
|
**/
|
|
FwupdStatus
|
|
fwupd_client_get_status(FwupdClient *self)
|
|
{
|
|
FwupdClientPrivate *priv = GET_PRIVATE(self);
|
|
g_return_val_if_fail(FWUPD_IS_CLIENT(self), FWUPD_STATUS_UNKNOWN);
|
|
return priv->status;
|
|
}
|
|
|
|
/**
|
|
* fwupd_client_get_tainted:
|
|
* @self: a #FwupdClient
|
|
*
|
|
* Gets if the daemon has been tainted by 3rd party code.
|
|
*
|
|
* Returns: %TRUE if the daemon is unsupported
|
|
*
|
|
* Since: 1.2.4
|
|
**/
|
|
gboolean
|
|
fwupd_client_get_tainted(FwupdClient *self)
|
|
{
|
|
FwupdClientPrivate *priv = GET_PRIVATE(self);
|
|
g_return_val_if_fail(FWUPD_IS_CLIENT(self), FALSE);
|
|
return priv->tainted;
|
|
}
|
|
|
|
/**
|
|
* fwupd_client_get_daemon_interactive:
|
|
* @self: a #FwupdClient
|
|
*
|
|
* Gets if the daemon is running in an interactive terminal.
|
|
*
|
|
* Returns: %TRUE if the daemon is running in an interactive terminal
|
|
*
|
|
* Since: 1.3.4
|
|
**/
|
|
gboolean
|
|
fwupd_client_get_daemon_interactive(FwupdClient *self)
|
|
{
|
|
FwupdClientPrivate *priv = GET_PRIVATE(self);
|
|
g_return_val_if_fail(FWUPD_IS_CLIENT(self), FALSE);
|
|
return priv->interactive;
|
|
}
|
|
|
|
#ifdef HAVE_GIO_UNIX
|
|
|
|
static void
|
|
fwupd_client_update_metadata_stream_cb(GObject *source, GAsyncResult *res, gpointer user_data)
|
|
{
|
|
g_autoptr(GDBusMessage) msg = NULL;
|
|
g_autoptr(GError) error = NULL;
|
|
g_autoptr(GTask) task = G_TASK(user_data);
|
|
|
|
msg = g_dbus_connection_send_message_with_reply_finish(G_DBUS_CONNECTION(source),
|
|
res,
|
|
&error);
|
|
if (msg == NULL) {
|
|
fwupd_client_fixup_dbus_error(error);
|
|
g_task_return_error(task, g_steal_pointer(&error));
|
|
return;
|
|
}
|
|
if (g_dbus_message_to_gerror(msg, &error)) {
|
|
fwupd_client_fixup_dbus_error(error);
|
|
g_task_return_error(task, g_steal_pointer(&error));
|
|
return;
|
|
}
|
|
|
|
/* success */
|
|
g_task_return_boolean(task, TRUE);
|
|
}
|
|
|
|
void
|
|
fwupd_client_update_metadata_stream_async(FwupdClient *self,
|
|
const gchar *remote_id,
|
|
GUnixInputStream *istr,
|
|
GUnixInputStream *istr_sig,
|
|
GCancellable *cancellable,
|
|
GAsyncReadyCallback callback,
|
|
gpointer callback_data)
|
|
{
|
|
FwupdClientPrivate *priv = GET_PRIVATE(self);
|
|
g_autoptr(GDBusMessage) request = NULL;
|
|
g_autoptr(GUnixFDList) fd_list = NULL;
|
|
g_autoptr(GTask) task = g_task_new(self, cancellable, callback, callback_data);
|
|
|
|
/* set out of band file descriptor */
|
|
fd_list = g_unix_fd_list_new();
|
|
g_unix_fd_list_append(fd_list, g_unix_input_stream_get_fd(istr), NULL);
|
|
g_unix_fd_list_append(fd_list, g_unix_input_stream_get_fd(istr_sig), NULL);
|
|
request = g_dbus_message_new_method_call(FWUPD_DBUS_SERVICE,
|
|
FWUPD_DBUS_PATH,
|
|
FWUPD_DBUS_INTERFACE,
|
|
"UpdateMetadata");
|
|
g_dbus_message_set_unix_fd_list(request, fd_list);
|
|
|
|
/* call into daemon */
|
|
g_dbus_message_set_body(request,
|
|
g_variant_new("(shh)",
|
|
remote_id,
|
|
g_unix_input_stream_get_fd(istr),
|
|
g_unix_input_stream_get_fd(istr_sig)));
|
|
g_dbus_connection_send_message_with_reply(g_dbus_proxy_get_connection(priv->proxy),
|
|
request,
|
|
G_DBUS_SEND_MESSAGE_FLAGS_NONE,
|
|
G_MAXINT,
|
|
NULL,
|
|
cancellable,
|
|
fwupd_client_update_metadata_stream_cb,
|
|
g_steal_pointer(&task));
|
|
}
|
|
#endif
|
|
|
|
/**
|
|
* fwupd_client_update_metadata_bytes_async:
|
|
* @self: a #FwupdClient
|
|
* @remote_id: remote ID, e.g. `lvfs-testing`
|
|
* @metadata: XML metadata data
|
|
* @signature: signature data
|
|
* @cancellable: (nullable): optional #GCancellable
|
|
* @callback: the function to run on completion
|
|
* @callback_data: the data to pass to @callback
|
|
*
|
|
* Updates the metadata. This allows a session process to download the metadata
|
|
* and metadata signing file to be passed into the daemon to be checked and
|
|
* parsed.
|
|
*
|
|
* The @remote_id allows the firmware to be tagged so that the remote can be
|
|
* matched when the firmware is downloaded.
|
|
*
|
|
* NOTE: This method is thread-safe, but progress signals will be
|
|
* emitted in the global default main context, if not explicitly set with
|
|
* [method@Client.set_main_context].
|
|
*
|
|
* Since: 1.5.0
|
|
**/
|
|
void
|
|
fwupd_client_update_metadata_bytes_async(FwupdClient *self,
|
|
const gchar *remote_id,
|
|
GBytes *metadata,
|
|
GBytes *signature,
|
|
GCancellable *cancellable,
|
|
GAsyncReadyCallback callback,
|
|
gpointer callback_data)
|
|
{
|
|
#ifdef HAVE_GIO_UNIX
|
|
FwupdClientPrivate *priv = GET_PRIVATE(self);
|
|
g_autoptr(GError) error = NULL;
|
|
g_autoptr(GUnixInputStream) istr = NULL;
|
|
g_autoptr(GUnixInputStream) istr_sig = NULL;
|
|
|
|
g_return_if_fail(FWUPD_IS_CLIENT(self));
|
|
g_return_if_fail(remote_id != NULL);
|
|
g_return_if_fail(metadata != NULL);
|
|
g_return_if_fail(signature != NULL);
|
|
g_return_if_fail(cancellable == NULL || G_IS_CANCELLABLE(cancellable));
|
|
g_return_if_fail(priv->proxy != NULL);
|
|
|
|
/* move to a thread if this ever takes more than a few ms */
|
|
istr = fwupd_unix_input_stream_from_bytes(metadata, &error);
|
|
if (istr == NULL) {
|
|
g_autoptr(GTask) task = g_task_new(self, cancellable, callback, callback_data);
|
|
g_task_return_error(task, g_steal_pointer(&error));
|
|
return;
|
|
}
|
|
istr_sig = fwupd_unix_input_stream_from_bytes(signature, &error);
|
|
if (istr_sig == NULL) {
|
|
g_autoptr(GTask) task = g_task_new(self, cancellable, callback, callback_data);
|
|
g_task_return_error(task, g_steal_pointer(&error));
|
|
return;
|
|
}
|
|
|
|
/* call into daemon */
|
|
fwupd_client_update_metadata_stream_async(self,
|
|
remote_id,
|
|
istr,
|
|
istr_sig,
|
|
cancellable,
|
|
callback,
|
|
callback_data);
|
|
#else
|
|
g_autoptr(GTask) task = g_task_new(self, cancellable, callback, callback_data);
|
|
g_task_return_new_error(task,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_NOT_SUPPORTED,
|
|
"Not supported as <glib-unix.h> is unavailable");
|
|
#endif
|
|
}
|
|
|
|
/**
|
|
* fwupd_client_update_metadata_bytes_finish:
|
|
* @self: a #FwupdClient
|
|
* @res: the asynchronous result
|
|
* @error: (nullable): optional return location for an error
|
|
*
|
|
* Gets the result of fwupd_client_update_metadata_bytes_async().
|
|
*
|
|
* Returns: %TRUE for success
|
|
*
|
|
* Since: 1.5.0
|
|
**/
|
|
gboolean
|
|
fwupd_client_update_metadata_bytes_finish(FwupdClient *self, GAsyncResult *res, GError **error)
|
|
{
|
|
g_return_val_if_fail(FWUPD_IS_CLIENT(self), FALSE);
|
|
g_return_val_if_fail(g_task_is_valid(res, self), FALSE);
|
|
g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
|
|
return g_task_propagate_boolean(G_TASK(res), error);
|
|
}
|
|
|
|
typedef struct {
|
|
FwupdRemote *remote;
|
|
GBytes *signature;
|
|
GBytes *metadata;
|
|
} FwupdClientRefreshRemoteData;
|
|
|
|
static void
|
|
fwupd_client_refresh_remote_data_free(FwupdClientRefreshRemoteData *data)
|
|
{
|
|
if (data->signature != NULL)
|
|
g_bytes_unref(data->signature);
|
|
if (data->metadata != NULL)
|
|
g_bytes_unref(data->metadata);
|
|
g_object_unref(data->remote);
|
|
g_free(data);
|
|
}
|
|
|
|
static void
|
|
fwupd_client_refresh_remote_update_cb(GObject *source, GAsyncResult *res, gpointer user_data)
|
|
{
|
|
g_autoptr(GError) error = NULL;
|
|
g_autoptr(GTask) task = G_TASK(user_data);
|
|
|
|
/* save metadata */
|
|
if (!fwupd_client_update_metadata_bytes_finish(FWUPD_CLIENT(source), res, &error)) {
|
|
g_task_return_error(task, g_steal_pointer(&error));
|
|
return;
|
|
}
|
|
|
|
/* success */
|
|
g_task_return_boolean(task, TRUE);
|
|
}
|
|
|
|
static void
|
|
fwupd_client_refresh_remote_metadata_cb(GObject *source, GAsyncResult *res, gpointer user_data)
|
|
{
|
|
g_autoptr(GBytes) bytes = NULL;
|
|
g_autoptr(GError) error = NULL;
|
|
g_autoptr(GTask) task = G_TASK(user_data);
|
|
FwupdClientRefreshRemoteData *data = g_task_get_task_data(task);
|
|
FwupdClient *self = g_task_get_source_object(task);
|
|
GCancellable *cancellable = g_task_get_cancellable(task);
|
|
|
|
/* save metadata */
|
|
bytes = fwupd_client_download_bytes_finish(FWUPD_CLIENT(source), res, &error);
|
|
if (bytes == NULL) {
|
|
g_task_return_error(task, g_steal_pointer(&error));
|
|
return;
|
|
}
|
|
data->metadata = g_steal_pointer(&bytes);
|
|
|
|
/* send all this to fwupd */
|
|
fwupd_client_update_metadata_bytes_async(self,
|
|
fwupd_remote_get_id(data->remote),
|
|
data->metadata,
|
|
data->signature,
|
|
cancellable,
|
|
fwupd_client_refresh_remote_update_cb,
|
|
g_steal_pointer(&task));
|
|
}
|
|
|
|
static void
|
|
fwupd_client_refresh_remote_signature_cb(GObject *source, GAsyncResult *res, gpointer user_data)
|
|
{
|
|
g_autoptr(GBytes) bytes = NULL;
|
|
g_autoptr(GError) error = NULL;
|
|
g_autoptr(GTask) task = G_TASK(user_data);
|
|
FwupdClientRefreshRemoteData *data = g_task_get_task_data(task);
|
|
FwupdClient *self = g_task_get_source_object(task);
|
|
GCancellable *cancellable = g_task_get_cancellable(task);
|
|
GChecksumType checksum_kind;
|
|
g_autofree gchar *checksum = NULL;
|
|
|
|
/* save signature */
|
|
bytes = fwupd_client_download_bytes_finish(FWUPD_CLIENT(source), res, &error);
|
|
if (bytes == NULL) {
|
|
g_task_return_error(task, g_steal_pointer(&error));
|
|
return;
|
|
}
|
|
data->signature = g_steal_pointer(&bytes);
|
|
if (fwupd_remote_get_keyring_kind(data->remote) == FWUPD_KEYRING_KIND_JCAT) {
|
|
if (!fwupd_remote_load_signature_bytes(data->remote, data->signature, &error)) {
|
|
g_prefix_error(&error, "Failed to load signature: ");
|
|
g_task_return_error(task, g_steal_pointer(&error));
|
|
return;
|
|
}
|
|
}
|
|
|
|
/* is the signature checksum the same? */
|
|
checksum_kind = fwupd_checksum_guess_kind(fwupd_remote_get_checksum(data->remote));
|
|
checksum =
|
|
g_compute_checksum_for_data(checksum_kind,
|
|
(const guchar *)g_bytes_get_data(data->signature, NULL),
|
|
g_bytes_get_size(data->signature));
|
|
if (g_strcmp0(checksum, fwupd_remote_get_checksum(data->remote)) == 0) {
|
|
g_debug("metadata signature of %s is unchanged, skipping",
|
|
fwupd_remote_get_id(data->remote));
|
|
g_task_return_boolean(task, TRUE);
|
|
return;
|
|
}
|
|
|
|
/* download metadata */
|
|
fwupd_client_download_bytes_async(self,
|
|
fwupd_remote_get_metadata_uri(data->remote),
|
|
FWUPD_CLIENT_DOWNLOAD_FLAG_NONE,
|
|
cancellable,
|
|
fwupd_client_refresh_remote_metadata_cb,
|
|
g_steal_pointer(&task));
|
|
}
|
|
|
|
/**
|
|
* fwupd_client_refresh_remote_async:
|
|
* @self: a #FwupdClient
|
|
* @remote: a #FwupdRemote
|
|
* @cancellable: (nullable): optional #GCancellable
|
|
* @callback: the function to run on completion
|
|
* @callback_data: the data to pass to @callback
|
|
*
|
|
* Refreshes a remote by downloading new metadata.
|
|
*
|
|
* NOTE: This method is thread-safe, but progress signals will be
|
|
* emitted in the global default main context, if not explicitly set with
|
|
* [method@Client.set_main_context].
|
|
*
|
|
* Since: 1.5.0
|
|
**/
|
|
void
|
|
fwupd_client_refresh_remote_async(FwupdClient *self,
|
|
FwupdRemote *remote,
|
|
GCancellable *cancellable,
|
|
GAsyncReadyCallback callback,
|
|
gpointer callback_data)
|
|
{
|
|
FwupdClientRefreshRemoteData *data;
|
|
g_autoptr(GTask) task = NULL;
|
|
|
|
g_return_if_fail(FWUPD_IS_CLIENT(self));
|
|
g_return_if_fail(FWUPD_IS_REMOTE(remote));
|
|
g_return_if_fail(cancellable == NULL || G_IS_CANCELLABLE(cancellable));
|
|
|
|
task = g_task_new(self, cancellable, callback, callback_data);
|
|
data = g_new0(FwupdClientRefreshRemoteData, 1);
|
|
data->remote = g_object_ref(remote);
|
|
g_task_set_task_data(task,
|
|
g_steal_pointer(&data),
|
|
(GDestroyNotify)fwupd_client_refresh_remote_data_free);
|
|
|
|
/* download signature */
|
|
fwupd_client_download_bytes_async(self,
|
|
fwupd_remote_get_metadata_uri_sig(remote),
|
|
FWUPD_CLIENT_DOWNLOAD_FLAG_NONE,
|
|
cancellable,
|
|
fwupd_client_refresh_remote_signature_cb,
|
|
g_steal_pointer(&task));
|
|
}
|
|
|
|
/**
|
|
* fwupd_client_refresh_remote_finish:
|
|
* @self: a #FwupdClient
|
|
* @res: the asynchronous result
|
|
* @error: (nullable): optional return location for an error
|
|
*
|
|
* Gets the result of fwupd_client_refresh_remote_async().
|
|
*
|
|
* Returns: %TRUE for success
|
|
*
|
|
* Since: 1.5.0
|
|
**/
|
|
gboolean
|
|
fwupd_client_refresh_remote_finish(FwupdClient *self, GAsyncResult *res, GError **error)
|
|
{
|
|
g_return_val_if_fail(FWUPD_IS_CLIENT(self), FALSE);
|
|
g_return_val_if_fail(g_task_is_valid(res, self), FALSE);
|
|
g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
|
|
return g_task_propagate_boolean(G_TASK(res), error);
|
|
}
|
|
|
|
static void
|
|
fwupd_client_get_remotes_cb(GObject *source, GAsyncResult *res, gpointer user_data)
|
|
{
|
|
g_autoptr(GTask) task = G_TASK(user_data);
|
|
g_autoptr(GError) error = NULL;
|
|
g_autoptr(GVariant) val = NULL;
|
|
|
|
val = g_dbus_proxy_call_finish(G_DBUS_PROXY(source), res, &error);
|
|
if (val == NULL) {
|
|
fwupd_client_fixup_dbus_error(error);
|
|
g_task_return_error(task, g_steal_pointer(&error));
|
|
return;
|
|
}
|
|
|
|
/* success */
|
|
g_task_return_pointer(task,
|
|
fwupd_remote_array_from_variant(val),
|
|
(GDestroyNotify)g_ptr_array_unref);
|
|
}
|
|
|
|
/**
|
|
* fwupd_client_get_remotes_async:
|
|
* @self: a #FwupdClient
|
|
* @cancellable: (nullable): optional #GCancellable
|
|
* @callback: the function to run on completion
|
|
* @callback_data: the data to pass to @callback
|
|
*
|
|
* Gets the list of remotes that have been configured for the system.
|
|
*
|
|
* You must have called [method@Client.connect_async] on @self before using
|
|
* this method.
|
|
*
|
|
* Since: 1.5.0
|
|
**/
|
|
void
|
|
fwupd_client_get_remotes_async(FwupdClient *self,
|
|
GCancellable *cancellable,
|
|
GAsyncReadyCallback callback,
|
|
gpointer callback_data)
|
|
{
|
|
FwupdClientPrivate *priv = GET_PRIVATE(self);
|
|
g_autoptr(GTask) task = NULL;
|
|
|
|
g_return_if_fail(FWUPD_IS_CLIENT(self));
|
|
g_return_if_fail(cancellable == NULL || G_IS_CANCELLABLE(cancellable));
|
|
g_return_if_fail(priv->proxy != NULL);
|
|
|
|
/* call into daemon */
|
|
task = g_task_new(self, cancellable, callback, callback_data);
|
|
g_dbus_proxy_call(priv->proxy,
|
|
"GetRemotes",
|
|
NULL,
|
|
G_DBUS_CALL_FLAGS_NONE,
|
|
-1,
|
|
cancellable,
|
|
fwupd_client_get_remotes_cb,
|
|
g_steal_pointer(&task));
|
|
}
|
|
|
|
/**
|
|
* fwupd_client_get_remotes_finish:
|
|
* @self: a #FwupdClient
|
|
* @res: the asynchronous result
|
|
* @error: (nullable): optional return location for an error
|
|
*
|
|
* Gets the result of fwupd_client_get_remotes_async().
|
|
*
|
|
* Returns: (element-type FwupdRemote) (transfer container): results
|
|
*
|
|
* Since: 1.5.0
|
|
**/
|
|
GPtrArray *
|
|
fwupd_client_get_remotes_finish(FwupdClient *self, GAsyncResult *res, GError **error)
|
|
{
|
|
g_return_val_if_fail(FWUPD_IS_CLIENT(self), NULL);
|
|
g_return_val_if_fail(g_task_is_valid(res, self), NULL);
|
|
g_return_val_if_fail(error == NULL || *error == NULL, NULL);
|
|
return g_task_propagate_pointer(G_TASK(res), error);
|
|
}
|
|
|
|
static void
|
|
fwupd_client_get_approved_firmware_cb(GObject *source, GAsyncResult *res, gpointer user_data)
|
|
{
|
|
g_auto(GStrv) strv = NULL;
|
|
g_autoptr(GTask) task = G_TASK(user_data);
|
|
g_autoptr(GError) error = NULL;
|
|
g_autoptr(GPtrArray) array = g_ptr_array_new_with_free_func(g_free);
|
|
g_autoptr(GVariant) val = NULL;
|
|
|
|
val = g_dbus_proxy_call_finish(G_DBUS_PROXY(source), res, &error);
|
|
if (val == NULL) {
|
|
fwupd_client_fixup_dbus_error(error);
|
|
g_task_return_error(task, g_steal_pointer(&error));
|
|
return;
|
|
}
|
|
|
|
g_variant_get(val, "(^as)", &strv);
|
|
for (guint i = 0; strv[i] != NULL; i++)
|
|
g_ptr_array_add(array, g_strdup(strv[i]));
|
|
|
|
/* success */
|
|
g_task_return_pointer(task, g_steal_pointer(&array), (GDestroyNotify)g_ptr_array_unref);
|
|
}
|
|
|
|
/**
|
|
* fwupd_client_get_approved_firmware_async:
|
|
* @self: a #FwupdClient
|
|
* @cancellable: (nullable): optional #GCancellable
|
|
* @callback: the function to run on completion
|
|
* @callback_data: the data to pass to @callback
|
|
*
|
|
* Gets the list of approved firmware.
|
|
*
|
|
* You must have called [method@Client.connect_async] on @self before using
|
|
* this method.
|
|
*
|
|
* Since: 1.5.0
|
|
**/
|
|
void
|
|
fwupd_client_get_approved_firmware_async(FwupdClient *self,
|
|
GCancellable *cancellable,
|
|
GAsyncReadyCallback callback,
|
|
gpointer callback_data)
|
|
{
|
|
FwupdClientPrivate *priv = GET_PRIVATE(self);
|
|
g_autoptr(GTask) task = NULL;
|
|
|
|
g_return_if_fail(FWUPD_IS_CLIENT(self));
|
|
g_return_if_fail(cancellable == NULL || G_IS_CANCELLABLE(cancellable));
|
|
g_return_if_fail(priv->proxy != NULL);
|
|
|
|
/* call into daemon */
|
|
task = g_task_new(self, cancellable, callback, callback_data);
|
|
g_dbus_proxy_call(priv->proxy,
|
|
"GetApprovedFirmware",
|
|
NULL,
|
|
G_DBUS_CALL_FLAGS_NONE,
|
|
-1,
|
|
cancellable,
|
|
fwupd_client_get_approved_firmware_cb,
|
|
g_steal_pointer(&task));
|
|
}
|
|
|
|
/**
|
|
* fwupd_client_get_approved_firmware_finish:
|
|
* @self: a #FwupdClient
|
|
* @res: the asynchronous result
|
|
* @error: (nullable): optional return location for an error
|
|
*
|
|
* Gets the result of fwupd_client_get_approved_firmware_async().
|
|
*
|
|
* Returns: (element-type utf8) (transfer container): checksums, or %NULL for error
|
|
*
|
|
* Since: 1.5.0
|
|
**/
|
|
GPtrArray *
|
|
fwupd_client_get_approved_firmware_finish(FwupdClient *self, GAsyncResult *res, GError **error)
|
|
{
|
|
g_return_val_if_fail(FWUPD_IS_CLIENT(self), NULL);
|
|
g_return_val_if_fail(g_task_is_valid(res, self), NULL);
|
|
g_return_val_if_fail(error == NULL || *error == NULL, NULL);
|
|
return g_task_propagate_pointer(G_TASK(res), error);
|
|
}
|
|
|
|
static void
|
|
fwupd_client_set_approved_firmware_cb(GObject *source, GAsyncResult *res, gpointer user_data)
|
|
{
|
|
g_autoptr(GTask) task = G_TASK(user_data);
|
|
g_autoptr(GError) error = NULL;
|
|
g_autoptr(GVariant) val = NULL;
|
|
|
|
val = g_dbus_proxy_call_finish(G_DBUS_PROXY(source), res, &error);
|
|
if (val == NULL) {
|
|
fwupd_client_fixup_dbus_error(error);
|
|
g_task_return_error(task, g_steal_pointer(&error));
|
|
return;
|
|
}
|
|
|
|
/* success */
|
|
g_task_return_boolean(task, TRUE);
|
|
}
|
|
|
|
/**
|
|
* fwupd_client_set_approved_firmware_async:
|
|
* @self: a #FwupdClient
|
|
* @checksums: (element-type utf8): firmware checksums
|
|
* @cancellable: (nullable): optional #GCancellable
|
|
* @callback: the function to run on completion
|
|
* @callback_data: the data to pass to @callback
|
|
*
|
|
* Sets the list of approved firmware.
|
|
*
|
|
* Since: 1.5.0
|
|
**/
|
|
void
|
|
fwupd_client_set_approved_firmware_async(FwupdClient *self,
|
|
GPtrArray *checksums,
|
|
GCancellable *cancellable,
|
|
GAsyncReadyCallback callback,
|
|
gpointer callback_data)
|
|
{
|
|
FwupdClientPrivate *priv = GET_PRIVATE(self);
|
|
g_autoptr(GTask) task = NULL;
|
|
g_auto(GStrv) strv = NULL;
|
|
|
|
g_return_if_fail(FWUPD_IS_CLIENT(self));
|
|
g_return_if_fail(cancellable == NULL || G_IS_CANCELLABLE(cancellable));
|
|
g_return_if_fail(priv->proxy != NULL);
|
|
|
|
/* call into daemon */
|
|
task = g_task_new(self, cancellable, callback, callback_data);
|
|
strv = g_new0(gchar *, checksums->len + 1);
|
|
for (guint i = 0; i < checksums->len; i++) {
|
|
const gchar *tmp = g_ptr_array_index(checksums, i);
|
|
strv[i] = g_strdup(tmp);
|
|
}
|
|
g_dbus_proxy_call(priv->proxy,
|
|
"SetApprovedFirmware",
|
|
g_variant_new("(^as)", strv),
|
|
G_DBUS_CALL_FLAGS_NONE,
|
|
-1,
|
|
cancellable,
|
|
fwupd_client_set_approved_firmware_cb,
|
|
g_steal_pointer(&task));
|
|
}
|
|
|
|
/**
|
|
* fwupd_client_set_approved_firmware_finish:
|
|
* @self: a #FwupdClient
|
|
* @res: the asynchronous result
|
|
* @error: (nullable): optional return location for an error
|
|
*
|
|
* Gets the result of fwupd_client_set_approved_firmware_async().
|
|
*
|
|
* Returns: %TRUE for success
|
|
*
|
|
* Since: 1.5.0
|
|
**/
|
|
gboolean
|
|
fwupd_client_set_approved_firmware_finish(FwupdClient *self, GAsyncResult *res, GError **error)
|
|
{
|
|
g_return_val_if_fail(FWUPD_IS_CLIENT(self), FALSE);
|
|
g_return_val_if_fail(g_task_is_valid(res, self), FALSE);
|
|
g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
|
|
return g_task_propagate_boolean(G_TASK(res), error);
|
|
}
|
|
|
|
static void
|
|
fwupd_client_get_blocked_firmware_cb(GObject *source, GAsyncResult *res, gpointer user_data)
|
|
{
|
|
g_auto(GStrv) strv = NULL;
|
|
g_autoptr(GTask) task = G_TASK(user_data);
|
|
g_autoptr(GError) error = NULL;
|
|
g_autoptr(GPtrArray) array = g_ptr_array_new_with_free_func(g_free);
|
|
g_autoptr(GVariant) val = NULL;
|
|
|
|
val = g_dbus_proxy_call_finish(G_DBUS_PROXY(source), res, &error);
|
|
if (val == NULL) {
|
|
fwupd_client_fixup_dbus_error(error);
|
|
g_task_return_error(task, g_steal_pointer(&error));
|
|
return;
|
|
}
|
|
|
|
g_variant_get(val, "(^as)", &strv);
|
|
for (guint i = 0; strv[i] != NULL; i++)
|
|
g_ptr_array_add(array, g_strdup(strv[i]));
|
|
|
|
/* success */
|
|
g_task_return_pointer(task, g_steal_pointer(&array), (GDestroyNotify)g_ptr_array_unref);
|
|
}
|
|
|
|
/**
|
|
* fwupd_client_get_blocked_firmware_async:
|
|
* @self: a #FwupdClient
|
|
* @cancellable: (nullable): optional #GCancellable
|
|
* @callback: the function to run on completion
|
|
* @callback_data: the data to pass to @callback
|
|
*
|
|
* Gets the list of blocked firmware.
|
|
*
|
|
* You must have called [method@Client.connect_async] on @self before using
|
|
* this method.
|
|
*
|
|
* Since: 1.5.0
|
|
**/
|
|
void
|
|
fwupd_client_get_blocked_firmware_async(FwupdClient *self,
|
|
GCancellable *cancellable,
|
|
GAsyncReadyCallback callback,
|
|
gpointer callback_data)
|
|
{
|
|
FwupdClientPrivate *priv = GET_PRIVATE(self);
|
|
g_autoptr(GTask) task = NULL;
|
|
|
|
g_return_if_fail(FWUPD_IS_CLIENT(self));
|
|
g_return_if_fail(cancellable == NULL || G_IS_CANCELLABLE(cancellable));
|
|
g_return_if_fail(priv->proxy != NULL);
|
|
|
|
/* call into daemon */
|
|
task = g_task_new(self, cancellable, callback, callback_data);
|
|
g_dbus_proxy_call(priv->proxy,
|
|
"GetBlockedFirmware",
|
|
NULL,
|
|
G_DBUS_CALL_FLAGS_NONE,
|
|
-1,
|
|
cancellable,
|
|
fwupd_client_get_blocked_firmware_cb,
|
|
g_steal_pointer(&task));
|
|
}
|
|
|
|
/**
|
|
* fwupd_client_get_blocked_firmware_finish:
|
|
* @self: a #FwupdClient
|
|
* @res: the asynchronous result
|
|
* @error: (nullable): optional return location for an error
|
|
*
|
|
* Gets the result of fwupd_client_get_blocked_firmware_async().
|
|
*
|
|
* Returns: (element-type utf8) (transfer container): checksums, or %NULL for error
|
|
*
|
|
* Since: 1.5.0
|
|
**/
|
|
GPtrArray *
|
|
fwupd_client_get_blocked_firmware_finish(FwupdClient *self, GAsyncResult *res, GError **error)
|
|
{
|
|
g_return_val_if_fail(FWUPD_IS_CLIENT(self), NULL);
|
|
g_return_val_if_fail(g_task_is_valid(res, self), NULL);
|
|
g_return_val_if_fail(error == NULL || *error == NULL, NULL);
|
|
return g_task_propagate_pointer(G_TASK(res), error);
|
|
}
|
|
|
|
static void
|
|
fwupd_client_set_blocked_firmware_cb(GObject *source, GAsyncResult *res, gpointer user_data)
|
|
{
|
|
g_autoptr(GTask) task = G_TASK(user_data);
|
|
g_autoptr(GError) error = NULL;
|
|
g_autoptr(GVariant) val = NULL;
|
|
|
|
val = g_dbus_proxy_call_finish(G_DBUS_PROXY(source), res, &error);
|
|
if (val == NULL) {
|
|
fwupd_client_fixup_dbus_error(error);
|
|
g_task_return_error(task, g_steal_pointer(&error));
|
|
return;
|
|
}
|
|
|
|
/* success */
|
|
g_task_return_boolean(task, TRUE);
|
|
}
|
|
|
|
/**
|
|
* fwupd_client_set_blocked_firmware_async:
|
|
* @self: a #FwupdClient
|
|
* @checksums: (element-type utf8): firmware checksums
|
|
* @cancellable: (nullable): optional #GCancellable
|
|
* @callback: the function to run on completion
|
|
* @callback_data: the data to pass to @callback
|
|
*
|
|
* Sets the list of blocked firmware.
|
|
*
|
|
* Since: 1.5.0
|
|
**/
|
|
void
|
|
fwupd_client_set_blocked_firmware_async(FwupdClient *self,
|
|
GPtrArray *checksums,
|
|
GCancellable *cancellable,
|
|
GAsyncReadyCallback callback,
|
|
gpointer callback_data)
|
|
{
|
|
FwupdClientPrivate *priv = GET_PRIVATE(self);
|
|
g_autoptr(GTask) task = NULL;
|
|
g_auto(GStrv) strv = NULL;
|
|
|
|
g_return_if_fail(FWUPD_IS_CLIENT(self));
|
|
g_return_if_fail(cancellable == NULL || G_IS_CANCELLABLE(cancellable));
|
|
g_return_if_fail(priv->proxy != NULL);
|
|
|
|
/* call into daemon */
|
|
task = g_task_new(self, cancellable, callback, callback_data);
|
|
strv = g_new0(gchar *, checksums->len + 1);
|
|
for (guint i = 0; i < checksums->len; i++) {
|
|
const gchar *tmp = g_ptr_array_index(checksums, i);
|
|
strv[i] = g_strdup(tmp);
|
|
}
|
|
g_dbus_proxy_call(priv->proxy,
|
|
"SetBlockedFirmware",
|
|
g_variant_new("(^as)", strv),
|
|
G_DBUS_CALL_FLAGS_NONE,
|
|
-1,
|
|
cancellable,
|
|
fwupd_client_set_blocked_firmware_cb,
|
|
g_steal_pointer(&task));
|
|
}
|
|
|
|
/**
|
|
* fwupd_client_set_blocked_firmware_finish:
|
|
* @self: a #FwupdClient
|
|
* @res: the asynchronous result
|
|
* @error: (nullable): optional return location for an error
|
|
*
|
|
* Gets the result of fwupd_client_set_blocked_firmware_async().
|
|
*
|
|
* Returns: %TRUE for success
|
|
*
|
|
* Since: 1.5.0
|
|
**/
|
|
gboolean
|
|
fwupd_client_set_blocked_firmware_finish(FwupdClient *self, GAsyncResult *res, GError **error)
|
|
{
|
|
g_return_val_if_fail(FWUPD_IS_CLIENT(self), FALSE);
|
|
g_return_val_if_fail(g_task_is_valid(res, self), FALSE);
|
|
g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
|
|
return g_task_propagate_boolean(G_TASK(res), error);
|
|
}
|
|
|
|
static void
|
|
fwupd_client_set_feature_flags_cb(GObject *source, GAsyncResult *res, gpointer user_data)
|
|
{
|
|
g_autoptr(GTask) task = G_TASK(user_data);
|
|
g_autoptr(GError) error = NULL;
|
|
g_autoptr(GVariant) val = NULL;
|
|
|
|
val = g_dbus_proxy_call_finish(G_DBUS_PROXY(source), res, &error);
|
|
if (val == NULL) {
|
|
fwupd_client_fixup_dbus_error(error);
|
|
g_task_return_error(task, g_steal_pointer(&error));
|
|
return;
|
|
}
|
|
|
|
/* success */
|
|
g_task_return_boolean(task, TRUE);
|
|
}
|
|
|
|
/**
|
|
* fwupd_client_set_feature_flags_async:
|
|
* @self: a #FwupdClient
|
|
* @feature_flags: feature flags, e.g. %FWUPD_FEATURE_FLAG_UPDATE_TEXT
|
|
* @cancellable: (nullable): optional #GCancellable
|
|
* @callback: the function to run on completion
|
|
* @callback_data: the data to pass to @callback
|
|
*
|
|
* Sets the features the client supports. This allows firmware to depend on
|
|
* specific front-end features, for instance showing the user an image on
|
|
* how to detach the hardware.
|
|
*
|
|
* Since: 1.5.0
|
|
**/
|
|
void
|
|
fwupd_client_set_feature_flags_async(FwupdClient *self,
|
|
FwupdFeatureFlags feature_flags,
|
|
GCancellable *cancellable,
|
|
GAsyncReadyCallback callback,
|
|
gpointer callback_data)
|
|
{
|
|
FwupdClientPrivate *priv = GET_PRIVATE(self);
|
|
g_autoptr(GTask) task = NULL;
|
|
|
|
g_return_if_fail(FWUPD_IS_CLIENT(self));
|
|
g_return_if_fail(cancellable == NULL || G_IS_CANCELLABLE(cancellable));
|
|
g_return_if_fail(priv->proxy != NULL);
|
|
|
|
/* call into daemon */
|
|
task = g_task_new(self, cancellable, callback, callback_data);
|
|
g_dbus_proxy_call(priv->proxy,
|
|
"SetFeatureFlags",
|
|
g_variant_new("(t)", (guint64)feature_flags),
|
|
G_DBUS_CALL_FLAGS_NONE,
|
|
-1,
|
|
cancellable,
|
|
fwupd_client_set_feature_flags_cb,
|
|
g_steal_pointer(&task));
|
|
}
|
|
|
|
/**
|
|
* fwupd_client_set_feature_flags_finish:
|
|
* @self: a #FwupdClient
|
|
* @res: the asynchronous result
|
|
* @error: (nullable): optional return location for an error
|
|
*
|
|
* Gets the result of fwupd_client_set_feature_flags_async().
|
|
*
|
|
* Returns: %TRUE for success
|
|
*
|
|
* Since: 1.5.0
|
|
**/
|
|
gboolean
|
|
fwupd_client_set_feature_flags_finish(FwupdClient *self, GAsyncResult *res, GError **error)
|
|
{
|
|
g_return_val_if_fail(FWUPD_IS_CLIENT(self), FALSE);
|
|
g_return_val_if_fail(g_task_is_valid(res, self), FALSE);
|
|
g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
|
|
return g_task_propagate_boolean(G_TASK(res), error);
|
|
}
|
|
|
|
static void
|
|
fwupd_client_self_sign_cb(GObject *source, GAsyncResult *res, gpointer user_data)
|
|
{
|
|
gchar *str = NULL;
|
|
g_autoptr(GTask) task = G_TASK(user_data);
|
|
g_autoptr(GError) error = NULL;
|
|
g_autoptr(GVariant) val = NULL;
|
|
|
|
val = g_dbus_proxy_call_finish(G_DBUS_PROXY(source), res, &error);
|
|
if (val == NULL) {
|
|
fwupd_client_fixup_dbus_error(error);
|
|
g_task_return_error(task, g_steal_pointer(&error));
|
|
return;
|
|
}
|
|
|
|
/* success */
|
|
g_variant_get(val, "(s)", &str);
|
|
g_task_return_pointer(task, g_steal_pointer(&str), (GDestroyNotify)g_free);
|
|
}
|
|
|
|
/**
|
|
* fwupd_client_self_sign_async:
|
|
* @self: a #FwupdClient
|
|
* @value: a string to sign, typically a JSON blob
|
|
* @flags: signing flags, e.g. %FWUPD_SELF_SIGN_FLAG_ADD_TIMESTAMP
|
|
* @cancellable: (nullable): optional #GCancellable
|
|
* @callback: the function to run on completion
|
|
* @callback_data: the data to pass to @callback
|
|
*
|
|
* Signs the data using the client self-signed certificate.
|
|
*
|
|
* You must have called [method@Client.connect_async] on @self before using
|
|
* this method.
|
|
*
|
|
* Since: 1.5.0
|
|
**/
|
|
void
|
|
fwupd_client_self_sign_async(FwupdClient *self,
|
|
const gchar *value,
|
|
FwupdSelfSignFlags flags,
|
|
GCancellable *cancellable,
|
|
GAsyncReadyCallback callback,
|
|
gpointer callback_data)
|
|
{
|
|
FwupdClientPrivate *priv = GET_PRIVATE(self);
|
|
GVariantBuilder builder;
|
|
g_autoptr(GTask) task = NULL;
|
|
|
|
g_return_if_fail(FWUPD_IS_CLIENT(self));
|
|
g_return_if_fail(value != NULL);
|
|
g_return_if_fail(cancellable == NULL || G_IS_CANCELLABLE(cancellable));
|
|
g_return_if_fail(priv->proxy != NULL);
|
|
|
|
/* set options */
|
|
g_variant_builder_init(&builder, G_VARIANT_TYPE_VARDICT);
|
|
if (flags & FWUPD_SELF_SIGN_FLAG_ADD_TIMESTAMP) {
|
|
g_variant_builder_add(&builder,
|
|
"{sv}",
|
|
"add-timestamp",
|
|
g_variant_new_boolean(TRUE));
|
|
}
|
|
if (flags & FWUPD_SELF_SIGN_FLAG_ADD_CERT) {
|
|
g_variant_builder_add(&builder, "{sv}", "add-cert", g_variant_new_boolean(TRUE));
|
|
}
|
|
|
|
/* call into daemon */
|
|
task = g_task_new(self, cancellable, callback, callback_data);
|
|
g_dbus_proxy_call(priv->proxy,
|
|
"SelfSign",
|
|
g_variant_new("(sa{sv})", value, &builder),
|
|
G_DBUS_CALL_FLAGS_NONE,
|
|
-1,
|
|
cancellable,
|
|
fwupd_client_self_sign_cb,
|
|
g_steal_pointer(&task));
|
|
}
|
|
|
|
/**
|
|
* fwupd_client_self_sign_finish:
|
|
* @self: a #FwupdClient
|
|
* @res: the asynchronous result
|
|
* @error: (nullable): optional return location for an error
|
|
*
|
|
* Gets the result of fwupd_client_self_sign_async().
|
|
*
|
|
* Returns: a signature, or %NULL for failure
|
|
*
|
|
* Since: 1.5.0
|
|
**/
|
|
gchar *
|
|
fwupd_client_self_sign_finish(FwupdClient *self, GAsyncResult *res, GError **error)
|
|
{
|
|
g_return_val_if_fail(FWUPD_IS_CLIENT(self), NULL);
|
|
g_return_val_if_fail(g_task_is_valid(res, self), NULL);
|
|
g_return_val_if_fail(error == NULL || *error == NULL, NULL);
|
|
return g_task_propagate_pointer(G_TASK(res), error);
|
|
}
|
|
|
|
static void
|
|
fwupd_client_modify_remote_cb(GObject *source, GAsyncResult *res, gpointer user_data)
|
|
{
|
|
g_autoptr(GTask) task = G_TASK(user_data);
|
|
g_autoptr(GError) error = NULL;
|
|
g_autoptr(GVariant) val = NULL;
|
|
|
|
val = g_dbus_proxy_call_finish(G_DBUS_PROXY(source), res, &error);
|
|
if (val == NULL) {
|
|
fwupd_client_fixup_dbus_error(error);
|
|
g_task_return_error(task, g_steal_pointer(&error));
|
|
return;
|
|
}
|
|
|
|
/* success */
|
|
g_task_return_boolean(task, TRUE);
|
|
}
|
|
|
|
/**
|
|
* fwupd_client_modify_remote_async:
|
|
* @self: a #FwupdClient
|
|
* @remote_id: the remote ID, e.g. `lvfs-testing`
|
|
* @key: the key, e.g. `Enabled`
|
|
* @value: the key, e.g. `true`
|
|
* @cancellable: (nullable): optional #GCancellable
|
|
* @callback: the function to run on completion
|
|
* @callback_data: the data to pass to @callback
|
|
*
|
|
* Modifies a system remote in a specific way.
|
|
*
|
|
* Since: 1.5.0
|
|
**/
|
|
void
|
|
fwupd_client_modify_remote_async(FwupdClient *self,
|
|
const gchar *remote_id,
|
|
const gchar *key,
|
|
const gchar *value,
|
|
GCancellable *cancellable,
|
|
GAsyncReadyCallback callback,
|
|
gpointer callback_data)
|
|
{
|
|
FwupdClientPrivate *priv = GET_PRIVATE(self);
|
|
g_autoptr(GTask) task = NULL;
|
|
|
|
g_return_if_fail(FWUPD_IS_CLIENT(self));
|
|
g_return_if_fail(remote_id != NULL);
|
|
g_return_if_fail(key != NULL);
|
|
g_return_if_fail(value != NULL);
|
|
g_return_if_fail(cancellable == NULL || G_IS_CANCELLABLE(cancellable));
|
|
g_return_if_fail(priv->proxy != NULL);
|
|
|
|
/* call into daemon */
|
|
task = g_task_new(self, cancellable, callback, callback_data);
|
|
g_dbus_proxy_call(priv->proxy,
|
|
"ModifyRemote",
|
|
g_variant_new("(sss)", remote_id, key, value),
|
|
G_DBUS_CALL_FLAGS_NONE,
|
|
-1,
|
|
cancellable,
|
|
fwupd_client_modify_remote_cb,
|
|
g_steal_pointer(&task));
|
|
}
|
|
|
|
/**
|
|
* fwupd_client_modify_remote_finish:
|
|
* @self: a #FwupdClient
|
|
* @res: the asynchronous result
|
|
* @error: (nullable): optional return location for an error
|
|
*
|
|
* Gets the result of fwupd_client_modify_remote_async().
|
|
*
|
|
* Returns: %TRUE for success
|
|
*
|
|
* Since: 1.5.0
|
|
**/
|
|
gboolean
|
|
fwupd_client_modify_remote_finish(FwupdClient *self, GAsyncResult *res, GError **error)
|
|
{
|
|
g_return_val_if_fail(FWUPD_IS_CLIENT(self), FALSE);
|
|
g_return_val_if_fail(g_task_is_valid(res, self), FALSE);
|
|
g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
|
|
return g_task_propagate_boolean(G_TASK(res), error);
|
|
}
|
|
|
|
static void
|
|
fwupd_client_modify_device_cb(GObject *source, GAsyncResult *res, gpointer user_data)
|
|
{
|
|
g_autoptr(GTask) task = G_TASK(user_data);
|
|
g_autoptr(GError) error = NULL;
|
|
g_autoptr(GVariant) val = NULL;
|
|
|
|
val = g_dbus_proxy_call_finish(G_DBUS_PROXY(source), res, &error);
|
|
if (val == NULL) {
|
|
fwupd_client_fixup_dbus_error(error);
|
|
g_task_return_error(task, g_steal_pointer(&error));
|
|
return;
|
|
}
|
|
|
|
/* success */
|
|
g_task_return_boolean(task, TRUE);
|
|
}
|
|
|
|
/**
|
|
* fwupd_client_modify_device_async:
|
|
* @self: a #FwupdClient
|
|
* @device_id: the device ID
|
|
* @key: the key, e.g. `Flags`
|
|
* @value: the value, e.g. `reported`
|
|
* @cancellable: (nullable): optional #GCancellable
|
|
* @callback: the function to run on completion
|
|
* @callback_data: the data to pass to @callback
|
|
*
|
|
* Modifies a device in a specific way. Not all properties on the #FwupdDevice
|
|
* are settable by the client, and some may have other restrictions on @value.
|
|
*
|
|
* Since: 1.5.0
|
|
**/
|
|
void
|
|
fwupd_client_modify_device_async(FwupdClient *self,
|
|
const gchar *device_id,
|
|
const gchar *key,
|
|
const gchar *value,
|
|
GCancellable *cancellable,
|
|
GAsyncReadyCallback callback,
|
|
gpointer callback_data)
|
|
{
|
|
FwupdClientPrivate *priv = GET_PRIVATE(self);
|
|
g_autoptr(GTask) task = NULL;
|
|
|
|
g_return_if_fail(FWUPD_IS_CLIENT(self));
|
|
g_return_if_fail(device_id != NULL);
|
|
g_return_if_fail(key != NULL);
|
|
g_return_if_fail(value != NULL);
|
|
g_return_if_fail(cancellable == NULL || G_IS_CANCELLABLE(cancellable));
|
|
g_return_if_fail(priv->proxy != NULL);
|
|
|
|
/* call into daemon */
|
|
task = g_task_new(self, cancellable, callback, callback_data);
|
|
g_dbus_proxy_call(priv->proxy,
|
|
"ModifyDevice",
|
|
g_variant_new("(sss)", device_id, key, value),
|
|
G_DBUS_CALL_FLAGS_NONE,
|
|
-1,
|
|
cancellable,
|
|
fwupd_client_modify_device_cb,
|
|
g_steal_pointer(&task));
|
|
}
|
|
|
|
/**
|
|
* fwupd_client_modify_device_finish:
|
|
* @self: a #FwupdClient
|
|
* @res: the asynchronous result
|
|
* @error: (nullable): optional return location for an error
|
|
*
|
|
* Gets the result of fwupd_client_modify_device_async().
|
|
*
|
|
* Returns: %TRUE for success
|
|
*
|
|
* Since: 1.5.0
|
|
**/
|
|
gboolean
|
|
fwupd_client_modify_device_finish(FwupdClient *self, GAsyncResult *res, GError **error)
|
|
{
|
|
g_return_val_if_fail(FWUPD_IS_CLIENT(self), FALSE);
|
|
g_return_val_if_fail(g_task_is_valid(res, self), FALSE);
|
|
g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
|
|
return g_task_propagate_boolean(G_TASK(res), error);
|
|
}
|
|
|
|
static FwupdRemote *
|
|
fwupd_client_get_remote_by_id_noref(GPtrArray *remotes, const gchar *remote_id)
|
|
{
|
|
for (guint i = 0; i < remotes->len; i++) {
|
|
FwupdRemote *remote = g_ptr_array_index(remotes, i);
|
|
if (g_strcmp0(remote_id, fwupd_remote_get_id(remote)) == 0)
|
|
return remote;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static void
|
|
fwupd_client_get_remote_by_id_cb(GObject *source, GAsyncResult *res, gpointer user_data)
|
|
{
|
|
FwupdRemote *remote_tmp;
|
|
g_autoptr(GTask) task = G_TASK(user_data);
|
|
g_autoptr(GError) error = NULL;
|
|
g_autoptr(GPtrArray) remotes = NULL;
|
|
const gchar *remote_id = g_task_get_task_data(task);
|
|
|
|
remotes = fwupd_client_get_remotes_finish(FWUPD_CLIENT(source), res, &error);
|
|
if (remotes == NULL) {
|
|
g_task_return_error(task, g_steal_pointer(&error));
|
|
return;
|
|
}
|
|
|
|
remote_tmp = fwupd_client_get_remote_by_id_noref(remotes, remote_id);
|
|
if (remote_tmp == NULL) {
|
|
g_task_return_new_error(task,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_NOT_FOUND,
|
|
"no remote '%s' found in search paths",
|
|
remote_id);
|
|
return;
|
|
}
|
|
|
|
/* success */
|
|
g_task_return_pointer(task, g_object_ref(remote_tmp), (GDestroyNotify)g_object_unref);
|
|
}
|
|
|
|
/**
|
|
* fwupd_client_get_remote_by_id_async:
|
|
* @self: a #FwupdClient
|
|
* @remote_id: the remote ID, e.g. `lvfs-testing`
|
|
* @cancellable: (nullable): optional #GCancellable
|
|
* @callback: the function to run on completion
|
|
* @callback_data: the data to pass to @callback
|
|
*
|
|
* Gets a specific remote that has been configured for the system.
|
|
*
|
|
* Since: 1.5.0
|
|
**/
|
|
void
|
|
fwupd_client_get_remote_by_id_async(FwupdClient *self,
|
|
const gchar *remote_id,
|
|
GCancellable *cancellable,
|
|
GAsyncReadyCallback callback,
|
|
gpointer callback_data)
|
|
{
|
|
FwupdClientPrivate *priv = GET_PRIVATE(self);
|
|
g_autoptr(GTask) task = NULL;
|
|
|
|
g_return_if_fail(FWUPD_IS_CLIENT(self));
|
|
g_return_if_fail(remote_id != NULL);
|
|
g_return_if_fail(cancellable == NULL || G_IS_CANCELLABLE(cancellable));
|
|
g_return_if_fail(priv->proxy != NULL);
|
|
|
|
/* call into daemon */
|
|
task = g_task_new(self, cancellable, callback, callback_data);
|
|
g_task_set_task_data(task, g_strdup(remote_id), g_free);
|
|
fwupd_client_get_remotes_async(self,
|
|
cancellable,
|
|
fwupd_client_get_remote_by_id_cb,
|
|
g_steal_pointer(&task));
|
|
}
|
|
|
|
/**
|
|
* fwupd_client_get_remote_by_id_finish:
|
|
* @self: a #FwupdClient
|
|
* @res: the asynchronous result
|
|
* @error: (nullable): optional return location for an error
|
|
*
|
|
* Gets the result of fwupd_client_get_remote_by_id_async().
|
|
*
|
|
* Returns: (transfer full): a #FwupdRemote, or %NULL if not found
|
|
*
|
|
* Since: 1.5.0
|
|
**/
|
|
FwupdRemote *
|
|
fwupd_client_get_remote_by_id_finish(FwupdClient *self, GAsyncResult *res, GError **error)
|
|
{
|
|
g_return_val_if_fail(FWUPD_IS_CLIENT(self), NULL);
|
|
g_return_val_if_fail(g_task_is_valid(res, self), NULL);
|
|
g_return_val_if_fail(error == NULL || *error == NULL, NULL);
|
|
return g_task_propagate_pointer(G_TASK(res), error);
|
|
}
|
|
|
|
/**
|
|
* fwupd_client_set_user_agent:
|
|
* @self: a #FwupdClient
|
|
* @user_agent: the user agent ID, e.g. `gnome-software/3.34.1`
|
|
*
|
|
* Manually sets the user agent that is used for downloading. The user agent
|
|
* should contain the runtime version of fwupd somewhere in the provided string.
|
|
*
|
|
* Since: 1.4.5
|
|
**/
|
|
void
|
|
fwupd_client_set_user_agent(FwupdClient *self, const gchar *user_agent)
|
|
{
|
|
FwupdClientPrivate *priv = GET_PRIVATE(self);
|
|
|
|
g_return_if_fail(FWUPD_IS_CLIENT(self));
|
|
g_return_if_fail(user_agent != NULL);
|
|
|
|
/* not changed */
|
|
if (g_strcmp0(priv->user_agent, user_agent) == 0)
|
|
return;
|
|
|
|
g_free(priv->user_agent);
|
|
priv->user_agent = g_strdup(user_agent);
|
|
}
|
|
|
|
/**
|
|
* fwupd_client_get_user_agent:
|
|
* @self: a #FwupdClient
|
|
*
|
|
* Gets the string that represents the user agent that is used for
|
|
* uploading and downloading. The user agent will contain the runtime
|
|
* version of fwupd somewhere in the provided string.
|
|
*
|
|
* Returns: a string, or %NULL for unknown.
|
|
*
|
|
* Since: 1.5.2
|
|
**/
|
|
const gchar *
|
|
fwupd_client_get_user_agent(FwupdClient *self)
|
|
{
|
|
FwupdClientPrivate *priv = GET_PRIVATE(self);
|
|
g_return_val_if_fail(FWUPD_IS_CLIENT(self), NULL);
|
|
return priv->user_agent;
|
|
}
|
|
|
|
/**
|
|
* fwupd_client_set_user_agent_for_package:
|
|
* @self: a #FwupdClient
|
|
* @package_name: client program name, e.g. `gnome-software`
|
|
* @package_version: client program version, e.g. `3.28.1`
|
|
*
|
|
* Builds a user-agent to use for the download.
|
|
*
|
|
* Supplying harmless details to the server means it knows more about each
|
|
* client. This allows the web service to respond in a different way, for
|
|
* instance sending a different metadata file for old versions of fwupd, or
|
|
* returning an error for Solaris machines.
|
|
*
|
|
* Before freaking out about theoretical privacy implications, much more data
|
|
* than this is sent to each and every website you visit.
|
|
*
|
|
* Since: 1.4.5
|
|
**/
|
|
void
|
|
fwupd_client_set_user_agent_for_package(FwupdClient *self,
|
|
const gchar *package_name,
|
|
const gchar *package_version)
|
|
{
|
|
FwupdClientPrivate *priv = GET_PRIVATE(self);
|
|
GString *str = g_string_new(NULL);
|
|
g_autofree gchar *system = NULL;
|
|
|
|
g_return_if_fail(FWUPD_IS_CLIENT(self));
|
|
g_return_if_fail(package_name != NULL);
|
|
g_return_if_fail(package_version != NULL);
|
|
|
|
/* application name and version */
|
|
g_string_append_printf(str, "%s/%s", package_name, package_version);
|
|
|
|
/* system information */
|
|
system = fwupd_build_user_agent_system();
|
|
if (system != NULL)
|
|
g_string_append_printf(str, " (%s)", system);
|
|
|
|
/* platform, which in our case is just fwupd */
|
|
if (g_strcmp0(package_name, "fwupd") != 0)
|
|
g_string_append_printf(str, " fwupd/%s", priv->daemon_version);
|
|
|
|
/* success */
|
|
g_free(priv->user_agent);
|
|
priv->user_agent = g_string_free(str, FALSE);
|
|
}
|
|
|
|
#ifdef HAVE_LIBCURL
|
|
static size_t
|
|
fwupd_client_download_write_callback_cb(char *ptr, size_t size, size_t nmemb, void *userdata)
|
|
{
|
|
GByteArray *buf = (GByteArray *)userdata;
|
|
gsize realsize = size * nmemb;
|
|
g_byte_array_append(buf, (const guint8 *)ptr, realsize);
|
|
return realsize;
|
|
}
|
|
|
|
static GBytes *
|
|
fwupd_client_stream_read_bytes(GInputStream *stream, GError **error)
|
|
{
|
|
guint8 tmp[0x8000] = {0x0};
|
|
g_autoptr(GByteArray) buf = g_byte_array_new();
|
|
|
|
/* read from stream in 32kB chunks */
|
|
while (TRUE) {
|
|
gssize sz;
|
|
sz = g_input_stream_read(stream, tmp, sizeof(tmp), NULL, error);
|
|
if (sz == 0)
|
|
break;
|
|
if (sz < 0)
|
|
return NULL;
|
|
g_byte_array_append(buf, tmp, sz);
|
|
}
|
|
return g_byte_array_free_to_bytes(g_steal_pointer(&buf));
|
|
}
|
|
|
|
static GBytes *
|
|
fwupd_client_download_ipfs(FwupdClient *self, const gchar *url, GError **error)
|
|
{
|
|
GInputStream *stream = NULL;
|
|
g_autofree gchar *path = NULL;
|
|
g_autoptr(GSubprocess) subprocess = NULL;
|
|
|
|
/* we get no detailed progress details */
|
|
fwupd_client_set_status(self, FWUPD_STATUS_DOWNLOADING);
|
|
fwupd_client_set_percentage(self, 0);
|
|
|
|
/* convert from URI to path */
|
|
if (g_str_has_prefix(url, "ipfs://")) {
|
|
path = g_strdup_printf("/ipfs/%s", url + 7);
|
|
} else if (g_str_has_prefix(url, "ipns://")) {
|
|
path = g_strdup_printf("/ipns/%s", url + 7);
|
|
} else {
|
|
path = g_strdup(url);
|
|
}
|
|
|
|
/* run sync */
|
|
subprocess =
|
|
g_subprocess_new(G_SUBPROCESS_FLAGS_STDOUT_PIPE, error, "ipfs", "cat", path, NULL);
|
|
if (subprocess == NULL)
|
|
return NULL;
|
|
if (!g_subprocess_wait_check(subprocess, NULL, error))
|
|
return NULL;
|
|
|
|
/* get raw stdout */
|
|
stream = g_subprocess_get_stdout_pipe(subprocess);
|
|
return fwupd_client_stream_read_bytes(stream, error);
|
|
}
|
|
|
|
static GBytes *
|
|
fwupd_client_download_http(FwupdClient *self, CURL *curl, const gchar *url, GError **error)
|
|
{
|
|
CURLcode res;
|
|
gchar errbuf[CURL_ERROR_SIZE] = {'\0'};
|
|
glong status_code = 0;
|
|
g_autoptr(GByteArray) buf = g_byte_array_new();
|
|
|
|
fwupd_client_set_status(self, FWUPD_STATUS_DOWNLOADING);
|
|
curl_easy_setopt(curl, CURLOPT_URL, url);
|
|
curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, errbuf);
|
|
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, fwupd_client_download_write_callback_cb);
|
|
curl_easy_setopt(curl, CURLOPT_WRITEDATA, buf);
|
|
res = curl_easy_perform(curl);
|
|
fwupd_client_set_status(self, FWUPD_STATUS_IDLE);
|
|
if (res != CURLE_OK) {
|
|
if (errbuf[0] != '\0') {
|
|
g_set_error(error,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_INVALID_FILE,
|
|
"failed to download file: %s",
|
|
errbuf);
|
|
return NULL;
|
|
}
|
|
g_set_error(error,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_INVALID_FILE,
|
|
"failed to download file: %s",
|
|
curl_easy_strerror(res));
|
|
return NULL;
|
|
}
|
|
|
|
/* check for server limit */
|
|
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &status_code);
|
|
g_debug("status-code was %ld", status_code);
|
|
if (status_code == 429) {
|
|
g_set_error_literal(error,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_INVALID_FILE,
|
|
"Failed to download due to server limit");
|
|
return NULL;
|
|
}
|
|
if (status_code >= 400) {
|
|
g_set_error(error,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_INVALID_FILE,
|
|
"Failed to download, server response was %u",
|
|
(guint)status_code);
|
|
return NULL;
|
|
}
|
|
|
|
return g_byte_array_free_to_bytes(g_steal_pointer(&buf));
|
|
}
|
|
|
|
static void
|
|
fwupd_client_download_bytes_thread_cb(GTask *task,
|
|
gpointer source_object,
|
|
gpointer task_data,
|
|
GCancellable *cancellable)
|
|
{
|
|
FwupdClient *self = FWUPD_CLIENT(source_object);
|
|
FwupdCurlHelper *helper = g_task_get_task_data(task);
|
|
g_autoptr(GBytes) blob = NULL;
|
|
|
|
for (guint i = 0; i < helper->urls->len; i++) {
|
|
const gchar *url = g_ptr_array_index(helper->urls, i);
|
|
g_autoptr(GError) error = NULL;
|
|
g_debug("downloading %s", url);
|
|
fwupd_client_curl_helper_set_proxy(self, helper, url);
|
|
if (fwupd_client_is_url_http(url)) {
|
|
blob = fwupd_client_download_http(self, helper->curl, url, &error);
|
|
if (blob != NULL)
|
|
break;
|
|
} else if (fwupd_client_is_url_ipfs(url)) {
|
|
blob = fwupd_client_download_ipfs(self, url, &error);
|
|
if (blob != NULL)
|
|
break;
|
|
} else {
|
|
g_set_error(&error,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_INVALID_FILE,
|
|
"not sure how to handle: %s",
|
|
url);
|
|
}
|
|
if (i == helper->urls->len - 1) {
|
|
g_task_return_error(task, g_steal_pointer(&error));
|
|
return;
|
|
}
|
|
fwupd_client_set_percentage(self, 0);
|
|
fwupd_client_set_status(self, FWUPD_STATUS_IDLE);
|
|
g_debug("failed to download %s: %s, trying next URI…", url, error->message);
|
|
}
|
|
g_task_return_pointer(task, g_steal_pointer(&blob), (GDestroyNotify)g_bytes_unref);
|
|
}
|
|
#endif
|
|
|
|
/* private */
|
|
void
|
|
fwupd_client_download_bytes2_async(FwupdClient *self,
|
|
GPtrArray *urls,
|
|
FwupdClientDownloadFlags flags,
|
|
GCancellable *cancellable,
|
|
GAsyncReadyCallback callback,
|
|
gpointer callback_data)
|
|
{
|
|
FwupdClientPrivate *priv = GET_PRIVATE(self);
|
|
g_autoptr(GTask) task = NULL;
|
|
#ifdef HAVE_LIBCURL
|
|
g_autoptr(GError) error = NULL;
|
|
g_autoptr(FwupdCurlHelper) helper = NULL;
|
|
#endif
|
|
|
|
g_return_if_fail(FWUPD_IS_CLIENT(self));
|
|
g_return_if_fail(urls != NULL);
|
|
g_return_if_fail(cancellable == NULL || G_IS_CANCELLABLE(cancellable));
|
|
g_return_if_fail(priv->proxy != NULL);
|
|
|
|
/* ensure networking set up */
|
|
task = g_task_new(self, cancellable, callback, callback_data);
|
|
#ifdef HAVE_LIBCURL
|
|
helper = fwupd_client_curl_new(self, &error);
|
|
if (helper == NULL) {
|
|
g_task_return_error(task, g_steal_pointer(&error));
|
|
return;
|
|
}
|
|
helper->urls = fwupd_client_filter_locations(urls, flags, &error);
|
|
if (helper->urls == NULL) {
|
|
g_task_return_error(task, g_steal_pointer(&error));
|
|
return;
|
|
}
|
|
g_task_set_task_data(task,
|
|
g_steal_pointer(&helper),
|
|
(GDestroyNotify)fwupd_client_curl_helper_free);
|
|
|
|
/* download data */
|
|
g_task_run_in_thread(task, fwupd_client_download_bytes_thread_cb);
|
|
#else
|
|
g_task_return_new_error(task, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "no libcurl support");
|
|
#endif
|
|
}
|
|
|
|
/**
|
|
* fwupd_client_download_bytes_async:
|
|
* @self: a #FwupdClient
|
|
* @url: the remote URL
|
|
* @flags: download flags, e.g. %FWUPD_CLIENT_DOWNLOAD_FLAG_NONE
|
|
* @cancellable: (nullable): optional #GCancellable
|
|
* @callback: the function to run on completion
|
|
* @callback_data: the data to pass to @callback
|
|
*
|
|
* Downloads data from a remote server. The [method@Client.set_user_agent] function
|
|
* should be called before this method is used.
|
|
*
|
|
* You must have called [method@Client.connect_async] on @self before using
|
|
* this method.
|
|
*
|
|
* NOTE: This method is thread-safe, but progress signals will be
|
|
* emitted in the global default main context, if not explicitly set with
|
|
* [method@Client.set_main_context].
|
|
*
|
|
* Since: 1.5.0
|
|
**/
|
|
void
|
|
fwupd_client_download_bytes_async(FwupdClient *self,
|
|
const gchar *url,
|
|
FwupdClientDownloadFlags flags,
|
|
GCancellable *cancellable,
|
|
GAsyncReadyCallback callback,
|
|
gpointer callback_data)
|
|
{
|
|
FwupdClientPrivate *priv = GET_PRIVATE(self);
|
|
g_autoptr(GPtrArray) urls = g_ptr_array_new_with_free_func(g_free);
|
|
|
|
g_return_if_fail(FWUPD_IS_CLIENT(self));
|
|
g_return_if_fail(url != NULL);
|
|
g_return_if_fail(cancellable == NULL || G_IS_CANCELLABLE(cancellable));
|
|
g_return_if_fail(priv->proxy != NULL);
|
|
|
|
/* just proxy */
|
|
g_ptr_array_add(urls, g_strdup(url));
|
|
fwupd_client_download_bytes2_async(self, urls, flags, cancellable, callback, callback_data);
|
|
}
|
|
|
|
/**
|
|
* fwupd_client_download_bytes_finish:
|
|
* @self: a #FwupdClient
|
|
* @res: the asynchronous result
|
|
* @error: (nullable): optional return location for an error
|
|
*
|
|
* Gets the result of fwupd_client_download_bytes_async().
|
|
*
|
|
* Returns: (transfer full): downloaded data, or %NULL for error
|
|
*
|
|
* Since: 1.5.0
|
|
**/
|
|
GBytes *
|
|
fwupd_client_download_bytes_finish(FwupdClient *self, GAsyncResult *res, GError **error)
|
|
{
|
|
g_return_val_if_fail(FWUPD_IS_CLIENT(self), NULL);
|
|
g_return_val_if_fail(g_task_is_valid(res, self), NULL);
|
|
g_return_val_if_fail(error == NULL || *error == NULL, NULL);
|
|
return g_task_propagate_pointer(G_TASK(res), error);
|
|
}
|
|
|
|
#ifdef HAVE_LIBCURL
|
|
static void
|
|
fwupd_client_upload_bytes_thread_cb(GTask *task,
|
|
gpointer source_object,
|
|
gpointer task_data,
|
|
GCancellable *cancellable)
|
|
{
|
|
FwupdClient *self = FWUPD_CLIENT(source_object);
|
|
FwupdCurlHelper *helper = g_task_get_task_data(task);
|
|
CURLcode res;
|
|
gchar errbuf[CURL_ERROR_SIZE] = {'\0'};
|
|
g_autoptr(GByteArray) buf = g_byte_array_new();
|
|
|
|
curl_easy_setopt(helper->curl, CURLOPT_ERRORBUFFER, errbuf);
|
|
curl_easy_setopt(helper->curl,
|
|
CURLOPT_WRITEFUNCTION,
|
|
fwupd_client_download_write_callback_cb);
|
|
curl_easy_setopt(helper->curl, CURLOPT_WRITEDATA, buf);
|
|
res = curl_easy_perform(helper->curl);
|
|
fwupd_client_set_status(self, FWUPD_STATUS_IDLE);
|
|
if (res != CURLE_OK) {
|
|
glong status_code = 0;
|
|
curl_easy_getinfo(helper->curl, CURLINFO_RESPONSE_CODE, &status_code);
|
|
g_debug("status-code was %ld", status_code);
|
|
if (errbuf[0] != '\0') {
|
|
g_task_return_new_error(task,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_INVALID_FILE,
|
|
"failed to upload file: %s",
|
|
errbuf);
|
|
return;
|
|
}
|
|
g_task_return_new_error(task,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_INVALID_FILE,
|
|
"failed to upload file: %s",
|
|
curl_easy_strerror(res));
|
|
|
|
return;
|
|
}
|
|
g_task_return_pointer(task,
|
|
g_byte_array_free_to_bytes(g_steal_pointer(&buf)),
|
|
(GDestroyNotify)g_bytes_unref);
|
|
}
|
|
#endif
|
|
|
|
/**
|
|
* fwupd_client_upload_bytes_async:
|
|
* @self: a #FwupdClient
|
|
* @url: the remote URL
|
|
* @payload: payload string
|
|
* @signature: (nullable): signature string
|
|
* @flags: download flags, e.g. %FWUPD_CLIENT_DOWNLOAD_FLAG_NONE
|
|
* @cancellable: (nullable): optional #GCancellable
|
|
* @callback: the function to run on completion
|
|
* @callback_data: the data to pass to @callback
|
|
*
|
|
* Uploads data to a remote server. The [method@Client.set_user_agent] function
|
|
* should be called before this method is used.
|
|
*
|
|
* You must have called [method@Client.connect_async] on @self before using
|
|
* this method.
|
|
*
|
|
* NOTE: This method is thread-safe, but progress signals will be
|
|
* emitted in the global default main context, if not explicitly set with
|
|
* [method@Client.set_main_context].
|
|
*
|
|
* Since: 1.5.0
|
|
**/
|
|
void
|
|
fwupd_client_upload_bytes_async(FwupdClient *self,
|
|
const gchar *url,
|
|
const gchar *payload,
|
|
const gchar *signature,
|
|
FwupdClientUploadFlags flags,
|
|
GCancellable *cancellable,
|
|
GAsyncReadyCallback callback,
|
|
gpointer callback_data)
|
|
{
|
|
FwupdClientPrivate *priv = GET_PRIVATE(self);
|
|
g_autoptr(GTask) task = NULL;
|
|
#ifdef HAVE_LIBCURL
|
|
g_autoptr(FwupdCurlHelper) helper = NULL;
|
|
g_autoptr(GError) error = NULL;
|
|
#endif
|
|
|
|
g_return_if_fail(FWUPD_IS_CLIENT(self));
|
|
g_return_if_fail(url != NULL);
|
|
g_return_if_fail(cancellable == NULL || G_IS_CANCELLABLE(cancellable));
|
|
g_return_if_fail(priv->proxy != NULL);
|
|
|
|
/* ensure networking set up */
|
|
task = g_task_new(self, cancellable, callback, callback_data);
|
|
#ifdef HAVE_LIBCURL
|
|
helper = fwupd_client_curl_new(self, &error);
|
|
if (helper == NULL) {
|
|
g_task_return_error(task, g_steal_pointer(&error));
|
|
return;
|
|
}
|
|
|
|
/* build message */
|
|
if ((flags & FWUPD_CLIENT_UPLOAD_FLAG_ALWAYS_MULTIPART) > 0 || signature != NULL) {
|
|
curl_mimepart *part;
|
|
helper->mime = curl_mime_init(helper->curl);
|
|
curl_easy_setopt(helper->curl, CURLOPT_MIMEPOST, helper->mime);
|
|
part = curl_mime_addpart(helper->mime);
|
|
curl_mime_data(part, payload, CURL_ZERO_TERMINATED);
|
|
curl_mime_name(part, "payload");
|
|
if (signature != NULL) {
|
|
part = curl_mime_addpart(helper->mime);
|
|
curl_mime_data(part, signature, CURL_ZERO_TERMINATED);
|
|
curl_mime_name(part, "signature");
|
|
}
|
|
} else {
|
|
helper->headers = curl_slist_append(helper->headers, "Content-Type: text/plain");
|
|
curl_easy_setopt(helper->curl, CURLOPT_HTTPHEADER, helper->headers);
|
|
curl_easy_setopt(helper->curl, CURLOPT_POST, 1L);
|
|
curl_easy_setopt(helper->curl, CURLOPT_POSTFIELDSIZE, strlen(payload));
|
|
curl_easy_setopt(helper->curl, CURLOPT_COPYPOSTFIELDS, payload);
|
|
}
|
|
|
|
fwupd_client_set_status(self, FWUPD_STATUS_IDLE);
|
|
g_debug("uploading to %s", url);
|
|
curl_easy_setopt(helper->curl, CURLOPT_URL, url);
|
|
g_task_set_task_data(task,
|
|
g_steal_pointer(&helper),
|
|
(GDestroyNotify)fwupd_client_curl_helper_free);
|
|
g_task_run_in_thread(task, fwupd_client_upload_bytes_thread_cb);
|
|
#else
|
|
g_task_return_new_error(task, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "no libcurl support");
|
|
#endif
|
|
}
|
|
|
|
/**
|
|
* fwupd_client_upload_bytes_finish:
|
|
* @self: a #FwupdClient
|
|
* @res: the asynchronous result
|
|
* @error: (nullable): optional return location for an error
|
|
*
|
|
* Gets the result of fwupd_client_upload_bytes_async().
|
|
*
|
|
* Returns: (transfer full): response data, or %NULL for error
|
|
*
|
|
* Since: 1.5.0
|
|
**/
|
|
GBytes *
|
|
fwupd_client_upload_bytes_finish(FwupdClient *self, GAsyncResult *res, GError **error)
|
|
{
|
|
g_return_val_if_fail(FWUPD_IS_CLIENT(self), NULL);
|
|
g_return_val_if_fail(g_task_is_valid(res, self), NULL);
|
|
g_return_val_if_fail(error == NULL || *error == NULL, NULL);
|
|
return g_task_propagate_pointer(G_TASK(res), error);
|
|
}
|
|
|
|
#ifdef SOUP_SESSION_COMPAT
|
|
/* this is bad; we dlopen libsoup-2.4.so.1 and get the gtype manually
|
|
* to avoid deps on both libcurl and libsoup whilst preserving ABI */
|
|
static void
|
|
fwupd_client_ensure_soup_session(FwupdClient *self)
|
|
{
|
|
FwupdClientObjectNewFunc func = NULL;
|
|
FwupdClientPrivate *priv = GET_PRIVATE(self);
|
|
GType soup_gtype;
|
|
|
|
/* already set up */
|
|
if (priv->soup_session != NULL)
|
|
return;
|
|
|
|
/* known GType, just create */
|
|
soup_gtype = g_type_from_name("SoupSession");
|
|
if (soup_gtype != 0) {
|
|
priv->soup_session = g_object_new(soup_gtype, NULL);
|
|
return;
|
|
}
|
|
|
|
/* load the library at runtime, leaking the module */
|
|
if (priv->soup_module == NULL) {
|
|
g_autofree gchar *fn = NULL;
|
|
fn = g_build_filename(FWUPD_LIBDIR, "libsoup-2.4.so.1", NULL);
|
|
priv->soup_module = g_module_open(fn, G_MODULE_BIND_LAZY);
|
|
if (priv->soup_module == NULL) {
|
|
g_warning("failed to find libsoup library");
|
|
return;
|
|
}
|
|
}
|
|
if (!g_module_symbol(priv->soup_module, "soup_session_new", (gpointer *)&func)) {
|
|
g_warning("failed to find soup_session_get_type()");
|
|
g_module_close(priv->soup_module);
|
|
priv->soup_module = NULL;
|
|
return;
|
|
}
|
|
priv->soup_session = func();
|
|
g_object_set(priv->soup_session, "timeout", (guint)60, NULL);
|
|
}
|
|
#endif
|
|
|
|
static void
|
|
fwupd_client_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
|
|
{
|
|
FwupdClient *self = FWUPD_CLIENT(object);
|
|
FwupdClientPrivate *priv = GET_PRIVATE(self);
|
|
|
|
switch (prop_id) {
|
|
case PROP_STATUS:
|
|
g_value_set_uint(value, priv->status);
|
|
break;
|
|
case PROP_TAINTED:
|
|
g_value_set_boolean(value, priv->tainted);
|
|
break;
|
|
case PROP_SOUP_SESSION:
|
|
#ifdef SOUP_SESSION_COMPAT
|
|
fwupd_client_ensure_soup_session(self);
|
|
g_value_set_object(value, priv->soup_session);
|
|
#else
|
|
g_value_set_object(value, NULL);
|
|
#endif
|
|
break;
|
|
case PROP_PERCENTAGE:
|
|
g_value_set_uint(value, priv->percentage);
|
|
break;
|
|
case PROP_DAEMON_VERSION:
|
|
g_value_set_string(value, priv->daemon_version);
|
|
break;
|
|
case PROP_HOST_PRODUCT:
|
|
g_value_set_string(value, priv->host_product);
|
|
break;
|
|
case PROP_HOST_MACHINE_ID:
|
|
g_value_set_string(value, priv->host_machine_id);
|
|
break;
|
|
case PROP_HOST_SECURITY_ID:
|
|
g_value_set_string(value, priv->host_security_id);
|
|
break;
|
|
case PROP_INTERACTIVE:
|
|
g_value_set_boolean(value, priv->interactive);
|
|
break;
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
fwupd_client_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
|
|
{
|
|
FwupdClient *self = FWUPD_CLIENT(object);
|
|
FwupdClientPrivate *priv = GET_PRIVATE(self);
|
|
|
|
switch (prop_id) {
|
|
case PROP_STATUS:
|
|
priv->status = g_value_get_uint(value);
|
|
break;
|
|
case PROP_PERCENTAGE:
|
|
priv->percentage = g_value_get_uint(value);
|
|
break;
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
fwupd_client_class_init(FwupdClientClass *klass)
|
|
{
|
|
GParamSpec *pspec;
|
|
GObjectClass *object_class = G_OBJECT_CLASS(klass);
|
|
object_class->finalize = fwupd_client_finalize;
|
|
object_class->get_property = fwupd_client_get_property;
|
|
object_class->set_property = fwupd_client_set_property;
|
|
|
|
/**
|
|
* FwupdClient::changed:
|
|
* @self: the #FwupdClient instance that emitted the signal
|
|
*
|
|
* The ::changed signal is emitted when the daemon internal has
|
|
* changed, for instance when a device has been added or removed.
|
|
*
|
|
* Since: 0.7.0
|
|
**/
|
|
signals[SIGNAL_CHANGED] = g_signal_new("changed",
|
|
G_TYPE_FROM_CLASS(object_class),
|
|
G_SIGNAL_RUN_LAST,
|
|
G_STRUCT_OFFSET(FwupdClientClass, changed),
|
|
NULL,
|
|
NULL,
|
|
g_cclosure_marshal_VOID__VOID,
|
|
G_TYPE_NONE,
|
|
0);
|
|
|
|
/**
|
|
* FwupdClient::state-changed:
|
|
* @self: the #FwupdClient instance that emitted the signal
|
|
* @status: the #FwupdStatus
|
|
*
|
|
* The ::state-changed signal is emitted when the daemon status has
|
|
* changed, e.g. going from %FWUPD_STATUS_IDLE to %FWUPD_STATUS_DEVICE_WRITE.
|
|
*
|
|
* Since: 0.7.0
|
|
**/
|
|
signals[SIGNAL_STATUS_CHANGED] =
|
|
g_signal_new("status-changed",
|
|
G_TYPE_FROM_CLASS(object_class),
|
|
G_SIGNAL_RUN_LAST,
|
|
G_STRUCT_OFFSET(FwupdClientClass, status_changed),
|
|
NULL,
|
|
NULL,
|
|
g_cclosure_marshal_VOID__UINT,
|
|
G_TYPE_NONE,
|
|
1,
|
|
G_TYPE_UINT);
|
|
|
|
/**
|
|
* FwupdClient::device-added:
|
|
* @self: the #FwupdClient instance that emitted the signal
|
|
* @result: the #FwupdDevice
|
|
*
|
|
* The ::device-added signal is emitted when a device has been
|
|
* added.
|
|
*
|
|
* Since: 0.7.1
|
|
**/
|
|
signals[SIGNAL_DEVICE_ADDED] = g_signal_new("device-added",
|
|
G_TYPE_FROM_CLASS(object_class),
|
|
G_SIGNAL_RUN_LAST,
|
|
G_STRUCT_OFFSET(FwupdClientClass, device_added),
|
|
NULL,
|
|
NULL,
|
|
g_cclosure_marshal_generic,
|
|
G_TYPE_NONE,
|
|
1,
|
|
FWUPD_TYPE_DEVICE);
|
|
|
|
/**
|
|
* FwupdClient::device-removed:
|
|
* @self: the #FwupdClient instance that emitted the signal
|
|
* @result: the #FwupdDevice
|
|
*
|
|
* The ::device-removed signal is emitted when a device has been
|
|
* removed.
|
|
*
|
|
* Since: 0.7.1
|
|
**/
|
|
signals[SIGNAL_DEVICE_REMOVED] =
|
|
g_signal_new("device-removed",
|
|
G_TYPE_FROM_CLASS(object_class),
|
|
G_SIGNAL_RUN_LAST,
|
|
G_STRUCT_OFFSET(FwupdClientClass, device_removed),
|
|
NULL,
|
|
NULL,
|
|
g_cclosure_marshal_generic,
|
|
G_TYPE_NONE,
|
|
1,
|
|
FWUPD_TYPE_DEVICE);
|
|
|
|
/**
|
|
* FwupdClient::device-changed:
|
|
* @self: the #FwupdClient instance that emitted the signal
|
|
* @result: the #FwupdDevice
|
|
*
|
|
* The ::device-changed signal is emitted when a device has been
|
|
* changed in some way, e.g. the version number is updated.
|
|
*
|
|
* Since: 0.7.1
|
|
**/
|
|
signals[SIGNAL_DEVICE_CHANGED] =
|
|
g_signal_new("device-changed",
|
|
G_TYPE_FROM_CLASS(object_class),
|
|
G_SIGNAL_RUN_LAST,
|
|
G_STRUCT_OFFSET(FwupdClientClass, device_changed),
|
|
NULL,
|
|
NULL,
|
|
g_cclosure_marshal_generic,
|
|
G_TYPE_NONE,
|
|
1,
|
|
FWUPD_TYPE_DEVICE);
|
|
|
|
/**
|
|
* FwupdClient::device-request:
|
|
* @self: the #FwupdClient instance that emitted the signal
|
|
* @msg: the #FwupdRequest
|
|
*
|
|
* The ::device-request signal is emitted when a device has been
|
|
* emitted some kind of event, e.g. a manual action is required.
|
|
*
|
|
* Since: 1.6.2
|
|
**/
|
|
signals[SIGNAL_DEVICE_REQUEST] =
|
|
g_signal_new("device-request",
|
|
G_TYPE_FROM_CLASS(object_class),
|
|
G_SIGNAL_RUN_LAST,
|
|
G_STRUCT_OFFSET(FwupdClientClass, device_changed),
|
|
NULL,
|
|
NULL,
|
|
g_cclosure_marshal_generic,
|
|
G_TYPE_NONE,
|
|
1,
|
|
FWUPD_TYPE_REQUEST);
|
|
|
|
/**
|
|
* FwupdClient:status:
|
|
*
|
|
* The last-reported status of the daemon.
|
|
*
|
|
* Since: 0.7.0
|
|
*/
|
|
pspec = g_param_spec_uint("status",
|
|
NULL,
|
|
NULL,
|
|
0,
|
|
FWUPD_STATUS_LAST,
|
|
FWUPD_STATUS_UNKNOWN,
|
|
G_PARAM_READWRITE | G_PARAM_STATIC_NAME);
|
|
g_object_class_install_property(object_class, PROP_STATUS, pspec);
|
|
|
|
/**
|
|
* FwupdClient:tainted:
|
|
*
|
|
* If the daemon is tainted by 3rd party code.
|
|
*
|
|
* Since: 1.2.4
|
|
*/
|
|
pspec = g_param_spec_boolean("tainted",
|
|
NULL,
|
|
NULL,
|
|
FALSE,
|
|
G_PARAM_READABLE | G_PARAM_STATIC_NAME);
|
|
g_object_class_install_property(object_class, PROP_TAINTED, pspec);
|
|
|
|
/**
|
|
* FwupdClient:interactive:
|
|
*
|
|
* If the daemon is running in an interactive terminal
|
|
*
|
|
* Since: 1.3.4
|
|
*/
|
|
pspec = g_param_spec_boolean("interactive",
|
|
NULL,
|
|
NULL,
|
|
FALSE,
|
|
G_PARAM_READABLE | G_PARAM_STATIC_NAME);
|
|
g_object_class_install_property(object_class, PROP_INTERACTIVE, pspec);
|
|
|
|
/**
|
|
* FwupdClient:percentage:
|
|
*
|
|
* The last-reported percentage of the daemon.
|
|
*
|
|
* Since: 0.7.3
|
|
*/
|
|
pspec = g_param_spec_uint("percentage",
|
|
NULL,
|
|
NULL,
|
|
0,
|
|
100,
|
|
0,
|
|
G_PARAM_READWRITE | G_PARAM_STATIC_NAME);
|
|
g_object_class_install_property(object_class, PROP_PERCENTAGE, pspec);
|
|
|
|
/**
|
|
* FwupdClient:daemon-version:
|
|
*
|
|
* The daemon version number.
|
|
*
|
|
* Since: 0.9.6
|
|
*/
|
|
pspec = g_param_spec_string("daemon-version",
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
G_PARAM_READABLE | G_PARAM_STATIC_NAME);
|
|
g_object_class_install_property(object_class, PROP_DAEMON_VERSION, pspec);
|
|
|
|
/**
|
|
* FwupdClient:soup-session:
|
|
*
|
|
* The libsoup session, now unused.
|
|
*
|
|
* Since: 1.4.5
|
|
*/
|
|
pspec = g_param_spec_object("soup-session",
|
|
NULL,
|
|
NULL,
|
|
G_TYPE_OBJECT,
|
|
G_PARAM_READABLE | G_PARAM_STATIC_NAME);
|
|
g_object_class_install_property(object_class, PROP_SOUP_SESSION, pspec);
|
|
|
|
/**
|
|
* FwupdClient:host-product:
|
|
*
|
|
* The host product string
|
|
*
|
|
* Since: 1.3.1
|
|
*/
|
|
pspec = g_param_spec_string("host-product",
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
G_PARAM_READABLE | G_PARAM_STATIC_NAME);
|
|
g_object_class_install_property(object_class, PROP_HOST_PRODUCT, pspec);
|
|
|
|
/**
|
|
* FwupdClient:host-machine-id:
|
|
*
|
|
* The host machine-id string
|
|
*
|
|
* Since: 1.3.2
|
|
*/
|
|
pspec = g_param_spec_string("host-machine-id",
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
G_PARAM_READABLE | G_PARAM_STATIC_NAME);
|
|
g_object_class_install_property(object_class, PROP_HOST_MACHINE_ID, pspec);
|
|
|
|
/**
|
|
* FwupdClient:host-security-id:
|
|
*
|
|
* The host machine-id string
|
|
*
|
|
* Since: 1.5.0
|
|
*/
|
|
pspec = g_param_spec_string("host-security-id",
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
G_PARAM_READABLE | G_PARAM_STATIC_NAME);
|
|
g_object_class_install_property(object_class, PROP_HOST_SECURITY_ID, pspec);
|
|
}
|
|
|
|
static void
|
|
fwupd_client_init(FwupdClient *self)
|
|
{
|
|
FwupdClientPrivate *priv = GET_PRIVATE(self);
|
|
g_mutex_init(&priv->proxy_mutex);
|
|
g_mutex_init(&priv->idle_mutex);
|
|
priv->idle_sources =
|
|
g_ptr_array_new_with_free_func((GDestroyNotify)fwupd_client_context_helper_free);
|
|
priv->proxy_resolver = g_proxy_resolver_get_default();
|
|
}
|
|
|
|
static void
|
|
fwupd_client_finalize(GObject *object)
|
|
{
|
|
FwupdClient *self = FWUPD_CLIENT(object);
|
|
FwupdClientPrivate *priv = GET_PRIVATE(self);
|
|
|
|
g_clear_pointer(&priv->main_ctx, g_main_context_unref);
|
|
g_free(priv->user_agent);
|
|
g_free(priv->daemon_version);
|
|
g_free(priv->host_product);
|
|
g_free(priv->host_machine_id);
|
|
g_free(priv->host_security_id);
|
|
g_mutex_clear(&priv->idle_mutex);
|
|
if (priv->idle_id != 0)
|
|
g_source_remove(priv->idle_id);
|
|
g_ptr_array_unref(priv->idle_sources);
|
|
g_mutex_clear(&priv->proxy_mutex);
|
|
if (priv->proxy != NULL)
|
|
g_object_unref(priv->proxy);
|
|
#ifdef SOUP_SESSION_COMPAT
|
|
if (priv->soup_session != NULL)
|
|
g_object_unref(priv->soup_session);
|
|
#endif
|
|
|
|
G_OBJECT_CLASS(fwupd_client_parent_class)->finalize(object);
|
|
}
|
|
|
|
/**
|
|
* fwupd_client_new:
|
|
*
|
|
* Creates a new client.
|
|
*
|
|
* Returns: a new #FwupdClient
|
|
*
|
|
* Since: 0.7.0
|
|
**/
|
|
FwupdClient *
|
|
fwupd_client_new(void)
|
|
{
|
|
FwupdClient *self;
|
|
self = g_object_new(FWUPD_TYPE_CLIENT, NULL);
|
|
return FWUPD_CLIENT(self);
|
|
}
|