Allow the daemon to request interactive action from the end user

The "return error and hope the client resubmits the firmware again"
pattern is clunky. There are two plugins doing this now, and about to
be one more.

This adds FwupdRequest which provides a structured way of asking the
user to perform an action, e.g. to replug the device or to press a
special key or button.

This replaces much of the UpdateMessage and UpdateImage API although
it is still used internally. Clients capable of processing the new
DeviceRequest signal should add REQUESTS to their feature flags.

Also, this allows us go back to the old meaning of _NEEDS_BOOTLOADER,
which was "needs rebooting into a bootloader mode" rather than the
slightly weird "user needs to do something and resubmit request".
This commit is contained in:
Richard Hughes 2021-07-13 22:34:32 +01:00
parent a71f100e5a
commit 19abf996c7
26 changed files with 1251 additions and 106 deletions

View File

@ -1,3 +1,3 @@
[suppress_type] [suppress_type]
type_kind = enum type_kind = enum
changed_enumerators = FWUPD_ERROR_LAST,FWUPD_GUID_FLAG_LAST,FWUPD_INSTALL_FLAG_LAST,FWUPD_KEYRING_KIND_LAST,FWUPD_REMOTE_KIND_LAST,FWUPD_SELF_SIGN_FLAG_LAST,FWUPD_STATUS_LAST,FWUPD_TRUST_FLAG_LAST,FWUPD_UPDATE_STATE_LAST,FWUPD_VERSION_FORMAT_LAST,FWUPD_CLIENT_DOWNLOAD_FLAG_LAST changed_enumerators = FWUPD_ERROR_LAST,FWUPD_GUID_FLAG_LAST,FWUPD_INSTALL_FLAG_LAST,FWUPD_KEYRING_KIND_LAST,FWUPD_REMOTE_KIND_LAST,FWUPD_SELF_SIGN_FLAG_LAST,FWUPD_STATUS_LAST,FWUPD_TRUST_FLAG_LAST,FWUPD_UPDATE_STATE_LAST,FWUPD_VERSION_FORMAT_LAST,FWUPD_CLIENT_DOWNLOAD_FLAG_LAST,FWUPD_FEATURE_FLAG_LAST

View File

@ -32,6 +32,7 @@
#include "fwupd-security-attr-private.h" #include "fwupd-security-attr-private.h"
#include "fwupd-release-private.h" #include "fwupd-release-private.h"
#include "fwupd-remote-private.h" #include "fwupd-remote-private.h"
#include "fwupd-request-private.h"
typedef GObject *(*FwupdClientObjectNewFunc) (void); typedef GObject *(*FwupdClientObjectNewFunc) (void);
@ -83,6 +84,7 @@ enum {
SIGNAL_DEVICE_ADDED, SIGNAL_DEVICE_ADDED,
SIGNAL_DEVICE_REMOVED, SIGNAL_DEVICE_REMOVED,
SIGNAL_DEVICE_CHANGED, SIGNAL_DEVICE_CHANGED,
SIGNAL_DEVICE_REQUEST,
SIGNAL_LAST SIGNAL_LAST
}; };
@ -131,13 +133,13 @@ typedef struct {
FwupdClient *self; FwupdClient *self;
gchar *property_name; gchar *property_name;
guint signal_id; guint signal_id;
FwupdDevice *device; GObject *payload;
} FwupdClientContextHelper; } FwupdClientContextHelper;
static void static void
fwupd_client_context_helper_free (FwupdClientContextHelper *helper) fwupd_client_context_helper_free (FwupdClientContextHelper *helper)
{ {
g_clear_object (&helper->device); g_clear_object (&helper->payload);
g_object_unref (helper->self); g_object_unref (helper->self);
g_free (helper->property_name); g_free (helper->property_name);
g_free (helper); g_free (helper);
@ -176,9 +178,9 @@ fwupd_client_context_idle_cb (gpointer user_data)
if (helper->property_name != NULL) if (helper->property_name != NULL)
fwupd_client_context_object_notify (self, helper->property_name); fwupd_client_context_object_notify (self, helper->property_name);
/* device signal */ /* payload signal */
if (helper->signal_id !=0 && helper->device != NULL) if (helper->signal_id != 0 && helper->payload != NULL)
g_signal_emit (self, signals[helper->signal_id], 0, helper->device); g_signal_emit (self, signals[helper->signal_id], 0, helper->payload);
} }
/* all done */ /* all done */
@ -228,14 +230,14 @@ fwupd_client_object_notify (FwupdClient *self, const gchar *property_name)
/* run callback in the correct thread */ /* run callback in the correct thread */
static void static void
fwupd_client_signal_emit_device (FwupdClient *self, guint signal_id, FwupdDevice *device) fwupd_client_signal_emit_object (FwupdClient *self, guint signal_id, GObject *payload)
{ {
FwupdClientPrivate *priv = GET_PRIVATE (self); FwupdClientPrivate *priv = GET_PRIVATE (self);
FwupdClientContextHelper *helper = NULL; FwupdClientContextHelper *helper = NULL;
/* shortcut */ /* shortcut */
if (g_main_context_is_owner (priv->main_ctx)) { if (g_main_context_is_owner (priv->main_ctx)) {
g_signal_emit (self, signals[signal_id], 0, device); g_signal_emit (self, signals[signal_id], 0, payload);
return; return;
} }
@ -243,7 +245,7 @@ fwupd_client_signal_emit_device (FwupdClient *self, guint signal_id, FwupdDevice
helper = g_new0 (FwupdClientContextHelper, 1); helper = g_new0 (FwupdClientContextHelper, 1);
helper->self = g_object_ref (self); helper->self = g_object_ref (self);
helper->signal_id = signal_id; helper->signal_id = signal_id;
helper->device = g_object_ref (device); helper->payload = g_object_ref (payload);
fwupd_client_context_helper (self, helper); fwupd_client_context_helper (self, helper);
} }
@ -407,21 +409,36 @@ fwupd_client_signal_cb (GDBusProxy *proxy,
dev = fwupd_device_from_variant (parameters); dev = fwupd_device_from_variant (parameters);
g_debug ("Emitting ::device-added(%s)", g_debug ("Emitting ::device-added(%s)",
fwupd_device_get_id (dev)); fwupd_device_get_id (dev));
fwupd_client_signal_emit_device (self, SIGNAL_DEVICE_ADDED, dev); fwupd_client_signal_emit_object (self,
SIGNAL_DEVICE_ADDED,
G_OBJECT (dev));
return; return;
} }
if (g_strcmp0 (signal_name, "DeviceRemoved") == 0) { if (g_strcmp0 (signal_name, "DeviceRemoved") == 0) {
dev = fwupd_device_from_variant (parameters); dev = fwupd_device_from_variant (parameters);
g_debug ("Emitting ::device-removed(%s)", g_debug ("Emitting ::device-removed(%s)",
fwupd_device_get_id (dev)); fwupd_device_get_id (dev));
fwupd_client_signal_emit_device (self, SIGNAL_DEVICE_REMOVED, dev); fwupd_client_signal_emit_object (self,
SIGNAL_DEVICE_REMOVED,
G_OBJECT (dev));
return; return;
} }
if (g_strcmp0 (signal_name, "DeviceChanged") == 0) { if (g_strcmp0 (signal_name, "DeviceChanged") == 0) {
dev = fwupd_device_from_variant (parameters); dev = fwupd_device_from_variant (parameters);
g_debug ("Emitting ::device-changed(%s)", g_debug ("Emitting ::device-changed(%s)",
fwupd_device_get_id (dev)); fwupd_device_get_id (dev));
fwupd_client_signal_emit_device (self, SIGNAL_DEVICE_CHANGED, 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; return;
} }
g_debug ("Unknown signal name '%s' from %s", signal_name, sender_name); g_debug ("Unknown signal name '%s' from %s", signal_name, sender_name);
@ -4952,6 +4969,23 @@ fwupd_client_class_init (FwupdClientClass *klass)
NULL, NULL, g_cclosure_marshal_generic, NULL, NULL, g_cclosure_marshal_generic,
G_TYPE_NONE, 1, FWUPD_TYPE_DEVICE); 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: * FwupdClient:status:
* *

View File

@ -11,6 +11,7 @@
#include "fwupd-enums.h" #include "fwupd-enums.h"
#include "fwupd-device.h" #include "fwupd-device.h"
#include "fwupd-request.h"
#include "fwupd-plugin.h" #include "fwupd-plugin.h"
#include "fwupd-remote.h" #include "fwupd-remote.h"
@ -31,6 +32,8 @@ struct _FwupdClientClass
FwupdDevice *result); FwupdDevice *result);
void (*device_changed) (FwupdClient *client, void (*device_changed) (FwupdClient *client,
FwupdDevice *result); FwupdDevice *result);
void (*device_request) (FwupdClient *client,
FwupdRequest *request);
/*< private >*/ /*< private >*/
void (*_fwupd_reserved1) (void); void (*_fwupd_reserved1) (void);
void (*_fwupd_reserved2) (void); void (*_fwupd_reserved2) (void);
@ -38,7 +41,6 @@ struct _FwupdClientClass
void (*_fwupd_reserved4) (void); void (*_fwupd_reserved4) (void);
void (*_fwupd_reserved5) (void); void (*_fwupd_reserved5) (void);
void (*_fwupd_reserved6) (void); void (*_fwupd_reserved6) (void);
void (*_fwupd_reserved7) (void);
}; };
/** /**

View File

@ -138,6 +138,14 @@ G_BEGIN_DECLS
* The D-Bus type signature string is 'u' i.e. a unsigned 32 bit integer. * The D-Bus type signature string is 'u' i.e. a unsigned 32 bit integer.
**/ **/
#define FWUPD_RESULT_KEY_URGENCY "Urgency" #define FWUPD_RESULT_KEY_URGENCY "Urgency"
/**
* FWUPD_RESULT_KEY_REQUEST_KIND:
*
* Result key to represent RequestKind
*
* The D-Bus type signature string is 'u' i.e. a unsigned 32 bit integer.
**/
#define FWUPD_RESULT_KEY_REQUEST_KIND "RequestKind"
/** /**
* FWUPD_RESULT_KEY_HSI_LEVEL: * FWUPD_RESULT_KEY_HSI_LEVEL:
* *

View File

@ -513,6 +513,8 @@ fwupd_feature_flag_to_string (FwupdFeatureFlags feature_flag)
return "update-action"; return "update-action";
if (feature_flag == FWUPD_FEATURE_FLAG_SWITCH_BRANCH) if (feature_flag == FWUPD_FEATURE_FLAG_SWITCH_BRANCH)
return "switch-branch"; return "switch-branch";
if (feature_flag == FWUPD_FEATURE_FLAG_REQUESTS)
return "requests";
return NULL; return NULL;
} }
@ -539,6 +541,8 @@ fwupd_feature_flag_from_string (const gchar *feature_flag)
return FWUPD_FEATURE_FLAG_UPDATE_ACTION; return FWUPD_FEATURE_FLAG_UPDATE_ACTION;
if (g_strcmp0 (feature_flag, "switch-branch") == 0) if (g_strcmp0 (feature_flag, "switch-branch") == 0)
return FWUPD_FEATURE_FLAG_SWITCH_BRANCH; return FWUPD_FEATURE_FLAG_SWITCH_BRANCH;
if (g_strcmp0 (feature_flag, "requests") == 0)
return FWUPD_FEATURE_FLAG_REQUESTS;
return FWUPD_FEATURE_FLAG_LAST; return FWUPD_FEATURE_FLAG_LAST;
} }

View File

@ -71,6 +71,7 @@ typedef enum {
* @FWUPD_FEATURE_FLAG_DETACH_ACTION: Can perform detach action, typically showing text * @FWUPD_FEATURE_FLAG_DETACH_ACTION: Can perform detach action, typically showing text
* @FWUPD_FEATURE_FLAG_UPDATE_ACTION: Can perform update action, typically showing text * @FWUPD_FEATURE_FLAG_UPDATE_ACTION: Can perform update action, typically showing text
* @FWUPD_FEATURE_FLAG_SWITCH_BRANCH: Can switch the firmware branch * @FWUPD_FEATURE_FLAG_SWITCH_BRANCH: Can switch the firmware branch
* @FWUPD_FEATURE_FLAG_REQUESTS: Can show interactive requests
* *
* The flags to the feature capabilities of the front-end client. * The flags to the feature capabilities of the front-end client.
**/ **/
@ -80,6 +81,7 @@ typedef enum {
FWUPD_FEATURE_FLAG_DETACH_ACTION = 1 << 1, /* Since: 1.4.5 */ FWUPD_FEATURE_FLAG_DETACH_ACTION = 1 << 1, /* Since: 1.4.5 */
FWUPD_FEATURE_FLAG_UPDATE_ACTION = 1 << 2, /* Since: 1.4.5 */ FWUPD_FEATURE_FLAG_UPDATE_ACTION = 1 << 2, /* Since: 1.4.5 */
FWUPD_FEATURE_FLAG_SWITCH_BRANCH = 1 << 3, /* Since: 1.5.0 */ FWUPD_FEATURE_FLAG_SWITCH_BRANCH = 1 << 3, /* Since: 1.5.0 */
FWUPD_FEATURE_FLAG_REQUESTS = 1 << 4, /* Since: 1.6.2 */
/*< private >*/ /*< private >*/
FWUPD_FEATURE_FLAG_LAST FWUPD_FEATURE_FLAG_LAST
} FwupdFeatureFlags; } FwupdFeatureFlags;

View File

@ -0,0 +1,15 @@
/*
* Copyright (C) 2017 Richard Hughes <richard@hughsie.com>
*
* SPDX-License-Identifier: LGPL-2.1+
*/
#pragma once
#include "fwupd-request.h"
G_BEGIN_DECLS
GVariant *fwupd_request_to_variant (FwupdRequest *self);
G_END_DECLS

626
libfwupd/fwupd-request.c Normal file
View File

@ -0,0 +1,626 @@
/*
* Copyright (C) 2021 Richard Hughes <richard@hughsie.com>
*
* SPDX-License-Identifier: LGPL-2.1+
*/
#include "config.h"
#include "fwupd-enums-private.h"
#include "fwupd-request-private.h"
/**
* FwupdRequest:
*
* A user request from the device.
*
* See also: [class@FwupdDevice]
*/
typedef struct {
gchar *id;
FwupdRequestKind kind;
guint64 created;
gchar *device_id;
gchar *message;
gchar *image;
} FwupdRequestPrivate;
enum {
PROP_0,
PROP_ID,
PROP_KIND,
PROP_MESSAGE,
PROP_IMAGE,
PROP_LAST
};
G_DEFINE_TYPE_WITH_PRIVATE (FwupdRequest, fwupd_request, G_TYPE_OBJECT)
#define GET_PRIVATE(o) (fwupd_request_get_instance_private (o))
/**
* fwupd_request_kind_to_string:
* @kind: a update message kind, e.g. %FWUPD_REQUEST_KIND_IMMEDIATE
*
* Converts a enumerated update message kind to a string.
*
* Returns: identifier string
*
* Since: 1.6.2
**/
const gchar *
fwupd_request_kind_to_string (FwupdRequestKind kind)
{
if (kind == FWUPD_REQUEST_KIND_UNKNOWN)
return "unknown";
if (kind == FWUPD_REQUEST_KIND_POST)
return "post";
if (kind == FWUPD_REQUEST_KIND_IMMEDIATE)
return "immediate";
return NULL;
}
/**
* fwupd_request_kind_from_string:
* @kind: a string, e.g. `immediate`
*
* Converts a string to an enumerated update message kind.
*
* Returns: enumerated value
*
* Since: 1.6.2
**/
FwupdRequestKind
fwupd_request_kind_from_string (const gchar *kind)
{
if (g_strcmp0 (kind, "unknown") == 0)
return FWUPD_REQUEST_KIND_UNKNOWN;
if (g_strcmp0 (kind, "post") == 0)
return FWUPD_REQUEST_KIND_POST;
if (g_strcmp0 (kind, "immediate") == 0)
return FWUPD_REQUEST_KIND_IMMEDIATE;
return FWUPD_REQUEST_KIND_LAST;
}
/**
* fwupd_request_get_id:
* @self: a #FwupdRequest
*
* Gets the ID.
*
* Returns: the ID, or %NULL if unset
*
* Since: 1.6.2
**/
const gchar *
fwupd_request_get_id (FwupdRequest *self)
{
FwupdRequestPrivate *priv = GET_PRIVATE (self);
g_return_val_if_fail (FWUPD_IS_REQUEST (self), NULL);
return priv->id;
}
/**
* fwupd_request_set_id:
* @self: a #FwupdRequest
* @id: (nullable): the request ID, e.g. `USB:foo`
*
* Sets the ID.
*
* Since: 1.6.2
**/
void
fwupd_request_set_id (FwupdRequest *self, const gchar *id)
{
FwupdRequestPrivate *priv = GET_PRIVATE (self);
g_return_if_fail (FWUPD_IS_REQUEST (self));
/* not changed */
if (g_strcmp0 (priv->id, id) == 0)
return;
g_free (priv->id);
priv->id = g_strdup (id);
}
/**
* fwupd_request_get_device_id:
* @self: a #FwupdRequest
*
* Gets the device_id that created the request.
*
* Returns: the device_id, or %NULL if unset
*
* Since: 1.6.2
**/
const gchar *
fwupd_request_get_device_id (FwupdRequest *self)
{
FwupdRequestPrivate *priv = GET_PRIVATE (self);
g_return_val_if_fail (FWUPD_IS_REQUEST (self), NULL);
return priv->device_id;
}
/**
* fwupd_request_set_device_id:
* @self: a #FwupdRequest
* @device_id: (nullable): the device_id, e.g. `colorhug`
*
* Sets the device_id that created the request.
*
* Since: 1.6.2
**/
void
fwupd_request_set_device_id (FwupdRequest *self, const gchar *device_id)
{
FwupdRequestPrivate *priv = GET_PRIVATE (self);
g_return_if_fail (FWUPD_IS_REQUEST (self));
/* not changed */
if (g_strcmp0 (priv->device_id, device_id) == 0)
return;
g_free (priv->device_id);
priv->device_id = g_strdup (device_id);
}
/**
* fwupd_request_get_created:
* @self: a #FwupdRequest
*
* Gets when the request was created.
*
* Returns: the UNIX time, or 0 if unset
*
* Since: 1.6.2
**/
guint64
fwupd_request_get_created (FwupdRequest *self)
{
FwupdRequestPrivate *priv = GET_PRIVATE (self);
g_return_val_if_fail (FWUPD_IS_REQUEST (self), 0);
return priv->created;
}
/**
* fwupd_request_set_created:
* @self: a #FwupdRequest
* @created: the UNIX time
*
* Sets when the request was created.
*
* Since: 1.6.2
**/
void
fwupd_request_set_created (FwupdRequest *self, guint64 created)
{
FwupdRequestPrivate *priv = GET_PRIVATE (self);
g_return_if_fail (FWUPD_IS_REQUEST (self));
priv->created = created;
}
/**
* fwupd_request_to_variant:
* @self: a #FwupdRequest
*
* Serialize the request data.
*
* Returns: the serialized data, or %NULL for error
*
* Since: 1.6.2
**/
GVariant *
fwupd_request_to_variant (FwupdRequest *self)
{
FwupdRequestPrivate *priv = GET_PRIVATE (self);
GVariantBuilder builder;
g_return_val_if_fail (FWUPD_IS_REQUEST (self), NULL);
/* create an array with all the metadata in */
g_variant_builder_init (&builder, G_VARIANT_TYPE_VARDICT);
if (priv->id != NULL) {
g_variant_builder_add (&builder, "{sv}",
FWUPD_RESULT_KEY_APPSTREAM_ID,
g_variant_new_string (priv->id));
}
if (priv->created > 0) {
g_variant_builder_add (&builder, "{sv}",
FWUPD_RESULT_KEY_CREATED,
g_variant_new_uint64 (priv->created));
}
if (priv->device_id != NULL) {
g_variant_builder_add (&builder, "{sv}",
FWUPD_RESULT_KEY_PLUGIN,
g_variant_new_string (priv->device_id));
}
if (priv->message != NULL) {
g_variant_builder_add (&builder, "{sv}",
FWUPD_RESULT_KEY_UPDATE_MESSAGE,
g_variant_new_string (priv->message));
}
if (priv->image != NULL) {
g_variant_builder_add (&builder, "{sv}",
FWUPD_RESULT_KEY_UPDATE_IMAGE,
g_variant_new_string (priv->image));
}
if (priv->kind != FWUPD_REQUEST_KIND_UNKNOWN) {
g_variant_builder_add (&builder, "{sv}",
FWUPD_RESULT_KEY_REQUEST_KIND,
g_variant_new_uint32 (priv->kind));
}
return g_variant_new ("a{sv}", &builder);
}
static void
fwupd_request_from_key_value (FwupdRequest *self, const gchar *key, GVariant *value)
{
if (g_strcmp0 (key, FWUPD_RESULT_KEY_APPSTREAM_ID) == 0) {
fwupd_request_set_id (self, g_variant_get_string (value, NULL));
return;
}
if (g_strcmp0 (key, FWUPD_RESULT_KEY_CREATED) == 0) {
fwupd_request_set_created (self, g_variant_get_uint64 (value));
return;
}
if (g_strcmp0 (key, FWUPD_RESULT_KEY_DEVICE_ID) == 0) {
fwupd_request_set_device_id (self, g_variant_get_string (value, NULL));
return;
}
if (g_strcmp0 (key, FWUPD_RESULT_KEY_UPDATE_MESSAGE) == 0) {
fwupd_request_set_message (self, g_variant_get_string (value, NULL));
return;
}
if (g_strcmp0 (key, FWUPD_RESULT_KEY_UPDATE_IMAGE) == 0) {
fwupd_request_set_image (self, g_variant_get_string (value, NULL));
return;
}
if (g_strcmp0 (key, FWUPD_RESULT_KEY_REQUEST_KIND) == 0) {
fwupd_request_set_kind (self, g_variant_get_uint32 (value));
return;
}
}
static void
fwupd_pad_kv_str (GString *str, const gchar *key, const gchar *value)
{
/* ignore */
if (key == NULL || value == NULL)
return;
g_string_append_printf (str, " %s: ", key);
for (gsize i = strlen (key); i < 20; i++)
g_string_append (str, " ");
g_string_append_printf (str, "%s\n", value);
}
static void
fwupd_pad_kv_unx (GString *str, const gchar *key, guint64 value)
{
g_autoptr(GDateTime) date = NULL;
g_autofree gchar *tmp = NULL;
/* ignore */
if (value == 0)
return;
date = g_date_time_new_from_unix_utc ((gint64) value);
tmp = g_date_time_format (date, "%F");
fwupd_pad_kv_str (str, key, tmp);
}
/**
* fwupd_request_get_message:
* @self: a #FwupdRequest
*
* Gets the update message.
*
* Returns: the update message, or %NULL if unset
*
* Since: 1.6.2
**/
const gchar *
fwupd_request_get_message (FwupdRequest *self)
{
FwupdRequestPrivate *priv = GET_PRIVATE (self);
g_return_val_if_fail (FWUPD_IS_REQUEST (self), NULL);
return priv->message;
}
/**
* fwupd_request_set_message:
* @self: a #FwupdRequest
* @message: (nullable): the update message string
*
* Sets the update message.
*
* Since: 1.6.2
**/
void
fwupd_request_set_message (FwupdRequest *self, const gchar *message)
{
FwupdRequestPrivate *priv = GET_PRIVATE (self);
g_return_if_fail (FWUPD_IS_REQUEST (self));
/* not changed */
if (g_strcmp0 (priv->message, message) == 0)
return;
g_free (priv->message);
priv->message = g_strdup (message);
g_object_notify (G_OBJECT (self), "message");
}
/**
* fwupd_request_get_image:
* @self: a #FwupdRequest
*
* Gets the update image.
*
* Returns: the update image URL, or %NULL if unset
*
* Since: 1.6.2
**/
const gchar *
fwupd_request_get_image (FwupdRequest *self)
{
FwupdRequestPrivate *priv = GET_PRIVATE (self);
g_return_val_if_fail (FWUPD_IS_REQUEST (self), NULL);
return priv->image;
}
/**
* fwupd_request_set_image:
* @self: a #FwupdRequest
* @image: (nullable): the update image URL
*
* Sets the update image.
*
* Since: 1.6.2
**/
void
fwupd_request_set_image (FwupdRequest *self, const gchar *image)
{
FwupdRequestPrivate *priv = GET_PRIVATE (self);
g_return_if_fail (FWUPD_IS_REQUEST (self));
/* not changed */
if (g_strcmp0 (priv->image, image) == 0)
return;
g_free (priv->image);
priv->image = g_strdup (image);
g_object_notify (G_OBJECT (self), "image");
}
/**
* fwupd_request_get_kind:
* @self: a #FwupdRequest
*
* Returns what the request is currently doing.
*
* Returns: the kind value, e.g. %FWUPD_STATUS_REQUEST_WRITE
*
* Since: 1.6.2
**/
FwupdRequestKind
fwupd_request_get_kind (FwupdRequest *self)
{
FwupdRequestPrivate *priv = GET_PRIVATE (self);
g_return_val_if_fail (FWUPD_IS_REQUEST (self), 0);
return priv->kind;
}
/**
* fwupd_request_set_kind:
* @self: a #FwupdRequest
* @kind: the kind value, e.g. %FWUPD_STATUS_REQUEST_WRITE
*
* Sets what the request is currently doing.
*
* Since: 1.6.2
**/
void
fwupd_request_set_kind (FwupdRequest *self, FwupdRequestKind kind)
{
FwupdRequestPrivate *priv = GET_PRIVATE (self);
g_return_if_fail (FWUPD_IS_REQUEST (self));
if (priv->kind == kind)
return;
priv->kind = kind;
g_object_notify (G_OBJECT (self), "kind");
}
/**
* fwupd_request_to_string:
* @self: a #FwupdRequest
*
* Builds a text representation of the object.
*
* Returns: text, or %NULL for invalid
*
* Since: 1.6.2
**/
gchar *
fwupd_request_to_string (FwupdRequest *self)
{
FwupdRequestPrivate *priv = GET_PRIVATE (self);
GString *str = g_string_new (NULL);
g_return_val_if_fail (FWUPD_IS_REQUEST (self), NULL);
fwupd_pad_kv_str (str, FWUPD_RESULT_KEY_APPSTREAM_ID, priv->id);
if (priv->kind != FWUPD_REQUEST_KIND_UNKNOWN) {
fwupd_pad_kv_str (str, FWUPD_RESULT_KEY_REQUEST_KIND,
fwupd_request_kind_to_string (priv->kind));
}
fwupd_pad_kv_str (str, FWUPD_RESULT_KEY_DEVICE_ID, priv->device_id);
fwupd_pad_kv_unx (str, FWUPD_RESULT_KEY_CREATED, priv->created);
fwupd_pad_kv_str (str, FWUPD_RESULT_KEY_UPDATE_MESSAGE, priv->message);
fwupd_pad_kv_str (str, FWUPD_RESULT_KEY_UPDATE_IMAGE, priv->image);
return g_string_free (str, FALSE);
}
static void
fwupd_request_get_property (GObject *object, guint prop_id,
GValue *value, GParamSpec *pspec)
{
FwupdRequest *self = FWUPD_REQUEST (object);
FwupdRequestPrivate *priv = GET_PRIVATE (self);
switch (prop_id) {
case PROP_ID:
g_value_set_string (value, priv->id);
break;
case PROP_MESSAGE:
g_value_set_string (value, priv->message);
break;
case PROP_IMAGE:
g_value_set_string (value, priv->image);
break;
case PROP_KIND:
g_value_set_uint (value, priv->kind);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
fwupd_request_set_property (GObject *object, guint prop_id,
const GValue *value, GParamSpec *pspec)
{
FwupdRequest *self = FWUPD_REQUEST (object);
switch (prop_id) {
case PROP_ID:
fwupd_request_set_id (self, g_value_get_string (value));
break;
case PROP_MESSAGE:
fwupd_request_set_message (self, g_value_get_string (value));
break;
case PROP_IMAGE:
fwupd_request_set_image (self, g_value_get_string (value));
break;
case PROP_KIND:
fwupd_request_set_kind (self, g_value_get_uint (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
fwupd_request_finalize (GObject *object)
{
FwupdRequest *self = FWUPD_REQUEST (object);
FwupdRequestPrivate *priv = GET_PRIVATE (self);
g_free (priv->id);
g_free (priv->device_id);
g_free (priv->message);
g_free (priv->image);
G_OBJECT_CLASS (fwupd_request_parent_class)->finalize (object);
}
static void
fwupd_request_init (FwupdRequest *self)
{
FwupdRequestPrivate *priv = GET_PRIVATE (self);
priv->created = g_get_real_time () / G_USEC_PER_SEC;
}
static void
fwupd_request_class_init (FwupdRequestClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GParamSpec *pspec;
object_class->finalize = fwupd_request_finalize;
object_class->get_property = fwupd_request_get_property;
object_class->set_property = fwupd_request_set_property;
pspec = g_param_spec_string ("id", NULL, NULL, NULL,
G_PARAM_READWRITE |
G_PARAM_STATIC_NAME);
g_object_class_install_property (object_class, PROP_ID, pspec);
pspec = g_param_spec_uint ("kind", NULL, NULL,
FWUPD_REQUEST_KIND_UNKNOWN,
FWUPD_REQUEST_KIND_LAST,
FWUPD_REQUEST_KIND_UNKNOWN,
G_PARAM_READWRITE |
G_PARAM_STATIC_NAME);
g_object_class_install_property (object_class, PROP_KIND, pspec);
pspec = g_param_spec_string ("message", NULL, NULL, NULL,
G_PARAM_READWRITE |
G_PARAM_STATIC_NAME);
g_object_class_install_property (object_class, PROP_MESSAGE, pspec);
pspec = g_param_spec_string ("image", NULL, NULL, NULL,
G_PARAM_READWRITE |
G_PARAM_STATIC_NAME);
g_object_class_install_property (object_class, PROP_IMAGE, pspec);
}
static void
fwupd_request_set_from_variant_iter (FwupdRequest *self, GVariantIter *iter)
{
GVariant *value;
const gchar *key;
while (g_variant_iter_next (iter, "{&sv}", &key, &value)) {
fwupd_request_from_key_value (self, key, value);
g_variant_unref (value);
}
}
/**
* fwupd_request_from_variant:
* @value: the serialized data
*
* Creates a new request using serialized data.
*
* Returns: (transfer full): a new #FwupdRequest, or %NULL if @value was invalid
*
* Since: 1.6.2
**/
FwupdRequest *
fwupd_request_from_variant (GVariant *value)
{
FwupdRequest *self = NULL;
const gchar *type_string;
g_autoptr(GVariantIter) iter = NULL;
/* format from GetDetails */
type_string = g_variant_get_type_string (value);
if (g_strcmp0 (type_string, "(a{sv})") == 0) {
self = fwupd_request_new ();
g_variant_get (value, "(a{sv})", &iter);
fwupd_request_set_from_variant_iter (self, iter);
} else if (g_strcmp0 (type_string, "a{sv}") == 0) {
self = fwupd_request_new ();
g_variant_get (value, "a{sv}", &iter);
fwupd_request_set_from_variant_iter (self, iter);
} else {
g_warning ("type %s not known", type_string);
}
return self;
}
/**
* fwupd_request_new:
*
* Creates a new request.
*
* Returns: a new #FwupdRequest
*
* Since: 1.6.2
**/
FwupdRequest *
fwupd_request_new (void)
{
FwupdRequest *self;
self = g_object_new (FWUPD_TYPE_REQUEST, NULL);
return FWUPD_REQUEST (self);
}

90
libfwupd/fwupd-request.h Normal file
View File

@ -0,0 +1,90 @@
/*
* Copyright (C) 2021 Richard Hughes <richard@hughsie.com>
*
* SPDX-License-Identifier: LGPL-2.1+
*/
#pragma once
#include <glib-object.h>
G_BEGIN_DECLS
#define FWUPD_TYPE_REQUEST (fwupd_request_get_type ())
G_DECLARE_DERIVABLE_TYPE (FwupdRequest, fwupd_request, FWUPD, REQUEST, GObject)
struct _FwupdRequestClass
{
GObjectClass parent_class;
/*< private >*/
void (*_fwupd_reserved1) (void);
void (*_fwupd_reserved2) (void);
void (*_fwupd_reserved3) (void);
void (*_fwupd_reserved4) (void);
void (*_fwupd_reserved5) (void);
void (*_fwupd_reserved6) (void);
void (*_fwupd_reserved7) (void);
};
/**
* FwupdRequestKind:
* @FWUPD_REQUEST_KIND_UNKNOWN: Unknown kind
* @FWUPD_REQUEST_KIND_POST: After the update
* @FWUPD_REQUEST_KIND_IMMEDIATE: Immediately
*
* The kind of request we are asking of the user.
**/
typedef enum {
FWUPD_REQUEST_KIND_UNKNOWN, /* Since: 1.6.2 */
FWUPD_REQUEST_KIND_POST, /* Since: 1.6.2 */
FWUPD_REQUEST_KIND_IMMEDIATE, /* Since: 1.6.2 */
/*< private >*/
FWUPD_REQUEST_KIND_LAST
} FwupdRequestKind;
/**
* FWPUD_REQUEST_ID_REMOVE_REPLUG:
*
* The user needs to remove and reinsert the device.
*
* Since 1.6.2
*/
#define FWPUD_REQUEST_ID_REMOVE_REPLUG "org.freedesktop.fwupd.request.remove-replug"
/**
* FWPUD_REQUEST_ID_PRESS_UNLOCK:
*
* The user needs to press unlock on the device.
*
* Since 1.6.2
*/
#define FWPUD_REQUEST_ID_PRESS_UNLOCK "org.freedesktop.fwupd.request.press-unlock"
const gchar *fwupd_request_kind_to_string (FwupdRequestKind kind);
FwupdRequestKind fwupd_request_kind_from_string (const gchar *kind);
FwupdRequest *fwupd_request_new (void);
gchar *fwupd_request_to_string (FwupdRequest *self);
const gchar *fwupd_request_get_id (FwupdRequest *self);
void fwupd_request_set_id (FwupdRequest *self,
const gchar *id);
guint64 fwupd_request_get_created (FwupdRequest *self);
void fwupd_request_set_created (FwupdRequest *self,
guint64 created);
const gchar *fwupd_request_get_device_id (FwupdRequest *self);
void fwupd_request_set_device_id (FwupdRequest *self,
const gchar *device_id);
const gchar *fwupd_request_get_message (FwupdRequest *self);
void fwupd_request_set_message (FwupdRequest *self,
const gchar *message);
const gchar *fwupd_request_get_image (FwupdRequest *self);
void fwupd_request_set_image (FwupdRequest *self,
const gchar *image);
FwupdRequestKind fwupd_request_get_kind (FwupdRequest *self);
void fwupd_request_set_kind (FwupdRequest *self,
FwupdRequestKind kind);
FwupdRequest *fwupd_request_from_variant (GVariant *value);
G_END_DECLS

View File

@ -20,6 +20,7 @@
#include "fwupd-device-private.h" #include "fwupd-device-private.h"
#include "fwupd-release-private.h" #include "fwupd-release-private.h"
#include "fwupd-remote-private.h" #include "fwupd-remote-private.h"
#include "fwupd-request-private.h"
static gboolean static gboolean
fu_test_compare_lines (const gchar *txt1, const gchar *txt2, GError **error) fu_test_compare_lines (const gchar *txt1, const gchar *txt2, GError **error)
@ -131,6 +132,11 @@ fwupd_enums_func (void)
g_assert_cmpstr (tmp, !=, NULL); g_assert_cmpstr (tmp, !=, NULL);
g_assert_cmpint (fwupd_trust_flag_from_string (tmp), ==, i); g_assert_cmpint (fwupd_trust_flag_from_string (tmp), ==, i);
} }
for (guint i = 0; i < FWUPD_REQUEST_KIND_LAST; i++) {
const gchar *tmp = fwupd_request_kind_to_string (i);
g_assert_cmpstr (tmp, !=, NULL);
g_assert_cmpint (fwupd_request_kind_from_string (tmp), ==, i);
}
for (guint i = FWUPD_RELEASE_URGENCY_UNKNOWN + 1; i < FWUPD_RELEASE_URGENCY_LAST; i++) { for (guint i = FWUPD_RELEASE_URGENCY_UNKNOWN + 1; i < FWUPD_RELEASE_URGENCY_LAST; i++) {
const gchar *tmp = fwupd_release_urgency_to_string (i); const gchar *tmp = fwupd_release_urgency_to_string (i);
g_assert_cmpstr (tmp, !=, NULL); g_assert_cmpstr (tmp, !=, NULL);
@ -345,6 +351,35 @@ fwupd_release_func (void)
g_assert_cmpstr (fwupd_release_get_metadata_item (release2, "baz"), ==, "bam"); g_assert_cmpstr (fwupd_release_get_metadata_item (release2, "baz"), ==, "bam");
} }
static void
fwupd_request_func (void)
{
g_autofree gchar *str = NULL;
g_autoptr(FwupdRequest) request = fwupd_request_new ();
g_autoptr(FwupdRequest) request2 = NULL;
g_autoptr(GVariant) data = NULL;
/* create dummy */
fwupd_request_set_kind (request, FWUPD_REQUEST_KIND_IMMEDIATE);
fwupd_request_set_id (request, FWPUD_REQUEST_ID_REMOVE_REPLUG);
fwupd_request_set_message (request, "foo");
fwupd_request_set_image (request, "bar");
str = fwupd_request_to_string (request);
g_debug ("%s", str);
/* set in init */
g_assert_cmpint (fwupd_request_get_created (request), >, 0);
/* to serialized and back again */
data = fwupd_request_to_variant (request);
request2 = fwupd_request_from_variant (data);
g_assert_cmpint (fwupd_request_get_kind (request2), ==, FWUPD_REQUEST_KIND_IMMEDIATE);
g_assert_cmpint (fwupd_request_get_created (request2), >, 0);
g_assert_cmpstr (fwupd_request_get_id (request2), ==, FWPUD_REQUEST_ID_REMOVE_REPLUG);
g_assert_cmpstr (fwupd_request_get_message (request2), ==, "foo");
g_assert_cmpstr (fwupd_request_get_image (request2), ==, "bar");
}
static void static void
fwupd_device_func (void) fwupd_device_func (void)
{ {
@ -714,6 +749,7 @@ main (int argc, char **argv)
g_test_add_func ("/fwupd/common{device-id}", fwupd_common_device_id_func); g_test_add_func ("/fwupd/common{device-id}", fwupd_common_device_id_func);
g_test_add_func ("/fwupd/common{guid}", fwupd_common_guid_func); g_test_add_func ("/fwupd/common{guid}", fwupd_common_guid_func);
g_test_add_func ("/fwupd/release", fwupd_release_func); g_test_add_func ("/fwupd/release", fwupd_release_func);
g_test_add_func ("/fwupd/request", fwupd_request_func);
g_test_add_func ("/fwupd/device", fwupd_device_func); g_test_add_func ("/fwupd/device", fwupd_device_func);
g_test_add_func ("/fwupd/remote{download}", fwupd_remote_download_func); g_test_add_func ("/fwupd/remote{download}", fwupd_remote_download_func);
g_test_add_func ("/fwupd/remote{base-uri}", fwupd_remote_baseuri_func); g_test_add_func ("/fwupd/remote{base-uri}", fwupd_remote_baseuri_func);

View File

@ -18,6 +18,7 @@
#include <libfwupd/fwupd-security-attr.h> #include <libfwupd/fwupd-security-attr.h>
#include <libfwupd/fwupd-release.h> #include <libfwupd/fwupd-release.h>
#include <libfwupd/fwupd-remote.h> #include <libfwupd/fwupd-remote.h>
#include <libfwupd/fwupd-request.h>
#include <libfwupd/fwupd-version.h> #include <libfwupd/fwupd-version.h>
#ifndef FWUPD_DISABLE_DEPRECATED #ifndef FWUPD_DISABLE_DEPRECATED

View File

@ -678,5 +678,24 @@ LIBFWUPD_1.6.2 {
fwupd_device_has_icon; fwupd_device_has_icon;
fwupd_device_remove_child; fwupd_device_remove_child;
fwupd_device_set_version_build_date; fwupd_device_set_version_build_date;
fwupd_request_from_variant;
fwupd_request_get_created;
fwupd_request_get_device_id;
fwupd_request_get_id;
fwupd_request_get_image;
fwupd_request_get_kind;
fwupd_request_get_message;
fwupd_request_get_type;
fwupd_request_kind_from_string;
fwupd_request_kind_to_string;
fwupd_request_new;
fwupd_request_set_created;
fwupd_request_set_device_id;
fwupd_request_set_id;
fwupd_request_set_image;
fwupd_request_set_kind;
fwupd_request_set_message;
fwupd_request_to_string;
fwupd_request_to_variant;
local: *; local: *;
} LIBFWUPD_1.6.1; } LIBFWUPD_1.6.1;

View File

@ -18,6 +18,7 @@ install_headers([
'fwupd-enums.h', 'fwupd-enums.h',
'fwupd-error.h', 'fwupd-error.h',
'fwupd-remote.h', 'fwupd-remote.h',
'fwupd-request.h',
'fwupd-security-attr.h', 'fwupd-security-attr.h',
'fwupd-release.h', 'fwupd-release.h',
'fwupd-plugin.h', 'fwupd-plugin.h',
@ -48,6 +49,7 @@ libfwupd_src = [
'fwupd-release.c', # fuzzing 'fwupd-release.c', # fuzzing
'fwupd-plugin.c', 'fwupd-plugin.c',
'fwupd-remote.c', 'fwupd-remote.c',
'fwupd-request.c', # fuzzing
'fwupd-version.c', 'fwupd-version.c',
] ]
@ -122,6 +124,9 @@ if get_option('introspection')
'fwupd-remote.c', 'fwupd-remote.c',
'fwupd-remote.h', 'fwupd-remote.h',
'fwupd-remote-private.h', 'fwupd-remote-private.h',
'fwupd-request.c',
'fwupd-request.h',
'fwupd-request-private.h',
'fwupd-version.c', 'fwupd-version.c',
fwupd_version_h, fwupd_version_h,
], ],

View File

@ -37,6 +37,8 @@ gchar *fu_device_get_guids_as_str (FuDevice *self);
GPtrArray *fu_device_get_possible_plugins (FuDevice *self); GPtrArray *fu_device_get_possible_plugins (FuDevice *self);
void fu_device_add_possible_plugin (FuDevice *self, void fu_device_add_possible_plugin (FuDevice *self,
const gchar *plugin); const gchar *plugin);
guint fu_device_get_request_cnt (FuDevice *self,
FwupdRequestKind request_kind);
guint64 fu_device_get_private_flags (FuDevice *self); guint64 fu_device_get_private_flags (FuDevice *self);
void fu_device_set_private_flags (FuDevice *self, void fu_device_set_private_flags (FuDevice *self,
guint64 flag); guint64 flag);

View File

@ -56,6 +56,7 @@ typedef struct {
guint progress; guint progress;
guint battery_level; guint battery_level;
guint battery_threshold; guint battery_threshold;
guint request_cnts[FWUPD_REQUEST_KIND_LAST];
gint order; gint order;
guint priority; guint priority;
guint poll_id; guint poll_id;
@ -102,6 +103,7 @@ enum {
enum { enum {
SIGNAL_CHILD_ADDED, SIGNAL_CHILD_ADDED,
SIGNAL_CHILD_REMOVED, SIGNAL_CHILD_REMOVED,
SIGNAL_REQUEST,
SIGNAL_LAST SIGNAL_LAST
}; };
@ -409,6 +411,27 @@ fu_device_get_private_flags (FuDevice *self)
return priv->private_flags; return priv->private_flags;
} }
/**
* fu_device_get_request_cnt:
* @self: a #FuDevice
* @request_kind: the type of request
*
* Returns the number of requests of a specific kind. This function is only
* useful to the daemon, which uses it to synthesize artificial events for
* plugins not yet ported to [class@FwupdRequest].
*
* Returns: integer, usually 0
*
* Since: 1.6.2
**/
guint
fu_device_get_request_cnt (FuDevice *self, FwupdRequestKind request_kind)
{
FuDevicePrivate *priv = GET_PRIVATE (self);
g_return_val_if_fail (FU_IS_DEVICE (self), G_MAXUINT);
return priv->request_cnts[request_kind];
}
/** /**
* fu_device_set_private_flags: * fu_device_set_private_flags:
* @self: a #FuDevice * @self: a #FuDevice
@ -3464,6 +3487,7 @@ fu_device_write_firmware (FuDevice *self,
GError **error) GError **error)
{ {
FuDeviceClass *klass = FU_DEVICE_GET_CLASS (self); FuDeviceClass *klass = FU_DEVICE_GET_CLASS (self);
FuDevicePrivate *priv = GET_PRIVATE (self);
g_autoptr(FuFirmware) firmware = NULL; g_autoptr(FuFirmware) firmware = NULL;
g_autofree gchar *str = NULL; g_autofree gchar *str = NULL;
@ -3487,7 +3511,23 @@ fu_device_write_firmware (FuDevice *self,
g_debug ("installing onto %s:\n%s", fu_device_get_id (self), str); g_debug ("installing onto %s:\n%s", fu_device_get_id (self), str);
/* call vfunc */ /* call vfunc */
return klass->write_firmware (self, firmware, flags, error); if (!klass->write_firmware (self, firmware, flags, error))
return FALSE;
/* the device set an UpdateMessage (possibly from a quirk, or XML file)
* but did not do an event; guess something */
if (priv->request_cnts[FWUPD_REQUEST_KIND_POST] == 0 &&
fu_device_get_update_message (self) != NULL) {
g_autoptr(FwupdRequest) request = fwupd_request_new ();
fwupd_request_set_kind (request, FWUPD_REQUEST_KIND_POST);
fwupd_request_set_id (request, FWPUD_REQUEST_ID_REMOVE_REPLUG);
fwupd_request_set_message (request, fu_device_get_update_message (self));
fwupd_request_set_image (request, fu_device_get_update_image (self));
fu_device_emit_request (self, request);
}
/* success */
return TRUE;
} }
/** /**
@ -4421,6 +4461,47 @@ fu_device_incorporate_from_component (FuDevice *self, XbNode *component)
fwupd_device_set_update_image (FWUPD_DEVICE (self), tmp); fwupd_device_set_update_image (FWUPD_DEVICE (self), tmp);
} }
/**
* fu_device_emit_request:
* @self: a device
* @request: a request
*
* Emit a request from a plugin to the client.
*
* Since: 1.6.2
**/
void
fu_device_emit_request (FuDevice *self, FwupdRequest *request)
{
FuDevicePrivate *priv = GET_PRIVATE (self);
g_return_if_fail (FU_IS_DEVICE (self));
g_return_if_fail (FWUPD_IS_REQUEST (request));
/* sanity check */
if (fwupd_request_get_kind (request) == FWUPD_REQUEST_KIND_UNKNOWN) {
g_critical ("a request must have an assigned kind");
return;
}
if (fwupd_request_get_id (request) == NULL) {
g_critical ("a request must have an assigned ID");
return;
}
/* ensure set */
fwupd_request_set_device_id (request, fu_device_get_id (self));
/* for compatibility with older clients */
if (fwupd_request_get_kind (request) == FWUPD_REQUEST_KIND_POST) {
fu_device_set_update_message (self, fwupd_request_get_message (request));
fu_device_set_update_image (self, fwupd_request_get_image (request));
}
/* proxy to the engine */
g_signal_emit (self, signals[SIGNAL_REQUEST], 0, request);
priv->request_cnts[fwupd_request_get_kind (request)]++;
}
static void static void
fu_device_class_init (FuDeviceClass *klass) fu_device_class_init (FuDeviceClass *klass)
{ {
@ -4442,6 +4523,12 @@ fu_device_class_init (FuDeviceClass *klass)
G_STRUCT_OFFSET (FuDeviceClass, child_removed), G_STRUCT_OFFSET (FuDeviceClass, child_removed),
NULL, NULL, g_cclosure_marshal_VOID__OBJECT, NULL, NULL, g_cclosure_marshal_VOID__OBJECT,
G_TYPE_NONE, 1, FU_TYPE_DEVICE); G_TYPE_NONE, 1, FU_TYPE_DEVICE);
signals[SIGNAL_REQUEST] =
g_signal_new ("request",
G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (FuDeviceClass, request),
NULL, NULL, g_cclosure_marshal_VOID__OBJECT,
G_TYPE_NONE, 1, FWUPD_TYPE_REQUEST);
pspec = g_param_spec_string ("physical-id", NULL, NULL, NULL, pspec = g_param_spec_string ("physical-id", NULL, NULL, NULL,
G_PARAM_READWRITE | G_PARAM_READWRITE |

View File

@ -106,8 +106,10 @@ struct _FuDeviceClass
FuDevice *child); FuDevice *child);
void (*child_removed) (FuDevice *self, /* signal */ void (*child_removed) (FuDevice *self, /* signal */
FuDevice *child); FuDevice *child);
void (*request) (FuDevice *self, /* signal */
FwupdRequest *request);
/*< private >*/ /*< private >*/
gpointer padding[7]; gpointer padding[6];
#endif #endif
}; };
@ -209,6 +211,8 @@ FuDevice *fu_device_new_with_context (FuContext *ctx);
#define fu_device_get_plugin(d) fwupd_device_get_plugin(FWUPD_DEVICE(d)) #define fu_device_get_plugin(d) fwupd_device_get_plugin(FWUPD_DEVICE(d))
#define fu_device_get_update_error(d) fwupd_device_get_update_error(FWUPD_DEVICE(d)) #define fu_device_get_update_error(d) fwupd_device_get_update_error(FWUPD_DEVICE(d))
#define fu_device_get_update_state(d) fwupd_device_get_update_state(FWUPD_DEVICE(d)) #define fu_device_get_update_state(d) fwupd_device_get_update_state(FWUPD_DEVICE(d))
#define fu_device_get_update_message(d) fwupd_device_get_update_message(FWUPD_DEVICE(d))
#define fu_device_get_update_image(d) fwupd_device_get_update_image(FWUPD_DEVICE(d))
#define fu_device_get_vendor(d) fwupd_device_get_vendor(FWUPD_DEVICE(d)) #define fu_device_get_vendor(d) fwupd_device_get_vendor(FWUPD_DEVICE(d))
#define fu_device_get_version(d) fwupd_device_get_version(FWUPD_DEVICE(d)) #define fu_device_get_version(d) fwupd_device_get_version(FWUPD_DEVICE(d))
#define fu_device_get_version_lowest(d) fwupd_device_get_version_lowest(FWUPD_DEVICE(d)) #define fu_device_get_version_lowest(d) fwupd_device_get_version_lowest(FWUPD_DEVICE(d))
@ -508,3 +512,5 @@ void fu_device_remove_private_flag (FuDevice *self,
guint64 flag); guint64 flag);
gboolean fu_device_has_private_flag (FuDevice *self, gboolean fu_device_has_private_flag (FuDevice *self,
guint64 flag); guint64 flag);
void fu_device_emit_request (FuDevice *self,
FwupdRequest *request);

View File

@ -826,8 +826,10 @@ LIBFWUPDPLUGIN_1.6.2 {
fu_device_add_guid_full; fu_device_add_guid_full;
fu_device_add_parent_physical_id; fu_device_add_parent_physical_id;
fu_device_add_private_flag; fu_device_add_private_flag;
fu_device_emit_request;
fu_device_get_parent_physical_ids; fu_device_get_parent_physical_ids;
fu_device_get_private_flags; fu_device_get_private_flags;
fu_device_get_request_cnt;
fu_device_has_parent_physical_id; fu_device_has_parent_physical_id;
fu_device_has_private_flag; fu_device_has_private_flag;
fu_device_new_with_context; fu_device_new_with_context;

View File

@ -362,32 +362,19 @@ fu_ebitdo_device_setup (FuDevice *device, GError **error)
} }
static gboolean static gboolean
fu_ebitdo_device_write_firmware (FuDevice *device, fu_ebitdo_device_detach (FuDevice *device, GError **error)
FuFirmware *firmware,
FwupdInstallFlags flags,
GError **error)
{ {
FuEbitdoDevice *self = FU_EBITDO_DEVICE (device); g_autoptr(FwupdRequest) request = fwupd_request_new ();
GUsbDevice *usb_device = fu_usb_device_get_dev (FU_USB_DEVICE (device));
const guint8 *buf;
gsize bufsz = 0;
guint32 serial_new[3];
g_autoptr(GBytes) fw_hdr = NULL;
g_autoptr(GBytes) fw_payload = NULL;
g_autoptr(GError) error_local = NULL;
g_autoptr(GPtrArray) chunks = NULL;
const guint32 app_key_index[16] = {
0x186976e5, 0xcac67acd, 0x38f27fee, 0x0a4948f1,
0xb75b7753, 0x1f8ffa5c, 0xbff8cf43, 0xc4936167,
0x92bd03f0, 0x5573c6ed, 0x57d8845b, 0x827197ac,
0xb91901c9, 0x3917edfe, 0xbcd6344f, 0xcf9e23b5
};
/* not in bootloader mode, so print what to do */ /* not required */
if (fu_device_has_flag (device, FWUPD_DEVICE_FLAG_NEEDS_BOOTLOADER)) { if (fu_device_has_flag (device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER))
g_autoptr(GString) msg = g_string_new ("Not in bootloader mode: "); return TRUE;
g_string_append (msg, "Disconnect the controller, ");
g_print ("1. \n"); /* generate a message if not already set from the metadata */
if (fu_device_get_update_message (device) == NULL) {
GUsbDevice *usb_device = fu_usb_device_get_dev (FU_USB_DEVICE (device));
g_autoptr(GString) msg = g_string_new (NULL);
g_string_append (msg, "Not in bootloader mode: Disconnect the controller, ");
switch (g_usb_device_get_pid (usb_device)) { switch (g_usb_device_get_pid (usb_device)) {
case 0xab11: /* FC30 */ case 0xab11: /* FC30 */
case 0xab12: /* NES30 */ case 0xab12: /* NES30 */
@ -420,18 +407,57 @@ fu_ebitdo_device_write_firmware (FuDevice *device,
"both white LED and green LED blink, "); "both white LED and green LED blink, ");
break; break;
case 0x9015: /* N30 Pro 2 */ case 0x9015: /* N30 Pro 2 */
g_string_append (msg, "press and hold L1+R1+START buttons " g_string_append (msg, "press and hold L1+R1+START buttons "
"until the yellow LED blinks, "); "until the yellow LED blinks, ");
break; break;
default: default:
g_string_append (msg, "do what it says in the manual, "); g_string_append (msg, "do what it says in the manual, ");
break; break;
} }
g_string_append (msg, "then re-connect controller"); g_string_append (msg, "then re-connect controller");
fu_device_set_update_message (device, msg->str);
}
/* wait */
fu_device_set_progress (device, 0);
fu_device_add_flag (device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG);
/* emit request */
fwupd_request_set_kind (request, FWUPD_REQUEST_KIND_IMMEDIATE);
fwupd_request_set_id (request, FWPUD_REQUEST_ID_REMOVE_REPLUG);
fwupd_request_set_message (request, fu_device_get_update_message (device));
fwupd_request_set_image (request, fu_device_get_update_image (device));
fu_device_emit_request (device, request);
return TRUE;
}
static gboolean
fu_ebitdo_device_write_firmware (FuDevice *device,
FuFirmware *firmware,
FwupdInstallFlags flags,
GError **error)
{
FuEbitdoDevice *self = FU_EBITDO_DEVICE (device);
const guint8 *buf;
gsize bufsz = 0;
guint32 serial_new[3];
g_autoptr(GBytes) fw_hdr = NULL;
g_autoptr(GBytes) fw_payload = NULL;
g_autoptr(GError) error_local = NULL;
g_autoptr(GPtrArray) chunks = NULL;
const guint32 app_key_index[16] = {
0x186976e5, 0xcac67acd, 0x38f27fee, 0x0a4948f1,
0xb75b7753, 0x1f8ffa5c, 0xbff8cf43, 0xc4936167,
0x92bd03f0, 0x5573c6ed, 0x57d8845b, 0x827197ac,
0xb91901c9, 0x3917edfe, 0xbcd6344f, 0xcf9e23b5
};
/* not in bootloader mode */
if (!fu_device_has_flag (device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER)) {
g_set_error_literal (error, g_set_error_literal (error,
FWUPD_ERROR, FWUPD_ERROR,
FWUPD_ERROR_NEEDS_USER_ACTION, FWUPD_ERROR_NEEDS_USER_ACTION,
msg->str); "Not in bootloader mode");
return FALSE; return FALSE;
} }
@ -586,7 +612,6 @@ fu_ebitdo_device_probe (FuDevice *device, GError **error)
if (!fu_device_has_flag (device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER)) { if (!fu_device_has_flag (device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER)) {
fu_device_add_counterpart_guid (device, "USB\\VID_0483&PID_5750"); fu_device_add_counterpart_guid (device, "USB\\VID_0483&PID_5750");
fu_device_add_counterpart_guid (device, "USB\\VID_2DC8&PID_5750"); fu_device_add_counterpart_guid (device, "USB\\VID_2DC8&PID_5750");
fu_device_add_flag (device, FWUPD_DEVICE_FLAG_NEEDS_BOOTLOADER);
} }
/* success */ /* success */
@ -619,6 +644,7 @@ fu_ebitdo_device_class_init (FuEbitdoDeviceClass *klass)
FuDeviceClass *klass_device = FU_DEVICE_CLASS (klass); FuDeviceClass *klass_device = FU_DEVICE_CLASS (klass);
klass_device->write_firmware = fu_ebitdo_device_write_firmware; klass_device->write_firmware = fu_ebitdo_device_write_firmware;
klass_device->setup = fu_ebitdo_device_setup; klass_device->setup = fu_ebitdo_device_setup;
klass_device->detach = fu_ebitdo_device_detach;
klass_device->attach = fu_ebitdo_device_attach; klass_device->attach = fu_ebitdo_device_attach;
klass_device->open = fu_ebitdo_device_open; klass_device->open = fu_ebitdo_device_open;
klass_device->probe = fu_ebitdo_device_probe; klass_device->probe = fu_ebitdo_device_probe;

View File

@ -96,31 +96,65 @@ fu_system76_launch_device_setup (FuDevice *device, GError **error)
} }
static gboolean static gboolean
fu_system76_launch_device_detach (FuDevice *device, GError **error) fu_system76_launch_device_reset (FuDevice *device, guint8 *rc, GError **error)
{ {
guint8 data[32] = { 0 }; guint8 data[32] = { SYSTEM76_LAUNCH_CMD_RESET, 0 };
/* execute reset command */ /* execute reset command */
data[0] = SYSTEM76_LAUNCH_CMD_RESET;
if (!fu_system76_launch_device_command (device, data, sizeof(data), error)) { if (!fu_system76_launch_device_command (device, data, sizeof(data), error)) {
g_prefix_error (error, "failed to execute reset command: "); g_prefix_error (error, "failed to execute reset command: ");
return FALSE; return FALSE;
} }
*rc = data[1];
return TRUE;
}
static gboolean
fu_system76_launch_device_detach (FuDevice *device, GError **error)
{
guint8 rc = 0x0;
g_autoptr(FwupdRequest) request = fwupd_request_new ();
g_autoptr(GTimer) timer = g_timer_new ();
/* prompt for unlock if reset was blocked */ /* prompt for unlock if reset was blocked */
if (data[1] != 0) { if (!fu_system76_launch_device_reset (device, &rc, error))
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_NEEDS_USER_ACTION,
"To ensure you have physical access, %s needs to be manually unlocked. "
"Please press Fn+Esc to unlock and re-run the update.",
fu_device_get_name (device));
return FALSE; return FALSE;
/* unlikely, but already unlocked */
if (rc == 0)
return TRUE;
/* generate a message if not already set */
if (fu_device_get_update_message (device) == NULL) {
g_autofree gchar *msg = NULL;
msg = g_strdup_printf ("To ensure you have physical access, %s needs to be manually unlocked. "
"Please press Fn+Esc to unlock and re-run the update.",
fu_device_get_name (device));
fu_device_set_update_message (device, msg);
} }
fu_device_set_status (device, FWUPD_STATUS_DEVICE_RESTART); /* the user has to do something */
fu_device_add_flag (device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); fwupd_request_set_kind (request, FWUPD_REQUEST_KIND_IMMEDIATE);
fwupd_request_set_id (request, FWPUD_REQUEST_ID_PRESS_UNLOCK);
fwupd_request_set_message (request, fu_device_get_update_message (device));
fu_device_emit_request (device, request);
/* poll for the user-unlock */
fu_device_set_progress (device, 0);
do {
g_usleep (G_USEC_PER_SEC);
if (!fu_system76_launch_device_reset (device, &rc, error))
return FALSE;
} while (rc != 0 &&
g_timer_elapsed (timer, NULL) * 1000.f < FU_DEVICE_REMOVE_DELAY_USER_REPLUG);
if (rc != 0) {
g_set_error_literal (error,
FWUPD_ERROR,
FWUPD_ERROR_NEEDS_USER_ACTION,
fu_device_get_update_message (device));
return FALSE;
}
return TRUE; return TRUE;
} }

View File

@ -121,6 +121,7 @@ enum {
SIGNAL_DEVICE_ADDED, SIGNAL_DEVICE_ADDED,
SIGNAL_DEVICE_REMOVED, SIGNAL_DEVICE_REMOVED,
SIGNAL_DEVICE_CHANGED, SIGNAL_DEVICE_CHANGED,
SIGNAL_DEVICE_REQUEST,
SIGNAL_STATUS_CHANGED, SIGNAL_STATUS_CHANGED,
SIGNAL_PERCENTAGE_CHANGED, SIGNAL_PERCENTAGE_CHANGED,
SIGNAL_LAST SIGNAL_LAST
@ -221,6 +222,14 @@ fu_engine_generic_notify_cb (FuDevice *device, GParamSpec *pspec, FuEngine *self
fu_engine_emit_device_changed (self, device); fu_engine_emit_device_changed (self, device);
} }
static void
fu_engine_device_request_cb (FuDevice *device, FwupdRequest *request, FuEngine *self)
{
g_debug ("Emitting DeviceRequest('Message'='%s')",
fwupd_request_get_message (request));
g_signal_emit (self, signals[SIGNAL_DEVICE_REQUEST], 0, request);
}
static void static void
fu_engine_watch_device (FuEngine *self, FuDevice *device) fu_engine_watch_device (FuEngine *self, FuDevice *device)
{ {
@ -232,6 +241,9 @@ fu_engine_watch_device (FuEngine *self, FuDevice *device)
g_signal_handlers_disconnect_by_func (device_old, g_signal_handlers_disconnect_by_func (device_old,
fu_engine_status_notify_cb, fu_engine_status_notify_cb,
self); self);
g_signal_handlers_disconnect_by_func (device_old,
fu_engine_device_request_cb,
self);
} }
g_signal_connect (device, "notify::progress", g_signal_connect (device, "notify::progress",
G_CALLBACK (fu_engine_progress_notify_cb), self); G_CALLBACK (fu_engine_progress_notify_cb), self);
@ -243,6 +255,8 @@ fu_engine_watch_device (FuEngine *self, FuDevice *device)
G_CALLBACK (fu_engine_generic_notify_cb), self); G_CALLBACK (fu_engine_generic_notify_cb), self);
g_signal_connect (device, "notify::update-image", g_signal_connect (device, "notify::update-image",
G_CALLBACK (fu_engine_generic_notify_cb), self); G_CALLBACK (fu_engine_generic_notify_cb), self);
g_signal_connect (device, "request",
G_CALLBACK (fu_engine_device_request_cb), self);
} }
static void static void
@ -2046,6 +2060,7 @@ fu_engine_install_tasks (FuEngine *self,
FwupdInstallFlags flags, FwupdInstallFlags flags,
GError **error) GError **error)
{ {
FwupdFeatureFlags feature_flags = fu_engine_request_get_feature_flags (request);
g_autoptr(FuIdleLocker) locker = NULL; g_autoptr(FuIdleLocker) locker = NULL;
g_autoptr(GPtrArray) devices = NULL; g_autoptr(GPtrArray) devices = NULL;
g_autoptr(GPtrArray) devices_new = NULL; g_autoptr(GPtrArray) devices_new = NULL;
@ -2070,7 +2085,9 @@ fu_engine_install_tasks (FuEngine *self,
/* all authenticated, so install all the things */ /* all authenticated, so install all the things */
for (guint i = 0; i < install_tasks->len; i++) { for (guint i = 0; i < install_tasks->len; i++) {
FuInstallTask *task = g_ptr_array_index (install_tasks, i); FuInstallTask *task = g_ptr_array_index (install_tasks, i);
if (!fu_engine_install (self, task, blob_cab, flags, error)) { if (!fu_engine_install (self, task, blob_cab,
flags, feature_flags,
error)) {
g_autoptr(GError) error_local = NULL; g_autoptr(GError) error_local = NULL;
if (!fu_engine_composite_cleanup (self, devices, &error_local)) { if (!fu_engine_composite_cleanup (self, devices, &error_local)) {
g_warning ("failed to cleanup failed composite action: %s", g_warning ("failed to cleanup failed composite action: %s",
@ -2332,6 +2349,7 @@ fu_engine_install_release (FuEngine *self,
XbNode *component, XbNode *component,
XbNode *rel, XbNode *rel,
FwupdInstallFlags flags, FwupdInstallFlags flags,
FwupdFeatureFlags feature_flags,
GError **error) GError **error)
{ {
FuPlugin *plugin; FuPlugin *plugin;
@ -2402,7 +2420,9 @@ fu_engine_install_release (FuEngine *self,
/* install firmware blob */ /* install firmware blob */
version_orig = g_strdup (fu_device_get_version (device)); version_orig = g_strdup (fu_device_get_version (device));
if (!fu_engine_install_blob (self, device, blob_fw2, flags, &error_local)) { if (!fu_engine_install_blob (self, device, blob_fw2,
flags, feature_flags,
&error_local)) {
fu_device_set_status (device, FWUPD_STATUS_IDLE); fu_device_set_status (device, FWUPD_STATUS_IDLE);
if (g_error_matches (error_local, if (g_error_matches (error_local,
FWUPD_ERROR, FWUPD_ERROR,
@ -2527,6 +2547,7 @@ fu_engine_sort_releases (FuEngine *self, FuDevice *device, GPtrArray *rels, GErr
* @task: a #FuInstallTask * @task: a #FuInstallTask
* @blob_cab: the #GBytes of the .cab file * @blob_cab: the #GBytes of the .cab file
* @flags: install flags, e.g. %FWUPD_INSTALL_FLAG_ALLOW_OLDER * @flags: install flags, e.g. %FWUPD_INSTALL_FLAG_ALLOW_OLDER
* @feature_flags: feature flags, e.g. %FWUPD_FEATURE_FLAG_NONE
* @error: (nullable): optional return location for an error * @error: (nullable): optional return location for an error
* *
* Installs a specific firmware file on a device. * Installs a specific firmware file on a device.
@ -2542,6 +2563,7 @@ fu_engine_install (FuEngine *self,
FuInstallTask *task, FuInstallTask *task,
GBytes *blob_cab, GBytes *blob_cab,
FwupdInstallFlags flags, FwupdInstallFlags flags,
FwupdFeatureFlags feature_flags,
GError **error) GError **error)
{ {
XbNode *component = fu_install_task_get_component (task); XbNode *component = fu_install_task_get_component (task);
@ -2559,28 +2581,16 @@ fu_engine_install (FuEngine *self,
/* not in bootloader mode */ /* not in bootloader mode */
device = g_object_ref (fu_install_task_get_device (task)); device = g_object_ref (fu_install_task_get_device (task));
if (fu_device_has_flag (device, FWUPD_DEVICE_FLAG_NEEDS_BOOTLOADER)) { if (!fu_device_has_flag (device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER)) {
const gchar *caption = NULL; const gchar *tmp = NULL;
caption = xb_node_query_text (component,
"screenshots/screenshot/caption", /* both optional; the plugin can specify a fallback */
NULL); tmp = xb_node_query_text (component, "screenshots/screenshot/caption", NULL);
if (caption != NULL) { if (tmp != NULL)
g_set_error (error, fu_device_set_update_message (device, tmp);
FWUPD_ERROR, tmp = xb_node_query_text (component, "screenshots/screenshot/image", NULL);
FWUPD_ERROR_NEEDS_USER_ACTION, if (tmp != NULL)
"Device %s needs to manually be put in update mode: %s", fu_device_set_update_image (device, tmp);
fu_device_get_name (device), caption);
} else {
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_NEEDS_USER_ACTION,
"Device %s needs to manually be put in update mode",
fu_device_get_name (device));
}
fu_device_set_update_state (device, FWUPD_UPDATE_STATE_FAILED_TRANSIENT);
if (error != NULL)
fu_device_set_update_error (device, (*error)->message);
return FALSE;
} }
/* get the newest version */ /* get the newest version */
@ -2649,11 +2659,23 @@ fu_engine_install (FuEngine *self,
return FALSE; return FALSE;
for (guint i = 0; i < rels->len; i++) { for (guint i = 0; i < rels->len; i++) {
XbNode *rel = g_ptr_array_index (rels, i); XbNode *rel = g_ptr_array_index (rels, i);
if (!fu_engine_install_release (self, device, component, rel, flags, error)) if (!fu_engine_install_release (self,
device,
component,
rel,
flags,
feature_flags,
error))
return FALSE; return FALSE;
} }
} else { } else {
if (!fu_engine_install_release (self, device, component, rel_newest, flags, error)) if (!fu_engine_install_release (self,
device,
component,
rel_newest,
flags,
feature_flags,
error))
return FALSE; return FALSE;
} }
@ -2829,7 +2851,10 @@ fu_engine_update_cleanup (FuEngine *self,
} }
static gboolean static gboolean
fu_engine_update_detach (FuEngine *self, const gchar *device_id, GError **error) fu_engine_update_detach (FuEngine *self,
const gchar *device_id,
FwupdFeatureFlags feature_flags,
GError **error)
{ {
FuPlugin *plugin; FuPlugin *plugin;
g_autofree gchar *str = NULL; g_autofree gchar *str = NULL;
@ -2848,6 +2873,29 @@ fu_engine_update_detach (FuEngine *self, const gchar *device_id, GError **error)
return FALSE; return FALSE;
if (!fu_plugin_runner_update_detach (plugin, device, error)) if (!fu_plugin_runner_update_detach (plugin, device, error))
return FALSE; return FALSE;
/* support older clients without the ability to do immediate requests */
if ((feature_flags & FWUPD_FEATURE_FLAG_REQUESTS) == 0 &&
fu_device_get_request_cnt (device, FWUPD_REQUEST_KIND_IMMEDIATE) > 0) {
/* fallback to something sane */
if (fu_device_get_update_message (device) == NULL) {
g_autofree gchar *tmp = NULL;
tmp = g_strdup_printf ("Device %s needs to manually be put in update mode",
fu_device_get_name (device));
fu_device_set_update_message (device, tmp);
}
/* abort and require client to re-submit */
fu_device_remove_flag (device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG);
g_set_error_literal (error,
FWUPD_ERROR,
FWUPD_ERROR_NEEDS_USER_ACTION,
fu_device_get_update_message (device));
return FALSE;
}
/* success */
return TRUE; return TRUE;
} }
@ -3043,6 +3091,7 @@ fu_engine_install_blob (FuEngine *self,
FuDevice *device, FuDevice *device,
GBytes *blob_fw, GBytes *blob_fw,
FwupdInstallFlags flags, FwupdInstallFlags flags,
FwupdFeatureFlags feature_flags,
GError **error) GError **error)
{ {
guint retries = 0; guint retries = 0;
@ -3081,7 +3130,7 @@ fu_engine_install_blob (FuEngine *self,
return FALSE; return FALSE;
/* detach to bootloader mode */ /* detach to bootloader mode */
if (!fu_engine_update_detach (self, device_id, error)) if (!fu_engine_update_detach (self, device_id, feature_flags, error))
return FALSE; return FALSE;
/* install */ /* install */
@ -4681,7 +4730,7 @@ fu_engine_add_releases_for_device_component (FuEngine *self,
update_message = fwupd_release_get_update_message (rel); update_message = fwupd_release_get_update_message (rel);
if (fwupd_device_get_update_message (FWUPD_DEVICE (device)) == NULL && if (fwupd_device_get_update_message (FWUPD_DEVICE (device)) == NULL &&
update_message != NULL) { update_message != NULL) {
fwupd_device_set_update_message (FWUPD_DEVICE (device), update_message); fu_device_set_update_message (device, update_message);
} }
update_image = fwupd_release_get_update_image (rel); update_image = fwupd_release_get_update_image (rel);
if (fwupd_device_get_update_image (FWUPD_DEVICE (device)) == NULL && if (fwupd_device_get_update_image (FWUPD_DEVICE (device)) == NULL &&
@ -6656,6 +6705,11 @@ fu_engine_class_init (FuEngineClass *klass)
G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST,
0, NULL, NULL, g_cclosure_marshal_VOID__OBJECT, 0, NULL, NULL, g_cclosure_marshal_VOID__OBJECT,
G_TYPE_NONE, 1, FU_TYPE_DEVICE); G_TYPE_NONE, 1, FU_TYPE_DEVICE);
signals[SIGNAL_DEVICE_REQUEST] =
g_signal_new ("device-request",
G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST,
0, NULL, NULL, g_cclosure_marshal_VOID__OBJECT,
G_TYPE_NONE, 1, FWUPD_TYPE_REQUEST);
signals[SIGNAL_STATUS_CHANGED] = signals[SIGNAL_STATUS_CHANGED] =
g_signal_new ("status-changed", g_signal_new ("status-changed",
G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST,

View File

@ -146,11 +146,13 @@ gboolean fu_engine_install (FuEngine *self,
FuInstallTask *task, FuInstallTask *task,
GBytes *blob_cab, GBytes *blob_cab,
FwupdInstallFlags flags, FwupdInstallFlags flags,
FwupdFeatureFlags feature_flags,
GError **error); GError **error);
gboolean fu_engine_install_blob (FuEngine *self, gboolean fu_engine_install_blob (FuEngine *self,
FuDevice *device, FuDevice *device,
GBytes *blob_fw, GBytes *blob_fw,
FwupdInstallFlags flags, FwupdInstallFlags flags,
FwupdFeatureFlags feature_flags,
GError **error); GError **error);
gboolean fu_engine_install_tasks (FuEngine *self, gboolean fu_engine_install_tasks (FuEngine *self,
FuEngineRequest *request, FuEngineRequest *request,

View File

@ -32,6 +32,7 @@
#include "fwupd-security-attr-private.h" #include "fwupd-security-attr-private.h"
#include "fwupd-release-private.h" #include "fwupd-release-private.h"
#include "fwupd-remote-private.h" #include "fwupd-remote-private.h"
#include "fwupd-request-private.h"
#include "fwupd-resources.h" #include "fwupd-resources.h"
#include "fu-common.h" #include "fu-common.h"
@ -161,6 +162,25 @@ fu_main_engine_device_changed_cb (FuEngine *engine,
g_variant_new_tuple (&val, 1), NULL); g_variant_new_tuple (&val, 1), NULL);
} }
static void
fu_main_engine_device_request_cb (FuEngine *engine,
FwupdRequest *request,
FuMainPrivate *priv)
{
GVariant *val;
/* not yet connected */
if (priv->connection == NULL)
return;
val = fwupd_request_to_variant (FWUPD_REQUEST (request));
g_dbus_connection_emit_signal (priv->connection,
NULL,
FWUPD_DBUS_PATH,
FWUPD_DBUS_INTERFACE,
"DeviceRequest",
g_variant_new_tuple (&val, 1), NULL);
}
static void static void
fu_main_emit_property_changed (FuMainPrivate *priv, fu_main_emit_property_changed (FuMainPrivate *priv,
const gchar *property_name, const gchar *property_name,
@ -1939,6 +1959,9 @@ main (int argc, char *argv[])
g_signal_connect (priv->engine, "device-changed", g_signal_connect (priv->engine, "device-changed",
G_CALLBACK (fu_main_engine_device_changed_cb), G_CALLBACK (fu_main_engine_device_changed_cb),
priv); priv);
g_signal_connect (priv->engine, "device-request",
G_CALLBACK (fu_main_engine_device_request_cb),
priv);
g_signal_connect (priv->engine, "status-changed", g_signal_connect (priv->engine, "status-changed",
G_CALLBACK (fu_main_engine_status_changed_cb), G_CALLBACK (fu_main_engine_status_changed_cb),
priv); priv);

View File

@ -1734,7 +1734,9 @@ fu_engine_history_func (gconstpointer user_data)
/* install it */ /* install it */
task = fu_install_task_new (device, component); task = fu_install_task_new (device, component);
ret = fu_engine_install (engine, task, blob_cab, ret = fu_engine_install (engine, task, blob_cab,
FWUPD_INSTALL_FLAG_NONE, &error); FWUPD_INSTALL_FLAG_NONE,
FWUPD_FEATURE_FLAG_NONE,
&error);
g_assert_no_error (error); g_assert_no_error (error);
g_assert (ret); g_assert (ret);
@ -1857,7 +1859,9 @@ fu_engine_multiple_rels_func (gconstpointer user_data)
/* install it */ /* install it */
task = fu_install_task_new (device, component); task = fu_install_task_new (device, component);
ret = fu_engine_install (engine, task, blob_cab, ret = fu_engine_install (engine, task, blob_cab,
FWUPD_INSTALL_FLAG_NONE, &error); FWUPD_INSTALL_FLAG_NONE,
FWUPD_FEATURE_FLAG_NONE,
&error);
g_assert_no_error (error); g_assert_no_error (error);
g_assert (ret); g_assert (ret);
@ -1930,7 +1934,9 @@ fu_engine_history_inherit (gconstpointer user_data)
g_setenv ("FWUPD_PLUGIN_TEST", "requires-activation", TRUE); g_setenv ("FWUPD_PLUGIN_TEST", "requires-activation", TRUE);
task = fu_install_task_new (device, component); task = fu_install_task_new (device, component);
ret = fu_engine_install (engine, task, blob_cab, ret = fu_engine_install (engine, task, blob_cab,
FWUPD_INSTALL_FLAG_NONE, &error); FWUPD_INSTALL_FLAG_NONE,
FWUPD_FEATURE_FLAG_NONE,
&error);
g_assert_no_error (error); g_assert_no_error (error);
g_assert (ret); g_assert (ret);
@ -1951,7 +1957,9 @@ fu_engine_history_inherit (gconstpointer user_data)
fu_device_set_version_format (device, FWUPD_VERSION_FORMAT_TRIPLET); fu_device_set_version_format (device, FWUPD_VERSION_FORMAT_TRIPLET);
fu_device_set_version (device, "1.2.2"); fu_device_set_version (device, "1.2.2");
ret = fu_engine_install (engine, task, blob_cab, ret = fu_engine_install (engine, task, blob_cab,
FWUPD_INSTALL_FLAG_NONE, &error); FWUPD_INSTALL_FLAG_NONE,
FWUPD_FEATURE_FLAG_NONE,
&error);
g_assert_no_error (error); g_assert_no_error (error);
g_assert (ret); g_assert (ret);
g_object_unref (engine); g_object_unref (engine);
@ -2057,7 +2065,9 @@ fu_engine_history_error_func (gconstpointer user_data)
g_assert_nonnull (component); g_assert_nonnull (component);
task = fu_install_task_new (device, component); task = fu_install_task_new (device, component);
ret = fu_engine_install (engine, task, blob_cab, ret = fu_engine_install (engine, task, blob_cab,
FWUPD_INSTALL_FLAG_NONE, &error); FWUPD_INSTALL_FLAG_NONE,
FWUPD_FEATURE_FLAG_NONE,
&error);
g_assert_error (error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED); g_assert_error (error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED);
g_assert (error != NULL); g_assert (error != NULL);
g_assert_cmpstr (error->message, ==, g_assert_cmpstr (error->message, ==,
@ -2796,7 +2806,9 @@ fu_plugin_module_func (gconstpointer user_data)
fu_engine_add_device (engine, device); fu_engine_add_device (engine, device);
fu_engine_add_plugin (engine, self->plugin); fu_engine_add_plugin (engine, self->plugin);
ret = fu_engine_install_blob (engine, device, blob_cab, ret = fu_engine_install_blob (engine, device, blob_cab,
FWUPD_INSTALL_FLAG_NONE, &error); FWUPD_INSTALL_FLAG_NONE,
FWUPD_FEATURE_FLAG_NONE,
&error);
g_assert_no_error (error); g_assert_no_error (error);
g_assert (ret); g_assert (ret);
g_assert_cmpint (cnt, ==, 4); g_assert_cmpint (cnt, ==, 4);

View File

@ -878,7 +878,10 @@ fu_util_install_blob (FuUtilPrivate *priv, gchar **values, GError **error)
} }
} }
priv->flags |= FWUPD_INSTALL_FLAG_NO_HISTORY; priv->flags |= FWUPD_INSTALL_FLAG_NO_HISTORY;
if (!fu_engine_install_blob (priv->engine, device, blob_fw, priv->flags, error)) if (!fu_engine_install_blob (priv->engine, device, blob_fw,
priv->flags,
fu_engine_request_get_feature_flags (priv->request),
error))
return FALSE; return FALSE;
if (priv->cleanup_blob) { if (priv->cleanup_blob) {
g_autoptr(FuDevice) device_new = NULL; g_autoptr(FuDevice) device_new = NULL;

View File

@ -66,7 +66,7 @@ struct FuUtilPrivate {
/* only valid in update and downgrade */ /* only valid in update and downgrade */
FuUtilOperation current_operation; FuUtilOperation current_operation;
FwupdDevice *current_device; FwupdDevice *current_device;
gchar *current_message; GPtrArray *post_requests;
FwupdDeviceFlags completion_flags; FwupdDeviceFlags completion_flags;
FwupdDeviceFlags filter_include; FwupdDeviceFlags filter_include;
FwupdDeviceFlags filter_exclude; FwupdDeviceFlags filter_exclude;
@ -84,6 +84,32 @@ fu_util_client_notify_cb (GObject *object,
fwupd_client_get_percentage (priv->client)); fwupd_client_get_percentage (priv->client));
} }
static void
fu_util_update_device_request_cb (FwupdClient *client,
FwupdRequest *request,
FuUtilPrivate *priv)
{
/* nothing sensible to show */
if (fwupd_request_get_message (request) == NULL)
return;
/* show this now */
if (fwupd_request_get_kind (request) == FWUPD_REQUEST_KIND_IMMEDIATE) {
g_autofree gchar *fmt = NULL;
g_autofree gchar *tmp = NULL;
/* TRANSLATORS: the user needs to do something, e.g. remove the device */
fmt = fu_util_term_format (_("Action Required:"), FU_UTIL_TERM_COLOR_RED);
tmp = g_strdup_printf ("%s %s", fmt,
fwupd_request_get_message (request));
fu_progressbar_set_title (priv->progressbar, tmp);
}
/* save for later */
if (fwupd_request_get_kind (request) == FWUPD_REQUEST_KIND_POST)
g_ptr_array_add (priv->post_requests, g_object_ref (request));
}
static void static void
fu_util_update_device_changed_cb (FwupdClient *client, fu_util_update_device_changed_cb (FwupdClient *client,
FwupdDevice *device, FwupdDevice *device,
@ -131,12 +157,6 @@ fu_util_update_device_changed_cb (FwupdClient *client,
g_warning ("no FuUtilOperation set"); g_warning ("no FuUtilOperation set");
} }
g_set_object (&priv->current_device, device); g_set_object (&priv->current_device, device);
if (priv->current_message == NULL) {
const gchar *tmp = fwupd_device_get_update_message (priv->current_device);
if (tmp != NULL)
priv->current_message = g_strdup (tmp);
}
} }
static gboolean static gboolean
@ -551,14 +571,14 @@ fu_util_download_if_required (FuUtilPrivate *priv, const gchar *perhapsfn, GErro
static void static void
fu_util_display_current_message (FuUtilPrivate *priv) fu_util_display_current_message (FuUtilPrivate *priv)
{ {
if (priv->current_message == NULL) {
/* TRANSLATORS: success message */
g_print ("%s\n", _("Successfully installed firmware"));
return;
}
/* TRANSLATORS: success message */ /* TRANSLATORS: success message */
g_print ("%s: %s\n", _("Successfully installed firmware"), priv->current_message); g_print ("%s\n", _("Successfully installed firmware"));
g_clear_pointer (&priv->current_message, g_free);
/* print all POST requests */
for (guint i = 0; i < priv->post_requests->len; i++) {
FwupdRequest *request = g_ptr_array_index (priv->post_requests, i);
g_print ("%s\n", fwupd_request_get_message (request));
}
} }
static gboolean static gboolean
@ -583,6 +603,8 @@ fu_util_install (FuUtilPrivate *priv, gchar **values, GError **error)
priv->current_operation = FU_UTIL_OPERATION_INSTALL; priv->current_operation = FU_UTIL_OPERATION_INSTALL;
g_signal_connect (priv->client, "device-changed", g_signal_connect (priv->client, "device-changed",
G_CALLBACK (fu_util_update_device_changed_cb), priv); G_CALLBACK (fu_util_update_device_changed_cb), priv);
g_signal_connect (priv->client, "device-request",
G_CALLBACK (fu_util_update_device_request_cb), priv);
/* install with flags chosen by the user */ /* install with flags chosen by the user */
filename = fu_util_download_if_required (priv, values[0], error); filename = fu_util_download_if_required (priv, values[0], error);
@ -1581,6 +1603,8 @@ fu_util_update_all (FuUtilPrivate *priv, GError **error)
priv->current_operation = FU_UTIL_OPERATION_UPDATE; priv->current_operation = FU_UTIL_OPERATION_UPDATE;
g_signal_connect (priv->client, "device-changed", g_signal_connect (priv->client, "device-changed",
G_CALLBACK (fu_util_update_device_changed_cb), priv); G_CALLBACK (fu_util_update_device_changed_cb), priv);
g_signal_connect (priv->client, "device-request",
G_CALLBACK (fu_util_update_device_request_cb), priv);
g_ptr_array_sort (devices, fu_util_sort_devices_by_flags_cb); g_ptr_array_sort (devices, fu_util_sort_devices_by_flags_cb);
for (guint i = 0; i < devices->len; i++) { for (guint i = 0; i < devices->len; i++) {
FwupdDevice *dev = g_ptr_array_index (devices, i); FwupdDevice *dev = g_ptr_array_index (devices, i);
@ -1667,6 +1691,8 @@ fu_util_update_by_id (FuUtilPrivate *priv, const gchar *device_id, GError **erro
priv->current_operation = FU_UTIL_OPERATION_UPDATE; priv->current_operation = FU_UTIL_OPERATION_UPDATE;
g_signal_connect (priv->client, "device-changed", g_signal_connect (priv->client, "device-changed",
G_CALLBACK (fu_util_update_device_changed_cb), priv); G_CALLBACK (fu_util_update_device_changed_cb), priv);
g_signal_connect (priv->client, "device-request",
G_CALLBACK (fu_util_update_device_request_cb), priv);
/* get the releases for this device and filter for validity */ /* get the releases for this device and filter for validity */
rels = fwupd_client_get_upgrades (priv->client, rels = fwupd_client_get_upgrades (priv->client,
@ -1874,6 +1900,8 @@ fu_util_downgrade (FuUtilPrivate *priv, gchar **values, GError **error)
priv->current_operation = FU_UTIL_OPERATION_DOWNGRADE; priv->current_operation = FU_UTIL_OPERATION_DOWNGRADE;
g_signal_connect (priv->client, "device-changed", g_signal_connect (priv->client, "device-changed",
G_CALLBACK (fu_util_update_device_changed_cb), priv); G_CALLBACK (fu_util_update_device_changed_cb), priv);
g_signal_connect (priv->client, "device-request",
G_CALLBACK (fu_util_update_device_request_cb), priv);
priv->flags |= FWUPD_INSTALL_FLAG_ALLOW_OLDER; priv->flags |= FWUPD_INSTALL_FLAG_ALLOW_OLDER;
if (!fu_util_update_device_with_release (priv, dev, rel, error)) if (!fu_util_update_device_with_release (priv, dev, rel, error))
return FALSE; return FALSE;
@ -1935,6 +1963,8 @@ fu_util_reinstall (FuUtilPrivate *priv, gchar **values, GError **error)
priv->current_operation = FU_UTIL_OPERATION_INSTALL; priv->current_operation = FU_UTIL_OPERATION_INSTALL;
g_signal_connect (priv->client, "device-changed", g_signal_connect (priv->client, "device-changed",
G_CALLBACK (fu_util_update_device_changed_cb), priv); G_CALLBACK (fu_util_update_device_changed_cb), priv);
g_signal_connect (priv->client, "device-request",
G_CALLBACK (fu_util_update_device_request_cb), priv);
priv->flags |= FWUPD_INSTALL_FLAG_ALLOW_REINSTALL; priv->flags |= FWUPD_INSTALL_FLAG_ALLOW_REINSTALL;
if (!fu_util_update_device_with_release (priv, dev, rel, error)) if (!fu_util_update_device_with_release (priv, dev, rel, error))
return FALSE; return FALSE;
@ -2059,6 +2089,8 @@ fu_util_switch_branch (FuUtilPrivate *priv, gchar **values, GError **error)
priv->current_operation = FU_UTIL_OPERATION_INSTALL; priv->current_operation = FU_UTIL_OPERATION_INSTALL;
g_signal_connect (priv->client, "device-changed", g_signal_connect (priv->client, "device-changed",
G_CALLBACK (fu_util_update_device_changed_cb), priv); G_CALLBACK (fu_util_update_device_changed_cb), priv);
g_signal_connect (priv->client, "device-request",
G_CALLBACK (fu_util_update_device_request_cb), priv);
priv->flags |= FWUPD_INSTALL_FLAG_ALLOW_REINSTALL; priv->flags |= FWUPD_INSTALL_FLAG_ALLOW_REINSTALL;
priv->flags |= FWUPD_INSTALL_FLAG_ALLOW_BRANCH_SWITCH; priv->flags |= FWUPD_INSTALL_FLAG_ALLOW_BRANCH_SWITCH;
if (!fu_util_update_device_with_release (priv, dev, rel, error)) if (!fu_util_update_device_with_release (priv, dev, rel, error))
@ -2485,7 +2517,7 @@ fu_util_private_free (FuUtilPrivate *priv)
g_object_unref (priv->client); g_object_unref (priv->client);
if (priv->current_device != NULL) if (priv->current_device != NULL)
g_object_unref (priv->current_device); g_object_unref (priv->current_device);
g_free (priv->current_message); g_ptr_array_unref (priv->post_requests);
g_main_context_unref (priv->main_ctx); g_main_context_unref (priv->main_ctx);
g_object_unref (priv->cancellable); g_object_unref (priv->cancellable);
g_object_unref (priv->progressbar); g_object_unref (priv->progressbar);
@ -2852,6 +2884,7 @@ main (int argc, char *argv[])
/* create helper object */ /* create helper object */
priv->main_ctx = g_main_context_new (); priv->main_ctx = g_main_context_new ();
priv->progressbar = fu_progressbar_new (); priv->progressbar = fu_progressbar_new ();
priv->post_requests = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
fu_progressbar_set_main_context (priv->progressbar, priv->main_ctx); fu_progressbar_set_main_context (priv->progressbar, priv->main_ctx);
/* add commands */ /* add commands */
@ -3267,6 +3300,7 @@ main (int argc, char *argv[])
if (!fwupd_client_set_feature_flags (priv->client, if (!fwupd_client_set_feature_flags (priv->client,
FWUPD_FEATURE_FLAG_CAN_REPORT | FWUPD_FEATURE_FLAG_CAN_REPORT |
FWUPD_FEATURE_FLAG_SWITCH_BRANCH | FWUPD_FEATURE_FLAG_SWITCH_BRANCH |
FWUPD_FEATURE_FLAG_REQUESTS |
FWUPD_FEATURE_FLAG_UPDATE_ACTION | FWUPD_FEATURE_FLAG_UPDATE_ACTION |
FWUPD_FEATURE_FLAG_DETACH_ACTION, FWUPD_FEATURE_FLAG_DETACH_ACTION,
priv->cancellable, &error)) { priv->cancellable, &error)) {

View File

@ -831,5 +831,23 @@
</doc:doc> </doc:doc>
</signal> </signal>
<!--***********************************************************-->
<signal name='DeviceRequest'>
<arg type='a{sv}' name='request' direction='out'>
<doc:doc>
<doc:summary>
<doc:para>A device request.</doc:para>
</doc:summary>
</doc:doc>
</arg>
<doc:doc>
<doc:description>
<doc:para>
A device request to the client.
</doc:para>
</doc:description>
</doc:doc>
</signal>
</interface> </interface>
</node> </node>