fwupd/libfwupd/fwupd-client.c
Richard Hughes 196c6c69db Add support for the Host Security ID
The HSI specification assigns a simple text ID to the current state of firmware
security. As new vulnerabilities are found, and as protection measures are
updated, new requirements will be added to the required firmware behaviours for
each HSI value.

The HSI specification is currently incomplete and in active development, and
so the --force flag is required in all command line tools. The current ID value
will probably change on a given platform so please do not start using the result
for any kind of compliance requirements.
2020-05-11 22:11:49 +01:00

2208 lines
60 KiB
C

/*
* Copyright (C) 2016-2018 Richard Hughes <richard@hughsie.com>
*
* SPDX-License-Identifier: LGPL-2.1+
*/
#include "config.h"
#include <glib-object.h>
#include <gio/gio.h>
#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.h"
#include "fwupd-common.h"
#include "fwupd-deprecated.h"
#include "fwupd-enums.h"
#include "fwupd-error.h"
#include "fwupd-device-private.h"
#include "fwupd-security-attr-private.h"
#include "fwupd-release-private.h"
#include "fwupd-remote-private.h"
/**
* SECTION:fwupd-client
* @short_description: a way of interfacing with the daemon
*
* An object that allows client code to call the daemon methods synchronously.
*
* See also: #FwupdDevice
*/
static void fwupd_client_finalize (GObject *object);
typedef struct {
FwupdStatus status;
gboolean tainted;
gboolean interactive;
guint percentage;
gchar *daemon_version;
gchar *host_product;
gchar *host_machine_id;
gchar *host_security_id;
GDBusConnection *conn;
GDBusProxy *proxy;
} FwupdClientPrivate;
enum {
SIGNAL_CHANGED,
SIGNAL_STATUS_CHANGED,
SIGNAL_DEVICE_ADDED,
SIGNAL_DEVICE_REMOVED,
SIGNAL_DEVICE_CHANGED,
SIGNAL_LAST
};
enum {
PROP_0,
PROP_STATUS,
PROP_PERCENTAGE,
PROP_DAEMON_VERSION,
PROP_TAINTED,
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))
typedef struct {
gboolean ret;
GError *error;
GMainLoop *loop;
GVariant *val;
GDBusMessage *message;
} FwupdClientHelper;
static void
fwupd_client_helper_free (FwupdClientHelper *helper)
{
if (helper->message != NULL)
g_object_unref (helper->message);
if (helper->val != NULL)
g_variant_unref (helper->val);
if (helper->error != NULL)
g_error_free (helper->error);
g_main_loop_unref (helper->loop);
g_free (helper);
}
static FwupdClientHelper *
fwupd_client_helper_new (void)
{
FwupdClientHelper *helper;
helper = g_new0 (FwupdClientHelper, 1);
helper->loop = g_main_loop_new (NULL, FALSE);
return helper;
}
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunused-function"
G_DEFINE_AUTOPTR_CLEANUP_FUNC(FwupdClientHelper, fwupd_client_helper_free)
#pragma clang diagnostic pop
static void
fwupd_client_set_host_product (FwupdClient *client, const gchar *host_product)
{
FwupdClientPrivate *priv = GET_PRIVATE (client);
g_free (priv->host_product);
priv->host_product = g_strdup (host_product);
g_object_notify (G_OBJECT (client), "host-product");
}
static void
fwupd_client_set_host_machine_id (FwupdClient *client, const gchar *host_machine_id)
{
FwupdClientPrivate *priv = GET_PRIVATE (client);
g_free (priv->host_machine_id);
priv->host_machine_id = g_strdup (host_machine_id);
g_object_notify (G_OBJECT (client), "host-machine-id");
}
static void
fwupd_client_set_host_security_id (FwupdClient *client, const gchar *host_security_id)
{
FwupdClientPrivate *priv = GET_PRIVATE (client);
g_free (priv->host_security_id);
priv->host_security_id = g_strdup (host_security_id);
g_object_notify (G_OBJECT (client), "host-security-id");
}
static void
fwupd_client_set_daemon_version (FwupdClient *client, const gchar *daemon_version)
{
FwupdClientPrivate *priv = GET_PRIVATE (client);
g_free (priv->daemon_version);
priv->daemon_version = g_strdup (daemon_version);
g_object_notify (G_OBJECT (client), "daemon-version");
}
static void
fwupd_client_properties_changed_cb (GDBusProxy *proxy,
GVariant *changed_properties,
GStrv invalidated_properties,
FwupdClient *client)
{
FwupdClientPrivate *priv = GET_PRIVATE (client);
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) {
priv->status = g_variant_get_uint32 (val);
g_debug ("Emitting ::status-changed() [%s]",
fwupd_status_to_string (priv->status));
g_signal_emit (client, signals[SIGNAL_STATUS_CHANGED], 0, priv->status);
g_object_notify (G_OBJECT (client), "status");
}
}
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);
g_object_notify (G_OBJECT (client), "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);
g_object_notify (G_OBJECT (client), "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) {
priv->percentage = g_variant_get_uint32 (val);
g_object_notify (G_OBJECT (client), "percentage");
}
}
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 (client, 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 (client, 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 (client, 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 (client, 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 *client)
{
g_autoptr(FwupdDevice) dev = NULL;
if (g_strcmp0 (signal_name, "Changed") == 0) {
g_debug ("Emitting ::changed()");
g_signal_emit (client, 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));
g_signal_emit (client, signals[SIGNAL_DEVICE_ADDED], 0, dev);
return;
}
if (g_strcmp0 (signal_name, "DeviceRemoved") == 0) {
dev = fwupd_device_from_variant (parameters);
g_signal_emit (client, signals[SIGNAL_DEVICE_REMOVED], 0, dev);
g_debug ("Emitting ::device-removed(%s)",
fwupd_device_get_id (dev));
return;
}
if (g_strcmp0 (signal_name, "DeviceChanged") == 0) {
dev = fwupd_device_from_variant (parameters);
g_signal_emit (client, signals[SIGNAL_DEVICE_CHANGED], 0, dev);
g_debug ("Emitting ::device-changed(%s)",
fwupd_device_get_id (dev));
return;
}
g_debug ("Unknown signal name '%s' from %s", signal_name, sender_name);
}
/**
* fwupd_client_connect:
* @client: A #FwupdClient
* @cancellable: the #GCancellable, or %NULL
* @error: the #GError, or %NULL
*
* Sets up the client ready for use. Most other methods call this
* for you, and do you only need to call this if you are just watching
* the client.
*
* Returns: %TRUE for success
*
* Since: 0.7.1
**/
gboolean
fwupd_client_connect (FwupdClient *client, GCancellable *cancellable, GError **error)
{
FwupdClientPrivate *priv = GET_PRIVATE (client);
g_autoptr(GVariant) val = NULL;
g_autoptr(GVariant) val2 = NULL;
g_return_val_if_fail (FWUPD_IS_CLIENT (client), FALSE);
g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
/* nothing to do */
if (priv->proxy != NULL)
return TRUE;
/* connect to the daemon */
priv->conn = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, error);
if (priv->conn == NULL) {
g_prefix_error (error, "Failed to connect to system D-Bus: ");
return FALSE;
}
priv->proxy = g_dbus_proxy_new_sync (priv->conn,
G_DBUS_PROXY_FLAGS_NONE,
NULL,
FWUPD_DBUS_SERVICE,
FWUPD_DBUS_PATH,
FWUPD_DBUS_INTERFACE,
NULL,
error);
if (priv->proxy == NULL)
return FALSE;
g_signal_connect (priv->proxy, "g-properties-changed",
G_CALLBACK (fwupd_client_properties_changed_cb), client);
g_signal_connect (priv->proxy, "g-signal",
G_CALLBACK (fwupd_client_signal_cb), client);
val = g_dbus_proxy_get_cached_property (priv->proxy, "DaemonVersion");
if (val != NULL)
fwupd_client_set_daemon_version (client, 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);
val2 = g_dbus_proxy_get_cached_property (priv->proxy, "Interactive");
if (val2 != NULL)
priv->interactive = g_variant_get_boolean (val2);
val = g_dbus_proxy_get_cached_property (priv->proxy, "HostProduct");
if (val != NULL)
fwupd_client_set_host_product (client, g_variant_get_string (val, NULL));
val = g_dbus_proxy_get_cached_property (priv->proxy, "HostMachineId");
if (val != NULL)
fwupd_client_set_host_machine_id (client, g_variant_get_string (val, NULL));
val = g_dbus_proxy_get_cached_property (priv->proxy, "HostSecurityId");
if (val != NULL)
fwupd_client_set_host_security_id (client, g_variant_get_string (val, NULL));
return TRUE;
}
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);
}
/**
* fwupd_client_get_host_security_attrs:
* @client: A #FwupdClient
* @cancellable: the #GCancellable, or %NULL
* @error: the #GError, or %NULL
*
* Gets all the host security attributes from the daemon.
*
* Returns: (element-type FwupdSecurityAttr) (transfer container): attributes
*
* Since: 1.5.0
**/
GPtrArray *
fwupd_client_get_host_security_attrs (FwupdClient *client, GCancellable *cancellable, GError **error)
{
FwupdClientPrivate *priv = GET_PRIVATE (client);
g_autoptr(GVariant) val = NULL;
g_return_val_if_fail (FWUPD_IS_CLIENT (client), NULL);
g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL);
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
/* connect */
if (!fwupd_client_connect (client, cancellable, error))
return NULL;
/* call into daemon */
val = g_dbus_proxy_call_sync (priv->proxy,
"GetHostSecurityAttrs",
NULL,
G_DBUS_CALL_FLAGS_NONE,
-1,
cancellable,
error);
if (val == NULL) {
if (error != NULL)
fwupd_client_fixup_dbus_error (*error);
return NULL;
}
return fwupd_security_attr_array_from_variant (val);
}
/**
* fwupd_client_get_devices:
* @client: A #FwupdClient
* @cancellable: the #GCancellable, or %NULL
* @error: the #GError, or %NULL
*
* Gets all the devices registered with the daemon.
*
* Returns: (element-type FwupdDevice) (transfer container): results
*
* Since: 0.9.2
**/
GPtrArray *
fwupd_client_get_devices (FwupdClient *client, GCancellable *cancellable, GError **error)
{
FwupdClientPrivate *priv = GET_PRIVATE (client);
g_autoptr(GVariant) val = NULL;
g_return_val_if_fail (FWUPD_IS_CLIENT (client), NULL);
g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL);
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
/* connect */
if (!fwupd_client_connect (client, cancellable, error))
return NULL;
/* call into daemon */
val = g_dbus_proxy_call_sync (priv->proxy,
"GetDevices",
NULL,
G_DBUS_CALL_FLAGS_NONE,
-1,
cancellable,
error);
if (val == NULL) {
if (error != NULL)
fwupd_client_fixup_dbus_error (*error);
return NULL;
}
return fwupd_device_array_from_variant (val);
}
/**
* fwupd_client_get_history:
* @client: A #FwupdClient
* @cancellable: the #GCancellable, or %NULL
* @error: the #GError, or %NULL
*
* Gets all the history.
*
* Returns: (element-type FwupdDevice) (transfer container): results
*
* Since: 1.0.4
**/
GPtrArray *
fwupd_client_get_history (FwupdClient *client, GCancellable *cancellable, GError **error)
{
FwupdClientPrivate *priv = GET_PRIVATE (client);
g_autoptr(GVariant) val = NULL;
g_return_val_if_fail (FWUPD_IS_CLIENT (client), NULL);
g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL);
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
/* connect */
if (!fwupd_client_connect (client, cancellable, error))
return NULL;
/* call into daemon */
val = g_dbus_proxy_call_sync (priv->proxy,
"GetHistory",
NULL,
G_DBUS_CALL_FLAGS_NONE,
-1,
cancellable,
error);
if (val == NULL) {
if (error != NULL)
fwupd_client_fixup_dbus_error (*error);
return NULL;
}
return fwupd_device_array_from_variant (val);
}
/**
* fwupd_client_get_device_by_id:
* @client: A #FwupdClient
* @device_id: the device ID, e.g. `usb:00:01:03:03`
* @cancellable: the #GCancellable, or %NULL
* @error: the #GError, or %NULL
*
* Gets a device by it's device ID.
*
* Returns: (transfer full): a #FwupdDevice or %NULL
*
* Since: 0.9.3
**/
FwupdDevice *
fwupd_client_get_device_by_id (FwupdClient *client,
const gchar *device_id,
GCancellable *cancellable,
GError **error)
{
g_autoptr(GPtrArray) devices = NULL;
g_return_val_if_fail (FWUPD_IS_CLIENT (client), NULL);
g_return_val_if_fail (device_id != NULL, NULL);
g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL);
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
/* get all the devices */
devices = fwupd_client_get_devices (client, cancellable, error);
if (devices == NULL)
return NULL;
/* 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)
return g_object_ref (dev);
}
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_NOT_FOUND,
"failed to find %s", device_id);
return NULL;
}
/**
* fwupd_client_get_devices_by_guid:
* @client: A #FwupdClient
* @guid: the GUID, e.g. `e22c4520-43dc-5bb3-8245-5787fead9b63`
* @cancellable: the #GCancellable, or %NULL
* @error: the #GError, or %NULL
*
* Gets any devices that provide a specific GUID. An error is returned if no
* devices contains this GUID.
*
* Returns: (element-type FwupdDevice) (transfer container): devices or %NULL
*
* Since: 1.4.1
**/
GPtrArray *
fwupd_client_get_devices_by_guid (FwupdClient *client,
const gchar *guid,
GCancellable *cancellable,
GError **error)
{
g_autoptr(GPtrArray) devices = NULL;
g_autoptr(GPtrArray) devices_tmp = NULL;
g_return_val_if_fail (FWUPD_IS_CLIENT (client), NULL);
g_return_val_if_fail (guid != NULL, NULL);
g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL);
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
/* get all the devices */
devices_tmp = fwupd_client_get_devices (client, cancellable, error);
if (devices_tmp == NULL)
return NULL;
/* 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_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_NOT_FOUND,
"failed to find any device providing %s", guid);
return NULL;
}
/* success */
return g_steal_pointer (&devices);
}
/**
* fwupd_client_get_releases:
* @client: A #FwupdClient
* @device_id: the device ID
* @cancellable: the #GCancellable, or %NULL
* @error: the #GError, or %NULL
*
* Gets all the releases for a specific device
*
* Returns: (element-type FwupdRelease) (transfer container): results
*
* Since: 0.9.3
**/
GPtrArray *
fwupd_client_get_releases (FwupdClient *client, const gchar *device_id,
GCancellable *cancellable, GError **error)
{
FwupdClientPrivate *priv = GET_PRIVATE (client);
g_autoptr(GVariant) val = NULL;
g_return_val_if_fail (FWUPD_IS_CLIENT (client), NULL);
g_return_val_if_fail (device_id != NULL, NULL);
g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL);
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
/* connect */
if (!fwupd_client_connect (client, cancellable, error))
return NULL;
/* call into daemon */
val = g_dbus_proxy_call_sync (priv->proxy,
"GetReleases",
g_variant_new ("(s)", device_id),
G_DBUS_CALL_FLAGS_NONE,
-1,
cancellable,
error);
if (val == NULL) {
if (error != NULL)
fwupd_client_fixup_dbus_error (*error);
return NULL;
}
return fwupd_release_array_from_variant (val);
}
/**
* fwupd_client_get_downgrades:
* @client: A #FwupdClient
* @device_id: the device ID
* @cancellable: the #GCancellable, or %NULL
* @error: the #GError, or %NULL
*
* Gets all the downgrades for a specific device.
*
* Returns: (element-type FwupdRelease) (transfer container): results
*
* Since: 0.9.8
**/
GPtrArray *
fwupd_client_get_downgrades (FwupdClient *client, const gchar *device_id,
GCancellable *cancellable, GError **error)
{
FwupdClientPrivate *priv = GET_PRIVATE (client);
g_autoptr(GVariant) val = NULL;
g_return_val_if_fail (FWUPD_IS_CLIENT (client), NULL);
g_return_val_if_fail (device_id != NULL, NULL);
g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL);
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
/* connect */
if (!fwupd_client_connect (client, cancellable, error))
return NULL;
/* call into daemon */
val = g_dbus_proxy_call_sync (priv->proxy,
"GetDowngrades",
g_variant_new ("(s)", device_id),
G_DBUS_CALL_FLAGS_NONE,
-1,
cancellable,
error);
if (val == NULL) {
if (error != NULL)
fwupd_client_fixup_dbus_error (*error);
return NULL;
}
return fwupd_release_array_from_variant (val);
}
/**
* fwupd_client_get_upgrades:
* @client: A #FwupdClient
* @device_id: the device ID
* @cancellable: the #GCancellable, or %NULL
* @error: the #GError, or %NULL
*
* Gets all the upgrades for a specific device.
*
* Returns: (element-type FwupdRelease) (transfer container): results
*
* Since: 0.9.8
**/
GPtrArray *
fwupd_client_get_upgrades (FwupdClient *client, const gchar *device_id,
GCancellable *cancellable, GError **error)
{
FwupdClientPrivate *priv = GET_PRIVATE (client);
g_autoptr(GVariant) val = NULL;
g_return_val_if_fail (FWUPD_IS_CLIENT (client), NULL);
g_return_val_if_fail (device_id != NULL, NULL);
g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL);
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
/* connect */
if (!fwupd_client_connect (client, cancellable, error))
return NULL;
/* call into daemon */
val = g_dbus_proxy_call_sync (priv->proxy,
"GetUpgrades",
g_variant_new ("(s)", device_id),
G_DBUS_CALL_FLAGS_NONE,
-1,
cancellable,
error);
if (val == NULL) {
if (error != NULL)
fwupd_client_fixup_dbus_error (*error);
return NULL;
}
return fwupd_release_array_from_variant (val);
}
static void
fwupd_client_proxy_call_cb (GObject *source, GAsyncResult *res, gpointer user_data)
{
FwupdClientHelper *helper = (FwupdClientHelper *) user_data;
helper->val = g_dbus_proxy_call_finish (G_DBUS_PROXY (source),
res, &helper->error);
if (helper->val != NULL)
helper->ret = TRUE;
if (helper->error != NULL)
fwupd_client_fixup_dbus_error (helper->error);
g_main_loop_quit (helper->loop);
}
/**
* fwupd_client_modify_config
* @client: A #FwupdClient
* @key: key, e.g. `BlacklistPlugins`
* @value: value, e.g. `*`
* @cancellable: the #GCancellable, or %NULL
* @error: the #GError, or %NULL
*
* Modifies a daemon config option.
* The daemon will only respond to this request with proper permissions
*
* Returns: %TRUE for success
*
* Since: 1.2.8
**/
gboolean
fwupd_client_modify_config (FwupdClient *client, const gchar *key, const gchar *value,
GCancellable *cancellable, GError **error)
{
FwupdClientPrivate *priv = GET_PRIVATE (client);
g_autoptr(FwupdClientHelper) helper = NULL;
g_return_val_if_fail (FWUPD_IS_CLIENT (client), FALSE);
g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
/* connect */
if (!fwupd_client_connect (client, cancellable, error))
return FALSE;
/* call into daemon */
helper = fwupd_client_helper_new ();
g_dbus_proxy_call (priv->proxy,
"ModifyConfig",
g_variant_new ("(ss)", key, value),
G_DBUS_CALL_FLAGS_NONE,
-1,
cancellable,
fwupd_client_proxy_call_cb,
helper);
g_main_loop_run (helper->loop);
if (!helper->ret) {
g_propagate_error (error, helper->error);
helper->error = NULL;
return FALSE;
}
return TRUE;
}
/**
* fwupd_client_activate:
* @client: A #FwupdClient
* @cancellable: the #GCancellable, or %NULL
* @device_id: a device
* @error: the #GError, or %NULL
*
* 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.
*
* Returns: %TRUE for success
*
* Since: 1.2.6
**/
gboolean
fwupd_client_activate (FwupdClient *client, GCancellable *cancellable,
const gchar *device_id, GError **error)
{
FwupdClientPrivate *priv = GET_PRIVATE (client);
g_autoptr(FwupdClientHelper) helper = NULL;
g_return_val_if_fail (FWUPD_IS_CLIENT (client), FALSE);
g_return_val_if_fail (device_id != NULL, FALSE);
g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
/* connect */
if (!fwupd_client_connect (client, cancellable, error))
return FALSE;
/* call into daemon */
helper = fwupd_client_helper_new ();
g_dbus_proxy_call (priv->proxy,
"Activate",
g_variant_new ("(s)", device_id),
G_DBUS_CALL_FLAGS_NONE,
-1,
cancellable,
fwupd_client_proxy_call_cb,
helper);
g_main_loop_run (helper->loop);
if (!helper->ret) {
g_propagate_error (error, helper->error);
helper->error = NULL;
return FALSE;
}
return TRUE;
}
/**
* fwupd_client_verify:
* @client: A #FwupdClient
* @device_id: the device ID
* @cancellable: the #GCancellable, or %NULL
* @error: the #GError, or %NULL
*
* Verify a specific device.
*
* Returns: %TRUE for verification success
*
* Since: 0.7.0
**/
gboolean
fwupd_client_verify (FwupdClient *client, const gchar *device_id,
GCancellable *cancellable, GError **error)
{
FwupdClientPrivate *priv = GET_PRIVATE (client);
g_autoptr(FwupdClientHelper) helper = NULL;
g_return_val_if_fail (FWUPD_IS_CLIENT (client), FALSE);
g_return_val_if_fail (device_id != NULL, FALSE);
g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
/* connect */
if (!fwupd_client_connect (client, cancellable, error))
return FALSE;
/* call into daemon */
helper = fwupd_client_helper_new ();
g_dbus_proxy_call (priv->proxy,
"Verify",
g_variant_new ("(s)", device_id),
G_DBUS_CALL_FLAGS_NONE,
-1,
cancellable,
fwupd_client_proxy_call_cb,
helper);
g_main_loop_run (helper->loop);
if (!helper->ret) {
g_propagate_error (error, helper->error);
helper->error = NULL;
return FALSE;
}
return TRUE;
}
/**
* fwupd_client_verify_update:
* @client: A #FwupdClient
* @device_id: the device ID
* @cancellable: the #GCancellable, or %NULL
* @error: the #GError, or %NULL
*
* Update the verification record for a specific device.
*
* Returns: %TRUE for verification success
*
* Since: 0.8.0
**/
gboolean
fwupd_client_verify_update (FwupdClient *client, const gchar *device_id,
GCancellable *cancellable, GError **error)
{
FwupdClientPrivate *priv = GET_PRIVATE (client);
g_autoptr(FwupdClientHelper) helper = NULL;
g_return_val_if_fail (FWUPD_IS_CLIENT (client), FALSE);
g_return_val_if_fail (device_id != NULL, FALSE);
g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
/* connect */
if (!fwupd_client_connect (client, cancellable, error))
return FALSE;
/* call into daemon */
helper = fwupd_client_helper_new ();
g_dbus_proxy_call (priv->proxy,
"VerifyUpdate",
g_variant_new ("(s)", device_id),
G_DBUS_CALL_FLAGS_NONE,
-1,
cancellable,
fwupd_client_proxy_call_cb,
helper);
g_main_loop_run (helper->loop);
if (!helper->ret) {
g_propagate_error (error, helper->error);
helper->error = NULL;
return FALSE;
}
return TRUE;
}
/**
* fwupd_client_unlock:
* @client: A #FwupdClient
* @device_id: the device ID
* @cancellable: the #GCancellable, or %NULL
* @error: the #GError, or %NULL
*
* Unlocks a specific device so firmware can be read or wrote.
*
* Returns: %TRUE for success
*
* Since: 0.7.0
**/
gboolean
fwupd_client_unlock (FwupdClient *client, const gchar *device_id,
GCancellable *cancellable, GError **error)
{
FwupdClientPrivate *priv = GET_PRIVATE (client);
g_autoptr(FwupdClientHelper) helper = NULL;
g_return_val_if_fail (FWUPD_IS_CLIENT (client), FALSE);
g_return_val_if_fail (device_id != NULL, FALSE);
g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
/* connect */
if (!fwupd_client_connect (client, cancellable, error))
return FALSE;
/* call into daemon */
helper = fwupd_client_helper_new ();
g_dbus_proxy_call (priv->proxy,
"Unlock",
g_variant_new ("(s)", device_id),
G_DBUS_CALL_FLAGS_NONE,
-1,
cancellable,
fwupd_client_proxy_call_cb,
helper);
g_main_loop_run (helper->loop);
if (!helper->ret) {
g_propagate_error (error, helper->error);
helper->error = NULL;
return FALSE;
}
return TRUE;
}
/**
* fwupd_client_clear_results:
* @client: A #FwupdClient
* @device_id: the device ID
* @cancellable: the #GCancellable, or %NULL
* @error: the #GError, or %NULL
*
* Clears the results for a specific device.
*
* Returns: %TRUE for success
*
* Since: 0.7.0
**/
gboolean
fwupd_client_clear_results (FwupdClient *client, const gchar *device_id,
GCancellable *cancellable, GError **error)
{
FwupdClientPrivate *priv = GET_PRIVATE (client);
g_autoptr(FwupdClientHelper) helper = NULL;
g_return_val_if_fail (FWUPD_IS_CLIENT (client), FALSE);
g_return_val_if_fail (device_id != NULL, FALSE);
g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
/* connect */
if (!fwupd_client_connect (client, cancellable, error))
return FALSE;
/* call into daemon */
helper = fwupd_client_helper_new ();
g_dbus_proxy_call (priv->proxy,
"ClearResults",
g_variant_new ("(s)", device_id),
G_DBUS_CALL_FLAGS_NONE,
-1,
cancellable,
fwupd_client_proxy_call_cb,
helper);
g_main_loop_run (helper->loop);
if (!helper->ret) {
g_propagate_error (error, helper->error);
helper->error = NULL;
return FALSE;
}
return TRUE;
}
/**
* fwupd_client_get_results:
* @client: A #FwupdClient
* @device_id: the device ID
* @cancellable: the #GCancellable, or %NULL
* @error: the #GError, or %NULL
*
* Gets the results of a previous firmware update for a specific device.
*
* Returns: (transfer full): a #FwupdDevice, or %NULL for failure
*
* Since: 0.7.0
**/
FwupdDevice *
fwupd_client_get_results (FwupdClient *client, const gchar *device_id,
GCancellable *cancellable, GError **error)
{
FwupdClientPrivate *priv = GET_PRIVATE (client);
g_autoptr(FwupdClientHelper) helper = NULL;
g_return_val_if_fail (FWUPD_IS_CLIENT (client), NULL);
g_return_val_if_fail (device_id != NULL, NULL);
g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL);
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
/* connect */
if (!fwupd_client_connect (client, cancellable, error))
return NULL;
/* call into daemon */
helper = fwupd_client_helper_new ();
g_dbus_proxy_call (priv->proxy,
"GetResults",
g_variant_new ("(s)", device_id),
G_DBUS_CALL_FLAGS_NONE,
-1,
cancellable,
fwupd_client_proxy_call_cb,
helper);
g_main_loop_run (helper->loop);
if (!helper->ret) {
g_propagate_error (error, helper->error);
helper->error = NULL;
return NULL;
}
return fwupd_device_from_variant (helper->val);
}
#ifdef HAVE_GIO_UNIX
static void
fwupd_client_send_message_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
{
FwupdClientHelper *helper = (FwupdClientHelper *) user_data;
GDBusConnection *con = G_DBUS_CONNECTION (source_object);
helper->message = g_dbus_connection_send_message_with_reply_finish (con, res,
&helper->error);
if (helper->message &&
!g_dbus_message_to_gerror (helper->message, &helper->error)) {
helper->ret = TRUE;
helper->val = g_dbus_message_get_body (helper->message);
if (helper->val != NULL)
g_variant_ref (helper->val);
}
if (helper->error != NULL)
fwupd_client_fixup_dbus_error (helper->error);
g_main_loop_quit (helper->loop);
}
#endif
/**
* fwupd_client_install:
* @client: A #FwupdClient
* @device_id: the device ID
* @filename: the filename to install
* @install_flags: the #FwupdInstallFlags, e.g. %FWUPD_INSTALL_FLAG_ALLOW_REINSTALL
* @cancellable: the #GCancellable, or %NULL
* @error: the #GError, or %NULL
*
* Install a file onto a specific device.
*
* Returns: %TRUE for success
*
* Since: 0.7.0
**/
gboolean
fwupd_client_install (FwupdClient *client,
const gchar *device_id,
const gchar *filename,
FwupdInstallFlags install_flags,
GCancellable *cancellable,
GError **error)
{
#ifdef HAVE_GIO_UNIX
FwupdClientPrivate *priv = GET_PRIVATE (client);
GVariant *body;
GVariantBuilder builder;
gint retval;
gint fd;
g_autoptr(FwupdClientHelper) helper = NULL;
g_autoptr(GDBusMessage) request = NULL;
g_autoptr(GUnixFDList) fd_list = NULL;
g_return_val_if_fail (FWUPD_IS_CLIENT (client), FALSE);
g_return_val_if_fail (device_id != NULL, FALSE);
g_return_val_if_fail (filename != NULL, FALSE);
g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
/* connect */
if (!fwupd_client_connect (client, cancellable, error))
return FALSE;
/* set options */
g_variant_builder_init (&builder, G_VARIANT_TYPE_VARDICT);
g_variant_builder_add (&builder, "{sv}",
"reason", g_variant_new_string ("user-action"));
g_variant_builder_add (&builder, "{sv}",
"filename", g_variant_new_string (filename));
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_FORCE) {
g_variant_builder_add (&builder, "{sv}",
"force", 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));
}
/* open file */
fd = open (filename, O_RDONLY);
if (fd < 0) {
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_FILE,
"failed to open %s",
filename);
return FALSE;
}
/* set out of band file descriptor */
fd_list = g_unix_fd_list_new ();
retval = g_unix_fd_list_append (fd_list, fd, NULL);
g_assert (retval != -1);
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);
/* g_unix_fd_list_append did a dup() already */
close (fd);
/* call into daemon */
helper = fwupd_client_helper_new ();
body = g_variant_new ("(sha{sv})", device_id, fd, &builder);
g_dbus_message_set_body (request, body);
g_dbus_connection_send_message_with_reply (priv->conn,
request,
G_DBUS_SEND_MESSAGE_FLAGS_NONE,
G_MAXINT,
NULL,
cancellable,
fwupd_client_send_message_cb,
helper);
g_main_loop_run (helper->loop);
if (!helper->ret) {
g_propagate_error (error, helper->error);
helper->error = NULL;
return FALSE;
}
return TRUE;
#else
g_set_error_literal (error,
FWUPD_ERROR,
FWUPD_ERROR_NOT_SUPPORTED,
"Not supported as <glib-unix.h> is unavailable");
return FALSE;
#endif
}
/**
* fwupd_client_get_details:
* @client: A #FwupdClient
* @filename: the firmware filename, e.g. `firmware.cab`
* @cancellable: the #GCancellable, or %NULL
* @error: the #GError, or %NULL
*
* Gets details about a specific firmware file.
*
* Returns: (transfer container) (element-type FwupdDevice): an array of results
*
* Since: 1.0.0
**/
GPtrArray *
fwupd_client_get_details (FwupdClient *client, const gchar *filename,
GCancellable *cancellable, GError **error)
{
#ifdef HAVE_GIO_UNIX
FwupdClientPrivate *priv = GET_PRIVATE (client);
GVariant *body;
gint fd;
gint retval;
g_autoptr(FwupdClientHelper) helper = NULL;
g_autoptr(GDBusMessage) request = NULL;
g_autoptr(GUnixFDList) fd_list = NULL;
g_return_val_if_fail (FWUPD_IS_CLIENT (client), NULL);
g_return_val_if_fail (filename != NULL, NULL);
g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL);
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
/* connect */
if (!fwupd_client_connect (client, cancellable, error))
return NULL;
/* open file */
fd = open (filename, O_RDONLY);
if (fd < 0) {
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_FILE,
"failed to open %s",
filename);
return NULL;
}
/* set out of band file descriptor */
fd_list = g_unix_fd_list_new ();
retval = g_unix_fd_list_append (fd_list, fd, NULL);
g_assert (retval != -1);
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);
/* g_unix_fd_list_append did a dup() already */
close (fd);
/* call into daemon */
helper = fwupd_client_helper_new ();
body = g_variant_new ("(h)", fd);
g_dbus_message_set_body (request, body);
g_dbus_connection_send_message_with_reply (priv->conn,
request,
G_DBUS_SEND_MESSAGE_FLAGS_NONE,
-1,
NULL,
cancellable,
fwupd_client_send_message_cb,
helper);
g_main_loop_run (helper->loop);
if (!helper->ret) {
g_propagate_error (error, helper->error);
helper->error = NULL;
return NULL;
}
/* return results */
return fwupd_device_array_from_variant (helper->val);
#else
g_set_error_literal (error,
FWUPD_ERROR,
FWUPD_ERROR_NOT_SUPPORTED,
"Not supported as <glib-unix.h> is unavailable");
return NULL;
#endif
}
/**
* fwupd_client_get_percentage:
* @client: 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 *client)
{
FwupdClientPrivate *priv = GET_PRIVATE (client);
g_return_val_if_fail (FWUPD_IS_CLIENT (client), 0);
return priv->percentage;
}
/**
* fwupd_client_get_daemon_version:
* @client: 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 *client)
{
FwupdClientPrivate *priv = GET_PRIVATE (client);
g_return_val_if_fail (FWUPD_IS_CLIENT (client), NULL);
return priv->daemon_version;
}
/**
* fwupd_client_get_host_product:
* @client: 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 *client)
{
FwupdClientPrivate *priv = GET_PRIVATE (client);
g_return_val_if_fail (FWUPD_IS_CLIENT (client), NULL);
return priv->host_product;
}
/**
* fwupd_client_get_host_machine_id:
* @client: 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 *client)
{
FwupdClientPrivate *priv = GET_PRIVATE (client);
g_return_val_if_fail (FWUPD_IS_CLIENT (client), NULL);
return priv->host_machine_id;
}
/**
* fwupd_client_get_host_security_id:
* @client: 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 *client)
{
FwupdClientPrivate *priv = GET_PRIVATE (client);
g_return_val_if_fail (FWUPD_IS_CLIENT (client), NULL);
return priv->host_security_id;
}
/**
* fwupd_client_get_status:
* @client: 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 *client)
{
FwupdClientPrivate *priv = GET_PRIVATE (client);
g_return_val_if_fail (FWUPD_IS_CLIENT (client), FWUPD_STATUS_UNKNOWN);
return priv->status;
}
/**
* fwupd_client_get_tainted:
* @client: 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 *client)
{
FwupdClientPrivate *priv = GET_PRIVATE (client);
g_return_val_if_fail (FWUPD_IS_CLIENT (client), FALSE);
return priv->tainted;
}
/**
* fwupd_client_get_daemon_interactive:
* @client: 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 *client)
{
FwupdClientPrivate *priv = GET_PRIVATE (client);
g_return_val_if_fail (FWUPD_IS_CLIENT (client), FALSE);
return priv->interactive;
}
/**
* fwupd_client_update_metadata:
* @client: A #FwupdClient
* @remote_id: the remote ID, e.g. `lvfs-testing`
* @metadata_fn: the XML metadata filename
* @signature_fn: the GPG signature file
* @cancellable: the #GCancellable, or %NULL
* @error: the #GError, or %NULL
*
* 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.
*
* Returns: %TRUE for success
*
* Since: 1.0.0
**/
gboolean
fwupd_client_update_metadata (FwupdClient *client,
const gchar *remote_id,
const gchar *metadata_fn,
const gchar *signature_fn,
GCancellable *cancellable,
GError **error)
{
#ifdef HAVE_GIO_UNIX
FwupdClientPrivate *priv = GET_PRIVATE (client);
GVariant *body;
gint fd;
gint fd_sig;
g_autoptr(FwupdClientHelper) helper = NULL;
g_autoptr(GDBusMessage) request = NULL;
g_autoptr(GUnixFDList) fd_list = NULL;
g_return_val_if_fail (FWUPD_IS_CLIENT (client), FALSE);
g_return_val_if_fail (remote_id != NULL, FALSE);
g_return_val_if_fail (metadata_fn != NULL, FALSE);
g_return_val_if_fail (signature_fn != NULL, FALSE);
g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
/* connect */
if (!fwupd_client_connect (client, cancellable, error))
return FALSE;
/* open file */
fd = open (metadata_fn, O_RDONLY);
if (fd < 0) {
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_FILE,
"failed to open %s",
metadata_fn);
return FALSE;
}
fd_sig = open (signature_fn, O_RDONLY);
if (fd_sig < 0) {
close (fd);
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_FILE,
"failed to open %s",
signature_fn);
return FALSE;
}
/* set out of band file descriptor */
fd_list = g_unix_fd_list_new ();
g_unix_fd_list_append (fd_list, fd, NULL);
g_unix_fd_list_append (fd_list, fd_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);
/* g_unix_fd_list_append did a dup() already */
close (fd);
close (fd_sig);
/* call into daemon */
body = g_variant_new ("(shh)", remote_id, fd, fd_sig);
g_dbus_message_set_body (request, body);
helper = fwupd_client_helper_new ();
g_dbus_connection_send_message_with_reply (priv->conn,
request,
G_DBUS_SEND_MESSAGE_FLAGS_NONE,
-1,
NULL,
cancellable,
fwupd_client_send_message_cb,
helper);
g_main_loop_run (helper->loop);
if (!helper->ret) {
g_propagate_error (error, helper->error);
helper->error = NULL;
return FALSE;
}
return TRUE;
#else
g_set_error_literal (error,
FWUPD_ERROR,
FWUPD_ERROR_NOT_SUPPORTED,
"Not supported as <glib-unix.h> is unavailable");
return FALSE;
#endif
}
/**
* fwupd_client_get_remotes:
* @client: A #FwupdClient
* @cancellable: the #GCancellable, or %NULL
* @error: the #GError, or %NULL
*
* Gets the list of remotes that have been configured for the system.
*
* Returns: (element-type FwupdRemote) (transfer container): list of remotes, or %NULL
*
* Since: 0.9.3
**/
GPtrArray *
fwupd_client_get_remotes (FwupdClient *client, GCancellable *cancellable, GError **error)
{
FwupdClientPrivate *priv = GET_PRIVATE (client);
g_autoptr(GVariant) val = NULL;
g_return_val_if_fail (FWUPD_IS_CLIENT (client), NULL);
g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL);
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
/* connect */
if (!fwupd_client_connect (client, cancellable, error))
return NULL;
/* call into daemon */
val = g_dbus_proxy_call_sync (priv->proxy,
"GetRemotes",
NULL,
G_DBUS_CALL_FLAGS_NONE,
-1,
cancellable,
error);
if (val == NULL) {
if (error != NULL)
fwupd_client_fixup_dbus_error (*error);
return NULL;
}
return fwupd_remote_array_from_variant (val);
}
/**
* fwupd_client_get_approved_firmware:
* @client: A #FwupdClient
* @cancellable: the #GCancellable, or %NULL
* @error: the #GError, or %NULL
*
* Gets the list of approved firmware.
*
* Returns: (transfer full): list of remotes, or %NULL
*
* Since: 1.2.6
**/
gchar **
fwupd_client_get_approved_firmware (FwupdClient *client,
GCancellable *cancellable,
GError **error)
{
FwupdClientPrivate *priv = GET_PRIVATE (client);
g_autoptr(GVariant) val = NULL;
gchar **retval = NULL;
g_return_val_if_fail (FWUPD_IS_CLIENT (client), NULL);
g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL);
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
/* connect */
if (!fwupd_client_connect (client, cancellable, error))
return NULL;
/* call into daemon */
val = g_dbus_proxy_call_sync (priv->proxy,
"GetApprovedFirmware",
NULL,
G_DBUS_CALL_FLAGS_NONE,
-1,
cancellable,
error);
if (val == NULL) {
if (error != NULL)
fwupd_client_fixup_dbus_error (*error);
return NULL;
}
g_variant_get (val, "(^as)", &retval);
return retval;
}
/**
* fwupd_client_set_approved_firmware:
* @client: A #FwupdClient
* @checksums: Array of checksums
* @cancellable: the #GCancellable, or %NULL
* @error: the #GError, or %NULL
*
* Sets the list of approved firmware.
*
* Returns: %TRUE for success
*
* Since: 1.2.6
**/
gboolean
fwupd_client_set_approved_firmware (FwupdClient *client,
gchar **checksums,
GCancellable *cancellable,
GError **error)
{
FwupdClientPrivate *priv = GET_PRIVATE (client);
g_autoptr(GVariant) val = NULL;
g_return_val_if_fail (FWUPD_IS_CLIENT (client), FALSE);
g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
/* connect */
if (!fwupd_client_connect (client, cancellable, error))
return FALSE;
/* call into daemon */
val = g_dbus_proxy_call_sync (priv->proxy,
"SetApprovedFirmware",
g_variant_new ("(^as)", checksums),
G_DBUS_CALL_FLAGS_NONE,
-1,
cancellable,
error);
if (val == NULL) {
if (error != NULL)
fwupd_client_fixup_dbus_error (*error);
return FALSE;
}
return TRUE;
}
/**
* fwupd_client_self_sign:
* @client: A #FwupdClient
* @value: A string to sign, typically a JSON blob
* @flags: #FwupdSelfSignFlags, e.g. %FWUPD_SELF_SIGN_FLAG_ADD_TIMESTAMP
* @cancellable: the #GCancellable, or %NULL
* @error: the #GError, or %NULL
*
* Signs the data using the client self-signed certificate.
*
* Returns: %TRUE for success
*
* Since: 1.2.6
**/
gchar *
fwupd_client_self_sign (FwupdClient *client,
const gchar *value,
FwupdSelfSignFlags flags,
GCancellable *cancellable,
GError **error)
{
FwupdClientPrivate *priv = GET_PRIVATE (client);
GVariantBuilder builder;
g_autoptr(GVariant) val = NULL;
gchar *retval = NULL;
g_return_val_if_fail (FWUPD_IS_CLIENT (client), NULL);
g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL);
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
/* connect */
if (!fwupd_client_connect (client, cancellable, error))
return 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 */
val = g_dbus_proxy_call_sync (priv->proxy,
"SelfSign",
g_variant_new ("(sa{sv})", value, &builder),
G_DBUS_CALL_FLAGS_NONE,
-1,
cancellable,
error);
if (val == NULL) {
if (error != NULL)
fwupd_client_fixup_dbus_error (*error);
return NULL;
}
g_variant_get (val, "(s)", &retval);
return retval;
}
/**
* fwupd_client_modify_remote:
* @client: A #FwupdClient
* @remote_id: the remote ID, e.g. `lvfs-testing`
* @key: the key, e.g. `Enabled`
* @value: the key, e.g. `true`
* @cancellable: the #GCancellable, or %NULL
* @error: the #GError, or %NULL
*
* Modifies a system remote in a specific way.
*
* NOTE: User authentication may be required to complete this action.
*
* Returns: %TRUE for success
*
* Since: 0.9.8
**/
gboolean
fwupd_client_modify_remote (FwupdClient *client,
const gchar *remote_id,
const gchar *key,
const gchar *value,
GCancellable *cancellable,
GError **error)
{
FwupdClientPrivate *priv = GET_PRIVATE (client);
g_autoptr(GVariant) val = NULL;
g_return_val_if_fail (FWUPD_IS_CLIENT (client), FALSE);
g_return_val_if_fail (remote_id != NULL, FALSE);
g_return_val_if_fail (key != NULL, FALSE);
g_return_val_if_fail (value != NULL, FALSE);
g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
/* connect */
if (!fwupd_client_connect (client, cancellable, error))
return FALSE;
/* call into daemon */
val = g_dbus_proxy_call_sync (priv->proxy,
"ModifyRemote",
g_variant_new ("(sss)", remote_id, key, value),
G_DBUS_CALL_FLAGS_NONE,
-1,
cancellable,
error);
if (val == NULL) {
if (error != NULL)
fwupd_client_fixup_dbus_error (*error);
return FALSE;
}
return TRUE;
}
/**
* fwupd_client_modify_device:
* @client: A #FwupdClient
* @device_id: the device ID
* @key: the key, e.g. `Flags`
* @value: the key, e.g. `reported`
* @cancellable: the #GCancellable, or %NULL
* @error: the #GError, or %NULL
*
* 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.
*
* NOTE: User authentication may be required to complete this action.
*
* Returns: %TRUE for success
*
* Since: 1.0.4
**/
gboolean
fwupd_client_modify_device (FwupdClient *client,
const gchar *remote_id,
const gchar *key,
const gchar *value,
GCancellable *cancellable,
GError **error)
{
FwupdClientPrivate *priv = GET_PRIVATE (client);
g_autoptr(GVariant) val = NULL;
g_return_val_if_fail (FWUPD_IS_CLIENT (client), FALSE);
g_return_val_if_fail (remote_id != NULL, FALSE);
g_return_val_if_fail (key != NULL, FALSE);
g_return_val_if_fail (value != NULL, FALSE);
g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
/* connect */
if (!fwupd_client_connect (client, cancellable, error))
return FALSE;
/* call into daemon */
val = g_dbus_proxy_call_sync (priv->proxy,
"ModifyDevice",
g_variant_new ("(sss)", remote_id, key, value),
G_DBUS_CALL_FLAGS_NONE,
-1,
cancellable,
error);
if (val == NULL) {
if (error != NULL)
fwupd_client_fixup_dbus_error (*error);
return FALSE;
}
return TRUE;
}
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;
}
/**
* fwupd_client_get_remote_by_id:
* @client: A #FwupdClient
* @remote_id: the remote ID, e.g. `lvfs-testing`
* @cancellable: the #GCancellable, or %NULL
* @error: the #GError, or %NULL
*
* Gets a specific remote that has been configured for the system.
*
* Returns: (transfer full): a #FwupdRemote, or %NULL if not found
*
* Since: 0.9.3
**/
FwupdRemote *
fwupd_client_get_remote_by_id (FwupdClient *client,
const gchar *remote_id,
GCancellable *cancellable,
GError **error)
{
FwupdRemote *remote;
g_autoptr(GPtrArray) remotes = NULL;
g_return_val_if_fail (FWUPD_IS_CLIENT (client), NULL);
g_return_val_if_fail (remote_id != NULL, NULL);
g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL);
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
/* find remote in list */
remotes = fwupd_client_get_remotes (client, cancellable, error);
if (remotes == NULL)
return NULL;
remote = fwupd_client_get_remote_by_id_noref (remotes, remote_id);
if (remote == NULL) {
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_NOT_FOUND,
"No remote '%s' found in search paths",
remote_id);
return NULL;
}
/* success */
return g_object_ref (remote);
}
static void
fwupd_client_get_property (GObject *object, guint prop_id,
GValue *value, GParamSpec *pspec)
{
FwupdClient *client = FWUPD_CLIENT (object);
FwupdClientPrivate *priv = GET_PRIVATE (client);
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_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 *client = FWUPD_CLIENT (object);
FwupdClientPrivate *priv = GET_PRIVATE (client);
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:
* @client: 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:
* @client: 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:
* @client: 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:
* @client: 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:
* @client: 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: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: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 *client)
{
}
static void
fwupd_client_finalize (GObject *object)
{
FwupdClient *client = FWUPD_CLIENT (object);
FwupdClientPrivate *priv = GET_PRIVATE (client);
g_free (priv->daemon_version);
g_free (priv->host_product);
g_free (priv->host_machine_id);
g_free (priv->host_security_id);
if (priv->conn != NULL)
g_object_unref (priv->conn);
if (priv->proxy != NULL)
g_object_unref (priv->proxy);
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 *client;
client = g_object_new (FWUPD_TYPE_CLIENT, NULL);
return FWUPD_CLIENT (client);
}