From 19abf996c72bfffe31c01c19fb58de0699b3136d Mon Sep 17 00:00:00 2001 From: Richard Hughes Date: Tue, 13 Jul 2021 22:34:32 +0100 Subject: [PATCH] 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". --- contrib/ci/abidiff.suppr | 2 +- libfwupd/fwupd-client.c | 56 +- libfwupd/fwupd-client.h | 4 +- libfwupd/fwupd-enums-private.h | 8 + libfwupd/fwupd-enums.c | 4 + libfwupd/fwupd-enums.h | 2 + libfwupd/fwupd-request-private.h | 15 + libfwupd/fwupd-request.c | 626 ++++++++++++++++++ libfwupd/fwupd-request.h | 90 +++ libfwupd/fwupd-self-test.c | 36 + libfwupd/fwupd.h | 1 + libfwupd/fwupd.map | 19 + libfwupd/meson.build | 5 + libfwupdplugin/fu-device-private.h | 2 + libfwupdplugin/fu-device.c | 89 ++- libfwupdplugin/fu-device.h | 8 +- libfwupdplugin/fwupdplugin.map | 2 + plugins/ebitdo/fu-ebitdo-device.c | 82 ++- .../fu-system76-launch-device.c | 58 +- src/fu-engine.c | 112 +++- src/fu-engine.h | 2 + src/fu-main.c | 23 + src/fu-self-test.c | 24 +- src/fu-tool.c | 5 +- src/fu-util.c | 64 +- src/org.freedesktop.fwupd.xml | 18 + 26 files changed, 1251 insertions(+), 106 deletions(-) create mode 100644 libfwupd/fwupd-request-private.h create mode 100644 libfwupd/fwupd-request.c create mode 100644 libfwupd/fwupd-request.h diff --git a/contrib/ci/abidiff.suppr b/contrib/ci/abidiff.suppr index 3dbcb28ef..114636b5b 100644 --- a/contrib/ci/abidiff.suppr +++ b/contrib/ci/abidiff.suppr @@ -1,3 +1,3 @@ [suppress_type] 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 diff --git a/libfwupd/fwupd-client.c b/libfwupd/fwupd-client.c index 1afe909ec..3f1ae3648 100644 --- a/libfwupd/fwupd-client.c +++ b/libfwupd/fwupd-client.c @@ -32,6 +32,7 @@ #include "fwupd-security-attr-private.h" #include "fwupd-release-private.h" #include "fwupd-remote-private.h" +#include "fwupd-request-private.h" typedef GObject *(*FwupdClientObjectNewFunc) (void); @@ -83,6 +84,7 @@ enum { SIGNAL_DEVICE_ADDED, SIGNAL_DEVICE_REMOVED, SIGNAL_DEVICE_CHANGED, + SIGNAL_DEVICE_REQUEST, SIGNAL_LAST }; @@ -131,13 +133,13 @@ typedef struct { FwupdClient *self; gchar *property_name; guint signal_id; - FwupdDevice *device; + GObject *payload; } FwupdClientContextHelper; static void fwupd_client_context_helper_free (FwupdClientContextHelper *helper) { - g_clear_object (&helper->device); + g_clear_object (&helper->payload); g_object_unref (helper->self); g_free (helper->property_name); g_free (helper); @@ -176,9 +178,9 @@ fwupd_client_context_idle_cb (gpointer user_data) if (helper->property_name != NULL) fwupd_client_context_object_notify (self, helper->property_name); - /* device signal */ - if (helper->signal_id !=0 && helper->device != NULL) - g_signal_emit (self, signals[helper->signal_id], 0, helper->device); + /* payload signal */ + if (helper->signal_id != 0 && helper->payload != NULL) + g_signal_emit (self, signals[helper->signal_id], 0, helper->payload); } /* all done */ @@ -228,14 +230,14 @@ fwupd_client_object_notify (FwupdClient *self, const gchar *property_name) /* run callback in the correct thread */ 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); FwupdClientContextHelper *helper = NULL; /* shortcut */ 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; } @@ -243,7 +245,7 @@ fwupd_client_signal_emit_device (FwupdClient *self, guint signal_id, FwupdDevice helper = g_new0 (FwupdClientContextHelper, 1); helper->self = g_object_ref (self); helper->signal_id = signal_id; - helper->device = g_object_ref (device); + helper->payload = g_object_ref (payload); fwupd_client_context_helper (self, helper); } @@ -407,21 +409,36 @@ fwupd_client_signal_cb (GDBusProxy *proxy, dev = fwupd_device_from_variant (parameters); g_debug ("Emitting ::device-added(%s)", 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; } if (g_strcmp0 (signal_name, "DeviceRemoved") == 0) { dev = fwupd_device_from_variant (parameters); g_debug ("Emitting ::device-removed(%s)", fwupd_device_get_id (dev)); - fwupd_client_signal_emit_device (self, SIGNAL_DEVICE_REMOVED, dev); + fwupd_client_signal_emit_object (self, + SIGNAL_DEVICE_REMOVED, + G_OBJECT (dev)); return; } if (g_strcmp0 (signal_name, "DeviceChanged") == 0) { dev = fwupd_device_from_variant (parameters); g_debug ("Emitting ::device-changed(%s)", fwupd_device_get_id (dev)); - fwupd_client_signal_emit_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; } 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, 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: * diff --git a/libfwupd/fwupd-client.h b/libfwupd/fwupd-client.h index 8b6f472ed..6a1fa423c 100644 --- a/libfwupd/fwupd-client.h +++ b/libfwupd/fwupd-client.h @@ -11,6 +11,7 @@ #include "fwupd-enums.h" #include "fwupd-device.h" +#include "fwupd-request.h" #include "fwupd-plugin.h" #include "fwupd-remote.h" @@ -31,6 +32,8 @@ struct _FwupdClientClass FwupdDevice *result); void (*device_changed) (FwupdClient *client, FwupdDevice *result); + void (*device_request) (FwupdClient *client, + FwupdRequest *request); /*< private >*/ void (*_fwupd_reserved1) (void); void (*_fwupd_reserved2) (void); @@ -38,7 +41,6 @@ struct _FwupdClientClass void (*_fwupd_reserved4) (void); void (*_fwupd_reserved5) (void); void (*_fwupd_reserved6) (void); - void (*_fwupd_reserved7) (void); }; /** diff --git a/libfwupd/fwupd-enums-private.h b/libfwupd/fwupd-enums-private.h index 250645bcb..b8a3dbc6e 100644 --- a/libfwupd/fwupd-enums-private.h +++ b/libfwupd/fwupd-enums-private.h @@ -138,6 +138,14 @@ G_BEGIN_DECLS * The D-Bus type signature string is 'u' i.e. a unsigned 32 bit integer. **/ #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: * diff --git a/libfwupd/fwupd-enums.c b/libfwupd/fwupd-enums.c index a58522c18..ebc129b46 100644 --- a/libfwupd/fwupd-enums.c +++ b/libfwupd/fwupd-enums.c @@ -513,6 +513,8 @@ fwupd_feature_flag_to_string (FwupdFeatureFlags feature_flag) return "update-action"; if (feature_flag == FWUPD_FEATURE_FLAG_SWITCH_BRANCH) return "switch-branch"; + if (feature_flag == FWUPD_FEATURE_FLAG_REQUESTS) + return "requests"; return NULL; } @@ -539,6 +541,8 @@ fwupd_feature_flag_from_string (const gchar *feature_flag) return FWUPD_FEATURE_FLAG_UPDATE_ACTION; if (g_strcmp0 (feature_flag, "switch-branch") == 0) return FWUPD_FEATURE_FLAG_SWITCH_BRANCH; + if (g_strcmp0 (feature_flag, "requests") == 0) + return FWUPD_FEATURE_FLAG_REQUESTS; return FWUPD_FEATURE_FLAG_LAST; } diff --git a/libfwupd/fwupd-enums.h b/libfwupd/fwupd-enums.h index 375ec4641..603e01623 100644 --- a/libfwupd/fwupd-enums.h +++ b/libfwupd/fwupd-enums.h @@ -71,6 +71,7 @@ typedef enum { * @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_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. **/ @@ -80,6 +81,7 @@ typedef enum { 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_SWITCH_BRANCH = 1 << 3, /* Since: 1.5.0 */ + FWUPD_FEATURE_FLAG_REQUESTS = 1 << 4, /* Since: 1.6.2 */ /*< private >*/ FWUPD_FEATURE_FLAG_LAST } FwupdFeatureFlags; diff --git a/libfwupd/fwupd-request-private.h b/libfwupd/fwupd-request-private.h new file mode 100644 index 000000000..0a516c2cf --- /dev/null +++ b/libfwupd/fwupd-request-private.h @@ -0,0 +1,15 @@ +/* + * Copyright (C) 2017 Richard Hughes + * + * 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 diff --git a/libfwupd/fwupd-request.c b/libfwupd/fwupd-request.c new file mode 100644 index 000000000..5f0ea87f2 --- /dev/null +++ b/libfwupd/fwupd-request.c @@ -0,0 +1,626 @@ +/* + * Copyright (C) 2021 Richard Hughes + * + * 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); +} diff --git a/libfwupd/fwupd-request.h b/libfwupd/fwupd-request.h new file mode 100644 index 000000000..de3052a99 --- /dev/null +++ b/libfwupd/fwupd-request.h @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2021 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +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 diff --git a/libfwupd/fwupd-self-test.c b/libfwupd/fwupd-self-test.c index 945d036f6..c19ce9e11 100644 --- a/libfwupd/fwupd-self-test.c +++ b/libfwupd/fwupd-self-test.c @@ -20,6 +20,7 @@ #include "fwupd-device-private.h" #include "fwupd-release-private.h" #include "fwupd-remote-private.h" +#include "fwupd-request-private.h" static gboolean 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_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++) { const gchar *tmp = fwupd_release_urgency_to_string (i); g_assert_cmpstr (tmp, !=, NULL); @@ -345,6 +351,35 @@ fwupd_release_func (void) 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 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{guid}", fwupd_common_guid_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/remote{download}", fwupd_remote_download_func); g_test_add_func ("/fwupd/remote{base-uri}", fwupd_remote_baseuri_func); diff --git a/libfwupd/fwupd.h b/libfwupd/fwupd.h index 5629d47dd..498e87095 100644 --- a/libfwupd/fwupd.h +++ b/libfwupd/fwupd.h @@ -18,6 +18,7 @@ #include #include #include +#include #include #ifndef FWUPD_DISABLE_DEPRECATED diff --git a/libfwupd/fwupd.map b/libfwupd/fwupd.map index aa5311738..47e93410a 100644 --- a/libfwupd/fwupd.map +++ b/libfwupd/fwupd.map @@ -678,5 +678,24 @@ LIBFWUPD_1.6.2 { fwupd_device_has_icon; fwupd_device_remove_child; 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: *; } LIBFWUPD_1.6.1; diff --git a/libfwupd/meson.build b/libfwupd/meson.build index 01443909e..4228e403d 100644 --- a/libfwupd/meson.build +++ b/libfwupd/meson.build @@ -18,6 +18,7 @@ install_headers([ 'fwupd-enums.h', 'fwupd-error.h', 'fwupd-remote.h', + 'fwupd-request.h', 'fwupd-security-attr.h', 'fwupd-release.h', 'fwupd-plugin.h', @@ -48,6 +49,7 @@ libfwupd_src = [ 'fwupd-release.c', # fuzzing 'fwupd-plugin.c', 'fwupd-remote.c', + 'fwupd-request.c', # fuzzing 'fwupd-version.c', ] @@ -122,6 +124,9 @@ if get_option('introspection') 'fwupd-remote.c', 'fwupd-remote.h', 'fwupd-remote-private.h', + 'fwupd-request.c', + 'fwupd-request.h', + 'fwupd-request-private.h', 'fwupd-version.c', fwupd_version_h, ], diff --git a/libfwupdplugin/fu-device-private.h b/libfwupdplugin/fu-device-private.h index 717cf5c3d..75750321d 100644 --- a/libfwupdplugin/fu-device-private.h +++ b/libfwupdplugin/fu-device-private.h @@ -37,6 +37,8 @@ gchar *fu_device_get_guids_as_str (FuDevice *self); GPtrArray *fu_device_get_possible_plugins (FuDevice *self); void fu_device_add_possible_plugin (FuDevice *self, const gchar *plugin); +guint fu_device_get_request_cnt (FuDevice *self, + FwupdRequestKind request_kind); guint64 fu_device_get_private_flags (FuDevice *self); void fu_device_set_private_flags (FuDevice *self, guint64 flag); diff --git a/libfwupdplugin/fu-device.c b/libfwupdplugin/fu-device.c index 15fed3334..9121cf954 100644 --- a/libfwupdplugin/fu-device.c +++ b/libfwupdplugin/fu-device.c @@ -56,6 +56,7 @@ typedef struct { guint progress; guint battery_level; guint battery_threshold; + guint request_cnts[FWUPD_REQUEST_KIND_LAST]; gint order; guint priority; guint poll_id; @@ -102,6 +103,7 @@ enum { enum { SIGNAL_CHILD_ADDED, SIGNAL_CHILD_REMOVED, + SIGNAL_REQUEST, SIGNAL_LAST }; @@ -409,6 +411,27 @@ fu_device_get_private_flags (FuDevice *self) 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: * @self: a #FuDevice @@ -3464,6 +3487,7 @@ fu_device_write_firmware (FuDevice *self, GError **error) { FuDeviceClass *klass = FU_DEVICE_GET_CLASS (self); + FuDevicePrivate *priv = GET_PRIVATE (self); g_autoptr(FuFirmware) firmware = 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); /* 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); } +/** + * 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 fu_device_class_init (FuDeviceClass *klass) { @@ -4442,6 +4523,12 @@ fu_device_class_init (FuDeviceClass *klass) G_STRUCT_OFFSET (FuDeviceClass, child_removed), NULL, NULL, g_cclosure_marshal_VOID__OBJECT, 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, G_PARAM_READWRITE | diff --git a/libfwupdplugin/fu-device.h b/libfwupdplugin/fu-device.h index 480dbb1ab..24f8383dd 100644 --- a/libfwupdplugin/fu-device.h +++ b/libfwupdplugin/fu-device.h @@ -106,8 +106,10 @@ struct _FuDeviceClass FuDevice *child); void (*child_removed) (FuDevice *self, /* signal */ FuDevice *child); + void (*request) (FuDevice *self, /* signal */ + FwupdRequest *request); /*< private >*/ - gpointer padding[7]; + gpointer padding[6]; #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_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_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_version(d) fwupd_device_get_version(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); gboolean fu_device_has_private_flag (FuDevice *self, guint64 flag); +void fu_device_emit_request (FuDevice *self, + FwupdRequest *request); diff --git a/libfwupdplugin/fwupdplugin.map b/libfwupdplugin/fwupdplugin.map index 745c26db6..e2a819b0d 100644 --- a/libfwupdplugin/fwupdplugin.map +++ b/libfwupdplugin/fwupdplugin.map @@ -826,8 +826,10 @@ LIBFWUPDPLUGIN_1.6.2 { fu_device_add_guid_full; fu_device_add_parent_physical_id; fu_device_add_private_flag; + fu_device_emit_request; fu_device_get_parent_physical_ids; fu_device_get_private_flags; + fu_device_get_request_cnt; fu_device_has_parent_physical_id; fu_device_has_private_flag; fu_device_new_with_context; diff --git a/plugins/ebitdo/fu-ebitdo-device.c b/plugins/ebitdo/fu-ebitdo-device.c index 0f6ecbeec..b18e910e2 100644 --- a/plugins/ebitdo/fu-ebitdo-device.c +++ b/plugins/ebitdo/fu-ebitdo-device.c @@ -362,32 +362,19 @@ fu_ebitdo_device_setup (FuDevice *device, GError **error) } static gboolean -fu_ebitdo_device_write_firmware (FuDevice *device, - FuFirmware *firmware, - FwupdInstallFlags flags, - GError **error) +fu_ebitdo_device_detach (FuDevice *device, GError **error) { - FuEbitdoDevice *self = FU_EBITDO_DEVICE (device); - 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 - }; + g_autoptr(FwupdRequest) request = fwupd_request_new (); - /* not in bootloader mode, so print what to do */ - if (fu_device_has_flag (device, FWUPD_DEVICE_FLAG_NEEDS_BOOTLOADER)) { - g_autoptr(GString) msg = g_string_new ("Not in bootloader mode: "); - g_string_append (msg, "Disconnect the controller, "); - g_print ("1. \n"); + /* not required */ + if (fu_device_has_flag (device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER)) + return TRUE; + + /* 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)) { case 0xab11: /* FC30 */ case 0xab12: /* NES30 */ @@ -420,18 +407,57 @@ fu_ebitdo_device_write_firmware (FuDevice *device, "both white LED and green LED blink, "); break; case 0x9015: /* N30 Pro 2 */ - g_string_append (msg, "press and hold L1+R1+START buttons " - "until the yellow LED blinks, "); + g_string_append (msg, "press and hold L1+R1+START buttons " + "until the yellow LED blinks, "); break; default: g_string_append (msg, "do what it says in the manual, "); break; } 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, FWUPD_ERROR, FWUPD_ERROR_NEEDS_USER_ACTION, - msg->str); + "Not in bootloader mode"); 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)) { 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_flag (device, FWUPD_DEVICE_FLAG_NEEDS_BOOTLOADER); } /* success */ @@ -619,6 +644,7 @@ fu_ebitdo_device_class_init (FuEbitdoDeviceClass *klass) FuDeviceClass *klass_device = FU_DEVICE_CLASS (klass); klass_device->write_firmware = fu_ebitdo_device_write_firmware; klass_device->setup = fu_ebitdo_device_setup; + klass_device->detach = fu_ebitdo_device_detach; klass_device->attach = fu_ebitdo_device_attach; klass_device->open = fu_ebitdo_device_open; klass_device->probe = fu_ebitdo_device_probe; diff --git a/plugins/system76-launch/fu-system76-launch-device.c b/plugins/system76-launch/fu-system76-launch-device.c index 11d701c42..25b58ab41 100644 --- a/plugins/system76-launch/fu-system76-launch-device.c +++ b/plugins/system76-launch/fu-system76-launch-device.c @@ -96,31 +96,65 @@ fu_system76_launch_device_setup (FuDevice *device, GError **error) } 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 */ - data[0] = SYSTEM76_LAUNCH_CMD_RESET; if (!fu_system76_launch_device_command (device, data, sizeof(data), error)) { g_prefix_error (error, "failed to execute reset command: "); 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 */ - if (data[1] != 0) { - 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)); + if (!fu_system76_launch_device_reset (device, &rc, error)) 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); - fu_device_add_flag (device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); + /* the user has to do something */ + 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; } diff --git a/src/fu-engine.c b/src/fu-engine.c index a3333042e..0eeae6951 100644 --- a/src/fu-engine.c +++ b/src/fu-engine.c @@ -121,6 +121,7 @@ enum { SIGNAL_DEVICE_ADDED, SIGNAL_DEVICE_REMOVED, SIGNAL_DEVICE_CHANGED, + SIGNAL_DEVICE_REQUEST, SIGNAL_STATUS_CHANGED, SIGNAL_PERCENTAGE_CHANGED, SIGNAL_LAST @@ -221,6 +222,14 @@ fu_engine_generic_notify_cb (FuDevice *device, GParamSpec *pspec, FuEngine *self 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 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, fu_engine_status_notify_cb, self); + g_signal_handlers_disconnect_by_func (device_old, + fu_engine_device_request_cb, + self); } g_signal_connect (device, "notify::progress", 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_signal_connect (device, "notify::update-image", G_CALLBACK (fu_engine_generic_notify_cb), self); + g_signal_connect (device, "request", + G_CALLBACK (fu_engine_device_request_cb), self); } static void @@ -2046,6 +2060,7 @@ fu_engine_install_tasks (FuEngine *self, FwupdInstallFlags flags, GError **error) { + FwupdFeatureFlags feature_flags = fu_engine_request_get_feature_flags (request); g_autoptr(FuIdleLocker) locker = NULL; g_autoptr(GPtrArray) devices = NULL; g_autoptr(GPtrArray) devices_new = NULL; @@ -2070,7 +2085,9 @@ fu_engine_install_tasks (FuEngine *self, /* all authenticated, so install all the things */ for (guint i = 0; i < install_tasks->len; 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; if (!fu_engine_composite_cleanup (self, devices, &error_local)) { g_warning ("failed to cleanup failed composite action: %s", @@ -2332,6 +2349,7 @@ fu_engine_install_release (FuEngine *self, XbNode *component, XbNode *rel, FwupdInstallFlags flags, + FwupdFeatureFlags feature_flags, GError **error) { FuPlugin *plugin; @@ -2402,7 +2420,9 @@ fu_engine_install_release (FuEngine *self, /* install firmware blob */ 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); if (g_error_matches (error_local, FWUPD_ERROR, @@ -2527,6 +2547,7 @@ fu_engine_sort_releases (FuEngine *self, FuDevice *device, GPtrArray *rels, GErr * @task: a #FuInstallTask * @blob_cab: the #GBytes of the .cab file * @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 * * Installs a specific firmware file on a device. @@ -2542,6 +2563,7 @@ fu_engine_install (FuEngine *self, FuInstallTask *task, GBytes *blob_cab, FwupdInstallFlags flags, + FwupdFeatureFlags feature_flags, GError **error) { XbNode *component = fu_install_task_get_component (task); @@ -2559,28 +2581,16 @@ fu_engine_install (FuEngine *self, /* not in bootloader mode */ device = g_object_ref (fu_install_task_get_device (task)); - if (fu_device_has_flag (device, FWUPD_DEVICE_FLAG_NEEDS_BOOTLOADER)) { - const gchar *caption = NULL; - caption = xb_node_query_text (component, - "screenshots/screenshot/caption", - NULL); - if (caption != NULL) { - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_NEEDS_USER_ACTION, - "Device %s needs to manually be put in update mode: %s", - 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; + if (!fu_device_has_flag (device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER)) { + const gchar *tmp = NULL; + + /* both optional; the plugin can specify a fallback */ + tmp = xb_node_query_text (component, "screenshots/screenshot/caption", NULL); + if (tmp != NULL) + fu_device_set_update_message (device, tmp); + tmp = xb_node_query_text (component, "screenshots/screenshot/image", NULL); + if (tmp != NULL) + fu_device_set_update_image (device, tmp); } /* get the newest version */ @@ -2649,11 +2659,23 @@ fu_engine_install (FuEngine *self, return FALSE; for (guint i = 0; i < rels->len; 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; } } 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; } @@ -2829,7 +2851,10 @@ fu_engine_update_cleanup (FuEngine *self, } 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; g_autofree gchar *str = NULL; @@ -2848,6 +2873,29 @@ fu_engine_update_detach (FuEngine *self, const gchar *device_id, GError **error) return FALSE; if (!fu_plugin_runner_update_detach (plugin, device, error)) 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; } @@ -3043,6 +3091,7 @@ fu_engine_install_blob (FuEngine *self, FuDevice *device, GBytes *blob_fw, FwupdInstallFlags flags, + FwupdFeatureFlags feature_flags, GError **error) { guint retries = 0; @@ -3081,7 +3130,7 @@ fu_engine_install_blob (FuEngine *self, return FALSE; /* 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; /* install */ @@ -4681,7 +4730,7 @@ fu_engine_add_releases_for_device_component (FuEngine *self, update_message = fwupd_release_get_update_message (rel); if (fwupd_device_get_update_message (FWUPD_DEVICE (device)) == 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); 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, 0, NULL, NULL, g_cclosure_marshal_VOID__OBJECT, 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] = g_signal_new ("status-changed", G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, diff --git a/src/fu-engine.h b/src/fu-engine.h index 2d00d6e37..784fb7934 100644 --- a/src/fu-engine.h +++ b/src/fu-engine.h @@ -146,11 +146,13 @@ gboolean fu_engine_install (FuEngine *self, FuInstallTask *task, GBytes *blob_cab, FwupdInstallFlags flags, + FwupdFeatureFlags feature_flags, GError **error); gboolean fu_engine_install_blob (FuEngine *self, FuDevice *device, GBytes *blob_fw, FwupdInstallFlags flags, + FwupdFeatureFlags feature_flags, GError **error); gboolean fu_engine_install_tasks (FuEngine *self, FuEngineRequest *request, diff --git a/src/fu-main.c b/src/fu-main.c index 261009ba0..b2718af03 100644 --- a/src/fu-main.c +++ b/src/fu-main.c @@ -32,6 +32,7 @@ #include "fwupd-security-attr-private.h" #include "fwupd-release-private.h" #include "fwupd-remote-private.h" +#include "fwupd-request-private.h" #include "fwupd-resources.h" #include "fu-common.h" @@ -161,6 +162,25 @@ fu_main_engine_device_changed_cb (FuEngine *engine, 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 fu_main_emit_property_changed (FuMainPrivate *priv, const gchar *property_name, @@ -1939,6 +1959,9 @@ main (int argc, char *argv[]) g_signal_connect (priv->engine, "device-changed", G_CALLBACK (fu_main_engine_device_changed_cb), 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_CALLBACK (fu_main_engine_status_changed_cb), priv); diff --git a/src/fu-self-test.c b/src/fu-self-test.c index 28da72208..9a7d66369 100644 --- a/src/fu-self-test.c +++ b/src/fu-self-test.c @@ -1734,7 +1734,9 @@ fu_engine_history_func (gconstpointer user_data) /* install it */ task = fu_install_task_new (device, component); 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 (ret); @@ -1857,7 +1859,9 @@ fu_engine_multiple_rels_func (gconstpointer user_data) /* install it */ task = fu_install_task_new (device, component); 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 (ret); @@ -1930,7 +1934,9 @@ fu_engine_history_inherit (gconstpointer user_data) g_setenv ("FWUPD_PLUGIN_TEST", "requires-activation", TRUE); task = fu_install_task_new (device, component); 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 (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 (device, "1.2.2"); 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 (ret); g_object_unref (engine); @@ -2057,7 +2065,9 @@ fu_engine_history_error_func (gconstpointer user_data) g_assert_nonnull (component); task = fu_install_task_new (device, component); 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 != NULL); 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_plugin (engine, self->plugin); 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 (ret); g_assert_cmpint (cnt, ==, 4); diff --git a/src/fu-tool.c b/src/fu-tool.c index 73dd53fc9..593306ce5 100644 --- a/src/fu-tool.c +++ b/src/fu-tool.c @@ -878,7 +878,10 @@ fu_util_install_blob (FuUtilPrivate *priv, gchar **values, GError **error) } } 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; if (priv->cleanup_blob) { g_autoptr(FuDevice) device_new = NULL; diff --git a/src/fu-util.c b/src/fu-util.c index 974a50f37..2c16d3336 100644 --- a/src/fu-util.c +++ b/src/fu-util.c @@ -66,7 +66,7 @@ struct FuUtilPrivate { /* only valid in update and downgrade */ FuUtilOperation current_operation; FwupdDevice *current_device; - gchar *current_message; + GPtrArray *post_requests; FwupdDeviceFlags completion_flags; FwupdDeviceFlags filter_include; FwupdDeviceFlags filter_exclude; @@ -84,6 +84,32 @@ fu_util_client_notify_cb (GObject *object, 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 fu_util_update_device_changed_cb (FwupdClient *client, FwupdDevice *device, @@ -131,12 +157,6 @@ fu_util_update_device_changed_cb (FwupdClient *client, g_warning ("no FuUtilOperation set"); } 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 @@ -551,14 +571,14 @@ fu_util_download_if_required (FuUtilPrivate *priv, const gchar *perhapsfn, GErro static void 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 */ - g_print ("%s: %s\n", _("Successfully installed firmware"), priv->current_message); - g_clear_pointer (&priv->current_message, g_free); + g_print ("%s\n", _("Successfully installed firmware")); + + /* 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 @@ -583,6 +603,8 @@ fu_util_install (FuUtilPrivate *priv, gchar **values, GError **error) priv->current_operation = FU_UTIL_OPERATION_INSTALL; g_signal_connect (priv->client, "device-changed", 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 */ 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; g_signal_connect (priv->client, "device-changed", 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); for (guint i = 0; i < devices->len; 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; g_signal_connect (priv->client, "device-changed", 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 */ 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; g_signal_connect (priv->client, "device-changed", 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; if (!fu_util_update_device_with_release (priv, dev, rel, error)) return FALSE; @@ -1935,6 +1963,8 @@ fu_util_reinstall (FuUtilPrivate *priv, gchar **values, GError **error) priv->current_operation = FU_UTIL_OPERATION_INSTALL; g_signal_connect (priv->client, "device-changed", 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; if (!fu_util_update_device_with_release (priv, dev, rel, error)) return FALSE; @@ -2059,6 +2089,8 @@ fu_util_switch_branch (FuUtilPrivate *priv, gchar **values, GError **error) priv->current_operation = FU_UTIL_OPERATION_INSTALL; g_signal_connect (priv->client, "device-changed", 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_BRANCH_SWITCH; 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); if (priv->current_device != NULL) 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_object_unref (priv->cancellable); g_object_unref (priv->progressbar); @@ -2852,6 +2884,7 @@ main (int argc, char *argv[]) /* create helper object */ priv->main_ctx = g_main_context_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); /* add commands */ @@ -3267,6 +3300,7 @@ main (int argc, char *argv[]) if (!fwupd_client_set_feature_flags (priv->client, FWUPD_FEATURE_FLAG_CAN_REPORT | FWUPD_FEATURE_FLAG_SWITCH_BRANCH | + FWUPD_FEATURE_FLAG_REQUESTS | FWUPD_FEATURE_FLAG_UPDATE_ACTION | FWUPD_FEATURE_FLAG_DETACH_ACTION, priv->cancellable, &error)) { diff --git a/src/org.freedesktop.fwupd.xml b/src/org.freedesktop.fwupd.xml index 58d407d9a..a63f2c643 100644 --- a/src/org.freedesktop.fwupd.xml +++ b/src/org.freedesktop.fwupd.xml @@ -831,5 +831,23 @@ + + + + + + A device request. + + + + + + + A device request to the client. + + + + +