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. + + + + +