blob: f930c1d46048c3b44422328a3c63e988c3d27c49 [file] [log] [blame]
Richard Hughes02c90d82018-08-09 12:13:03 +01001/*
Richard Hughes2de8f132018-01-17 09:12:02 +00002 * Copyright (C) 2015-2018 Richard Hughes <richard@hughsie.com>
Richard Hughes9945edb2017-06-19 10:03:55 +01003 *
Mario Limonciello51308e62018-05-28 20:05:46 -05004 * SPDX-License-Identifier: LGPL-2.1+
Richard Hughes9945edb2017-06-19 10:03:55 +01005 */
6
Richard Hughesb08e7bc2018-09-11 10:51:13 +01007#define G_LOG_DOMAIN "FuEngine"
8
Richard Hughes9945edb2017-06-19 10:03:55 +01009#include "config.h"
10
Richard Hughes9945edb2017-06-19 10:03:55 +010011#include <gio/gio.h>
Richard Hughes9e5675e2019-11-22 09:35:03 +000012#ifdef HAVE_GIO_UNIX
Richard Hughes9945edb2017-06-19 10:03:55 +010013#include <gio/gunixinputstream.h>
Richard Hughes9e5675e2019-11-22 09:35:03 +000014#endif
Richard Hughes9945edb2017-06-19 10:03:55 +010015#include <glib-object.h>
Richard Hughes16658372019-11-22 09:23:59 +000016#ifdef HAVE_GUDEV
Richard Hughes9d6e0e72018-08-24 20:20:17 +010017#include <gudev/gudev.h>
Richard Hughes16658372019-11-22 09:23:59 +000018#endif
Richard Hughesbd4d2852017-09-13 14:05:14 +010019#include <string.h>
Richard Hughesfc1e2672019-11-22 08:53:33 +000020#ifdef HAVE_UTSNAME_H
Richard Hughes473c5202018-01-11 21:06:16 +000021#include <sys/utsname.h>
Richard Hughesfc1e2672019-11-22 08:53:33 +000022#endif
Richard Hughes019a1bc2019-11-26 10:19:33 +000023#include <errno.h>
Richard Hughes9945edb2017-06-19 10:03:55 +010024
25#include "fwupd-common-private.h"
26#include "fwupd-enums-private.h"
27#include "fwupd-error.h"
28#include "fwupd-release-private.h"
29#include "fwupd-remote-private.h"
30#include "fwupd-resources.h"
31
Richard Hughesc6eb4162020-02-27 10:02:36 +000032#include "fu-cabinet.h"
Richard Hughesdeea2da2017-12-15 15:40:44 +000033#include "fu-common-cab.h"
Richard Hughes943d2c92017-06-21 09:04:39 +010034#include "fu-common.h"
Richard Hughes9945edb2017-06-19 10:03:55 +010035#include "fu-config.h"
36#include "fu-debug.h"
Richard Hughes0a7e7832017-11-22 11:01:13 +000037#include "fu-device-list.h"
Richard Hughes9dde04f2017-09-13 12:07:15 +010038#include "fu-device-private.h"
Richard Hughes9945edb2017-06-19 10:03:55 +010039#include "fu-engine.h"
Mario Limonciellod81ea2e2020-01-13 14:11:43 -060040#include "fu-engine-helper.h"
Richard Hughes9945edb2017-06-19 10:03:55 +010041#include "fu-hwids.h"
Richard Hughes75b965d2018-11-15 13:51:21 +000042#include "fu-idle.h"
Richard Hughes7383ce22018-05-08 14:14:35 +010043#include "fu-keyring-utils.h"
Richard Hughesf425d292019-01-18 17:57:39 +000044#include "fu-hash.h"
Richard Hughesbc3a4e12018-01-06 22:41:47 +000045#include "fu-history.h"
Richard Hughes37d09432018-09-09 10:39:45 +010046#include "fu-mutex.h"
Richard Hughes9945edb2017-06-19 10:03:55 +010047#include "fu-plugin.h"
Richard Hughese7e95452017-11-22 09:05:53 +000048#include "fu-plugin-list.h"
Richard Hughes9945edb2017-06-19 10:03:55 +010049#include "fu-plugin-private.h"
50#include "fu-quirks.h"
Richard Hughesd1808aa2019-12-10 15:20:30 +000051#include "fu-remote-list.h"
Richard Hughesb246bca2020-05-18 14:31:35 +010052#include "fu-security-attr.h"
Richard Hughesf58ac732020-05-12 15:23:44 +010053#include "fu-security-attrs-private.h"
Mario Limonciello6b0e6632019-11-22 13:04:32 -060054#include "fu-smbios-private.h"
Richard Hughes405baeb2018-09-06 14:23:08 +010055#include "fu-udev-device-private.h"
Richard Hughesc125ec02018-09-05 19:35:17 +010056#include "fu-usb-device-private.h"
Richard Hughes9945edb2017-06-19 10:03:55 +010057
Richard Hughes95c98a92019-10-22 16:03:15 +010058#include "fu-dfu-firmware.h"
59#include "fu-ihex-firmware.h"
60#include "fu-srec-firmware.h"
61
Richard Hughes3d005222019-05-17 14:02:41 +010062#ifdef HAVE_SYSTEMD
63#include "fu-systemd.h"
64#endif
65
Richard Hughes9945edb2017-06-19 10:03:55 +010066static void fu_engine_finalize (GObject *obj);
67
68struct _FuEngine
69{
70 GObject parent_instance;
Richard Hughes5b5f6552018-05-18 10:22:39 +010071 FuAppFlags app_flags;
Richard Hughes9945edb2017-06-19 10:03:55 +010072 GUsbContext *usb_ctx;
Richard Hughes16658372019-11-22 09:23:59 +000073#ifdef HAVE_GUDEV
Richard Hughes9d6e0e72018-08-24 20:20:17 +010074 GUdevClient *gudev_client;
Richard Hughes16658372019-11-22 09:23:59 +000075#endif
Richard Hughes9945edb2017-06-19 10:03:55 +010076 FuConfig *config;
Richard Hughesd1808aa2019-12-10 15:20:30 +000077 FuRemoteList *remote_list;
Richard Hughes0a7e7832017-11-22 11:01:13 +000078 FuDeviceList *device_list;
Richard Hughes9945edb2017-06-19 10:03:55 +010079 FwupdStatus status;
Richard Hughesf425d292019-01-18 17:57:39 +000080 gboolean tainted;
Richard Hughes9945edb2017-06-19 10:03:55 +010081 guint percentage;
Richard Hughesbc3a4e12018-01-06 22:41:47 +000082 FuHistory *history;
Richard Hughes75b965d2018-11-15 13:51:21 +000083 FuIdle *idle;
Richard Hughes481aa2a2018-09-18 20:51:46 +010084 XbSilo *silo;
Richard Hughes9945edb2017-06-19 10:03:55 +010085 gboolean coldplug_running;
86 guint coldplug_id;
87 guint coldplug_delay;
Richard Hughese7e95452017-11-22 09:05:53 +000088 FuPluginList *plugin_list;
Richard Hughesc02ee4d2018-05-22 15:46:03 +010089 GPtrArray *plugin_filter;
Richard Hughes9d6e0e72018-08-24 20:20:17 +010090 GPtrArray *udev_subsystems;
Richard Hughes16658372019-11-22 09:23:59 +000091#ifdef HAVE_GUDEV
Richard Hughes5e952ce2019-08-26 11:09:46 +010092 GHashTable *udev_changed_ids; /* sysfs:FuEngineUdevChangedHelper */
Richard Hughes16658372019-11-22 09:23:59 +000093#endif
Richard Hughes49e5e052017-09-03 12:15:41 +010094 FuSmbios *smbios;
Richard Hughesd7704d42017-08-08 20:29:09 +010095 FuHwids *hwids;
Richard Hughes9c028f02017-10-28 21:14:28 +010096 FuQuirks *quirks;
Richard Hughes0eb123b2018-04-19 12:00:04 +010097 GHashTable *runtime_versions;
Richard Hughes34e0dab2018-04-20 16:43:00 +010098 GHashTable *compile_versions;
Richard Hughes8dd4c1c2019-03-03 18:27:57 +000099 GHashTable *approved_firmware;
Richard Hughes95c98a92019-10-22 16:03:15 +0100100 GHashTable *firmware_gtypes;
Richard Hughes0917fb62019-09-21 12:55:37 +0100101 gchar *host_machine_id;
Richard Hughesd5aab652020-02-25 12:47:50 +0000102 JcatContext *jcat_context;
Mario Limonciello46aaee82019-01-10 12:58:00 -0600103 gboolean loaded;
Richard Hughes196c6c62020-05-11 19:42:47 +0100104 gchar *host_security_id;
105 gboolean host_security_id_valid;
Richard Hughes9945edb2017-06-19 10:03:55 +0100106};
107
108enum {
109 SIGNAL_CHANGED,
110 SIGNAL_DEVICE_ADDED,
111 SIGNAL_DEVICE_REMOVED,
112 SIGNAL_DEVICE_CHANGED,
113 SIGNAL_STATUS_CHANGED,
114 SIGNAL_PERCENTAGE_CHANGED,
115 SIGNAL_LAST
116};
117
118static guint signals[SIGNAL_LAST] = { 0 };
119
120G_DEFINE_TYPE (FuEngine, fu_engine, G_TYPE_OBJECT)
121
Richard Hughes9945edb2017-06-19 10:03:55 +0100122static void
123fu_engine_emit_changed (FuEngine *self)
124{
125 g_signal_emit (self, signals[SIGNAL_CHANGED], 0);
Richard Hughes75b965d2018-11-15 13:51:21 +0000126 fu_engine_idle_reset (self);
Mario Limonciellod81ea2e2020-01-13 14:11:43 -0600127
128 /* update the motd */
129 if (self->loaded &&
130 fu_config_get_update_motd (self->config)) {
131 g_autoptr(GError) error_local = NULL;
132 if (!fu_engine_update_motd (self, &error_local))
133 g_debug ("%s", error_local->message);
134 }
Richard Hughes9945edb2017-06-19 10:03:55 +0100135}
136
137static void
Richard Hughes170c0c12017-11-22 11:26:24 +0000138fu_engine_emit_device_changed (FuEngine *self, FuDevice *device)
139{
Richard Hughes196c6c62020-05-11 19:42:47 +0100140 self->host_security_id_valid = FALSE;
Richard Hughes170c0c12017-11-22 11:26:24 +0000141 g_signal_emit (self, signals[SIGNAL_DEVICE_CHANGED], 0, device);
142}
143
Mario Limoncielloda0d1882020-05-05 14:16:39 -0500144static gint
145fu_engine_gtypes_sort_cb (gconstpointer a, gconstpointer b)
146{
147 const gchar *stra = *((const gchar **) a);
148 const gchar *strb = *((const gchar **) b);
149 return g_strcmp0 (stra, strb);
150}
151
Richard Hughes95c98a92019-10-22 16:03:15 +0100152GPtrArray *
153fu_engine_get_firmware_gtype_ids (FuEngine *self)
154{
155 GPtrArray *firmware_gtypes = g_ptr_array_new_with_free_func (g_free);
156 g_autoptr(GList) keys = g_hash_table_get_keys (self->firmware_gtypes);
157 for (GList *l = keys; l != NULL; l = l->next) {
158 const gchar *id = l->data;
159 g_ptr_array_add (firmware_gtypes, g_strdup (id));
160 }
Mario Limoncielloda0d1882020-05-05 14:16:39 -0500161 g_ptr_array_sort (firmware_gtypes, fu_engine_gtypes_sort_cb);
Richard Hughes95c98a92019-10-22 16:03:15 +0100162 return firmware_gtypes;
163}
164
165GType
166fu_engine_get_firmware_gtype_by_id (FuEngine *self, const gchar *id)
167{
168 return GPOINTER_TO_SIZE (g_hash_table_lookup (self->firmware_gtypes, id));
169}
170
171static void
172fu_engine_add_firmware_gtype (FuEngine *self, const gchar *id, GType gtype)
173{
174 g_hash_table_insert (self->firmware_gtypes, g_strdup (id), GSIZE_TO_POINTER (gtype));
175}
176
Richard Hughes9945edb2017-06-19 10:03:55 +0100177/**
178 * fu_engine_get_status:
179 * @self: A #FuEngine
Richard Hughes9945edb2017-06-19 10:03:55 +0100180 *
181 * Gets the current engine status.
182 *
183 * Returns: a #FwupdStatus, e.g. %FWUPD_STATUS_DECOMPRESSING
184 **/
185FwupdStatus
186fu_engine_get_status (FuEngine *self)
187{
188 g_return_val_if_fail (FU_IS_ENGINE (self), 0);
189 return self->status;
190}
191
Richard Hughes9945edb2017-06-19 10:03:55 +0100192static void
193fu_engine_set_status (FuEngine *self, FwupdStatus status)
194{
195 if (self->status == status)
196 return;
197 self->status = status;
198
199 /* emit changed */
200 g_debug ("Emitting PropertyChanged('Status'='%s')",
201 fwupd_status_to_string (status));
202 g_signal_emit (self, signals[SIGNAL_STATUS_CHANGED], 0, status);
203}
204
205static void
206fu_engine_set_percentage (FuEngine *self, guint percentage)
207{
208 if (self->percentage == percentage)
209 return;
210 self->percentage = percentage;
211
212 /* emit changed */
213 g_signal_emit (self, signals[SIGNAL_PERCENTAGE_CHANGED], 0, percentage);
214}
215
216static void
Richard Hughes4a036012017-11-30 16:33:24 +0000217fu_engine_progress_notify_cb (FuDevice *device, GParamSpec *pspec, FuEngine *self)
218{
Richard Hughes2a77afc2019-01-30 11:19:48 +0000219 if (fu_device_get_status (device) == FWUPD_STATUS_UNKNOWN)
220 return;
Richard Hughes4a036012017-11-30 16:33:24 +0000221 fu_engine_set_percentage (self, fu_device_get_progress (device));
Richard Hughesec85ebc2018-08-10 09:52:30 +0100222 fu_engine_emit_device_changed (self, device);
Richard Hughes4a036012017-11-30 16:33:24 +0000223}
224
225static void
226fu_engine_status_notify_cb (FuDevice *device, GParamSpec *pspec, FuEngine *self)
227{
228 fu_engine_set_status (self, fu_device_get_status (device));
Richard Hughesec85ebc2018-08-10 09:52:30 +0100229 fu_engine_emit_device_changed (self, device);
Richard Hughes4a036012017-11-30 16:33:24 +0000230}
231
232static void
Richard Hughesfbcebe02017-12-11 16:37:19 +0000233fu_engine_watch_device (FuEngine *self, FuDevice *device)
Richard Hughes4a036012017-11-30 16:33:24 +0000234{
Richard Hughes5a9a6bd2018-09-10 10:02:20 +0100235 g_autoptr(FuDevice) device_old = fu_device_list_get_old (self->device_list, device);
Richard Hughesfbcebe02017-12-11 16:37:19 +0000236 if (device_old != NULL) {
237 g_signal_handlers_disconnect_by_func (device_old,
238 fu_engine_progress_notify_cb,
239 self);
240 g_signal_handlers_disconnect_by_func (device_old,
241 fu_engine_status_notify_cb,
242 self);
243 }
Richard Hughes4a036012017-11-30 16:33:24 +0000244 g_signal_connect (device, "notify::progress",
245 G_CALLBACK (fu_engine_progress_notify_cb), self);
246 g_signal_connect (device, "notify::status",
247 G_CALLBACK (fu_engine_status_notify_cb), self);
Richard Hughesfbcebe02017-12-11 16:37:19 +0000248}
249
250static void
251fu_engine_device_added_cb (FuDeviceList *device_list, FuDevice *device, FuEngine *self)
252{
253 fu_engine_watch_device (self, device);
Richard Hughes4a036012017-11-30 16:33:24 +0000254 g_signal_emit (self, signals[SIGNAL_DEVICE_ADDED], 0, device);
255}
256
257static void
Mario Limoncielloe260ead2018-09-01 09:19:24 -0500258fu_engine_device_runner_device_removed (FuEngine *self, FuDevice *device)
259{
260 GPtrArray *plugins = fu_plugin_list_get_all (self->plugin_list);
261 for (guint j = 0; j < plugins->len; j++) {
262 FuPlugin *plugin_tmp = g_ptr_array_index (plugins, j);
263 fu_plugin_runner_device_removed (plugin_tmp, device);
264 }
265}
266
267static void
Richard Hughes4a036012017-11-30 16:33:24 +0000268fu_engine_device_removed_cb (FuDeviceList *device_list, FuDevice *device, FuEngine *self)
269{
Mario Limoncielloe260ead2018-09-01 09:19:24 -0500270 fu_engine_device_runner_device_removed (self, device);
Richard Hughes4a036012017-11-30 16:33:24 +0000271 g_signal_handlers_disconnect_by_data (device, self);
272 g_signal_emit (self, signals[SIGNAL_DEVICE_REMOVED], 0, device);
273}
274
275static void
276fu_engine_device_changed_cb (FuDeviceList *device_list, FuDevice *device, FuEngine *self)
277{
Richard Hughesfbcebe02017-12-11 16:37:19 +0000278 fu_engine_watch_device (self, device);
Richard Hughes4a036012017-11-30 16:33:24 +0000279 fu_engine_emit_device_changed (self, device);
280}
281
Richard Hughes481aa2a2018-09-18 20:51:46 +0100282/* convert hex and decimal versions to dotted style */
283static gchar *
Richard Hughes2c40b372019-04-17 13:41:47 +0100284fu_engine_get_release_version (FuEngine *self, FuDevice *dev, XbNode *rel, GError **error)
Richard Hughes611f1a92018-01-11 11:54:28 +0000285{
Mario Limonciellof675c212020-02-25 11:12:35 -0600286 FwupdVersionFormat fmt = fu_device_get_version_format (dev);
Richard Hughes481aa2a2018-09-18 20:51:46 +0100287 const gchar *version;
Richard Hughes481aa2a2018-09-18 20:51:46 +0100288 guint64 ver_uint32;
289
290 /* get version */
291 version = xb_node_get_attr (rel, "version");
Richard Hughes0b3e9fd2019-04-08 11:13:20 +0100292 if (version == NULL) {
293 g_set_error_literal (error,
294 FWUPD_ERROR,
295 FWUPD_ERROR_NOT_SUPPORTED,
296 "version unset");
Richard Hughes611f1a92018-01-11 11:54:28 +0000297 return NULL;
Richard Hughes0b3e9fd2019-04-08 11:13:20 +0100298 }
Richard Hughes481aa2a2018-09-18 20:51:46 +0100299
300 /* already dotted notation */
301 if (g_strstr_len (version, -1, ".") != NULL)
302 return g_strdup (version);
303
Richard Hughes481aa2a2018-09-18 20:51:46 +0100304 /* don't touch my version! */
Richard Hughesc84b36c2019-04-27 09:24:42 +0100305 if (fmt == FWUPD_VERSION_FORMAT_PLAIN)
Richard Hughes481aa2a2018-09-18 20:51:46 +0100306 return g_strdup (version);
307
308 /* parse as integer */
309 ver_uint32 = fu_common_strtoull (version);
Mario Limonciellof675c212020-02-25 11:12:35 -0600310 if (fmt == FWUPD_VERSION_FORMAT_UNKNOWN ||
311 ver_uint32 == 0 || ver_uint32 > G_MAXUINT32)
Richard Hughes481aa2a2018-09-18 20:51:46 +0100312 return g_strdup (version);
313
314 /* convert to dotted decimal */
315 return fu_common_version_from_uint32 ((guint32) ver_uint32, fmt);
Richard Hughes611f1a92018-01-11 11:54:28 +0000316}
317
Richard Hughes0b3e9fd2019-04-08 11:13:20 +0100318static gboolean
Richard Hughesbd4d2852017-09-13 14:05:14 +0100319fu_engine_set_release_from_appstream (FuEngine *self,
Richard Hughes2c40b372019-04-17 13:41:47 +0100320 FuDevice *dev,
Richard Hughesbd4d2852017-09-13 14:05:14 +0100321 FwupdRelease *rel,
Richard Hughes481aa2a2018-09-18 20:51:46 +0100322 XbNode *component,
Richard Hughes0b3e9fd2019-04-08 11:13:20 +0100323 XbNode *release,
324 GError **error)
Richard Hughes9945edb2017-06-19 10:03:55 +0100325{
Richard Hughesc6afb512017-08-22 10:22:20 +0100326 FwupdRemote *remote = NULL;
Richard Hughes9945edb2017-06-19 10:03:55 +0100327 const gchar *tmp;
Richard Hughes611f1a92018-01-11 11:54:28 +0000328 const gchar *remote_id;
Richard Hughes481aa2a2018-09-18 20:51:46 +0100329 guint64 tmp64;
330 g_autofree gchar *version_rel = NULL;
Richard Hughes02ac92c2019-03-29 17:56:46 +0000331 g_autoptr(GPtrArray) cats = NULL;
Richard Hughes0ad59cb2019-09-16 10:33:05 +0100332 g_autoptr(GPtrArray) issues = NULL;
Richard Hughes481aa2a2018-09-18 20:51:46 +0100333 g_autoptr(XbNode) description = NULL;
Richard Hughes9945edb2017-06-19 10:03:55 +0100334
Richard Hughes481aa2a2018-09-18 20:51:46 +0100335 /* set from the component */
336 tmp = xb_node_query_text (component, "id", NULL);
337 if (tmp != NULL)
338 fwupd_release_set_appstream_id (rel, tmp);
339 tmp = xb_node_query_text (component, "url[@type='homepage']", NULL);
340 if (tmp != NULL)
341 fwupd_release_set_homepage (rel, tmp);
342 tmp = xb_node_query_text (component, "project_license", NULL);
343 if (tmp != NULL)
344 fwupd_release_set_license (rel, tmp);
345 tmp = xb_node_query_text (component, "name", NULL);
346 if (tmp != NULL)
347 fwupd_release_set_name (rel, tmp);
348 tmp = xb_node_query_text (component, "summary", NULL);
349 if (tmp != NULL)
350 fwupd_release_set_summary (rel, tmp);
351 tmp = xb_node_query_text (component, "developer_name", NULL);
352 if (tmp != NULL)
353 fwupd_release_set_vendor (rel, tmp);
354
Mario Limonciello92ff9c72020-03-09 16:54:56 -0500355 /* refresh the device and release to the new version format too */
356 fu_engine_md_refresh_device_from_component (self, dev, component);
357
Richard Hughes481aa2a2018-09-18 20:51:46 +0100358 /* the version is fixed up at runtime */
Richard Hughes2c40b372019-04-17 13:41:47 +0100359 version_rel = fu_engine_get_release_version (self, dev, release, error);
Richard Hughes0b3e9fd2019-04-08 11:13:20 +0100360 if (version_rel == NULL)
361 return FALSE;
362 fwupd_release_set_version (rel, version_rel);
Richard Hughes74fa2ca2018-01-10 21:33:39 +0000363
Richard Hughesc6afb512017-08-22 10:22:20 +0100364 /* find the remote */
Richard Hughes33171fd2018-11-09 13:29:11 +0000365 remote_id = xb_node_query_text (component, "../custom/value[@key='fwupd::RemoteId']", NULL);
Richard Hughes611f1a92018-01-11 11:54:28 +0000366 if (remote_id != NULL) {
367 fwupd_release_set_remote_id (rel, remote_id);
Richard Hughesd1808aa2019-12-10 15:20:30 +0000368 remote = fu_remote_list_get_by_id (self->remote_list, remote_id);
Richard Hughes481aa2a2018-09-18 20:51:46 +0100369 if (remote == NULL)
370 g_warning ("no remote found for release %s", version_rel);
Richard Hughesbd4d2852017-09-13 14:05:14 +0100371 }
Richard Hughes481aa2a2018-09-18 20:51:46 +0100372 description = xb_node_query_first (release, "description", NULL);
373 if (description != NULL) {
374 g_autofree gchar *xml = NULL;
375 xml = xb_node_export (description, XB_NODE_EXPORT_FLAG_ONLY_CHILDREN, NULL);
376 if (xml != NULL)
377 fwupd_release_set_description (rel, xml);
378 }
379 tmp = xb_node_query_text (release, "location", NULL);
Richard Hughesc6afb512017-08-22 10:22:20 +0100380 if (tmp != NULL) {
381 g_autofree gchar *uri = NULL;
382 if (remote != NULL)
383 uri = fwupd_remote_build_firmware_uri (remote, tmp, NULL);
384 if (uri == NULL)
385 uri = g_strdup (tmp);
386 fwupd_release_set_uri (rel, uri);
Mario Limonciello4f24d0b2019-01-26 01:19:59 -0600387 } else if (remote != NULL &&
388 fwupd_remote_get_kind (remote) == FWUPD_REMOTE_KIND_DIRECTORY) {
389 g_autofree gchar *uri = NULL;
390 tmp = xb_node_query_text (component, "../custom/value[@key='fwupd::FilenameCache']", NULL);
391 if (tmp != NULL) {
392 uri = g_strdup_printf ("file://%s", tmp);
393 fwupd_release_set_uri (rel, uri);
394 }
Richard Hughesc6afb512017-08-22 10:22:20 +0100395 }
Richard Hughes481aa2a2018-09-18 20:51:46 +0100396 tmp = xb_node_query_text (release, "checksum[@target='content']", NULL);
397 if (tmp != NULL)
398 fwupd_release_set_filename (rel, tmp);
Richard Hughes71858282019-01-26 12:15:55 +0000399 tmp = xb_node_query_text (release, "url[@type='details']", NULL);
400 if (tmp != NULL)
401 fwupd_release_set_details_url (rel, tmp);
402 tmp = xb_node_query_text (release, "url[@type='source']", NULL);
403 if (tmp != NULL)
404 fwupd_release_set_source_url (rel, tmp);
Richard Hughes481aa2a2018-09-18 20:51:46 +0100405 tmp = xb_node_query_text (release, "checksum[@target='container']", NULL);
406 if (tmp != NULL)
407 fwupd_release_add_checksum (rel, tmp);
408 tmp64 = xb_node_query_text_as_uint (release, "size[@type='installed']", NULL);
409 if (tmp64 != G_MAXUINT64) {
410 fwupd_release_set_size (rel, tmp64);
411 } else {
412 GBytes *sz = xb_node_get_data (release, "fwupd::ReleaseSize");
413 if (sz != NULL) {
414 const guint64 *sizeptr = g_bytes_get_data (sz, NULL);
415 fwupd_release_set_size (rel, *sizeptr);
416 }
Richard Hughes9945edb2017-06-19 10:03:55 +0100417 }
Richard Hughes52c1a4d2020-04-02 11:21:06 +0100418 tmp = xb_node_get_attr (release, "urgency");
419 if (tmp != NULL)
420 fwupd_release_set_urgency (rel, fwupd_release_urgency_from_string (tmp));
Richard Hughes6e0c8f82018-11-11 21:46:41 +0000421 tmp64 = xb_node_get_attr_as_uint (release, "install_duration");
Richard Hughes319dbcb2018-12-18 17:59:05 +0000422 if (tmp64 != G_MAXUINT64)
Richard Hughes6e0c8f82018-11-11 21:46:41 +0000423 fwupd_release_set_install_duration (rel, tmp64);
Richard Hughes14797f82020-04-02 10:52:04 +0100424 tmp64 = xb_node_get_attr_as_uint (release, "timestamp");
425 if (tmp64 != G_MAXUINT64)
426 fwupd_release_set_created (rel, tmp64);
Richard Hughes02ac92c2019-03-29 17:56:46 +0000427 cats = xb_node_query (component, "categories/category", 0, NULL);
428 if (cats != NULL) {
429 for (guint i = 0; i < cats->len; i++) {
430 XbNode *n = g_ptr_array_index (cats, i);
431 fwupd_release_add_category (rel, xb_node_get_text (n));
432 }
433 }
Richard Hughes0ad59cb2019-09-16 10:33:05 +0100434 issues = xb_node_query (component, "issues/issue", 0, NULL);
435 if (issues != NULL) {
436 for (guint i = 0; i < issues->len; i++) {
437 XbNode *n = g_ptr_array_index (issues, i);
438 fwupd_release_add_issue (rel, xb_node_get_text (n));
439 }
440 }
Richard Hughes868db4e2019-09-26 14:56:57 +0100441 tmp = xb_node_query_text (component, "screenshots/screenshot/caption", NULL);
442 if (tmp != NULL)
443 fwupd_release_set_detach_caption (rel, tmp);
444 tmp = xb_node_query_text (component, "screenshots/screenshot/image", NULL);
445 if (tmp != NULL)
446 fwupd_release_set_detach_image (rel, tmp);
Richard Hughesadeb2b12018-12-14 11:56:41 +0000447 tmp = xb_node_query_text (component, "custom/value[@key='LVFS::UpdateProtocol']", NULL);
448 if (tmp != NULL)
449 fwupd_release_set_protocol (rel, tmp);
Mario Limonciello32241f42019-01-24 10:12:41 -0600450 tmp = xb_node_query_text (component, "custom/value[@key='LVFS::UpdateMessage']", NULL);
451 if (tmp != NULL)
452 fwupd_release_set_update_message (rel, tmp);
Richard Hughes0b3e9fd2019-04-08 11:13:20 +0100453 return TRUE;
Richard Hughes9945edb2017-06-19 10:03:55 +0100454}
455
Richard Hughes481aa2a2018-09-18 20:51:46 +0100456/* finds the remote-id for the first firmware in the silo that matches this
Richard Hughes534255c2018-01-28 19:51:56 +0000457 * container checksum */
458static const gchar *
459fu_engine_get_remote_id_for_checksum (FuEngine *self, const gchar *csum)
460{
Richard Hughes481aa2a2018-09-18 20:51:46 +0100461 g_autofree gchar *xpath = NULL;
462 g_autoptr(XbNode) key = NULL;
Richard Hughes33171fd2018-11-09 13:29:11 +0000463 xpath = g_strdup_printf ("components/component/releases/release/"
464 "checksum[@target='container'][text()='%s']/../../"
465 "../../custom/value[@key='fwupd::RemoteId']", csum);
Richard Hughes481aa2a2018-09-18 20:51:46 +0100466 key = xb_silo_query_first (self->silo, xpath, NULL);
467 if (key == NULL)
468 return NULL;
469 return xb_node_get_text (key);
Richard Hughes534255c2018-01-28 19:51:56 +0000470}
471
Richard Hughes9945edb2017-06-19 10:03:55 +0100472/**
473 * fu_engine_unlock:
474 * @self: A #FuEngine
475 * @device_id: A device ID
476 * @error: A #GError, or %NULL
477 *
478 * Unlocks a device.
479 *
480 * Returns: %TRUE for success
481 **/
482gboolean
483fu_engine_unlock (FuEngine *self, const gchar *device_id, GError **error)
484{
Richard Hughes34834102017-11-21 21:55:00 +0000485 FuPlugin *plugin;
Richard Hughes5a9a6bd2018-09-10 10:02:20 +0100486 g_autoptr(FuDevice) device = NULL;
Richard Hughes9945edb2017-06-19 10:03:55 +0100487
488 g_return_val_if_fail (FU_IS_ENGINE (self), FALSE);
489 g_return_val_if_fail (device_id != NULL, FALSE);
490 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
491
492 /* check the device exists */
Richard Hughes40127542018-01-12 20:25:55 +0000493 device = fu_device_list_get_by_id (self->device_list, device_id, error);
Richard Hughes0a7e7832017-11-22 11:01:13 +0000494 if (device == NULL)
Richard Hughes9945edb2017-06-19 10:03:55 +0100495 return FALSE;
496
Richard Hughes34834102017-11-21 21:55:00 +0000497 /* get the plugin */
Richard Hughese7e95452017-11-22 09:05:53 +0000498 plugin = fu_plugin_list_find_by_name (self->plugin_list,
Richard Hughes0a7e7832017-11-22 11:01:13 +0000499 fu_device_get_plugin (device),
Richard Hughese7e95452017-11-22 09:05:53 +0000500 error);
Richard Hughes34834102017-11-21 21:55:00 +0000501 if (plugin == NULL)
502 return FALSE;
503
Richard Hughes9945edb2017-06-19 10:03:55 +0100504 /* run the correct plugin that added this */
Richard Hughes0a7e7832017-11-22 11:01:13 +0000505 if (!fu_plugin_runner_unlock (plugin, device, error))
Richard Hughes9945edb2017-06-19 10:03:55 +0100506 return FALSE;
507
508 /* make the UI update */
Richard Hughes0a7e7832017-11-22 11:01:13 +0000509 fu_engine_emit_device_changed (self, device);
Richard Hughes9945edb2017-06-19 10:03:55 +0100510 fu_engine_emit_changed (self);
511 return TRUE;
512}
513
Mario Limonciellobfcf75b2019-04-17 15:05:39 +0100514gboolean
515fu_engine_modify_config (FuEngine *self, const gchar *key, const gchar *value, GError **error)
516{
517 const gchar *keys[] = {
518 "ArchiveSizeMax",
519 "BlacklistDevices",
520 "BlacklistPlugins",
521 "IdleTimeout",
Mario Limonciello38027e62019-04-17 15:16:07 +0100522 "VerboseDomains",
Mario Limonciellod81ea2e2020-01-13 14:11:43 -0600523 "UpdateMotd",
Mario Limonciello4fa95a72020-03-28 10:50:57 -0500524 "EnumerateAllDevices",
Mario Limonciellobfcf75b2019-04-17 15:05:39 +0100525 NULL };
526
527 g_return_val_if_fail (FU_IS_ENGINE (self), FALSE);
528 g_return_val_if_fail (key != NULL, FALSE);
529 g_return_val_if_fail (value != NULL, FALSE);
530 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
531
532 /* check keys are valid */
533 if (!g_strv_contains (keys, key)) {
534 g_set_error (error,
535 FWUPD_ERROR,
536 FWUPD_ERROR_NOT_FOUND,
537 "key %s not supported", key);
538 return FALSE;
539 }
540
541 /* modify, effective next reboot */
Richard Hughesd1808aa2019-12-10 15:20:30 +0000542 return fu_config_set_key_value (self->config, key, value, error);
Mario Limonciellobfcf75b2019-04-17 15:05:39 +0100543}
544
Richard Hughes9945edb2017-06-19 10:03:55 +0100545/**
Richard Hughesa6bd5582017-09-07 14:32:22 +0100546 * fu_engine_modify_remote:
547 * @self: A #FuEngine
548 * @remote_id: A remote ID
Richard Hughes4eada342017-10-03 21:20:32 +0100549 * @key: the key, e.g. `Enabled`
550 * @value: the key, e.g. `true`
Richard Hughesa6bd5582017-09-07 14:32:22 +0100551 * @error: A #GError, or %NULL
552 *
Richard Hughes481aa2a2018-09-18 20:51:46 +0100553 * Updates the verification silo entry for a specific device.
Richard Hughesa6bd5582017-09-07 14:32:22 +0100554 *
555 * Returns: %TRUE for success
556 **/
557gboolean
558fu_engine_modify_remote (FuEngine *self,
559 const gchar *remote_id,
560 const gchar *key,
561 const gchar *value,
562 GError **error)
563{
Richard Hughes9bc9deb2020-05-19 19:49:51 +0100564 const gchar *keys[] = {
565 "AutomaticReports",
566 "AutomaticSecurityReports",
567 "Enabled",
568 "FirmwareBaseURI",
569 "MetadataURI",
570 "ReportURI",
571 "SecurityReportURI",
572 NULL,
573 };
Richard Hughesa6bd5582017-09-07 14:32:22 +0100574
575 /* check keys are valid */
576 if (!g_strv_contains (keys, key)) {
577 g_set_error (error,
578 FWUPD_ERROR,
579 FWUPD_ERROR_NOT_FOUND,
580 "key %s not supported", key);
581 return FALSE;
582 }
Richard Hughesd1808aa2019-12-10 15:20:30 +0000583 return fu_remote_list_set_key_value (self->remote_list, remote_id, key, value, error);
Richard Hughesa6bd5582017-09-07 14:32:22 +0100584}
585
586/**
Richard Hughes6b222952018-01-11 10:20:48 +0000587 * fu_engine_modify_device:
588 * @self: A #FuEngine
589 * @device_id: A device ID
590 * @key: the key, e.g. `Flags`
591 * @value: the key, e.g. `reported`
592 * @error: A #GError, or %NULL
593 *
594 * Sets the reported flag for a specific device. This ensures that other
595 * front-end clients for fwupd do not report the same event.
596 *
597 * Returns: %TRUE for success
598 **/
599gboolean
600fu_engine_modify_device (FuEngine *self,
601 const gchar *device_id,
602 const gchar *key,
603 const gchar *value,
604 GError **error)
605{
606 g_autoptr(FuDevice) device = NULL;
607
608 /* find the correct device */
Richard Hughes0b9d9962018-01-12 16:31:28 +0000609 device = fu_history_get_device_by_id (self->history, device_id, error);
Richard Hughes6b222952018-01-11 10:20:48 +0000610 if (device == NULL)
611 return FALSE;
612
613 /* support adding a subset of the device flags */
614 if (g_strcmp0 (key, "Flags") == 0) {
615 FwupdDeviceFlags flag = fwupd_device_flag_from_string (value);
616 if (flag == FWUPD_DEVICE_FLAG_UNKNOWN) {
617 g_set_error (error,
618 FWUPD_ERROR,
619 FWUPD_ERROR_NOT_SUPPORTED,
620 "key %s not a valid flag", key);
621 return FALSE;
622 }
Richard Hughesad54f652018-01-30 17:22:37 +0000623 if (flag != FWUPD_DEVICE_FLAG_REPORTED &&
624 flag != FWUPD_DEVICE_FLAG_NOTIFIED) {
Richard Hughes6b222952018-01-11 10:20:48 +0000625 g_set_error (error,
626 FWUPD_ERROR,
627 FWUPD_ERROR_NOT_SUPPORTED,
628 "flag %s cannot be set from client", key);
629 return FALSE;
630 }
Richard Hughesc0cd0232018-01-31 15:02:00 +0000631 fu_device_add_flag (device, flag);
Richard Hughes0bbef292019-11-01 12:15:15 +0000632 return fu_history_modify_device (self->history, device, error);
Richard Hughes6b222952018-01-11 10:20:48 +0000633 }
634
635 /* others invalid */
636 g_set_error (error,
637 FWUPD_ERROR,
638 FWUPD_ERROR_NOT_SUPPORTED,
639 "key %s not supported", key);
640 return FALSE;
641}
642
Richard Hughes481aa2a2018-09-18 20:51:46 +0100643static const gchar *
644fu_engine_checksum_type_to_string (GChecksumType checksum_type)
645{
646 if (checksum_type == G_CHECKSUM_SHA1)
647 return "sha1";
648 if (checksum_type == G_CHECKSUM_SHA256)
649 return "sha256";
650 if (checksum_type == G_CHECKSUM_SHA512)
651 return "sha512";
652 return "sha1";
653}
654
Richard Hughes6b222952018-01-11 10:20:48 +0000655/**
Richard Hughes9945edb2017-06-19 10:03:55 +0100656 * fu_engine_verify_update:
657 * @self: A #FuEngine
658 * @device_id: A device ID
659 * @error: A #GError, or %NULL
660 *
Richard Hughes481aa2a2018-09-18 20:51:46 +0100661 * Updates the verification silo entry for a specific device.
Richard Hughes9945edb2017-06-19 10:03:55 +0100662 *
663 * Returns: %TRUE for success
664 **/
665gboolean
666fu_engine_verify_update (FuEngine *self, const gchar *device_id, GError **error)
667{
Richard Hughes34834102017-11-21 21:55:00 +0000668 FuPlugin *plugin;
Richard Hughes9945edb2017-06-19 10:03:55 +0100669 GPtrArray *checksums;
Richard Hughes481aa2a2018-09-18 20:51:46 +0100670 GPtrArray *guids;
671 g_autofree gchar *fn = NULL;
672 g_autofree gchar *localstatedir = NULL;
Richard Hughes5a9a6bd2018-09-10 10:02:20 +0100673 g_autoptr(FuDevice) device = NULL;
Richard Hughes9945edb2017-06-19 10:03:55 +0100674 g_autoptr(GFile) file = NULL;
Richard Hughes481aa2a2018-09-18 20:51:46 +0100675 g_autoptr(XbBuilder) builder = xb_builder_new ();
676 g_autoptr(XbBuilderNode) component = NULL;
677 g_autoptr(XbBuilderNode) provides = NULL;
678 g_autoptr(XbBuilderNode) release = NULL;
679 g_autoptr(XbBuilderNode) releases = NULL;
680 g_autoptr(XbSilo) silo = NULL;
Richard Hughes9945edb2017-06-19 10:03:55 +0100681
682 g_return_val_if_fail (FU_IS_ENGINE (self), FALSE);
683 g_return_val_if_fail (device_id != NULL, FALSE);
684 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
685
686 /* check the devices still exists */
Richard Hughes40127542018-01-12 20:25:55 +0000687 device = fu_device_list_get_by_id (self->device_list, device_id, error);
Richard Hughes0a7e7832017-11-22 11:01:13 +0000688 if (device == NULL)
Richard Hughes9945edb2017-06-19 10:03:55 +0100689 return FALSE;
690
Richard Hughes34834102017-11-21 21:55:00 +0000691 /* get the plugin */
Richard Hughese7e95452017-11-22 09:05:53 +0000692 plugin = fu_plugin_list_find_by_name (self->plugin_list,
Richard Hughes0a7e7832017-11-22 11:01:13 +0000693 fu_device_get_plugin (device),
Richard Hughese7e95452017-11-22 09:05:53 +0000694 error);
Richard Hughes34834102017-11-21 21:55:00 +0000695 if (plugin == NULL)
696 return FALSE;
697
Richard Hughes9945edb2017-06-19 10:03:55 +0100698 /* get the checksum */
Richard Hughes0a7e7832017-11-22 11:01:13 +0000699 checksums = fu_device_get_checksums (device);
Richard Hughes9945edb2017-06-19 10:03:55 +0100700 if (checksums->len == 0) {
Richard Hughes0a7e7832017-11-22 11:01:13 +0000701 if (!fu_plugin_runner_verify (plugin, device,
Richard Hughes9945edb2017-06-19 10:03:55 +0100702 FU_PLUGIN_VERIFY_FLAG_NONE,
703 error))
704 return FALSE;
Richard Hughes0a7e7832017-11-22 11:01:13 +0000705 fu_engine_emit_device_changed (self, device);
Richard Hughes9945edb2017-06-19 10:03:55 +0100706 }
707
708 /* we got nothing */
709 if (checksums->len == 0) {
710 g_set_error_literal (error,
711 FWUPD_ERROR,
712 FWUPD_ERROR_NOT_SUPPORTED,
713 "device verification not supported");
714 return FALSE;
715 }
716
Richard Hughes481aa2a2018-09-18 20:51:46 +0100717 /* build XML */
718 component = xb_builder_node_insert (NULL, "component",
719 "type", "firmware",
720 NULL);
721 provides = xb_builder_node_insert (component, "provides", NULL);
722 guids = fu_device_get_guids (device);
723 for (guint i = 0; i < guids->len; i++) {
724 const gchar *guid = g_ptr_array_index (guids, i);
725 g_autoptr(XbBuilderNode) provide = NULL;
726 provide = xb_builder_node_insert (provides, "firmware",
727 "type", "flashed",
728 NULL);
729 xb_builder_node_set_text (provide, guid, -1);
730 }
731 releases = xb_builder_node_insert (component, "releases", NULL);
732 release = xb_builder_node_insert (releases, "release",
733 "version", fu_device_get_version (device),
734 NULL);
735 for (guint i = 0; i < checksums->len; i++) {
736 const gchar *checksum = g_ptr_array_index (checksums, i);
737 GChecksumType kind = fwupd_checksum_guess_kind (checksum);
738 g_autoptr(XbBuilderNode) csum = NULL;
Richard Hughesaaa60c62018-11-07 11:05:50 +0000739 csum = xb_builder_node_insert (release, "checksum",
Richard Hughes481aa2a2018-09-18 20:51:46 +0100740 "type", fu_engine_checksum_type_to_string (kind),
741 "target", "content",
742 NULL);
743 xb_builder_node_set_text (csum, checksum, -1);
744 }
745 xb_builder_import_node (builder, component);
746
747 /* save silo */
748 localstatedir = fu_common_get_path (FU_PATH_KIND_LOCALSTATEDIR_PKG);
749 fn = g_strdup_printf ("%s/verify/%s.xml", localstatedir, device_id);
Richard Hughes11f612c2019-03-15 16:27:18 +0000750 if (!fu_common_mkdir_parent (fn, error))
751 return FALSE;
Richard Hughes481aa2a2018-09-18 20:51:46 +0100752 file = g_file_new_for_path (fn);
753 silo = xb_builder_compile (builder, XB_BUILDER_COMPILE_FLAG_NONE, NULL, error);
754 if (silo == NULL)
755 return FALSE;
756 if (!xb_silo_export_file (silo, file,
757 XB_NODE_EXPORT_FLAG_FORMAT_MULTILINE,
758 NULL, error))
Richard Hughes9945edb2017-06-19 10:03:55 +0100759 return FALSE;
760
Richard Hughes481aa2a2018-09-18 20:51:46 +0100761 /* success */
762 return TRUE;
Richard Hughes9945edb2017-06-19 10:03:55 +0100763}
764
Mario Limonciello4f24d0b2019-01-26 01:19:59 -0600765XbNode *
766fu_engine_get_component_by_guids (FuEngine *self, FuDevice *device)
Richard Hughes9945edb2017-06-19 10:03:55 +0100767{
768 GPtrArray *guids = fu_device_get_guids (device);
Richard Hughesab1bc892018-11-15 15:04:35 +0000769 g_autoptr(GString) xpath = g_string_new (NULL);
770 g_autoptr(XbNode) component = NULL;
Richard Hughes9945edb2017-06-19 10:03:55 +0100771 for (guint i = 0; i < guids->len; i++) {
Richard Hughes481aa2a2018-09-18 20:51:46 +0100772 const gchar *guid = g_ptr_array_index (guids, i);
Richard Hughesab1bc892018-11-15 15:04:35 +0000773 xb_string_append_union (xpath,
774 "components/component/"
775 "provides/firmware[@type='flashed'][text()='%s']/"
776 "../..", guid);
Richard Hughes9945edb2017-06-19 10:03:55 +0100777 }
Mario Limonciello4f24d0b2019-01-26 01:19:59 -0600778 component = xb_silo_query_first (self->silo, xpath->str, NULL);
Richard Hughesab1bc892018-11-15 15:04:35 +0000779 if (component != NULL)
780 return g_steal_pointer (&component);
Richard Hughes9945edb2017-06-19 10:03:55 +0100781 return NULL;
782}
783
784/**
785 * fu_engine_verify:
786 * @self: A #FuEngine
787 * @device_id: A device ID
788 * @error: A #GError, or %NULL
789 *
Richard Hughes481aa2a2018-09-18 20:51:46 +0100790 * Verifies a device firmware checksum using the verification silo entry.
Richard Hughes9945edb2017-06-19 10:03:55 +0100791 *
792 * Returns: %TRUE for success
793 **/
794gboolean
795fu_engine_verify (FuEngine *self, const gchar *device_id, GError **error)
796{
Richard Hughes34834102017-11-21 21:55:00 +0000797 FuPlugin *plugin;
Richard Hughes9945edb2017-06-19 10:03:55 +0100798 GPtrArray *checksums;
Richard Hughes481aa2a2018-09-18 20:51:46 +0100799 const gchar *version;
800 g_autofree gchar *fn = NULL;
801 g_autofree gchar *localstatedir = NULL;
Richard Hughes5a9a6bd2018-09-10 10:02:20 +0100802 g_autoptr(FuDevice) device = NULL;
Richard Hughes481aa2a2018-09-18 20:51:46 +0100803 g_autoptr(GFile) file = NULL;
Richard Hughes45bbfc92018-12-12 10:57:08 +0000804 g_autoptr(GString) xpath_csum = g_string_new (NULL);
Richard Hughes481aa2a2018-09-18 20:51:46 +0100805 g_autoptr(XbNode) csum = NULL;
806 g_autoptr(XbNode) release = NULL;
807 g_autoptr(XbSilo) silo = xb_silo_new ();
Richard Hughes9945edb2017-06-19 10:03:55 +0100808
809 g_return_val_if_fail (FU_IS_ENGINE (self), FALSE);
810 g_return_val_if_fail (device_id != NULL, FALSE);
811 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
812
813 /* check the id exists */
Richard Hughes40127542018-01-12 20:25:55 +0000814 device = fu_device_list_get_by_id (self->device_list, device_id, error);
Richard Hughes0a7e7832017-11-22 11:01:13 +0000815 if (device == NULL)
Richard Hughes9945edb2017-06-19 10:03:55 +0100816 return FALSE;
817
Richard Hughes34834102017-11-21 21:55:00 +0000818 /* get the plugin */
Richard Hughese7e95452017-11-22 09:05:53 +0000819 plugin = fu_plugin_list_find_by_name (self->plugin_list,
Richard Hughes0a7e7832017-11-22 11:01:13 +0000820 fu_device_get_plugin (device),
Richard Hughese7e95452017-11-22 09:05:53 +0000821 error);
Richard Hughes34834102017-11-21 21:55:00 +0000822 if (plugin == NULL)
823 return FALSE;
824
Mario Limonciello8fa0b382019-10-14 07:36:04 -0500825 /* update the device firmware hashes if possible */
826 if (fu_device_has_flag (device, FWUPD_DEVICE_FLAG_CAN_VERIFY_IMAGE)) {
827 if (!fu_plugin_runner_verify (plugin, device,
828 FU_PLUGIN_VERIFY_FLAG_NONE, error))
829 return FALSE;
830 }
Richard Hughes9945edb2017-06-19 10:03:55 +0100831
832 /* find component in metadata */
Richard Hughes481aa2a2018-09-18 20:51:46 +0100833 version = fu_device_get_version (device);
834 localstatedir = fu_common_get_path (FU_PATH_KIND_LOCALSTATEDIR_PKG);
835 fn = g_strdup_printf ("%s/verify/%s.xml", localstatedir, device_id);
836 file = g_file_new_for_path (fn);
837 if (g_file_query_exists (file, NULL)) {
838 g_autofree gchar *xpath = NULL;
839 g_autoptr(XbBuilder) builder = xb_builder_new ();
840 g_autoptr(XbBuilderSource) source = xb_builder_source_new ();
841 if (!xb_builder_source_load_file (source, file,
842 XB_BUILDER_SOURCE_FLAG_NONE,
843 NULL, error))
844 return FALSE;
845 xb_builder_import_source (builder, source);
846 silo = xb_builder_compile (builder,
847 XB_BUILDER_COMPILE_FLAG_NONE,
848 NULL, error);
849 if (silo == NULL)
850 return FALSE;
851 xpath = g_strdup_printf ("component/releases/release[@version='%s']", version);
852 release = xb_silo_query_first (silo, xpath, NULL);
Richard Hughes9945edb2017-06-19 10:03:55 +0100853 }
854
Richard Hughes481aa2a2018-09-18 20:51:46 +0100855 /* try again with the system metadata */
Richard Hughes9945edb2017-06-19 10:03:55 +0100856 if (release == NULL) {
Richard Hughes481aa2a2018-09-18 20:51:46 +0100857 GPtrArray *guids = fu_device_get_guids (device);
Mario Limonciello91d36092019-10-14 08:44:39 -0500858 FwupdVersionFormat fmt = fu_device_get_version_format (device);
Richard Hughes481aa2a2018-09-18 20:51:46 +0100859 for (guint i = 0; i < guids->len; i++) {
860 const gchar *guid = g_ptr_array_index (guids, i);
861 g_autofree gchar *xpath2 = NULL;
Mario Limonciello91d36092019-10-14 08:44:39 -0500862 g_autoptr(GPtrArray) releases = NULL;
Richard Hughes481aa2a2018-09-18 20:51:46 +0100863 xpath2 = g_strdup_printf ("components/component/"
864 "provides/firmware[@type='flashed'][text()='%s']/"
Mario Limonciello91d36092019-10-14 08:44:39 -0500865 "../../releases/release",
866 guid);
867 releases = xb_silo_query (self->silo, xpath2, 0, error);
868 if (releases == NULL)
869 return FALSE;
870 for (guint j = 0; j < releases->len; j++) {
871 XbNode *rel = g_ptr_array_index (releases, j);
872 const gchar *rel_ver = xb_node_get_attr (rel, "version");
873 g_autofree gchar *tmp_ver = fu_common_version_parse_from_format (rel_ver, fmt);
Richard Hughes9a680842020-02-20 11:11:13 +0000874 if (fu_common_vercmp_full (tmp_ver, version, fmt) == 0) {
Mario Limonciello91d36092019-10-14 08:44:39 -0500875 release = g_object_ref (rel);
876 break;
877 }
878 }
Richard Hughes481aa2a2018-09-18 20:51:46 +0100879 if (release != NULL)
880 break;
Richard Hughes9945edb2017-06-19 10:03:55 +0100881 }
Richard Hughes9945edb2017-06-19 10:03:55 +0100882 }
883 if (release == NULL) {
884 g_set_error (error,
885 FWUPD_ERROR,
886 FWUPD_ERROR_NOT_FOUND,
Mario Limonciello8fa0b382019-10-14 07:36:04 -0500887 "No release found for version %s", version);
Richard Hughes9945edb2017-06-19 10:03:55 +0100888 return FALSE;
889 }
890
Richard Hughes9945edb2017-06-19 10:03:55 +0100891 /* get the matching checksum */
Richard Hughes0a7e7832017-11-22 11:01:13 +0000892 checksums = fu_device_get_checksums (device);
Richard Hughes9945edb2017-06-19 10:03:55 +0100893 if (checksums->len == 0) {
894 g_set_error (error,
895 FWUPD_ERROR,
896 FWUPD_ERROR_NOT_FOUND,
897 "No device checksums for %s", version);
898 return FALSE;
899 }
Richard Hughes45bbfc92018-12-12 10:57:08 +0000900
901 /* do any of the checksums in the release match any in the device */
Richard Hughes9945edb2017-06-19 10:03:55 +0100902 for (guint j = 0; j < checksums->len; j++) {
903 const gchar *hash_tmp = g_ptr_array_index (checksums, j);
Richard Hughes45bbfc92018-12-12 10:57:08 +0000904 xb_string_append_union (xpath_csum,
905 "checksum[@target='device'][text()='%s']",
906 hash_tmp);
907 xb_string_append_union (xpath_csum,
908 "checksum[@target='content'][text()='%s']",
909 hash_tmp);
910 }
911 csum = xb_node_query_first (release, xpath_csum->str, NULL);
912 if (csum == NULL) {
913 g_autoptr(GString) checksums_device = g_string_new (NULL);
914 g_autoptr(GString) checksums_metadata = g_string_new (NULL);
915 g_autoptr(GPtrArray) csums = NULL;
916 g_autoptr(GString) xpath = g_string_new (NULL);
917
918 /* get all checksums to display a useful error */
919 xb_string_append_union (xpath, "checksum[@target='device']");
Mario Limonciello2a0e20b2019-10-14 12:09:35 -0500920 if (fu_device_has_flag (device, FWUPD_DEVICE_FLAG_CAN_VERIFY_IMAGE))
921 xb_string_append_union (xpath, "checksum[@target='content']");
Richard Hughes45bbfc92018-12-12 10:57:08 +0000922 csums = xb_node_query (release, xpath->str, 0, NULL);
923 if (csums == NULL) {
924 g_set_error (error,
925 FWUPD_ERROR,
926 FWUPD_ERROR_NOT_FOUND,
Mario Limonciello2a0e20b2019-10-14 12:09:35 -0500927 "No stored checksums for %s",
Richard Hughes45bbfc92018-12-12 10:57:08 +0000928 version);
929 return FALSE;
Richard Hughes9945edb2017-06-19 10:03:55 +0100930 }
Richard Hughes45bbfc92018-12-12 10:57:08 +0000931 for (guint i = 0; i < csums->len; i++) {
932 XbNode *csum_tmp = g_ptr_array_index (csums, i);
933 xb_string_append_union (checksums_metadata,
934 "%s", xb_node_get_text (csum_tmp));
935 }
936 for (guint i = 0; i < checksums->len; i++) {
937 const gchar *hash_tmp = g_ptr_array_index (checksums, i);
938 xb_string_append_union (checksums_device, "%s", hash_tmp);
939 }
Richard Hughes9945edb2017-06-19 10:03:55 +0100940 g_set_error (error,
941 FWUPD_ERROR,
942 FWUPD_ERROR_NOT_FOUND,
Mario Limonciello2a0e20b2019-10-14 12:09:35 -0500943 "For %s %s expected %s, got %s",
944 fu_device_get_name (device),
Richard Hughes9945edb2017-06-19 10:03:55 +0100945 version,
Richard Hughes45bbfc92018-12-12 10:57:08 +0000946 checksums_metadata->str,
947 checksums_device->str);
Richard Hughes9945edb2017-06-19 10:03:55 +0100948 return FALSE;
949 }
950
951 /* success */
952 return TRUE;
953}
954
Richard Hughes481aa2a2018-09-18 20:51:46 +0100955static gboolean
Richard Hughes9a680842020-02-20 11:11:13 +0000956fu_engine_require_vercmp (XbNode *req,
957 const gchar *version,
958 FwupdVersionFormat fmt,
959 GError **error)
Richard Hughes650dade2017-12-14 14:43:11 +0000960{
Richard Hughes481aa2a2018-09-18 20:51:46 +0100961 gboolean ret = FALSE;
962 gint rc = 0;
963 const gchar *tmp = xb_node_get_attr (req, "compare");
964 const gchar *version_req = xb_node_get_attr (req, "version");
965
966 if (g_strcmp0 (tmp, "eq") == 0) {
Richard Hughes9a680842020-02-20 11:11:13 +0000967 rc = fu_common_vercmp_full (version, version_req, fmt);
Richard Hughes481aa2a2018-09-18 20:51:46 +0100968 ret = rc == 0;
969 } else if (g_strcmp0 (tmp, "ne") == 0) {
Richard Hughes9a680842020-02-20 11:11:13 +0000970 rc = fu_common_vercmp_full (version, version_req, fmt);
Richard Hughes481aa2a2018-09-18 20:51:46 +0100971 ret = rc != 0;
972 } else if (g_strcmp0 (tmp, "lt") == 0) {
Richard Hughes9a680842020-02-20 11:11:13 +0000973 rc = fu_common_vercmp_full (version, version_req, fmt);
Richard Hughes481aa2a2018-09-18 20:51:46 +0100974 ret = rc < 0;
975 } else if (g_strcmp0 (tmp, "gt") == 0) {
Richard Hughes9a680842020-02-20 11:11:13 +0000976 rc = fu_common_vercmp_full (version, version_req, fmt);
Richard Hughes481aa2a2018-09-18 20:51:46 +0100977 ret = rc > 0;
978 } else if (g_strcmp0 (tmp, "le") == 0) {
Richard Hughes9a680842020-02-20 11:11:13 +0000979 rc = fu_common_vercmp_full (version, version_req, fmt);
Richard Hughes481aa2a2018-09-18 20:51:46 +0100980 ret = rc <= 0;
981 } else if (g_strcmp0 (tmp, "ge") == 0) {
Richard Hughes9a680842020-02-20 11:11:13 +0000982 rc = fu_common_vercmp_full (version, version_req, fmt);
Richard Hughes481aa2a2018-09-18 20:51:46 +0100983 ret = rc >= 0;
984 } else if (g_strcmp0 (tmp, "glob") == 0) {
Richard Hughes5c508de2019-11-22 09:57:34 +0000985 ret = fu_common_fnmatch (version_req, version);
Richard Hughes481aa2a2018-09-18 20:51:46 +0100986 } else if (g_strcmp0 (tmp, "regex") == 0) {
987 ret = g_regex_match_simple (version_req, version, 0, 0);
988 } else {
989 g_set_error (error,
990 FWUPD_ERROR,
991 FWUPD_ERROR_NOT_SUPPORTED,
992 "failed to compare [%s] and [%s]",
993 version_req,
994 version);
995 return FALSE;
Richard Hughes650dade2017-12-14 14:43:11 +0000996 }
Richard Hughes481aa2a2018-09-18 20:51:46 +0100997
998 /* set error */
999 if (!ret) {
1000 g_set_error (error,
1001 FWUPD_ERROR,
1002 FWUPD_ERROR_INTERNAL,
1003 "failed predicate [%s %s %s]",
1004 version_req, tmp, version);
1005 }
1006 return ret;
Richard Hughes650dade2017-12-14 14:43:11 +00001007}
1008
Richard Hughes9945edb2017-06-19 10:03:55 +01001009static gboolean
Richard Hughesb62c3a42019-04-08 12:13:47 +01001010fu_engine_check_requirement_not_child (FuEngine *self, XbNode *req,
1011 FuDevice *device, GError **error)
1012{
1013 GPtrArray *children = fu_device_get_children (device);
1014
1015 /* only <firmware> supported */
1016 if (g_strcmp0 (xb_node_get_element (req), "firmware") != 0) {
1017 g_set_error (error,
1018 FWUPD_ERROR,
1019 FWUPD_ERROR_NOT_SUPPORTED,
1020 "cannot handle not-child %s requirement",
1021 xb_node_get_element (req));
1022 return FALSE;
1023 }
1024
1025 /* check each child */
1026 for (guint i = 0; i < children->len; i++) {
1027 FuDevice *child = g_ptr_array_index (children, i);
1028 const gchar *version = fu_device_get_version (child);
Richard Hughesec14e4b2019-11-01 12:07:10 +00001029 if (version == NULL) {
1030 g_set_error (error,
1031 FWUPD_ERROR,
1032 FWUPD_ERROR_NOT_SUPPORTED,
1033 "no version provided by %s, child of %s",
1034 fu_device_get_name (child),
1035 fu_device_get_name (device));
1036 return FALSE;
1037 }
Richard Hughes9a680842020-02-20 11:11:13 +00001038 if (fu_engine_require_vercmp (req, version,
1039 fu_device_get_version_format (child),
1040 NULL)) {
Richard Hughesb62c3a42019-04-08 12:13:47 +01001041 g_set_error (error,
1042 FWUPD_ERROR,
1043 FWUPD_ERROR_NOT_SUPPORTED,
1044 "Not compatible with child device version %s",
1045 version);
1046 return FALSE;
1047 }
1048 }
1049 return TRUE;
1050}
1051
1052static gboolean
Richard Hughes481aa2a2018-09-18 20:51:46 +01001053fu_engine_check_requirement_firmware (FuEngine *self, XbNode *req,
Richard Hughes0eb123b2018-04-19 12:00:04 +01001054 FuDevice *device, GError **error)
Richard Hughes9945edb2017-06-19 10:03:55 +01001055{
Richard Hughesf7006d22019-11-14 13:42:52 +00001056 guint64 depth;
1057 g_autoptr(FuDevice) device_actual = g_object_ref (device);
Richard Hughes88adcbe2017-11-21 14:33:56 +00001058 g_autoptr(GError) error_local = NULL;
Richard Hughes9945edb2017-06-19 10:03:55 +01001059
Richard Hughesf7006d22019-11-14 13:42:52 +00001060 /* look at the parent device */
1061 depth = xb_node_get_attr_as_uint (req, "depth");
1062 if (depth != G_MAXUINT64) {
1063 for (guint64 i = 0; i < depth; i++) {
1064 FuDevice *device_tmp = fu_device_get_parent (device_actual);
1065 if (device_actual == NULL) {
1066 g_set_error (error,
1067 FWUPD_ERROR,
1068 FWUPD_ERROR_NOT_SUPPORTED,
1069 "No parent device for %s "
1070 "(%" G_GUINT64_FORMAT "/%" G_GUINT64_FORMAT ")",
1071 fu_device_get_name (device_actual), i, depth);
1072 return FALSE;
1073 }
1074 g_set_object (&device_actual, device_tmp);
1075 }
1076 }
1077
Richard Hughes0eb123b2018-04-19 12:00:04 +01001078 /* old firmware version */
Richard Hughes481aa2a2018-09-18 20:51:46 +01001079 if (xb_node_get_text (req) == NULL) {
Richard Hughesf7006d22019-11-14 13:42:52 +00001080 const gchar *version = fu_device_get_version (device_actual);
Richard Hughes9a680842020-02-20 11:11:13 +00001081 if (!fu_engine_require_vercmp (req, version,
1082 fu_device_get_version_format (device_actual),
1083 &error_local)) {
Richard Hughes481aa2a2018-09-18 20:51:46 +01001084 if (g_strcmp0 (xb_node_get_attr (req, "compare"), "ge") == 0) {
Richard Hughes88adcbe2017-11-21 14:33:56 +00001085 g_set_error (error,
1086 FWUPD_ERROR,
1087 FWUPD_ERROR_INVALID_FILE,
1088 "Not compatible with firmware version %s, requires >= %s",
Richard Hughes481aa2a2018-09-18 20:51:46 +01001089 version, xb_node_get_attr (req, "version"));
Richard Hughes88adcbe2017-11-21 14:33:56 +00001090 } else {
1091 g_set_error (error,
1092 FWUPD_ERROR,
1093 FWUPD_ERROR_INVALID_FILE,
1094 "Not compatible with firmware version: %s",
1095 error_local->message);
1096 }
1097 return FALSE;
1098 }
Richard Hughes0eb123b2018-04-19 12:00:04 +01001099 return TRUE;
1100 }
Richard Hughes88adcbe2017-11-21 14:33:56 +00001101
Richard Hughes0eb123b2018-04-19 12:00:04 +01001102 /* bootloader version */
Richard Hughes481aa2a2018-09-18 20:51:46 +01001103 if (g_strcmp0 (xb_node_get_text (req), "bootloader") == 0) {
Richard Hughesf7006d22019-11-14 13:42:52 +00001104 const gchar *version = fu_device_get_version_bootloader (device_actual);
Richard Hughes9a680842020-02-20 11:11:13 +00001105 if (!fu_engine_require_vercmp (req, version,
1106 fu_device_get_version_format (device_actual),
1107 &error_local)) {
Richard Hughes481aa2a2018-09-18 20:51:46 +01001108 if (g_strcmp0 (xb_node_get_attr (req, "compare"), "ge") == 0) {
Richard Hughes88adcbe2017-11-21 14:33:56 +00001109 g_set_error (error,
1110 FWUPD_ERROR,
Mario Limoncielloc23e6122019-12-12 14:30:27 -06001111 FWUPD_ERROR_NOT_SUPPORTED,
Richard Hughes88adcbe2017-11-21 14:33:56 +00001112 "Not compatible with bootloader version %s, requires >= %s",
Mario Limoncielloc23e6122019-12-12 14:30:27 -06001113 version, xb_node_get_attr (req, "version"));
1114
Richard Hughes88adcbe2017-11-21 14:33:56 +00001115 } else {
Mario Limoncielloc23e6122019-12-12 14:30:27 -06001116 g_debug ("Bootloader is not compatible: %s", error_local->message);
1117 g_set_error_literal (error,
1118 FWUPD_ERROR,
1119 FWUPD_ERROR_NOT_SUPPORTED,
1120 "Bootloader is not compatible");
Richard Hughes88adcbe2017-11-21 14:33:56 +00001121 }
1122 return FALSE;
1123 }
Richard Hughes0eb123b2018-04-19 12:00:04 +01001124 return TRUE;
1125 }
Richard Hughes88adcbe2017-11-21 14:33:56 +00001126
Richard Hughes0eb123b2018-04-19 12:00:04 +01001127 /* vendor ID */
Richard Hughes12843af2019-12-09 10:30:19 +00001128 if (g_strcmp0 (xb_node_get_text (req), "vendor-id") == 0 &&
1129 fu_device_get_vendor_id (device_actual) != NULL) {
Richard Hughesf7006d22019-11-14 13:42:52 +00001130 const gchar *version = fu_device_get_vendor_id (device_actual);
Richard Hughes9a680842020-02-20 11:11:13 +00001131 if (!fu_engine_require_vercmp (req, version,
1132 fu_device_get_version_format (device_actual),
1133 &error_local)) {
Richard Hughes38857892019-12-09 10:32:00 +00001134 g_set_error (error,
1135 FWUPD_ERROR,
1136 FWUPD_ERROR_INVALID_FILE,
1137 "Not compatible with vendor: %s",
1138 error_local->message);
Richard Hughes88adcbe2017-11-21 14:33:56 +00001139 return FALSE;
1140 }
Richard Hughes0eb123b2018-04-19 12:00:04 +01001141 return TRUE;
Richard Hughes9945edb2017-06-19 10:03:55 +01001142 }
1143
Richard Hughesb62c3a42019-04-08 12:13:47 +01001144 /* child version */
1145 if (g_strcmp0 (xb_node_get_text (req), "not-child") == 0)
Richard Hughesf7006d22019-11-14 13:42:52 +00001146 return fu_engine_check_requirement_not_child (self, req, device_actual, error);
Richard Hughesb62c3a42019-04-08 12:13:47 +01001147
Richard Hughes12c84992018-10-02 11:07:28 +01001148 /* another device */
Richard Hughes592baed2019-02-03 18:30:24 +00001149 if (fwupd_guid_is_valid (xb_node_get_text (req))) {
Richard Hughes481aa2a2018-09-18 20:51:46 +01001150 const gchar *guid = xb_node_get_text (req);
Richard Hughes12c84992018-10-02 11:07:28 +01001151 const gchar *version;
Richard Hughes12c84992018-10-02 11:07:28 +01001152
1153 /* find if the other device exists */
Richard Hughesf7006d22019-11-14 13:42:52 +00001154 if (depth == G_MAXUINT64) {
1155 g_autoptr(FuDevice) device_tmp = NULL;
1156 device_tmp = fu_device_list_get_by_guid (self->device_list, guid, error);
1157 if (device_tmp == NULL)
1158 return FALSE;
1159 g_set_object (&device_actual, device_tmp);
1160
1161 /* verify the parent device has the GUID */
1162 } else {
1163 if (!fu_device_has_guid (device_actual, guid)) {
1164 g_set_error (error,
1165 FWUPD_ERROR,
1166 FWUPD_ERROR_NOT_SUPPORTED,
1167 "No GUID of %s on parent device %s",
1168 guid, fu_device_get_name (device_actual));
1169 return FALSE;
1170 }
1171 }
Richard Hughes12c84992018-10-02 11:07:28 +01001172
1173 /* get the version of the other device */
Richard Hughesf7006d22019-11-14 13:42:52 +00001174 version = fu_device_get_version (device_actual);
Richard Hughesaaf0ce72019-07-16 08:43:57 +01001175 if (version != NULL &&
Richard Hughesf7006d22019-11-14 13:42:52 +00001176 xb_node_get_attr (req, "compare") != NULL &&
Richard Hughes9a680842020-02-20 11:11:13 +00001177 !fu_engine_require_vercmp (req, version,
1178 fu_device_get_version_format (device_actual),
1179 &error_local)) {
Richard Hughes481aa2a2018-09-18 20:51:46 +01001180 if (g_strcmp0 (xb_node_get_attr (req, "compare"), "ge") == 0) {
Richard Hughes12c84992018-10-02 11:07:28 +01001181 g_set_error (error,
1182 FWUPD_ERROR,
1183 FWUPD_ERROR_INVALID_FILE,
1184 "Not compatible with %s version %s, requires >= %s",
Richard Hughesf7006d22019-11-14 13:42:52 +00001185 fu_device_get_name (device_actual),
Richard Hughes12c84992018-10-02 11:07:28 +01001186 version,
Richard Hughes481aa2a2018-09-18 20:51:46 +01001187 xb_node_get_attr (req, "version"));
Richard Hughes12c84992018-10-02 11:07:28 +01001188 } else {
1189 g_set_error (error,
1190 FWUPD_ERROR,
1191 FWUPD_ERROR_INVALID_FILE,
1192 "Not compatible with %s: %s",
Richard Hughesf7006d22019-11-14 13:42:52 +00001193 fu_device_get_name (device_actual),
Richard Hughes12c84992018-10-02 11:07:28 +01001194 error_local->message);
1195 }
1196 return FALSE;
1197 }
1198 return TRUE;
1199
1200 }
1201
Richard Hughes0eb123b2018-04-19 12:00:04 +01001202 /* not supported */
1203 g_set_error (error,
1204 FWUPD_ERROR,
1205 FWUPD_ERROR_NOT_SUPPORTED,
Richard Hughes481aa2a2018-09-18 20:51:46 +01001206 "cannot handle firmware requirement '%s'",
1207 xb_node_get_text (req));
Richard Hughes0eb123b2018-04-19 12:00:04 +01001208 return FALSE;
Richard Hughes9945edb2017-06-19 10:03:55 +01001209}
Richard Hughes9945edb2017-06-19 10:03:55 +01001210
1211static gboolean
Richard Hughes481aa2a2018-09-18 20:51:46 +01001212fu_engine_check_requirement_id (FuEngine *self, XbNode *req, GError **error)
Richard Hughes2ec78d62017-11-03 21:48:54 +00001213{
Richard Hughes0eb123b2018-04-19 12:00:04 +01001214 g_autoptr(GError) error_local = NULL;
1215 const gchar *version = g_hash_table_lookup (self->runtime_versions,
Richard Hughes481aa2a2018-09-18 20:51:46 +01001216 xb_node_get_text (req));
Richard Hughes0eb123b2018-04-19 12:00:04 +01001217 if (version == NULL) {
1218 g_set_error (error,
1219 FWUPD_ERROR,
1220 FWUPD_ERROR_NOT_FOUND,
1221 "no version available for %s",
Richard Hughes481aa2a2018-09-18 20:51:46 +01001222 xb_node_get_text (req));
Richard Hughes0eb123b2018-04-19 12:00:04 +01001223 return FALSE;
1224 }
Richard Hughes9a680842020-02-20 11:11:13 +00001225 if (!fu_engine_require_vercmp (req, version, FWUPD_VERSION_FORMAT_UNKNOWN, &error_local)) {
Richard Hughes481aa2a2018-09-18 20:51:46 +01001226 if (g_strcmp0 (xb_node_get_attr (req, "compare"), "ge") == 0) {
Richard Hughes2ec78d62017-11-03 21:48:54 +00001227 g_set_error (error,
1228 FWUPD_ERROR,
1229 FWUPD_ERROR_INVALID_FILE,
Richard Hughes0eb123b2018-04-19 12:00:04 +01001230 "Not compatible with %s version %s, requires >= %s",
Richard Hughes481aa2a2018-09-18 20:51:46 +01001231 xb_node_get_text (req), version,
1232 xb_node_get_attr (req, "version"));
Richard Hughes0eb123b2018-04-19 12:00:04 +01001233 } else {
1234 g_set_error (error,
1235 FWUPD_ERROR,
1236 FWUPD_ERROR_INVALID_FILE,
1237 "Not compatible with %s version: %s",
Richard Hughes481aa2a2018-09-18 20:51:46 +01001238 xb_node_get_text (req), error_local->message);
Richard Hughes2ec78d62017-11-03 21:48:54 +00001239 }
Richard Hughes0eb123b2018-04-19 12:00:04 +01001240 return FALSE;
Richard Hughes2ec78d62017-11-03 21:48:54 +00001241 }
1242
Richard Hughes0eb123b2018-04-19 12:00:04 +01001243 g_debug ("requirement %s %s %s on %s passed",
Richard Hughes481aa2a2018-09-18 20:51:46 +01001244 xb_node_get_attr (req, "version"),
1245 xb_node_get_attr (req, "compare"),
1246 version, xb_node_get_text (req));
Richard Hughes2ec78d62017-11-03 21:48:54 +00001247 return TRUE;
1248}
Richard Hughes2ec78d62017-11-03 21:48:54 +00001249
1250static gboolean
Richard Hughes481aa2a2018-09-18 20:51:46 +01001251fu_engine_check_requirement_hardware (FuEngine *self, XbNode *req, GError **error)
Richard Hughes0eb123b2018-04-19 12:00:04 +01001252{
Richard Hughes3d71c162018-04-30 16:40:44 +01001253 g_auto(GStrv) hwid_split = NULL;
1254
1255 /* split and treat as OR */
Richard Hughes481aa2a2018-09-18 20:51:46 +01001256 hwid_split = g_strsplit (xb_node_get_text (req), "|", -1);
Richard Hughes3d71c162018-04-30 16:40:44 +01001257 for (guint i = 0; hwid_split[i] != NULL; i++) {
1258 if (fu_hwids_has_guid (self->hwids, hwid_split[i])) {
1259 g_debug ("HWID provided %s", hwid_split[i]);
1260 return TRUE;
1261 }
Richard Hughes0eb123b2018-04-19 12:00:04 +01001262 }
Richard Hughes3d71c162018-04-30 16:40:44 +01001263
1264 /* nothing matched */
1265 g_set_error (error,
1266 FWUPD_ERROR,
1267 FWUPD_ERROR_INVALID_FILE,
1268 "no HWIDs matched %s",
Richard Hughes481aa2a2018-09-18 20:51:46 +01001269 xb_node_get_text (req));
Richard Hughes3d71c162018-04-30 16:40:44 +01001270 return FALSE;
Richard Hughes0eb123b2018-04-19 12:00:04 +01001271}
1272
1273static gboolean
Richard Hughes481aa2a2018-09-18 20:51:46 +01001274fu_engine_check_requirement (FuEngine *self, XbNode *req, FuDevice *device, GError **error)
Richard Hughes0eb123b2018-04-19 12:00:04 +01001275{
1276 /* ensure component requirement */
Richard Hughes481aa2a2018-09-18 20:51:46 +01001277 if (g_strcmp0 (xb_node_get_element (req), "id") == 0)
Richard Hughes0eb123b2018-04-19 12:00:04 +01001278 return fu_engine_check_requirement_id (self, req, error);
1279
1280 /* ensure firmware requirement */
Richard Hughes481aa2a2018-09-18 20:51:46 +01001281 if (g_strcmp0 (xb_node_get_element (req), "firmware") == 0) {
Richard Hughes881f6242018-08-06 11:03:06 +01001282 if (device == NULL)
1283 return TRUE;
Richard Hughes0eb123b2018-04-19 12:00:04 +01001284 return fu_engine_check_requirement_firmware (self, req, device, error);
Richard Hughes881f6242018-08-06 11:03:06 +01001285 }
Richard Hughes0eb123b2018-04-19 12:00:04 +01001286
1287 /* ensure hardware requirement */
Richard Hughes481aa2a2018-09-18 20:51:46 +01001288 if (g_strcmp0 (xb_node_get_element (req), "hardware") == 0)
Richard Hughes0eb123b2018-04-19 12:00:04 +01001289 return fu_engine_check_requirement_hardware (self, req, error);
1290
1291 /* not supported */
1292 g_set_error (error,
1293 FWUPD_ERROR,
1294 FWUPD_ERROR_NOT_SUPPORTED,
1295 "cannot handle requirement type %s",
Richard Hughes481aa2a2018-09-18 20:51:46 +01001296 xb_node_get_element (req));
Richard Hughes0eb123b2018-04-19 12:00:04 +01001297 return FALSE;
1298}
1299
1300gboolean
Richard Hughes1d1f5cf2018-05-19 23:06:03 +01001301fu_engine_check_requirements (FuEngine *self, FuInstallTask *task,
1302 FwupdInstallFlags flags, GError **error)
Richard Hughes9945edb2017-06-19 10:03:55 +01001303{
Richard Hughes1d1f5cf2018-05-19 23:06:03 +01001304 FuDevice *device = fu_install_task_get_device (task);
Richard Hughes481aa2a2018-09-18 20:51:46 +01001305 g_autoptr(GError) error_local = NULL;
1306 g_autoptr(GPtrArray) reqs = NULL;
Richard Hughes1d1f5cf2018-05-19 23:06:03 +01001307
1308 /* all install task checks require a device */
1309 if (device != NULL) {
1310 if (!fu_install_task_check_requirements (task, flags, error))
1311 return FALSE;
1312 }
1313
1314 /* do engine checks */
Richard Hughes481aa2a2018-09-18 20:51:46 +01001315 reqs = xb_node_query (fu_install_task_get_component (task),
1316 "requires/*", 0, &error_local);
1317 if (reqs == NULL) {
1318 if (g_error_matches (error_local, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
1319 return TRUE;
1320 if (g_error_matches (error_local, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT))
1321 return TRUE;
1322 g_propagate_error (error, g_steal_pointer (&error_local));
1323 return FALSE;
1324 }
Richard Hughes0eb123b2018-04-19 12:00:04 +01001325 for (guint i = 0; i < reqs->len; i++) {
Richard Hughes481aa2a2018-09-18 20:51:46 +01001326 XbNode *req = g_ptr_array_index (reqs, i);
Richard Hughes0eb123b2018-04-19 12:00:04 +01001327 if (!fu_engine_check_requirement (self, req, device, error))
1328 return FALSE;
Richard Hughes9945edb2017-06-19 10:03:55 +01001329 }
Richard Hughes9945edb2017-06-19 10:03:55 +01001330 return TRUE;
1331}
1332
Richard Hughes75b965d2018-11-15 13:51:21 +00001333void
1334fu_engine_idle_reset (FuEngine *self)
1335{
1336 fu_idle_reset (self->idle);
1337}
1338
Richard Hughes9945edb2017-06-19 10:03:55 +01001339static gchar *
Richard Hughes59c2ebe2018-01-12 09:47:40 +00001340fu_engine_get_boot_time (void)
1341{
1342 g_autofree gchar *buf = NULL;
1343 g_auto(GStrv) lines = NULL;
1344 if (!g_file_get_contents ("/proc/stat", &buf, NULL, NULL))
1345 return NULL;
1346 lines = g_strsplit (buf, "\n", -1);
1347 for (guint i = 0; lines[i] != NULL; i++) {
1348 if (g_str_has_prefix (lines[i], "btime "))
1349 return g_strdup (lines[i] + 6);
1350 }
1351 return NULL;
1352}
1353
Richard Hughes6ecc4ca2020-05-20 18:42:46 +01001354static gboolean
1355fu_engine_get_report_metadata_os_release (GHashTable *hash, GError **error)
Richard Hughes473c5202018-01-11 21:06:16 +00001356{
Richard Hughes6ecc4ca2020-05-20 18:42:46 +01001357 g_autoptr(GHashTable) os_release = NULL;
1358 struct {
1359 const gchar *key;
1360 const gchar *val;
1361 } distro_kv[] = {
1362 { "ID", "DistroId" },
1363 { "VERSION_ID", "DistroVersion" },
1364 { "VARIANT_ID", "DistroVariant" },
1365 { NULL, NULL }
1366 };
1367
1368 /* get all required os-release keys */
1369 os_release = fwupd_get_os_release (error);
1370 if (os_release == NULL)
1371 return FALSE;
1372 for (guint i = 0; distro_kv[i].key != NULL; i++) {
1373 const gchar *tmp = g_hash_table_lookup (os_release, distro_kv[i].key);
1374 if (tmp != NULL) {
1375 g_hash_table_insert (hash,
1376 g_strdup (distro_kv[i].val),
1377 g_strdup (tmp));
1378 }
1379 }
1380 return TRUE;
1381}
1382
Richard Hughesdc867dd2020-05-20 18:47:20 +01001383static gboolean
1384fu_engine_get_report_metadata_kernel_cmdline (GHashTable *hash, GError **error)
1385{
1386 gsize bufsz = 0;
1387 g_autofree gchar *buf = NULL;
1388 const gchar *ignore[] = {
1389 "",
Richard Hughesc05ac2d2020-05-20 22:35:09 +01001390 "auto",
Richard Hughesdc867dd2020-05-20 18:47:20 +01001391 "BOOT_IMAGE",
1392 "console",
1393 "cryptdevice",
1394 "initrd",
1395 "LANG",
1396 "loglevel",
Richard Hughesc05ac2d2020-05-20 22:35:09 +01001397 "noplymouth",
Richard Hughesdc867dd2020-05-20 18:47:20 +01001398 "ostree",
1399 "quiet",
1400 "rd.luks.uuid",
1401 "rd.lvm.lv",
Richard Hughes4a623292020-05-21 20:58:28 +01001402 "rd.md.uuid",
Richard Hughesdc867dd2020-05-20 18:47:20 +01001403 "resume",
1404 "rhgb",
1405 "ro",
1406 "root",
1407 "rootflags",
1408 "rw",
1409 "showopts",
1410 "splash",
1411 "swap",
Richard Hughesc05ac2d2020-05-20 22:35:09 +01001412 "verbose",
Richard Hughesdc867dd2020-05-20 18:47:20 +01001413 "vt.handoff",
1414 "zfs",
1415 NULL, /* last entry */
1416 };
1417
1418 /* get a PII-safe kernel command line */
1419 if (!g_file_get_contents ("/proc/cmdline", &buf, &bufsz, error))
1420 return FALSE;
1421 if (bufsz > 0) {
1422 g_auto(GStrv) tokens = fu_common_strnsplit (buf, bufsz - 1, " ", -1);
1423 g_autoptr(GString) cmdline_safe = g_string_new (NULL);
1424 for (guint i = 0; tokens[i] != NULL; i++) {
1425 g_auto(GStrv) kv = g_strsplit (tokens[i], "=", 2);
1426 if (g_strv_contains (ignore, kv[0]))
1427 continue;
1428 if (cmdline_safe->len > 0)
1429 g_string_append (cmdline_safe, " ");
1430 g_string_append (cmdline_safe, tokens[i]);
1431 }
1432 if (cmdline_safe->len > 0) {
1433 g_hash_table_insert (hash,
1434 g_strdup ("KernelCmdline"),
1435 g_strdup (cmdline_safe->str));
1436 }
1437 }
1438 return TRUE;
1439}
1440
Richard Hughes6ecc4ca2020-05-20 18:42:46 +01001441GHashTable *
1442fu_engine_get_report_metadata (FuEngine *self, GError **error)
1443{
Richard Hughesa778ac92020-05-20 18:44:24 +01001444 const gchar *tmp;
Richard Hughes59c2ebe2018-01-12 09:47:40 +00001445 gchar *btime;
Richard Hughesfc1e2672019-11-22 08:53:33 +00001446#ifdef HAVE_UTSNAME_H
Mario Limoncielloed1ac2a2018-04-17 14:43:28 -05001447 struct utsname name_tmp;
Richard Hughesfc1e2672019-11-22 08:53:33 +00001448#endif
Richard Hughes6ecc4ca2020-05-20 18:42:46 +01001449 g_autoptr(GHashTable) hash = NULL;
Richard Hughes34e0dab2018-04-20 16:43:00 +01001450 g_autoptr(GList) compile_keys = g_hash_table_get_keys (self->compile_versions);
1451 g_autoptr(GList) runtime_keys = g_hash_table_get_keys (self->runtime_versions);
Richard Hughes473c5202018-01-11 21:06:16 +00001452
Richard Hughes34e0dab2018-04-20 16:43:00 +01001453 /* convert all the runtime and compile-time versions */
Richard Hughes473c5202018-01-11 21:06:16 +00001454 hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
Richard Hughes34e0dab2018-04-20 16:43:00 +01001455 for (GList *l = compile_keys; l != NULL; l = l->next) {
1456 const gchar *id = l->data;
1457 const gchar *version = g_hash_table_lookup (self->compile_versions, id);
1458 g_hash_table_insert (hash,
1459 g_strdup_printf ("CompileVersion(%s)", id),
1460 g_strdup (version));
1461 }
1462 for (GList *l = runtime_keys; l != NULL; l = l->next) {
1463 const gchar *id = l->data;
1464 const gchar *version = g_hash_table_lookup (self->runtime_versions, id);
1465 g_hash_table_insert (hash,
1466 g_strdup_printf ("RuntimeVersion(%s)", id),
1467 g_strdup (version));
1468 }
Richard Hughes6ecc4ca2020-05-20 18:42:46 +01001469 if (!fu_engine_get_report_metadata_os_release (hash, error))
1470 return NULL;
Richard Hughesdc867dd2020-05-20 18:47:20 +01001471 if (!fu_engine_get_report_metadata_kernel_cmdline (hash, error))
1472 return NULL;
Richard Hughes473c5202018-01-11 21:06:16 +00001473
Richard Hughesa778ac92020-05-20 18:44:24 +01001474 /* DMI data */
1475 tmp = fu_hwids_get_value (self->hwids, FU_HWIDS_KEY_PRODUCT_NAME);
1476 if (tmp != NULL)
1477 g_hash_table_insert (hash, g_strdup ("HostProduct"), g_strdup (tmp));
1478 tmp = fu_hwids_get_value (self->hwids, FU_HWIDS_KEY_FAMILY);
1479 if (tmp != NULL)
1480 g_hash_table_insert (hash, g_strdup ("HostFamily"), g_strdup (tmp));
1481 tmp = fu_hwids_get_value (self->hwids, FU_HWIDS_KEY_PRODUCT_SKU);
1482 if (tmp != NULL)
1483 g_hash_table_insert (hash, g_strdup ("HostSku"), g_strdup (tmp));
1484 tmp = fu_hwids_get_value (self->hwids, FU_HWIDS_KEY_MANUFACTURER);
1485 if (tmp != NULL)
1486 g_hash_table_insert (hash, g_strdup ("HostVendor"), g_strdup (tmp));
1487
Richard Hughes473c5202018-01-11 21:06:16 +00001488 /* kernel version is often important for debugging failures */
Richard Hughesfc1e2672019-11-22 08:53:33 +00001489#ifdef HAVE_UTSNAME_H
Mario Limoncielloed1ac2a2018-04-17 14:43:28 -05001490 memset (&name_tmp, 0, sizeof (struct utsname));
Richard Hughes473c5202018-01-11 21:06:16 +00001491 if (uname (&name_tmp) >= 0) {
1492 g_hash_table_insert (hash,
1493 g_strdup ("CpuArchitecture"),
1494 g_strdup (name_tmp.machine));
Richard Hughes08bb9222020-05-20 18:43:59 +01001495 g_hash_table_insert (hash,
1496 g_strdup ("KernelVersion"),
1497 g_strdup (name_tmp.release));
Richard Hughes473c5202018-01-11 21:06:16 +00001498 }
Richard Hughesfc1e2672019-11-22 08:53:33 +00001499#endif
Richard Hughes473c5202018-01-11 21:06:16 +00001500
Richard Hughes59c2ebe2018-01-12 09:47:40 +00001501 /* add the kernel boot time so we can detect a reboot */
1502 btime = fu_engine_get_boot_time ();
1503 if (btime != NULL)
1504 g_hash_table_insert (hash, g_strdup ("BootTime"), btime);
1505
Richard Hughes6ecc4ca2020-05-20 18:42:46 +01001506 return g_steal_pointer (&hash);
Richard Hughes473c5202018-01-11 21:06:16 +00001507}
1508
Richard Hughes9945edb2017-06-19 10:03:55 +01001509/**
Richard Hughesdbd8c762018-06-15 20:31:40 +01001510 * fu_engine_composite_prepare:
1511 * @self: A #FuEngine
1512 * @devices: (element-type #FuDevice): devices that will be updated
1513 * @error: A #GError, or %NULL
1514 *
1515 * Calls into the plugin loader, informing each plugin of the pending upgrade(s).
1516 *
1517 * Any failure in any plugin will abort all of the actions before they are started.
1518 *
1519 * Returns: %TRUE for success
1520 **/
1521gboolean
1522fu_engine_composite_prepare (FuEngine *self, GPtrArray *devices, GError **error)
1523{
1524 GPtrArray *plugins = fu_plugin_list_get_all (self->plugin_list);
1525 for (guint j = 0; j < plugins->len; j++) {
1526 FuPlugin *plugin_tmp = g_ptr_array_index (plugins, j);
1527 if (!fu_plugin_runner_composite_prepare (plugin_tmp, devices, error))
1528 return FALSE;
1529 }
1530 return TRUE;
1531}
1532
1533/**
1534 * fu_engine_composite_cleanup:
1535 * @self: A #FuEngine
1536 * @devices: (element-type #FuDevice): devices that will be updated
1537 * @error: A #GError, or %NULL
1538 *
1539 * Calls into the plugin loader, informing each plugin of the pending upgrade(s).
1540 *
1541 * Returns: %TRUE for success
1542 **/
1543gboolean
1544fu_engine_composite_cleanup (FuEngine *self, GPtrArray *devices, GError **error)
1545{
1546 GPtrArray *plugins = fu_plugin_list_get_all (self->plugin_list);
1547 for (guint j = 0; j < plugins->len; j++) {
1548 FuPlugin *plugin_tmp = g_ptr_array_index (plugins, j);
1549 if (!fu_plugin_runner_composite_cleanup (plugin_tmp, devices, error))
1550 return FALSE;
1551 }
1552 return TRUE;
1553}
1554
1555/**
1556 * fu_engine_install_tasks:
1557 * @self: A #FuEngine
1558 * @install_tasks: (element-type FuInstallTask): A #FuDevice
1559 * @blob_cab: The #GBytes of the .cab file
1560 * @flags: The #FwupdInstallFlags, e.g. %FWUPD_DEVICE_FLAG_UPDATABLE
1561 * @error: A #GError, or %NULL
1562 *
1563 * Installs a specific firmware file on one or more install tasks.
1564 *
1565 * By this point all the requirements and tests should have been done in
1566 * fu_engine_check_requirements() so this should not fail before running
1567 * the plugin loader.
1568 *
1569 * Returns: %TRUE for success
1570 **/
1571gboolean
1572fu_engine_install_tasks (FuEngine *self,
1573 GPtrArray *install_tasks,
1574 GBytes *blob_cab,
1575 FwupdInstallFlags flags,
1576 GError **error)
1577{
Richard Hughes75b965d2018-11-15 13:51:21 +00001578 g_autoptr(FuIdleLocker) locker = NULL;
Richard Hughesdbd8c762018-06-15 20:31:40 +01001579 g_autoptr(GPtrArray) devices = NULL;
Mario Limonciellob6fa4732018-09-07 15:12:33 +01001580 g_autoptr(GPtrArray) devices_new = NULL;
Richard Hughesdbd8c762018-06-15 20:31:40 +01001581
Richard Hughes75b965d2018-11-15 13:51:21 +00001582 /* do not allow auto-shutdown during this time */
1583 locker = fu_idle_locker_new (self->idle, "performing update");
1584 g_assert (locker != NULL);
1585
Richard Hughesdbd8c762018-06-15 20:31:40 +01001586 /* notify the plugins about the composite action */
1587 devices = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
1588 for (guint i = 0; i < install_tasks->len; i++) {
1589 FuInstallTask *task = g_ptr_array_index (install_tasks, i);
Richard Hughesca6af0b2020-04-16 17:08:33 +01001590 g_debug ("composite update %u: %s", i + 1,
1591 fu_device_get_id (fu_install_task_get_device (task)));
Richard Hughesdbd8c762018-06-15 20:31:40 +01001592 g_ptr_array_add (devices, g_object_ref (fu_install_task_get_device (task)));
1593 }
1594 if (!fu_engine_composite_prepare (self, devices, error)) {
1595 g_prefix_error (error, "failed to prepare composite action: ");
1596 return FALSE;
1597 }
1598
1599 /* all authenticated, so install all the things */
1600 for (guint i = 0; i < install_tasks->len; i++) {
1601 FuInstallTask *task = g_ptr_array_index (install_tasks, i);
1602 if (!fu_engine_install (self, task, blob_cab, flags, error)) {
1603 g_autoptr(GError) error_local = NULL;
1604 if (!fu_engine_composite_cleanup (self, devices, &error_local)) {
1605 g_warning ("failed to cleanup failed composite action: %s",
1606 error_local->message);
1607 }
1608 return FALSE;
1609 }
1610 }
1611
Richard Hughes96019e82019-01-30 11:12:57 +00001612 /* set all the device statuses back to unknown */
1613 for (guint i = 0; i < install_tasks->len; i++) {
1614 FuInstallTask *task = g_ptr_array_index (install_tasks, i);
1615 FuDevice *device = fu_install_task_get_device (task);
1616 fu_device_set_status (device, FWUPD_STATUS_UNKNOWN);
1617 }
1618
Mario Limonciellob6fa4732018-09-07 15:12:33 +01001619 /* get a new list of devices in case they replugged */
1620 devices_new = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
1621 for (guint i = 0; i < devices->len; i++) {
1622 FuDevice *device;
Richard Hughes5a9a6bd2018-09-10 10:02:20 +01001623 g_autoptr(FuDevice) device_new = NULL;
Mario Limonciellob6fa4732018-09-07 15:12:33 +01001624 g_autoptr(GError) error_local = NULL;
1625 device = g_ptr_array_index (devices, i);
1626 device_new = fu_device_list_get_by_id (self->device_list,
1627 fu_device_get_id (device),
1628 &error_local);
1629 if (device_new == NULL) {
Mario Limonciello769d7682018-09-28 08:43:37 -05001630 g_debug ("failed to find new device: %s",
1631 error_local->message);
Mario Limonciellob6fa4732018-09-07 15:12:33 +01001632 continue;
1633 }
Richard Hughes5a9a6bd2018-09-10 10:02:20 +01001634 g_ptr_array_add (devices_new, g_steal_pointer (&device_new));
Mario Limonciellob6fa4732018-09-07 15:12:33 +01001635 }
1636
Richard Hughesdbd8c762018-06-15 20:31:40 +01001637 /* notify the plugins about the composite action */
Mario Limonciellob6fa4732018-09-07 15:12:33 +01001638 if (!fu_engine_composite_cleanup (self, devices_new, error)) {
Richard Hughesdbd8c762018-06-15 20:31:40 +01001639 g_prefix_error (error, "failed to cleanup composite action: ");
1640 return FALSE;
1641 }
1642
1643 /* success */
1644 return TRUE;
1645}
1646
Richard Hughes81d7a5c2019-03-25 14:26:04 +00001647static FwupdRelease *
1648fu_engine_create_release_metadata (FuEngine *self, FuPlugin *plugin, GError **error)
1649{
Richard Hughesdad35972019-12-06 11:00:25 +00001650 GPtrArray *metadata_sources;
Richard Hughes81d7a5c2019-03-25 14:26:04 +00001651 g_autoptr(FwupdRelease) release = fwupd_release_new ();
1652 g_autoptr(GHashTable) metadata_hash = NULL;
Richard Hughes81d7a5c2019-03-25 14:26:04 +00001653
1654 /* build the version metadata */
Richard Hughes6ecc4ca2020-05-20 18:42:46 +01001655 metadata_hash = fu_engine_get_report_metadata (self, error);
1656 if (metadata_hash == NULL)
1657 return NULL;
Richard Hughes81d7a5c2019-03-25 14:26:04 +00001658 fwupd_release_add_metadata (release, metadata_hash);
1659 fwupd_release_add_metadata (release, fu_plugin_get_report_metadata (plugin));
1660
Richard Hughesdad35972019-12-06 11:00:25 +00001661 /* allow other plugins to contribute metadata too */
1662 metadata_sources = fu_plugin_get_rules (plugin, FU_PLUGIN_RULE_METADATA_SOURCE);
1663 for (guint i = 0; i < metadata_sources->len; i++) {
1664 FuPlugin *plugin_tmp;
1665 const gchar *plugin_name = g_ptr_array_index (metadata_sources, i);
1666 g_autoptr(GError) error_local = NULL;
1667
1668 plugin_tmp = fu_plugin_list_find_by_name (self->plugin_list,
1669 plugin_name,
1670 &error_local);
1671 if (plugin_tmp == NULL) {
1672 g_warning ("could not add metadata for %s: %s",
1673 plugin_name,
1674 error_local->message);
1675 continue;
1676 }
1677 fwupd_release_add_metadata (release,
1678 fu_plugin_get_report_metadata (plugin_tmp));
1679 }
Richard Hughes81d7a5c2019-03-25 14:26:04 +00001680 return g_steal_pointer (&release);
1681}
1682
Richard Hughes3d005222019-05-17 14:02:41 +01001683static gboolean
1684fu_engine_is_running_offline (FuEngine *self)
1685{
1686#ifdef HAVE_SYSTEMD
1687 g_autofree gchar *default_target = NULL;
1688 g_autoptr(GError) error = NULL;
1689 default_target = fu_systemd_get_default_target (&error);
1690 if (default_target == NULL) {
1691 g_warning ("failed to get default.target: %s", error->message);
1692 return FALSE;
1693 }
1694 return g_strcmp0 (default_target, "system-update.target") == 0;
1695#else
1696 return FALSE;
1697#endif
1698}
1699
Richard Hughes019a1bc2019-11-26 10:19:33 +00001700static gboolean
1701fu_engine_offline_setup (GError **error)
1702{
Richard Hughes9e5675e2019-11-22 09:35:03 +00001703#ifdef HAVE_GIO_UNIX
Richard Hughes019a1bc2019-11-26 10:19:33 +00001704 gint rc;
1705 g_autofree gchar *filename = NULL;
1706 g_autofree gchar *symlink_target = fu_common_get_path (FU_PATH_KIND_LOCALSTATEDIR_PKG);
1707 g_autofree gchar *trigger = fu_common_get_path (FU_PATH_KIND_OFFLINE_TRIGGER);
1708
1709 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
1710
1711 /* does already exist */
1712 filename = fu_common_realpath (trigger, NULL);
1713 if (g_strcmp0 (filename, symlink_target) == 0) {
1714 g_debug ("%s already points to %s, skipping creation",
1715 trigger, symlink_target);
1716 return TRUE;
1717 }
1718
1719 /* create symlink for the systemd-system-update-generator */
1720 rc = symlink (symlink_target, trigger);
1721 if (rc < 0) {
1722 g_set_error (error,
1723 FWUPD_ERROR,
1724 FWUPD_ERROR_INTERNAL,
1725 "Failed to create symlink %s to %s: %s",
1726 trigger, symlink_target, strerror (errno));
1727 return FALSE;
1728 }
1729 return TRUE;
Richard Hughes9e5675e2019-11-22 09:35:03 +00001730#else
1731 g_set_error (error,
1732 FWUPD_ERROR,
1733 FWUPD_ERROR_NOT_SUPPORTED,
1734 "Not supported as <gio-unix.h> not available");
1735 return FALSE;
1736#endif
1737
Richard Hughes019a1bc2019-11-26 10:19:33 +00001738}
1739
1740static gboolean
1741fu_engine_offline_invalidate (GError **error)
1742{
1743 g_autofree gchar *trigger = fu_common_get_path (FU_PATH_KIND_OFFLINE_TRIGGER);
1744 g_autoptr(GError) error_local = NULL;
1745 g_autoptr(GFile) file1 = NULL;
1746
1747 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
1748
1749 file1 = g_file_new_for_path (trigger);
1750 if (!g_file_query_exists (file1, NULL))
1751 return TRUE;
1752 if (!g_file_delete (file1, NULL, &error_local)) {
1753 g_set_error (error,
1754 FWUPD_ERROR,
1755 FWUPD_ERROR_INTERNAL,
1756 "Cannot delete %s: %s",
1757 trigger,
1758 error_local->message);
1759 return FALSE;
1760 }
1761 return TRUE;
1762}
1763
1764/**
1765 * fu_engine_schedule_update:
1766 * @self: a #FuEngine
1767 * @device: a #FuDevice
1768 * @release: A #FwupdRelease
1769 * @blob_cab: A #GBytes
1770 * @flags: #FwupdInstallFlags
1771 * @error: A #GError or NULL
1772 *
1773 * Schedule an offline update for the device
1774 *
1775 * Returns: #TRUE for success, #FALSE for failure
1776 *
1777 * Since: 1.3.5
1778 **/
1779gboolean
1780fu_engine_schedule_update (FuEngine *self,
1781 FuDevice *device,
1782 FwupdRelease *release,
1783 GBytes *blob_cab,
1784 FwupdInstallFlags flags,
1785 GError **error)
1786{
1787 gchar tmpname[] = {"XXXXXX.cab"};
1788 g_autofree gchar *dirname = NULL;
1789 g_autofree gchar *filename = NULL;
1790 g_autoptr(FuHistory) history = NULL;
1791 g_autoptr(GFile) file = NULL;
1792
1793 /* id already exists */
1794 history = fu_history_new ();
1795 if ((flags & FWUPD_INSTALL_FLAG_FORCE) == 0) {
1796 g_autoptr(FuDevice) res_tmp = NULL;
1797 res_tmp = fu_history_get_device_by_id (history, fu_device_get_id (device), NULL);
1798 if (res_tmp != NULL &&
1799 fu_device_get_update_state (res_tmp) == FWUPD_UPDATE_STATE_PENDING) {
1800 g_set_error (error,
1801 FWUPD_ERROR,
1802 FWUPD_ERROR_ALREADY_PENDING,
1803 "%s is already scheduled to be updated",
1804 fu_device_get_id (device));
1805 return FALSE;
1806 }
1807 }
1808
1809 /* create directory */
1810 dirname = fu_common_get_path (FU_PATH_KIND_LOCALSTATEDIR_PKG);
1811 file = g_file_new_for_path (dirname);
1812 if (!g_file_query_exists (file, NULL)) {
1813 if (!g_file_make_directory_with_parents (file, NULL, error))
1814 return FALSE;
1815 }
1816
1817 /* get a random filename */
1818 for (guint i = 0; i < 6; i++)
1819 tmpname[i] = (gchar) g_random_int_range ('A', 'Z');
1820 filename = g_build_filename (dirname, tmpname, NULL);
1821
1822 /* just copy to the temp file */
1823 fu_device_set_status (device, FWUPD_STATUS_SCHEDULING);
1824 if (!g_file_set_contents (filename,
1825 g_bytes_get_data (blob_cab, NULL),
1826 (gssize) g_bytes_get_size (blob_cab),
1827 error))
1828 return FALSE;
1829
1830 /* schedule for next boot */
1831 g_debug ("schedule %s to be installed to %s on next boot",
1832 filename, fu_device_get_id (device));
1833 fwupd_release_set_filename (release, filename);
1834
1835 /* add to database */
1836 fu_device_add_flag (device, FWUPD_DEVICE_FLAG_NEEDS_REBOOT);
1837 fu_device_set_update_state (device, FWUPD_UPDATE_STATE_PENDING);
1838 if (!fu_history_add_device (history, device, release, error))
1839 return FALSE;
1840
1841 /* next boot we run offline */
1842 fu_device_set_progress (device, 100);
1843 return fu_engine_offline_setup (error);
1844}
1845
Richard Hughes4bd2e042019-12-22 12:19:52 +00001846static gboolean
1847fu_engine_install_release (FuEngine *self,
Richard Hughes293a64d2020-02-14 12:08:08 +00001848 FuDevice *device_orig,
Richard Hughes4bd2e042019-12-22 12:19:52 +00001849 XbNode *component,
1850 XbNode *rel,
1851 FwupdInstallFlags flags,
1852 GError **error)
Richard Hughes9945edb2017-06-19 10:03:55 +01001853{
Richard Hughesafca2032019-02-01 18:05:30 +00001854 FuPlugin *plugin;
Richard Hughes9a680842020-02-20 11:11:13 +00001855 FwupdVersionFormat fmt;
Richard Hughes9945edb2017-06-19 10:03:55 +01001856 GBytes *blob_fw;
Richard Hughesd5aab652020-02-25 12:47:50 +00001857 const gchar *tmp;
Richard Hughes84af6e72019-02-01 18:19:41 +00001858 g_autofree gchar *version_orig = NULL;
Richard Hughes481aa2a2018-09-18 20:51:46 +01001859 g_autofree gchar *version_rel = NULL;
Richard Hughes68db74b2019-03-14 09:52:05 +00001860 g_autoptr(FuDevice) device_tmp = NULL;
Richard Hughes293a64d2020-02-14 12:08:08 +00001861 g_autoptr(FuDevice) device = g_object_ref (device_orig);
Richard Hughes41cbe2a2017-08-08 14:13:35 +01001862 g_autoptr(GBytes) blob_fw2 = NULL;
Richard Hughes481aa2a2018-09-18 20:51:46 +01001863 g_autoptr(GError) error_local = NULL;
Richard Hughes9945edb2017-06-19 10:03:55 +01001864
Richard Hughesd5aab652020-02-25 12:47:50 +00001865 /* get per-release firmware blob */
1866 blob_fw = xb_node_get_data (rel, "fwupd::FirmwareBlob");
Richard Hughes9945edb2017-06-19 10:03:55 +01001867 if (blob_fw == NULL) {
Richard Hughesd5aab652020-02-25 12:47:50 +00001868 g_set_error_literal (error,
1869 FWUPD_ERROR,
1870 FWUPD_ERROR_INTERNAL,
1871 "Failed to get firmware blob from release");
Richard Hughes9945edb2017-06-19 10:03:55 +01001872 return FALSE;
1873 }
1874
Richard Hughes41cbe2a2017-08-08 14:13:35 +01001875 /* use a bubblewrap helper script to build the firmware */
Richard Hughes481aa2a2018-09-18 20:51:46 +01001876 tmp = g_object_get_data (G_OBJECT (component), "fwupd::BuilderScript");
Richard Hughes41cbe2a2017-08-08 14:13:35 +01001877 if (tmp != NULL) {
Richard Hughes481aa2a2018-09-18 20:51:46 +01001878 const gchar *tmp2 = g_object_get_data (G_OBJECT (component), "fwupd::BuilderOutput");
Richard Hughes41cbe2a2017-08-08 14:13:35 +01001879 if (tmp2 == NULL)
1880 tmp2 = "firmware.bin";
1881 blob_fw2 = fu_common_firmware_builder (blob_fw, tmp, tmp2, error);
1882 if (blob_fw2 == NULL)
1883 return FALSE;
1884 } else {
1885 blob_fw2 = g_bytes_ref (blob_fw);
1886 }
1887
Richard Hughesafca2032019-02-01 18:05:30 +00001888 /* get the plugin */
1889 plugin = fu_plugin_list_find_by_name (self->plugin_list,
1890 fu_device_get_plugin (device),
1891 error);
1892 if (plugin == NULL)
1893 return FALSE;
1894
Richard Hughesf8e353e2019-03-25 14:26:57 +00001895 /* schedule this for the next reboot if not in system-update.target,
1896 * but first check if allowed on battery power */
Richard Hughes2c40b372019-04-17 13:41:47 +01001897 version_rel = fu_engine_get_release_version (self, device, rel, error);
Richard Hughes0b3e9fd2019-04-08 11:13:20 +01001898 if (version_rel == NULL) {
1899 g_prefix_error (error, "failed to get release version: ");
1900 return FALSE;
1901 }
Richard Hughesf8e353e2019-03-25 14:26:57 +00001902
Richard Hughesafca2032019-02-01 18:05:30 +00001903 /* add device to database */
Richard Hughesafca2032019-02-01 18:05:30 +00001904 if ((flags & FWUPD_INSTALL_FLAG_NO_HISTORY) == 0) {
Richard Hughes81d7a5c2019-03-25 14:26:04 +00001905 g_autoptr(FwupdRelease) release_tmp = NULL;
1906 release_tmp = fu_engine_create_release_metadata (self, plugin, error);
1907 if (release_tmp == NULL)
Richard Hughesafca2032019-02-01 18:05:30 +00001908 return FALSE;
Richard Hughesafca2032019-02-01 18:05:30 +00001909 tmp = xb_node_query_text (component,
1910 "releases/release/checksum[@target='container']",
1911 NULL);
Richard Hughesb9bbe4c2019-03-25 14:03:27 +00001912 if (tmp != NULL)
Richard Hughes81d7a5c2019-03-25 14:26:04 +00001913 fwupd_release_add_checksum (release_tmp, tmp);
1914 fwupd_release_set_version (release_tmp, version_rel);
Richard Hughesafca2032019-02-01 18:05:30 +00001915 fu_device_set_update_state (device, FWUPD_UPDATE_STATE_FAILED);
Richard Hughes81d7a5c2019-03-25 14:26:04 +00001916 if (!fu_history_add_device (self->history, device, release_tmp, error))
Richard Hughesafca2032019-02-01 18:05:30 +00001917 return FALSE;
1918 }
1919
1920 /* install firmware blob */
Richard Hughes84af6e72019-02-01 18:19:41 +00001921 version_orig = g_strdup (fu_device_get_version (device));
Richard Hughes03df0d52019-02-01 18:31:39 +00001922 if (!fu_engine_install_blob (self, device, blob_fw2, flags, &error_local)) {
1923 fu_device_set_status (device, FWUPD_STATUS_IDLE);
Richard Hughescce6a1c2019-04-16 17:25:48 +01001924 if (g_error_matches (error_local,
1925 FWUPD_ERROR,
Richard Hughes7886c082019-04-18 10:00:17 +01001926 FWUPD_ERROR_AC_POWER_REQUIRED) ||
1927 g_error_matches (error_local,
1928 FWUPD_ERROR,
Richard Hughes4266ac42019-07-11 16:49:50 +01001929 FWUPD_ERROR_BATTERY_LEVEL_TOO_LOW) ||
1930 g_error_matches (error_local,
1931 FWUPD_ERROR,
Mario Limonciello2e06dcd2019-10-30 19:28:52 -05001932 FWUPD_ERROR_NEEDS_USER_ACTION) ||
1933 g_error_matches (error_local,
1934 FWUPD_ERROR,
Richard Hughes7886c082019-04-18 10:00:17 +01001935 FWUPD_ERROR_BROKEN_SYSTEM)) {
Richard Hughescce6a1c2019-04-16 17:25:48 +01001936 fu_device_set_update_state (device, FWUPD_UPDATE_STATE_FAILED_TRANSIENT);
1937 } else {
1938 fu_device_set_update_state (device, FWUPD_UPDATE_STATE_FAILED);
1939 }
Richard Hughes03df0d52019-02-01 18:31:39 +00001940 fu_device_set_update_error (device, error_local->message);
1941 if ((flags & FWUPD_INSTALL_FLAG_NO_HISTORY) == 0 &&
Richard Hughes0bbef292019-11-01 12:15:15 +00001942 !fu_history_modify_device (self->history, device, error)) {
Richard Hughes03df0d52019-02-01 18:31:39 +00001943 return FALSE;
1944 }
1945 g_propagate_error (error, g_steal_pointer (&error_local));
Richard Hughes84af6e72019-02-01 18:19:41 +00001946 return FALSE;
Richard Hughes03df0d52019-02-01 18:31:39 +00001947 }
Richard Hughes84af6e72019-02-01 18:19:41 +00001948
Richard Hughes68db74b2019-03-14 09:52:05 +00001949 /* the device may have changed */
1950 device_tmp = fu_device_list_get_by_id (self->device_list,
1951 fu_device_get_id (device),
1952 error);
1953 if (device_tmp == NULL) {
1954 g_prefix_error (error, "failed to get device after install: ");
1955 return FALSE;
1956 }
1957 g_set_object (&device, device_tmp);
1958
Richard Hughes84af6e72019-02-01 18:19:41 +00001959 /* update database */
1960 if (fu_device_has_flag (device, FWUPD_DEVICE_FLAG_NEEDS_REBOOT) ||
1961 fu_device_has_flag (device, FWUPD_DEVICE_FLAG_NEEDS_SHUTDOWN)) {
1962 fu_device_set_update_state (device, FWUPD_UPDATE_STATE_NEEDS_REBOOT);
1963 if ((flags & FWUPD_INSTALL_FLAG_NO_HISTORY) == 0 &&
Richard Hughes0bbef292019-11-01 12:15:15 +00001964 !fu_history_modify_device (self->history, device, error))
Richard Hughes84af6e72019-02-01 18:19:41 +00001965 return FALSE;
1966 /* success */
1967 return TRUE;
1968 }
1969
1970 /* for online updates, verify the version changed if not a re-install */
Richard Hughes9a680842020-02-20 11:11:13 +00001971 fmt = fu_device_get_version_format (device);
Richard Hughes84af6e72019-02-01 18:19:41 +00001972 if (version_rel != NULL &&
Richard Hughes9a680842020-02-20 11:11:13 +00001973 fu_common_vercmp_full (version_orig, version_rel, fmt) != 0 &&
1974 fu_common_vercmp_full (version_orig, fu_device_get_version (device), fmt) == 0) {
Richard Hughes68db74b2019-03-14 09:52:05 +00001975 g_autofree gchar *str = NULL;
Richard Hughes84af6e72019-02-01 18:19:41 +00001976 fu_device_set_update_state (device, FWUPD_UPDATE_STATE_FAILED);
Richard Hughes68db74b2019-03-14 09:52:05 +00001977 str = g_strdup_printf ("device version not updated on success, %s != %s",
1978 version_rel, fu_device_get_version (device));
1979 fu_device_set_update_error (device, str);
Richard Hughes84af6e72019-02-01 18:19:41 +00001980 if ((flags & FWUPD_INSTALL_FLAG_NO_HISTORY) == 0 &&
Richard Hughes0bbef292019-11-01 12:15:15 +00001981 !fu_history_modify_device (self->history, device, error))
Richard Hughes84af6e72019-02-01 18:19:41 +00001982 return FALSE;
1983 /* success */
1984 return TRUE;
1985 }
1986
1987 /* ensure the new version matched what we expected */
1988 if (version_rel != NULL &&
1989 g_strcmp0 (fu_device_get_version (device), version_rel) != 0) {
1990 g_warning ("new device version '%s' was is not '%s', fixing up",
1991 fu_device_get_version (device), version_rel);
Richard Hughesf50ff2c2020-02-25 09:45:15 +00001992 fu_device_set_version_format (device, fu_device_get_version_format (device));
1993 fu_device_set_version (device, version_rel);
Richard Hughes84af6e72019-02-01 18:19:41 +00001994 }
1995
1996 /* success */
Richard Hughes84af6e72019-02-01 18:19:41 +00001997 if ((flags & FWUPD_INSTALL_FLAG_NO_HISTORY) == 0 &&
Richard Hughes0bbef292019-11-01 12:15:15 +00001998 !fu_history_modify_device (self->history, device, error))
Richard Hughes84af6e72019-02-01 18:19:41 +00001999 return FALSE;
Mario Limonciellod81ea2e2020-01-13 14:11:43 -06002000
2001 /* make the UI update */
2002 fu_engine_emit_changed (self);
2003
Richard Hughes84af6e72019-02-01 18:19:41 +00002004 return TRUE;
Richard Hughes6e7419d2018-05-18 10:11:36 +01002005}
2006
Richard Hughesaf140732019-12-22 18:42:12 +00002007typedef struct {
2008 gboolean ret;
2009 GError **error;
2010 FuEngine *self;
2011 FuDevice *device;
2012} FuEngineSortHelper;
2013
2014static gint
2015fu_engine_sort_release_versions_cb (gconstpointer a, gconstpointer b, gpointer user_data)
2016{
2017 FuEngineSortHelper *helper = (FuEngineSortHelper *) user_data;
2018 XbNode *na = *((XbNode **) a);
2019 XbNode *nb = *((XbNode **) b);
2020 g_autofree gchar *va = NULL;
2021 g_autofree gchar *vb = NULL;
2022
2023 /* already failed */
2024 if (!helper->ret)
2025 return 0;
2026
2027 /* get the semver from the release */
2028 va = fu_engine_get_release_version (helper->self, helper->device, na, helper->error);
2029 if (va == NULL) {
2030 g_prefix_error (helper->error, "failed to get release version: ");
2031 return 0;
2032 }
2033 vb = fu_engine_get_release_version (helper->self, helper->device, nb, helper->error);
2034 if (vb == NULL) {
2035 g_prefix_error (helper->error, "failed to get release version: ");
2036 return 0;
2037 }
Richard Hughes9a680842020-02-20 11:11:13 +00002038 return fu_common_vercmp_full (va, vb, fu_device_get_version_format (helper->device));
Richard Hughesaf140732019-12-22 18:42:12 +00002039}
2040
2041static gboolean
2042fu_engine_sort_releases (FuEngine *self, FuDevice *device, GPtrArray *rels, GError **error)
2043{
2044 FuEngineSortHelper helper = {
2045 .ret = TRUE,
2046 .self = self,
2047 .device = device,
2048 .error = error,
2049 };
2050 g_ptr_array_sort_with_data (rels, fu_engine_sort_release_versions_cb, &helper);
2051 return helper.ret;
2052}
2053
Richard Hughes8c71a3f2018-05-22 19:19:52 +01002054/**
Richard Hughes4bd2e042019-12-22 12:19:52 +00002055 * fu_engine_install:
2056 * @self: A #FuEngine
2057 * @task: A #FuInstallTask
2058 * @blob_cab: The #GBytes of the .cab file
2059 * @flags: The #FwupdInstallFlags, e.g. %FWUPD_DEVICE_FLAG_UPDATABLE
2060 * @error: A #GError, or %NULL
2061 *
2062 * Installs a specific firmware file on a device.
2063 *
2064 * By this point all the requirements and tests should have been done in
2065 * fu_engine_check_requirements() so this should not fail before running
2066 * the plugin loader.
2067 *
2068 * Returns: %TRUE for success
2069 **/
2070gboolean
2071fu_engine_install (FuEngine *self,
2072 FuInstallTask *task,
2073 GBytes *blob_cab,
2074 FwupdInstallFlags flags,
2075 GError **error)
2076{
2077 XbNode *component = fu_install_task_get_component (task);
2078 g_autoptr(FuDevice) device = NULL;
2079 g_autoptr(GError) error_local = NULL;
2080 g_autoptr(XbNode) rel_newest = NULL;
2081
2082 g_return_val_if_fail (FU_IS_ENGINE (self), FALSE);
2083 g_return_val_if_fail (XB_IS_NODE (component), FALSE);
2084 g_return_val_if_fail (blob_cab != NULL, FALSE);
2085 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
2086
2087 /* not in bootloader mode */
2088 device = g_object_ref (fu_install_task_get_device (task));
2089 if (fu_device_has_flag (device, FWUPD_DEVICE_FLAG_NEEDS_BOOTLOADER)) {
2090 const gchar *caption = NULL;
2091 caption = xb_node_query_text (component,
2092 "screenshots/screenshot/caption",
2093 NULL);
2094 if (caption != NULL) {
2095 g_set_error (error,
2096 FWUPD_ERROR,
2097 FWUPD_ERROR_NEEDS_USER_ACTION,
2098 "Device %s needs to manually be put in update mode: %s",
2099 fu_device_get_name (device), caption);
2100 } else {
2101 g_set_error (error,
2102 FWUPD_ERROR,
2103 FWUPD_ERROR_NEEDS_USER_ACTION,
2104 "Device %s needs to manually be put in update mode",
2105 fu_device_get_name (device));
2106 }
2107 fu_device_set_update_state (device, FWUPD_UPDATE_STATE_FAILED_TRANSIENT);
2108 if (error != NULL)
2109 fu_device_set_update_error (device, (*error)->message);
2110 return FALSE;
2111 }
2112
2113 /* get the newest version */
2114 rel_newest = xb_node_query_first (component, "releases/release", &error_local);
2115 if (rel_newest == NULL) {
2116 g_set_error (error,
2117 FWUPD_ERROR,
2118 FWUPD_ERROR_INVALID_FILE,
2119 "No releases in the firmware component: %s",
2120 error_local->message);
2121 return FALSE;
2122 }
2123
2124 /* schedule this for the next reboot if not in system-update.target,
2125 * but first check if allowed on battery power */
2126 if ((flags & FWUPD_INSTALL_FLAG_OFFLINE) > 0 &&
2127 !fu_engine_is_running_offline (self)) {
2128 FuPlugin *plugin;
2129 g_autoptr(FwupdRelease) release_tmp = NULL;
2130 g_autofree gchar *version_rel = NULL;
2131 version_rel = fu_engine_get_release_version (self, device, rel_newest, error);
2132 if (version_rel == NULL) {
2133 g_prefix_error (error, "failed to get release version: ");
2134 return FALSE;
2135 }
2136 plugin = fu_plugin_list_find_by_name (self->plugin_list, "upower", NULL);
2137 if (plugin != NULL) {
2138 if (!fu_plugin_runner_update_prepare (plugin, flags, device, error))
2139 return FALSE;
2140 }
2141 release_tmp = fu_engine_create_release_metadata (self, plugin, error);
2142 if (release_tmp == NULL)
2143 return FALSE;
2144 fwupd_release_set_version (release_tmp, version_rel);
2145 return fu_engine_schedule_update (self, device, release_tmp,
2146 blob_cab, flags, error);
2147 }
2148
Richard Hughesaf140732019-12-22 18:42:12 +00002149 /* install each intermediate release, or install only the newest version */
2150 if (fu_device_has_flag (device, FWUPD_DEVICE_FLAG_INSTALL_ALL_RELEASES)) {
2151 g_autoptr(GPtrArray) rels = NULL;
2152 rels = xb_node_query (component, "releases/release", 0, &error_local);
2153 if (rels == NULL) {
2154 g_set_error (error,
2155 FWUPD_ERROR,
2156 FWUPD_ERROR_INVALID_FILE,
2157 "No releases in the firmware component: %s",
2158 error_local->message);
2159 return FALSE;
2160 }
2161 if (!fu_engine_sort_releases (self, device, rels, error))
2162 return FALSE;
2163 for (guint i = 0; i < rels->len; i++) {
2164 XbNode *rel = g_ptr_array_index (rels, i);
2165 if (!fu_engine_install_release (self, device, component, rel, flags, error))
2166 return FALSE;
2167 }
2168 } else {
2169 if (!fu_engine_install_release (self, device, component, rel_newest, flags, error))
2170 return FALSE;
2171 }
Richard Hughes4bd2e042019-12-22 12:19:52 +00002172
2173 /* success */
2174 fu_device_set_update_state (device, FWUPD_UPDATE_STATE_SUCCESS);
2175 return TRUE;
2176}
2177
2178/**
Richard Hughes8c71a3f2018-05-22 19:19:52 +01002179 * fu_engine_get_plugins:
2180 * @self: A #FuPluginList
2181 *
2182 * Gets all the plugins that have been added.
2183 *
2184 * Returns: (transfer none) (element-type FuPlugin): the plugins
2185 *
2186 * Since: 1.0.8
2187 **/
2188GPtrArray *
2189fu_engine_get_plugins (FuEngine *self)
2190{
2191 g_return_val_if_fail (FU_IS_ENGINE (self), NULL);
2192 return fu_plugin_list_get_all (self->plugin_list);
2193}
2194
Richard Hughesd583baf2018-11-23 13:57:22 +00002195static FuDevice *
2196fu_engine_get_device_by_id (FuEngine *self, const gchar *device_id, GError **error)
2197{
2198 g_autoptr(FuDevice) device1 = NULL;
2199 g_autoptr(FuDevice) device2 = NULL;
Richard Hughesc3afed32020-03-10 16:05:29 +00002200 g_autoptr(FuDevice) root = NULL;
Richard Hughesd583baf2018-11-23 13:57:22 +00002201
2202 /* find device */
2203 device1 = fu_device_list_get_by_id (self->device_list, device_id, error);
2204 if (device1 == NULL)
2205 return NULL;
2206
Richard Hughesd583baf2018-11-23 13:57:22 +00002207 /* wait for device to disconnect and reconnect */
Richard Hughesc3afed32020-03-10 16:05:29 +00002208 root = fu_device_get_root (device1);
2209 if (fu_device_has_flag (device1, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG)) {
2210 if (!fu_device_list_wait_for_replug (self->device_list, device1, error)) {
2211 g_prefix_error (error, "failed to wait for detach replug: ");
2212 return NULL;
2213 }
2214 } else if (fu_device_has_flag (root, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG)) {
2215 if (!fu_device_list_wait_for_replug (self->device_list, root, error)) {
2216 g_prefix_error (error, "failed to wait for detach replug: ");
2217 return NULL;
2218 }
2219 } else {
2220 /* no replug required */
2221 return g_steal_pointer (&device1);
Richard Hughesd583baf2018-11-23 13:57:22 +00002222 }
2223
2224 /* get the new device */
2225 device2 = fu_device_list_get_by_id (self->device_list, device_id, error);
2226 if (device2 == NULL) {
2227 g_prefix_error (error, "failed to get device after replug: ");
2228 return NULL;
2229 }
2230
2231 /* success */
2232 return g_steal_pointer (&device2);
2233}
2234
Richard Hughes2031ce32019-10-30 14:16:24 +00002235/* same as FuDevice->prepare, but with the device open */
2236static gboolean
2237fu_engine_device_prepare (FuEngine *self,
2238 FuDevice *device,
2239 FwupdInstallFlags flags,
2240 GError **error)
2241{
2242 g_autoptr(FuDeviceLocker) locker = fu_device_locker_new (device, error);
Richard Hughes8bb64982020-04-16 17:03:18 +01002243 if (locker == NULL) {
2244 g_prefix_error (error, "failed to open device for prepare: ");
Richard Hughes2031ce32019-10-30 14:16:24 +00002245 return FALSE;
Richard Hughes8bb64982020-04-16 17:03:18 +01002246 }
Richard Hughes2031ce32019-10-30 14:16:24 +00002247 return fu_device_prepare (device, flags, error);
2248}
2249
2250/* same as FuDevice->cleanup, but with the device open */
2251static gboolean
2252fu_engine_device_cleanup (FuEngine *self,
2253 FuDevice *device,
2254 FwupdInstallFlags flags,
2255 GError **error)
2256{
Mario Limoncielloe2b8a272019-11-09 22:10:35 -06002257 g_autoptr(FuDeviceLocker) locker = NULL;
2258
2259 if (fu_device_has_flag (device, FWUPD_DEVICE_FLAG_WILL_DISAPPEAR)) {
2260 g_debug ("skipping device cleanup due to will-disappear flag");
2261 return TRUE;
2262 }
2263
2264 locker = fu_device_locker_new (device, error);
Richard Hughes8bb64982020-04-16 17:03:18 +01002265 if (locker == NULL) {
2266 g_prefix_error (error, "failed to open device for cleanup: ");
Richard Hughes2031ce32019-10-30 14:16:24 +00002267 return FALSE;
Richard Hughes8bb64982020-04-16 17:03:18 +01002268 }
Richard Hughes2031ce32019-10-30 14:16:24 +00002269 return fu_device_cleanup (device, flags, error);
2270}
2271
Richard Hughes03df0d52019-02-01 18:31:39 +00002272static gboolean
2273fu_engine_update_prepare (FuEngine *self,
2274 FwupdInstallFlags flags,
2275 const gchar *device_id,
2276 GError **error)
2277{
2278 GPtrArray *plugins = fu_plugin_list_get_all (self->plugin_list);
Richard Hughes791c91b2019-10-17 13:33:30 +01002279 g_autofree gchar *str = NULL;
Richard Hughes03df0d52019-02-01 18:31:39 +00002280 g_autoptr(FuDevice) device = NULL;
2281
2282 /* the device and plugin both may have changed */
2283 device = fu_engine_get_device_by_id (self, device_id, error);
2284 if (device == NULL)
2285 return FALSE;
Richard Hughes791c91b2019-10-17 13:33:30 +01002286 str = fu_device_to_string (device);
2287 g_debug ("performing prepare on %s", str);
Richard Hughes2031ce32019-10-30 14:16:24 +00002288 if (!fu_engine_device_prepare (self, device, flags, error))
Mario Limonciello44b9e462019-10-22 14:32:10 -05002289 return FALSE;
Richard Hughes03df0d52019-02-01 18:31:39 +00002290 for (guint j = 0; j < plugins->len; j++) {
2291 FuPlugin *plugin_tmp = g_ptr_array_index (plugins, j);
2292 if (!fu_plugin_runner_update_prepare (plugin_tmp, flags, device, error))
2293 return FALSE;
2294 }
Richard Hughes802bb312019-10-17 13:32:23 +01002295
2296 /* wait for device to disconnect and reconnect */
2297 if (fu_device_has_flag (device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG)) {
2298 if (!fu_device_list_wait_for_replug (self->device_list, device, error)) {
2299 g_prefix_error (error, "failed to wait for prepare replug: ");
2300 return FALSE;
2301 }
2302 }
Richard Hughes03df0d52019-02-01 18:31:39 +00002303 return TRUE;
2304}
2305
2306static gboolean
2307fu_engine_update_cleanup (FuEngine *self,
2308 FwupdInstallFlags flags,
2309 const gchar *device_id,
2310 GError **error)
2311{
2312 GPtrArray *plugins = fu_plugin_list_get_all (self->plugin_list);
Richard Hughes791c91b2019-10-17 13:33:30 +01002313 g_autofree gchar *str = NULL;
Richard Hughes03df0d52019-02-01 18:31:39 +00002314 g_autoptr(FuDevice) device = NULL;
2315
2316 /* the device and plugin both may have changed */
2317 device = fu_engine_get_device_by_id (self, device_id, error);
2318 if (device == NULL)
2319 return FALSE;
Richard Hughes791c91b2019-10-17 13:33:30 +01002320 str = fu_device_to_string (device);
2321 g_debug ("performing cleanup on %s", str);
Richard Hughes2031ce32019-10-30 14:16:24 +00002322 if (!fu_engine_device_cleanup (self, device, flags, error))
Richard Hughes791c91b2019-10-17 13:33:30 +01002323 return FALSE;
Richard Hughes03df0d52019-02-01 18:31:39 +00002324 for (guint j = 0; j < plugins->len; j++) {
2325 FuPlugin *plugin_tmp = g_ptr_array_index (plugins, j);
2326 if (!fu_plugin_runner_update_cleanup (plugin_tmp, flags, device, error))
2327 return FALSE;
2328 }
Richard Hughes802bb312019-10-17 13:32:23 +01002329
2330 /* wait for device to disconnect and reconnect */
2331 if (fu_device_has_flag (device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG)) {
2332 if (!fu_device_list_wait_for_replug (self->device_list, device, error)) {
2333 g_prefix_error (error, "failed to wait for cleanup replug: ");
2334 return FALSE;
2335 }
2336 }
Richard Hughes03df0d52019-02-01 18:31:39 +00002337 return TRUE;
2338}
2339
2340static gboolean
2341fu_engine_update_detach (FuEngine *self, const gchar *device_id, GError **error)
2342{
2343 FuPlugin *plugin;
Richard Hughes791c91b2019-10-17 13:33:30 +01002344 g_autofree gchar *str = NULL;
Richard Hughes03df0d52019-02-01 18:31:39 +00002345 g_autoptr(FuDevice) device = NULL;
2346
2347 /* the device and plugin both may have changed */
2348 device = fu_engine_get_device_by_id (self, device_id, error);
2349 if (device == NULL)
2350 return FALSE;
Richard Hughes791c91b2019-10-17 13:33:30 +01002351 str = fu_device_to_string (device);
2352 g_debug ("performing detach on %s", str);
Richard Hughes03df0d52019-02-01 18:31:39 +00002353 plugin = fu_plugin_list_find_by_name (self->plugin_list,
2354 fu_device_get_plugin (device),
2355 error);
2356 if (plugin == NULL)
2357 return FALSE;
2358 if (!fu_plugin_runner_update_detach (plugin, device, error))
2359 return FALSE;
2360 return TRUE;
2361}
2362
2363static gboolean
2364fu_engine_update_attach (FuEngine *self, const gchar *device_id, GError **error)
2365{
2366 FuPlugin *plugin;
Richard Hughes791c91b2019-10-17 13:33:30 +01002367 g_autofree gchar *str = NULL;
Richard Hughes03df0d52019-02-01 18:31:39 +00002368 g_autoptr(FuDevice) device = NULL;
2369
2370 /* the device and plugin both may have changed */
2371 device = fu_engine_get_device_by_id (self, device_id, error);
2372 if (device == NULL) {
2373 g_prefix_error (error, "failed to get device after update: ");
2374 return FALSE;
2375 }
Richard Hughes791c91b2019-10-17 13:33:30 +01002376 str = fu_device_to_string (device);
2377 g_debug ("performing attach on %s", str);
Richard Hughes03df0d52019-02-01 18:31:39 +00002378 plugin = fu_plugin_list_find_by_name (self->plugin_list,
2379 fu_device_get_plugin (device),
2380 error);
2381 if (plugin == NULL)
2382 return FALSE;
Mario Limoncielloe1b4d8e2019-10-12 11:19:10 -05002383
Richard Hughes03df0d52019-02-01 18:31:39 +00002384 if (!fu_plugin_runner_update_attach (plugin, device, error))
2385 return FALSE;
2386 return TRUE;
2387}
2388
Mario Limonciello96a0dd52019-02-25 13:50:03 -06002389gboolean
2390fu_engine_activate (FuEngine *self, const gchar *device_id, GError **error)
2391{
2392 FuPlugin *plugin;
Richard Hughes791c91b2019-10-17 13:33:30 +01002393 g_autofree gchar *str = NULL;
Mario Limonciello96a0dd52019-02-25 13:50:03 -06002394 g_autoptr(FuDevice) device = NULL;
2395
2396 g_return_val_if_fail (FU_IS_ENGINE (self), FALSE);
2397 g_return_val_if_fail (device_id != NULL, FALSE);
2398 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
2399
2400 /* check the device exists */
2401 device = fu_device_list_get_by_id (self->device_list, device_id, error);
2402 if (device == NULL)
2403 return FALSE;
Richard Hughes791c91b2019-10-17 13:33:30 +01002404 str = fu_device_to_string (device);
2405 g_debug ("performing activate on %s", str);
Mario Limonciello96a0dd52019-02-25 13:50:03 -06002406 plugin = fu_plugin_list_find_by_name (self->plugin_list,
2407 fu_device_get_plugin (device),
2408 error);
2409 if (plugin == NULL)
2410 return FALSE;
2411 g_debug ("Activating %s", fu_device_get_name (device));
2412
2413 if (!fu_plugin_runner_activate (plugin, device, error))
2414 return FALSE;
2415
2416 fu_engine_emit_device_changed (self, device);
2417 fu_engine_emit_changed (self);
2418
2419 return TRUE;
2420}
2421
Richard Hughes03df0d52019-02-01 18:31:39 +00002422static gboolean
2423fu_engine_update_reload (FuEngine *self, const gchar *device_id, GError **error)
2424{
2425 FuPlugin *plugin;
Richard Hughes791c91b2019-10-17 13:33:30 +01002426 g_autofree gchar *str = NULL;
Richard Hughes03df0d52019-02-01 18:31:39 +00002427 g_autoptr(FuDevice) device = NULL;
2428
2429 /* the device and plugin both may have changed */
2430 device = fu_engine_get_device_by_id (self, device_id, error);
2431 if (device == NULL) {
2432 g_prefix_error (error, "failed to get device after update: ");
2433 return FALSE;
2434 }
Richard Hughes791c91b2019-10-17 13:33:30 +01002435 str = fu_device_to_string (device);
2436 g_debug ("performing reload on %s", str);
Richard Hughes03df0d52019-02-01 18:31:39 +00002437 plugin = fu_plugin_list_find_by_name (self->plugin_list,
2438 fu_device_get_plugin (device),
2439 error);
2440 if (plugin == NULL)
2441 return FALSE;
Mario Limoncielloe1b4d8e2019-10-12 11:19:10 -05002442
2443 if (fu_device_has_flag (device, FWUPD_DEVICE_FLAG_WILL_DISAPPEAR)) {
2444 g_debug ("skipping reload due to will-disappear flag");
2445 return TRUE;
2446 }
2447
Richard Hughes03df0d52019-02-01 18:31:39 +00002448 if (!fu_plugin_runner_update_reload (plugin, device, error)) {
2449 g_prefix_error (error, "failed to reload device: ");
2450 return FALSE;
2451 }
2452 return TRUE;
2453}
2454
2455static gboolean
2456fu_engine_update (FuEngine *self,
2457 const gchar *device_id,
2458 GBytes *blob_fw2,
2459 FwupdInstallFlags flags,
2460 GError **error)
2461{
2462 FuPlugin *plugin;
Richard Hughes791c91b2019-10-17 13:33:30 +01002463 g_autofree gchar *str = NULL;
Richard Hughes03df0d52019-02-01 18:31:39 +00002464 g_autoptr(FuDevice) device = NULL;
Richard Hughes019a1bc2019-11-26 10:19:33 +00002465 g_autoptr(FuDevice) device_pending = NULL;
2466
2467 /* cancel the pending action */
2468 if (!fu_engine_offline_invalidate (error))
2469 return FALSE;
Richard Hughes03df0d52019-02-01 18:31:39 +00002470
2471 /* the device and plugin both may have changed */
2472 device = fu_engine_get_device_by_id (self, device_id, error);
2473 if (device == NULL) {
2474 g_prefix_error (error, "failed to get device after detach: ");
2475 return FALSE;
2476 }
Richard Hughes019a1bc2019-11-26 10:19:33 +00002477 device_pending = fu_history_get_device_by_id (self->history, device_id, NULL);
Richard Hughes791c91b2019-10-17 13:33:30 +01002478 str = fu_device_to_string (device);
2479 g_debug ("performing update on %s", str);
Richard Hughes03df0d52019-02-01 18:31:39 +00002480 plugin = fu_plugin_list_find_by_name (self->plugin_list,
2481 fu_device_get_plugin (device),
2482 error);
2483 if (plugin == NULL)
2484 return FALSE;
2485 if (!fu_plugin_runner_update (plugin, device, blob_fw2, flags, error)) {
2486 g_autoptr(GError) error_attach = NULL;
2487 g_autoptr(GError) error_cleanup = NULL;
2488
2489 /* attack back into runtime then cleanup */
2490 if (!fu_plugin_runner_update_attach (plugin,
2491 device,
2492 &error_attach)) {
2493 g_warning ("failed to attach device after failed update: %s",
2494 error_attach->message);
2495 }
2496 if (!fu_engine_update_cleanup (self, flags, device_id, &error_cleanup)) {
2497 g_warning ("failed to update-cleanup after failed update: %s",
2498 error_cleanup->message);
2499 }
2500 return FALSE;
2501 }
Richard Hughes019a1bc2019-11-26 10:19:33 +00002502
2503 /* cleanup */
2504 if (device_pending != NULL) {
2505 const gchar *tmp;
2506 FwupdRelease *release;
2507
2508 /* update history database */
2509 fu_device_set_update_state (device, FWUPD_UPDATE_STATE_SUCCESS);
2510 if (!fu_history_modify_device (self->history, device, error))
2511 return FALSE;
2512
2513 /* delete cab file */
2514 release = fu_device_get_release_default (device_pending);
2515 tmp = fwupd_release_get_filename (release);
2516 if (tmp != NULL && g_str_has_prefix (tmp, FWUPD_LIBEXECDIR)) {
2517 g_autoptr(GError) error_delete = NULL;
2518 g_autoptr(GFile) file = NULL;
2519 file = g_file_new_for_path (tmp);
2520 if (!g_file_delete (file, NULL, &error_delete)) {
2521 g_set_error (error,
2522 FWUPD_ERROR,
2523 FWUPD_ERROR_INVALID_FILE,
2524 "Failed to delete %s: %s",
2525 tmp, error_delete->message);
2526 return FALSE;
2527 }
2528 }
2529 }
Richard Hughes03df0d52019-02-01 18:31:39 +00002530 return TRUE;
2531}
2532
Richard Hughesa58510b2019-10-30 10:03:12 +00002533GBytes *
2534fu_engine_firmware_read (FuEngine *self,
2535 FuDevice *device,
2536 FwupdInstallFlags flags,
2537 GError **error)
2538{
2539 g_autoptr(FuDeviceLocker) locker = NULL;
2540 g_autoptr(FuFirmware) firmware = NULL;
2541
2542 /* open, detach, read, attach, serialize */
2543 locker = fu_device_locker_new (device, error);
Richard Hughes8bb64982020-04-16 17:03:18 +01002544 if (locker == NULL) {
2545 g_prefix_error (error, "failed to open device for firmware read: ");
Richard Hughesa58510b2019-10-30 10:03:12 +00002546 return NULL;
Richard Hughes8bb64982020-04-16 17:03:18 +01002547 }
Richard Hughesa58510b2019-10-30 10:03:12 +00002548 if (!fu_device_detach (device, error))
2549 return NULL;
2550 firmware = fu_device_read_firmware (device, error);
2551 if (firmware == NULL) {
2552 g_autoptr(GError) error_local = NULL;
2553 if (!fu_device_attach (device, &error_local)) {
2554 g_warning ("failed to attach after read image failure: %s",
2555 error_local->message);
2556 }
2557 return NULL;
2558 }
2559 if (!fu_device_attach (device, error))
2560 return NULL;
2561 return fu_firmware_write (firmware, error);
2562}
2563
Richard Hughes6e7419d2018-05-18 10:11:36 +01002564gboolean
2565fu_engine_install_blob (FuEngine *self,
Richard Hughes03df0d52019-02-01 18:31:39 +00002566 FuDevice *device,
2567 GBytes *blob_fw,
Richard Hughes6e7419d2018-05-18 10:11:36 +01002568 FwupdInstallFlags flags,
2569 GError **error)
2570{
Richard Hughes52976782019-02-01 21:23:01 +00002571 guint retries = 0;
Richard Hughes03df0d52019-02-01 18:31:39 +00002572 g_autofree gchar *device_id = NULL;
Mario Limonciello4afe7fb2018-08-05 23:14:39 -05002573 g_autoptr(GTimer) timer = g_timer_new ();
Richard Hughes6e7419d2018-05-18 10:11:36 +01002574
Richard Hughesadcc16a2017-08-21 12:26:46 +01002575 /* test the firmware is not an empty blob */
Richard Hughes03df0d52019-02-01 18:31:39 +00002576 if (g_bytes_get_size (blob_fw) == 0) {
Richard Hughesadcc16a2017-08-21 12:26:46 +01002577 g_set_error (error,
2578 FWUPD_ERROR,
2579 FWUPD_ERROR_INVALID_FILE,
2580 "Firmware is invalid as has zero size");
2581 return FALSE;
2582 }
2583
Richard Hughesbc3a4e12018-01-06 22:41:47 +00002584 /* mark this as modified even if we actually fail to do the update */
2585 fu_device_set_modified (device, (guint64) g_get_real_time () / G_USEC_PER_SEC);
2586
Richard Hughes52976782019-02-01 21:23:01 +00002587 /* plugins can set FWUPD_DEVICE_FLAG_ANOTHER_WRITE_REQUIRED to run again, but they
2588 * must return TRUE rather than an error */
Richard Hughes03df0d52019-02-01 18:31:39 +00002589 device_id = g_strdup (fu_device_get_id (device));
Richard Hughes52976782019-02-01 21:23:01 +00002590 do {
2591 /* check for a loop */
2592 if (++retries > 5) {
2593 g_set_error_literal (error,
2594 FWUPD_ERROR,
2595 FWUPD_ERROR_INTERNAL,
2596 "aborting device write loop, limit 5");
2597 return FALSE;
2598 }
Aleksander Morgado37b985c2019-01-20 11:23:32 +01002599
Richard Hughes52976782019-02-01 21:23:01 +00002600 /* don't rely on a plugin clearing this */
2601 fu_device_remove_flag (device, FWUPD_DEVICE_FLAG_ANOTHER_WRITE_REQUIRED);
Richard Hughesbc3a4e12018-01-06 22:41:47 +00002602
Richard Hughes52976782019-02-01 21:23:01 +00002603 /* signal to all the plugins the update is about to happen */
2604 if (!fu_engine_update_prepare (self, flags, device_id, error))
2605 return FALSE;
Richard Hughesbc3a4e12018-01-06 22:41:47 +00002606
Richard Hughes52976782019-02-01 21:23:01 +00002607 /* detach to bootloader mode */
2608 if (!fu_engine_update_detach (self, device_id, error))
2609 return FALSE;
2610
2611 /* install */
2612 if (!fu_engine_update (self, device_id, blob_fw, flags, error))
2613 return FALSE;
2614
2615 /* attach into runtime mode */
2616 if (!fu_engine_update_attach (self, device_id, error))
2617 return FALSE;
2618
2619 } while (fu_device_has_flag (device, FWUPD_DEVICE_FLAG_ANOTHER_WRITE_REQUIRED));
Aleksander Morgado37b985c2019-01-20 11:23:32 +01002620
Richard Hughes0d7fdb32017-11-11 20:23:14 +00002621 /* get the new version number */
Richard Hughes03df0d52019-02-01 18:31:39 +00002622 if (!fu_engine_update_reload (self, device_id, error))
Richard Hughes0d7fdb32017-11-11 20:23:14 +00002623 return FALSE;
Richard Hughes9945edb2017-06-19 10:03:55 +01002624
2625 /* signal to all the plugins the update has happened */
Richard Hughes03df0d52019-02-01 18:31:39 +00002626 if (!fu_engine_update_cleanup (self, flags, device_id, error))
2627 return FALSE;
Richard Hughes9945edb2017-06-19 10:03:55 +01002628
2629 /* make the UI update */
Richard Hughes7772dcf2018-09-10 15:49:59 +01002630 fu_engine_set_status (self, FWUPD_STATUS_IDLE);
Mario Limonciello4afe7fb2018-08-05 23:14:39 -05002631 g_debug ("Updating %s took %f seconds", fu_device_get_name (device),
2632 g_timer_elapsed (timer, NULL));
Richard Hughes76e0f942018-05-14 16:24:00 +01002633 return TRUE;
Richard Hughes9945edb2017-06-19 10:03:55 +01002634}
2635
Richard Hughes0a7e7832017-11-22 11:01:13 +00002636static FuDevice *
Richard Hughesbc3a4e12018-01-06 22:41:47 +00002637fu_engine_get_item_by_id_fallback_history (FuEngine *self, const gchar *id, GError **error)
Richard Hughes9945edb2017-06-19 10:03:55 +01002638{
Richard Hughes9945edb2017-06-19 10:03:55 +01002639 g_autoptr(GPtrArray) devices = NULL;
2640
2641 /* not a wildcard */
Richard Hughes65e44ca2018-01-30 17:26:30 +00002642 if (g_strcmp0 (id, FWUPD_DEVICE_ID_ANY) != 0) {
2643 g_autoptr(FuDevice) dev = NULL;
2644 g_autoptr(GError) error_local = NULL;
2645
2646 /* get this one device */
2647 dev = fu_history_get_device_by_id (self->history, id, &error_local);
2648 if (dev == NULL) {
2649 g_set_error (error,
2650 FWUPD_ERROR,
2651 FWUPD_ERROR_NOTHING_TO_DO,
2652 "Failed to find %s in history database: %s",
2653 id, error_local->message);
2654 return NULL;
2655 }
2656
2657 /* only useful */
2658 if (fu_device_get_update_state (dev) == FWUPD_UPDATE_STATE_SUCCESS ||
Richard Hughescce6a1c2019-04-16 17:25:48 +01002659 fu_device_get_update_state (dev) == FWUPD_UPDATE_STATE_FAILED_TRANSIENT ||
Richard Hughes65e44ca2018-01-30 17:26:30 +00002660 fu_device_get_update_state (dev) == FWUPD_UPDATE_STATE_FAILED) {
2661 return g_steal_pointer (&dev);
2662 }
2663
2664 /* nothing in database */
2665 g_set_error (error,
2666 FWUPD_ERROR,
2667 FWUPD_ERROR_NOTHING_TO_DO,
2668 "Device %s has no results to report",
2669 fu_device_get_id (dev));
2670 return NULL;
2671 }
Richard Hughes9945edb2017-06-19 10:03:55 +01002672
2673 /* allow '*' for any */
Richard Hughesbc3a4e12018-01-06 22:41:47 +00002674 devices = fu_history_get_devices (self->history, error);
Richard Hughes9945edb2017-06-19 10:03:55 +01002675 if (devices == NULL)
2676 return NULL;
2677 for (guint i = 0; i < devices->len; i++) {
Richard Hughes65e44ca2018-01-30 17:26:30 +00002678 FuDevice *dev = g_ptr_array_index (devices, i);
2679 if (fu_device_get_update_state (dev) == FWUPD_UPDATE_STATE_SUCCESS ||
Richard Hughescce6a1c2019-04-16 17:25:48 +01002680 fu_device_get_update_state (dev) == FWUPD_UPDATE_STATE_FAILED_TRANSIENT ||
Richard Hughes65e44ca2018-01-30 17:26:30 +00002681 fu_device_get_update_state (dev) == FWUPD_UPDATE_STATE_FAILED)
2682 return g_object_ref (dev);
Richard Hughes9945edb2017-06-19 10:03:55 +01002683 }
Richard Hughes65e44ca2018-01-30 17:26:30 +00002684 g_set_error_literal (error,
2685 FWUPD_ERROR,
2686 FWUPD_ERROR_NOTHING_TO_DO,
2687 "Failed to find any useful results to report");
2688 return NULL;
Richard Hughes9945edb2017-06-19 10:03:55 +01002689}
2690
Richard Hughes481aa2a2018-09-18 20:51:46 +01002691/* for the self tests */
2692void
2693fu_engine_set_silo (FuEngine *self, XbSilo *silo)
Richard Hughesbd4d2852017-09-13 14:05:14 +01002694{
Richard Hughes481aa2a2018-09-18 20:51:46 +01002695 g_return_if_fail (FU_IS_ENGINE (self));
2696 g_return_if_fail (XB_IS_SILO (silo));
2697 g_set_object (&self->silo, silo);
Richard Hughes9945edb2017-06-19 10:03:55 +01002698}
2699
2700static gboolean
Richard Hughes1af48b12018-12-03 11:51:17 +00002701fu_engine_appstream_upgrade_cb (XbBuilderFixup *self,
2702 XbBuilderNode *bn,
2703 gpointer user_data,
2704 GError **error)
2705{
2706 if (g_strcmp0 (xb_builder_node_get_element (bn), "metadata") == 0)
2707 xb_builder_node_set_element (bn, "custom");
2708 return TRUE;
2709}
2710
Mario Limonciello4f24d0b2019-01-26 01:19:59 -06002711static XbBuilderSource *
2712fu_engine_create_metadata_builder_source (FuEngine *self,
2713 const gchar *fn,
2714 GError **error)
2715{
2716 g_autoptr(GBytes) blob = NULL;
2717 g_autoptr(XbSilo) silo = NULL;
2718 g_autoptr(XbBuilderSource) source = xb_builder_source_new ();
2719 g_autofree gchar *xml = NULL;
2720
2721 g_debug ("building metadata for %s", fn);
2722 blob = fu_common_get_contents_bytes (fn, error);
2723 if (blob == NULL)
2724 return NULL;
2725
2726 /* convert the silo for the CAB into a XbBuilderSource */
2727 silo = fu_engine_get_silo_from_blob (self, blob, error);
2728 if (silo == NULL)
2729 return NULL;
2730 xml = xb_silo_export (silo, XB_NODE_EXPORT_FLAG_NONE, error);
2731 if (xml == NULL)
2732 return NULL;
2733 if (!xb_builder_source_load_xml (source, xml,
2734 XB_BUILDER_SOURCE_FLAG_NONE,
2735 error))
2736 return NULL;
2737 return g_steal_pointer (&source);
2738}
2739
2740static gboolean
2741fu_engine_create_metadata (FuEngine *self, XbBuilder *builder,
2742 FwupdRemote *remote, GError **error)
2743{
2744 g_autoptr(GPtrArray) files = NULL;
2745 const gchar *path;
2746
2747 /* find all files in directory */
2748 path = fwupd_remote_get_filename_cache (remote);
2749 files = fu_common_get_files_recursive (path, error);
2750 if (files == NULL)
2751 return FALSE;
2752
2753 /* add each source */
2754 for (guint i = 0; i < files->len; i++) {
2755 g_autoptr(XbBuilderNode) custom = NULL;
2756 g_autoptr(XbBuilderSource) source = NULL;
2757 g_autoptr(GError) error_local = NULL;
2758 const gchar *fn = g_ptr_array_index (files, i);
Crag Wangae422982020-03-05 23:42:53 +08002759 g_autofree gchar *fn_lowercase = g_ascii_strdown (fn, -1);
Mario Limonciello4f24d0b2019-01-26 01:19:59 -06002760
2761 /* check is cab file */
Crag Wangae422982020-03-05 23:42:53 +08002762 if (!g_str_has_suffix (fn_lowercase, ".cab")) {
Mario Limonciello4f24d0b2019-01-26 01:19:59 -06002763 g_debug ("ignoring: %s", fn);
2764 continue;
2765 }
2766
2767 /* build source for file */
2768 source = fu_engine_create_metadata_builder_source (self, fn, &error_local);
2769 if (source == NULL) {
2770 g_warning ("%s", error_local->message);
2771 continue;
2772 }
2773
2774 /* add metadata */
2775 custom = xb_builder_node_new ("custom");
2776 xb_builder_node_insert_text (custom,
2777 "value", fn,
2778 "key", "fwupd::FilenameCache",
2779 NULL);
2780 xb_builder_node_insert_text (custom,
2781 "value", fwupd_remote_get_id (remote),
2782 "key", "fwupd::RemoteId",
2783 NULL);
2784 xb_builder_source_set_info (source, custom);
2785 xb_builder_import_source (builder, source);
2786 }
2787 return TRUE;
2788}
2789
Richard Hughesf43381f2020-02-24 10:11:31 +00002790static void
Richard Hughes1ee18002020-03-31 21:46:49 +01002791fu_engine_ensure_device_supported (FuEngine *self, FuDevice *device)
Richard Hughesf43381f2020-02-24 10:11:31 +00002792{
Richard Hughes1ee18002020-03-31 21:46:49 +01002793 gboolean is_supported = FALSE;
2794 g_autoptr(GError) error = NULL;
2795 g_autoptr(GPtrArray) releases = NULL;
2796
2797 /* get all releases that pass the requirements */
2798 releases = fu_engine_get_releases_for_device (self,
2799 device,
2800 &error);
2801 if (releases == NULL) {
2802 if (!g_error_matches (error,
2803 FWUPD_ERROR,
2804 FWUPD_ERROR_NOTHING_TO_DO) &&
2805 !g_error_matches (error,
2806 FWUPD_ERROR,
2807 FWUPD_ERROR_NOT_SUPPORTED)) {
2808 g_warning ("failed to get releases for %s: %s",
2809 fu_device_get_name (device),
2810 error->message);
2811 }
2812 } else {
2813 if (releases->len > 0)
2814 is_supported = TRUE;
2815 }
2816
Richard Hughesf43381f2020-02-24 10:11:31 +00002817 /* was supported, now unsupported */
Richard Hughes1ee18002020-03-31 21:46:49 +01002818 if (!is_supported) {
Richard Hughesf43381f2020-02-24 10:11:31 +00002819 if (fu_device_has_flag (device, FWUPD_DEVICE_FLAG_SUPPORTED)) {
2820 fu_device_remove_flag (device, FWUPD_DEVICE_FLAG_SUPPORTED);
2821 fu_engine_emit_device_changed (self, device);
2822 }
2823 return;
2824 }
2825
2826 /* was unsupported, now supported */
2827 if (!fu_device_has_flag (device, FWUPD_DEVICE_FLAG_SUPPORTED)) {
2828 fu_device_add_flag (device, FWUPD_DEVICE_FLAG_SUPPORTED);
2829 fu_engine_emit_device_changed (self, device);
2830 }
2831}
2832
2833static void
2834fu_engine_md_refresh_device_name (FuEngine *self, FuDevice *device, XbNode *component)
2835{
2836 const gchar *name = NULL;
2837
2838 /* require data */
2839 if (component == NULL)
2840 return;
2841
2842 /* copy 1:1 */
2843 name = xb_node_query_text (component, "name", NULL);
2844 if (name != NULL)
2845 fu_device_set_name (device, name);
2846}
2847
2848static const gchar *
2849fu_common_device_category_to_name (const gchar *cat)
2850{
2851 if (g_strcmp0 (cat, "X-EmbeddedController") == 0)
2852 return "Embedded Controller";
2853 if (g_strcmp0 (cat, "X-ManagementEngine") == 0)
2854 return "Intel Management Engine";
2855 if (g_strcmp0 (cat, "X-CorporateManagementEngine") == 0)
2856 return "Intel Management Engine";
2857 if (g_strcmp0 (cat, "X-ConsumerManagementEngine") == 0)
2858 return "Intel Management Engine";
2859 if (g_strcmp0 (cat, "X-ThunderboltController") == 0)
2860 return "Thunderbolt Controller";
2861 if (g_strcmp0 (cat, "X-PlatformSecurityProcessor") == 0)
2862 return "Platform Security Processor";
Mario Limonciello7a9bb7e2020-04-02 10:28:41 -05002863 if (g_strcmp0 (cat, "X-CpuMicrocode") == 0)
2864 return "CPU Microcode";
Richard Hughesf43381f2020-02-24 10:11:31 +00002865 return NULL;
2866}
2867
2868static void
2869fu_engine_md_refresh_device_name_category (FuEngine *self, FuDevice *device, XbNode *component)
2870{
2871 const gchar *name = NULL;
2872 g_autoptr(GPtrArray) cats = NULL;
2873
2874 /* require data */
2875 if (component == NULL)
2876 return;
2877
2878 /* get AppStream and safe-compat categories */
2879 cats = xb_node_query (component, "categories/category|X-categories/category", 0, NULL);
2880 if (cats == NULL)
2881 return;
2882 for (guint i = 0; i < cats->len; i++) {
2883 XbNode *n = g_ptr_array_index (cats, i);
2884 name = fu_common_device_category_to_name (xb_node_get_text (n));
2885 if (name != NULL)
2886 break;
2887 }
2888 if (name != NULL)
2889 fu_device_set_name (device, name);
2890}
2891
2892static void
Richard Hughesb0976032020-02-24 14:17:04 +00002893_g_ptr_array_reverse (GPtrArray *array)
2894{
2895 guint last_idx = array->len - 1;
2896 for (guint i = 0; i < array->len / 2; i++) {
2897 gpointer tmp = array->pdata[i];
2898 array->pdata[i] = array->pdata[last_idx - i];
2899 array->pdata[last_idx - i] = tmp;
2900 }
2901}
2902
2903static void
2904fu_engine_md_refresh_device_verfmt (FuEngine *self, FuDevice *device, XbNode *component)
2905{
2906 FwupdVersionFormat verfmt = FWUPD_VERSION_FORMAT_UNKNOWN;
2907 g_autoptr(GPtrArray) verfmts = NULL;
2908
2909 /* require data */
2910 if (component == NULL)
2911 return;
2912
2913 /* get metadata */
2914 verfmts = xb_node_query (component, "custom/value[@key='LVFS::VersionFormat']", 0, NULL);
2915 if (verfmts == NULL)
2916 return;
2917 _g_ptr_array_reverse (verfmts);
2918 for (guint i = 0; i < verfmts->len; i++) {
2919 XbNode *value = g_ptr_array_index (verfmts, i);
2920 verfmt = fwupd_version_format_from_string (xb_node_get_text (value));
2921 if (verfmt != FWUPD_VERSION_FORMAT_UNKNOWN)
2922 break;
2923 }
2924
2925 /* found and different to existing */
2926 if (verfmt != FWUPD_VERSION_FORMAT_UNKNOWN &&
2927 fu_device_get_version_format (device) != verfmt) {
2928 fu_device_set_version_format (device, verfmt);
2929 if (fu_device_get_version_raw (device) != 0x0) {
2930 g_autofree gchar *version = NULL;
2931 version = fu_common_version_from_uint32 (fu_device_get_version_raw (device), verfmt);
2932 fu_device_set_version (device, version);
2933 }
2934 if (fu_device_get_version_lowest_raw (device) != 0x0) {
2935 g_autofree gchar *version = NULL;
2936 version = fu_common_version_from_uint32 (fu_device_get_version_lowest_raw (device), verfmt);
2937 fu_device_set_version_lowest (device, version);
2938 }
2939 if (fu_device_get_version_bootloader_raw (device) != 0x0) {
2940 g_autofree gchar *version = NULL;
2941 version = fu_common_version_from_uint32 (fu_device_get_version_bootloader_raw (device), verfmt);
2942 fu_device_set_version_bootloader (device, version);
2943 }
2944 }
2945}
2946
Mario Limonciello537da0e2020-03-09 15:38:17 -05002947void
Mario Limonciellof430da02020-03-09 14:03:03 -05002948fu_engine_md_refresh_device_from_component (FuEngine *self, FuDevice *device, XbNode *component)
Richard Hughesf43381f2020-02-24 10:11:31 +00002949{
Richard Hughesf43381f2020-02-24 10:11:31 +00002950 /* set the name */
2951 if (fu_device_has_flag (device, FWUPD_DEVICE_FLAG_MD_SET_NAME))
2952 fu_engine_md_refresh_device_name (self, device, component);
2953 if (fu_device_has_flag (device, FWUPD_DEVICE_FLAG_MD_SET_NAME_CATEGORY))
2954 fu_engine_md_refresh_device_name_category (self, device, component);
Richard Hughesb0976032020-02-24 14:17:04 +00002955
2956 /* fix the version */
2957 if (fu_device_has_flag (device, FWUPD_DEVICE_FLAG_MD_SET_VERFMT))
2958 fu_engine_md_refresh_device_verfmt (self, device, component);
Richard Hughesf43381f2020-02-24 10:11:31 +00002959}
2960
2961static void
2962fu_engine_md_refresh_devices (FuEngine *self)
2963{
2964 g_autoptr(GPtrArray) devices = fu_device_list_get_all (self->device_list);
2965 for (guint i = 0; i < devices->len; i++) {
2966 FuDevice *device = g_ptr_array_index (devices, i);
Mario Limonciellof430da02020-03-09 14:03:03 -05002967 g_autoptr(XbNode) component = fu_engine_get_component_by_guids (self, device);
Mario Limonciello537da0e2020-03-09 15:38:17 -05002968
2969 /* set or clear the SUPPORTED flag */
Richard Hughes1ee18002020-03-31 21:46:49 +01002970 fu_engine_ensure_device_supported (self, device);
Mario Limonciello537da0e2020-03-09 15:38:17 -05002971
2972 /* fixup the name and format as needed */
Mario Limonciellof430da02020-03-09 14:03:03 -05002973 fu_engine_md_refresh_device_from_component (self, device, component);
Richard Hughesf43381f2020-02-24 10:11:31 +00002974 }
2975}
2976
Richard Hughes1af48b12018-12-03 11:51:17 +00002977static gboolean
Richard Hughesc8cc77c2019-03-07 11:57:24 +00002978fu_engine_load_metadata_store (FuEngine *self, FuEngineLoadFlags flags, GError **error)
Richard Hughes9945edb2017-06-19 10:03:55 +01002979{
Richard Hughes9945edb2017-06-19 10:03:55 +01002980 GPtrArray *remotes;
Richard Hughesc8cc77c2019-03-07 11:57:24 +00002981 XbBuilderCompileFlags compile_flags = XB_BUILDER_COMPILE_FLAG_IGNORE_INVALID;
Richard Hughes481aa2a2018-09-18 20:51:46 +01002982 g_autofree gchar *cachedirpkg = NULL;
Richard Hughes481aa2a2018-09-18 20:51:46 +01002983 g_autofree gchar *xmlbfn = NULL;
2984 g_autoptr(GFile) xmlb = NULL;
Richard Hughes481aa2a2018-09-18 20:51:46 +01002985 g_autoptr(GPtrArray) components = NULL;
2986 g_autoptr(XbBuilder) builder = xb_builder_new ();
Richard Hughes9945edb2017-06-19 10:03:55 +01002987
Richard Hughes481aa2a2018-09-18 20:51:46 +01002988 /* clear existing silo */
2989 g_clear_object (&self->silo);
2990
2991 /* verbose profiling */
2992 if (g_getenv ("FWUPD_VERBOSE") != NULL) {
2993 xb_builder_set_profile_flags (builder,
2994 XB_SILO_PROFILE_FLAG_XPATH |
2995 XB_SILO_PROFILE_FLAG_DEBUG);
2996 }
Richard Hughes9945edb2017-06-19 10:03:55 +01002997
2998 /* load each enabled metadata file */
Richard Hughesd1808aa2019-12-10 15:20:30 +00002999 remotes = fu_remote_list_get_all (self->remote_list);
Richard Hughes9945edb2017-06-19 10:03:55 +01003000 for (guint i = 0; i < remotes->len; i++) {
3001 const gchar *path = NULL;
Richard Hughes5f733f22017-11-26 16:14:20 +00003002 g_autoptr(GError) error_local = NULL;
Richard Hughes481aa2a2018-09-18 20:51:46 +01003003 g_autoptr(GFile) file = NULL;
Richard Hughes1af48b12018-12-03 11:51:17 +00003004 g_autoptr(XbBuilderFixup) fixup = NULL;
Richard Hughes481aa2a2018-09-18 20:51:46 +01003005 g_autoptr(XbBuilderNode) custom = NULL;
3006 g_autoptr(XbBuilderSource) source = xb_builder_source_new ();
3007
Richard Hughes9945edb2017-06-19 10:03:55 +01003008 FwupdRemote *remote = g_ptr_array_index (remotes, i);
3009 if (!fwupd_remote_get_enabled (remote)) {
3010 g_debug ("remote %s not enabled, so skipping",
3011 fwupd_remote_get_id (remote));
3012 continue;
3013 }
3014 path = fwupd_remote_get_filename_cache (remote);
3015 if (!g_file_test (path, G_FILE_TEST_EXISTS)) {
3016 g_debug ("no %s, so skipping", path);
3017 continue;
3018 }
Richard Hughes481aa2a2018-09-18 20:51:46 +01003019
Mario Limonciello4f24d0b2019-01-26 01:19:59 -06003020 /* generate all metadata on demand */
3021 if (fwupd_remote_get_kind (remote) == FWUPD_REMOTE_KIND_DIRECTORY) {
3022 g_debug ("building metadata for remote '%s'",
3023 fwupd_remote_get_id (remote));
3024 if (!fu_engine_create_metadata (self, builder, remote, &error_local)) {
3025 g_warning ("failed to generate remote %s: %s",
3026 fwupd_remote_get_id (remote),
3027 error_local->message);
3028 }
3029 continue;
3030 }
3031
Richard Hughes481aa2a2018-09-18 20:51:46 +01003032 /* save the remote-id in the custom metadata space */
Richard Hughes481aa2a2018-09-18 20:51:46 +01003033 file = g_file_new_for_path (path);
3034 if (!xb_builder_source_load_file (source, file,
3035 XB_BUILDER_SOURCE_FLAG_NONE,
3036 NULL, &error_local)) {
Richard Hughes068d3432017-09-16 08:26:46 +01003037 g_warning ("failed to load remote %s: %s",
3038 fwupd_remote_get_id (remote),
3039 error_local->message);
3040 continue;
3041 }
Richard Hughes481aa2a2018-09-18 20:51:46 +01003042
Richard Hughes1af48b12018-12-03 11:51:17 +00003043 /* fix up any legacy installed files */
3044 fixup = xb_builder_fixup_new ("AppStreamUpgrade",
3045 fu_engine_appstream_upgrade_cb,
3046 self, NULL);
3047 xb_builder_fixup_set_max_depth (fixup, 3);
3048 xb_builder_source_add_fixup (source, fixup);
3049
Richard Hughes33171fd2018-11-09 13:29:11 +00003050 /* add metadata */
3051 custom = xb_builder_node_new ("custom");
3052 xb_builder_node_insert_text (custom,
3053 "value", path,
3054 "key", "fwupd::FilenameCache",
3055 NULL);
3056 xb_builder_node_insert_text (custom,
3057 "value", fwupd_remote_get_id (remote),
3058 "key", "fwupd::RemoteId",
3059 NULL);
3060 xb_builder_source_set_info (source, custom);
3061
Richard Hughes481aa2a2018-09-18 20:51:46 +01003062 /* we need to watch for changes? */
3063 xb_builder_import_source (builder, source);
Richard Hughes9945edb2017-06-19 10:03:55 +01003064 }
3065
Richard Hughes88dc0f42019-03-07 11:58:11 +00003066 /* on a read-only filesystem don't care about the cache GUID */
3067 if (flags & FU_ENGINE_LOAD_FLAG_READONLY_FS)
3068 compile_flags |= XB_BUILDER_COMPILE_FLAG_IGNORE_GUID;
Richard Hughes88dc0f42019-03-07 11:58:11 +00003069
Richard Hughes481aa2a2018-09-18 20:51:46 +01003070 /* ensure silo is up to date */
3071 cachedirpkg = fu_common_get_path (FU_PATH_KIND_CACHEDIR_PKG);
3072 xmlbfn = g_build_filename (cachedirpkg, "metadata.xmlb", NULL);
3073 xmlb = g_file_new_for_path (xmlbfn);
Richard Hughesc8cc77c2019-03-07 11:57:24 +00003074 self->silo = xb_builder_ensure (builder, xmlb, compile_flags, NULL, error);
Richard Hughes481aa2a2018-09-18 20:51:46 +01003075 if (self->silo == NULL)
3076 return FALSE;
3077
Richard Hughes9945edb2017-06-19 10:03:55 +01003078 /* print what we've got */
Richard Hughes481aa2a2018-09-18 20:51:46 +01003079 components = xb_silo_query (self->silo, "components/component", 0, NULL);
3080 if (components != NULL)
3081 g_debug ("%u components now in silo", components->len);
Richard Hughes9945edb2017-06-19 10:03:55 +01003082
Richard Hughes634da032018-11-05 11:42:20 +00003083 /* build the index */
3084 if (!xb_silo_query_build_index (self->silo,
3085 "components/component/provides/firmware",
3086 "type", error))
3087 return FALSE;
3088 if (!xb_silo_query_build_index (self->silo,
3089 "components/component/provides/firmware",
3090 NULL, error))
3091 return FALSE;
3092
Richard Hughesf43381f2020-02-24 10:11:31 +00003093 /* success */
Richard Hughes9945edb2017-06-19 10:03:55 +01003094 return TRUE;
3095}
3096
Mario Limonciello263cab92019-08-20 17:16:00 -05003097static void
3098fu_engine_config_changed_cb (FuConfig *config, FuEngine *self)
3099{
Richard Hughesd1808aa2019-12-10 15:20:30 +00003100 fu_idle_set_timeout (self->idle, fu_config_get_idle_timeout (config));
3101}
3102
3103static void
3104fu_engine_remote_list_changed_cb (FuRemoteList *remote_list, FuEngine *self)
3105{
Mario Limonciello263cab92019-08-20 17:16:00 -05003106 g_autoptr(GError) error_local = NULL;
3107 if (!fu_engine_load_metadata_store (self, FU_ENGINE_LOAD_FLAG_NONE,
3108 &error_local))
3109 g_warning ("Failed to reload metadata store: %s",
3110 error_local->message);
Mario Limonciellod81ea2e2020-01-13 14:11:43 -06003111
Richard Hughesf43381f2020-02-24 10:11:31 +00003112 /* set device properties from the metadata */
3113 fu_engine_md_refresh_devices (self);
3114
Mario Limonciellod81ea2e2020-01-13 14:11:43 -06003115 /* make the UI update */
3116 fu_engine_emit_changed (self);
Mario Limonciello263cab92019-08-20 17:16:00 -05003117}
3118
Richard Hughesd5aab652020-02-25 12:47:50 +00003119static gint
3120fu_engine_sort_jcat_results_timestamp_cb (gconstpointer a, gconstpointer b)
3121{
3122 JcatResult *ra = *((JcatResult **) a);
3123 JcatResult *rb = *((JcatResult **) b);
3124 if (jcat_result_get_timestamp (ra) < jcat_result_get_timestamp (rb))
3125 return -1;
3126 if (jcat_result_get_timestamp (ra) > jcat_result_get_timestamp (rb))
3127 return 1;
3128 return 0;
3129}
3130
3131static JcatResult *
3132fu_engine_get_system_jcat_result (FuEngine *self, FwupdRemote *remote, GError **error)
Richard Hughesf69a4812017-08-16 12:27:51 +01003133{
3134 g_autoptr(GBytes) blob = NULL;
3135 g_autoptr(GBytes) blob_sig = NULL;
Richard Hughesd5aab652020-02-25 12:47:50 +00003136 g_autoptr(GInputStream) istream = NULL;
3137 g_autoptr(GPtrArray) results = NULL;
3138 g_autoptr(JcatItem) jcat_item = NULL;
3139 g_autoptr(JcatFile) jcat_file = jcat_file_new ();
3140
Richard Hughesf69a4812017-08-16 12:27:51 +01003141 blob = fu_common_get_contents_bytes (fwupd_remote_get_filename_cache (remote), error);
3142 if (blob == NULL)
3143 return NULL;
3144 blob_sig = fu_common_get_contents_bytes (fwupd_remote_get_filename_cache_sig (remote), error);
3145 if (blob_sig == NULL)
3146 return NULL;
Richard Hughesd5aab652020-02-25 12:47:50 +00003147 istream = g_memory_input_stream_new_from_bytes (blob_sig);
3148 if (!jcat_file_import_stream (jcat_file, istream,
3149 JCAT_IMPORT_FLAG_NONE,
3150 NULL, error))
3151 return NULL;
3152 jcat_item = jcat_file_get_item_default (jcat_file, error);
3153 if (jcat_item == NULL)
3154 return NULL;
3155 results = jcat_context_verify_item (self->jcat_context,
3156 blob, jcat_item,
3157 JCAT_VERIFY_FLAG_REQUIRE_CHECKSUM |
3158 JCAT_VERIFY_FLAG_REQUIRE_SIGNATURE,
3159 error);
3160 if (results == NULL)
3161 return NULL;
3162 g_ptr_array_sort (results, fu_engine_sort_jcat_results_timestamp_cb);
3163
3164 /* return the newest one */
3165 return g_object_ref (g_ptr_array_index (results, 0));
3166}
3167
3168static gboolean
3169fu_engine_validate_result_timestamp (JcatResult *jcat_result,
3170 JcatResult *jcat_result_old,
3171 GError **error)
3172{
3173 gint64 delta = 0;
3174
3175 g_return_val_if_fail (JCAT_IS_RESULT (jcat_result), FALSE);
3176 g_return_val_if_fail (JCAT_IS_RESULT (jcat_result_old), FALSE);
3177
Richard Hughes64ebf912020-05-29 07:51:38 +01003178 if (jcat_result_get_timestamp (jcat_result) == 0) {
3179 g_set_error (error,
3180 FWUPD_ERROR,
3181 FWUPD_ERROR_INVALID_FILE,
3182 "no signing timestamp");
3183 return FALSE;
3184 }
3185 if (jcat_result_get_timestamp (jcat_result_old) > 0) {
Richard Hughesd5aab652020-02-25 12:47:50 +00003186 delta = jcat_result_get_timestamp (jcat_result) -
3187 jcat_result_get_timestamp (jcat_result_old);
3188 }
3189 if (delta < 0) {
3190 g_set_error (error,
3191 FWUPD_ERROR,
3192 FWUPD_ERROR_INVALID_FILE,
3193 "new signing timestamp was %"
3194 G_GINT64_FORMAT " seconds older",
3195 -delta);
3196 return FALSE;
3197 }
3198 if (delta > 0)
3199 g_debug ("timestamp increased, so no rollback");
3200 return TRUE;
Richard Hughesf69a4812017-08-16 12:27:51 +01003201}
3202
Richard Hughes9945edb2017-06-19 10:03:55 +01003203/**
Richard Hughesbe5b0192020-01-17 14:32:23 +00003204 * fu_engine_update_metadata_bytes:
Richard Hughes9945edb2017-06-19 10:03:55 +01003205 * @self: A #FuEngine
Richard Hughes4eada342017-10-03 21:20:32 +01003206 * @remote_id: A remote ID, e.g. `lvfs`
Richard Hughesbe5b0192020-01-17 14:32:23 +00003207 * @bytes_raw: Blob of metadata
Richard Hughesd5aab652020-02-25 12:47:50 +00003208 * @bytes_sig: Blob of metadata signature, typically Jcat binary format
Richard Hughes9945edb2017-06-19 10:03:55 +01003209 * @error: A #GError, or %NULL
3210 *
3211 * Updates the metadata for a specific remote.
3212 *
Richard Hughes9945edb2017-06-19 10:03:55 +01003213 * Returns: %TRUE for success
3214 **/
3215gboolean
Richard Hughesbe5b0192020-01-17 14:32:23 +00003216fu_engine_update_metadata_bytes (FuEngine *self, const gchar *remote_id,
3217 GBytes *bytes_raw, GBytes *bytes_sig, GError **error)
Richard Hughes9945edb2017-06-19 10:03:55 +01003218{
Richard Hughes7403dc52017-08-10 15:34:10 +01003219 FwupdKeyringKind keyring_kind;
Richard Hughes9945edb2017-06-19 10:03:55 +01003220 FwupdRemote *remote;
Mario Limonciellocf63aec2018-06-11 12:10:54 -05003221 g_autofree gchar *pki_dir = NULL;
3222 g_autofree gchar *sysconfdir = NULL;
Richard Hughes9945edb2017-06-19 10:03:55 +01003223
3224 g_return_val_if_fail (FU_IS_ENGINE (self), FALSE);
3225 g_return_val_if_fail (remote_id != NULL, FALSE);
Richard Hughesbe5b0192020-01-17 14:32:23 +00003226 g_return_val_if_fail (bytes_raw != NULL, FALSE);
3227 g_return_val_if_fail (bytes_sig != NULL, FALSE);
Richard Hughes9945edb2017-06-19 10:03:55 +01003228 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
3229
Richard Hughes9945edb2017-06-19 10:03:55 +01003230 /* check remote is valid */
Richard Hughesd1808aa2019-12-10 15:20:30 +00003231 remote = fu_remote_list_get_by_id (self->remote_list, remote_id);
Richard Hughes9945edb2017-06-19 10:03:55 +01003232 if (remote == NULL) {
3233 g_set_error (error,
3234 FWUPD_ERROR,
3235 FWUPD_ERROR_NOT_FOUND,
3236 "remote %s not found", remote_id);
3237 return FALSE;
3238 }
3239 if (!fwupd_remote_get_enabled (remote)) {
3240 g_set_error (error,
3241 FWUPD_ERROR,
3242 FWUPD_ERROR_NOT_SUPPORTED,
3243 "remote %s not enabled", remote_id);
3244 return FALSE;
3245 }
3246
Richard Hughes9945edb2017-06-19 10:03:55 +01003247 /* verify file */
Richard Hughes7403dc52017-08-10 15:34:10 +01003248 keyring_kind = fwupd_remote_get_keyring_kind (remote);
3249 if (keyring_kind != FWUPD_KEYRING_KIND_NONE) {
Richard Hughesd5aab652020-02-25 12:47:50 +00003250 JcatResult *jcat_result;
Richard Hughesf69a4812017-08-16 12:27:51 +01003251 g_autoptr(GError) error_local = NULL;
Richard Hughesd5aab652020-02-25 12:47:50 +00003252 g_autoptr(GInputStream) istream = NULL;
3253 g_autoptr(GPtrArray) results = NULL;
3254 g_autoptr(JcatFile) jcat_file = jcat_file_new ();
3255 g_autoptr(JcatItem) jcat_item = NULL;
3256 g_autoptr(JcatResult) jcat_result_old = NULL;
3257
3258 /* load Jcat file */
3259 istream = g_memory_input_stream_new_from_bytes (bytes_sig);
3260 if (!jcat_file_import_stream (jcat_file, istream,
3261 JCAT_IMPORT_FLAG_NONE,
3262 NULL, error))
Richard Hughes7403dc52017-08-10 15:34:10 +01003263 return FALSE;
Richard Hughesd5aab652020-02-25 12:47:50 +00003264
3265 /* this should only be signing one thing */
3266 jcat_item = jcat_file_get_item_default (jcat_file, error);
3267 if (jcat_item == NULL)
Richard Hughes14047d72017-08-18 10:58:47 +01003268 return FALSE;
Richard Hughesd5aab652020-02-25 12:47:50 +00003269 results = jcat_context_verify_item (self->jcat_context,
3270 bytes_raw, jcat_item,
3271 JCAT_VERIFY_FLAG_REQUIRE_CHECKSUM |
3272 JCAT_VERIFY_FLAG_REQUIRE_SIGNATURE,
Richard Hughesf28abe72019-03-07 16:01:02 +00003273 error);
Richard Hughesd5aab652020-02-25 12:47:50 +00003274 if (results == NULL)
Richard Hughes7403dc52017-08-10 15:34:10 +01003275 return FALSE;
Richard Hughesf69a4812017-08-16 12:27:51 +01003276
Richard Hughesd5aab652020-02-25 12:47:50 +00003277 /* return the newest one */
3278 g_ptr_array_sort (results, fu_engine_sort_jcat_results_timestamp_cb);
3279 jcat_result = g_ptr_array_index (results, 0);
3280
Richard Hughesf69a4812017-08-16 12:27:51 +01003281 /* verify the metadata was signed later than the existing
3282 * metadata for this remote to mitigate a rollback attack */
Richard Hughesd5aab652020-02-25 12:47:50 +00003283 jcat_result_old = fu_engine_get_system_jcat_result (self, remote, &error_local);
3284 if (jcat_result_old == NULL) {
Richard Hughesf69a4812017-08-16 12:27:51 +01003285 if (g_error_matches (error_local,
3286 G_FILE_ERROR,
3287 G_FILE_ERROR_NOENT)) {
3288 g_debug ("no existing valid keyrings: %s",
3289 error_local->message);
3290 } else {
3291 g_warning ("could not get existing keyring result: %s",
3292 error_local->message);
3293 }
3294 } else {
Richard Hughesd5aab652020-02-25 12:47:50 +00003295 if (!fu_engine_validate_result_timestamp (jcat_result,
3296 jcat_result_old,
3297 error))
Richard Hughesf69a4812017-08-16 12:27:51 +01003298 return FALSE;
Richard Hughesf69a4812017-08-16 12:27:51 +01003299 }
Richard Hughes7403dc52017-08-10 15:34:10 +01003300 }
Richard Hughes9945edb2017-06-19 10:03:55 +01003301
Richard Hughes99e621d2017-08-16 12:24:18 +01003302 /* save XML and signature to remotes.d */
Richard Hughes943d2c92017-06-21 09:04:39 +01003303 if (!fu_common_set_contents_bytes (fwupd_remote_get_filename_cache (remote),
3304 bytes_raw, error))
Richard Hughes9945edb2017-06-19 10:03:55 +01003305 return FALSE;
Richard Hughes99e621d2017-08-16 12:24:18 +01003306 if (keyring_kind != FWUPD_KEYRING_KIND_NONE) {
3307 if (!fu_common_set_contents_bytes (fwupd_remote_get_filename_cache_sig (remote),
3308 bytes_sig, error))
3309 return FALSE;
3310 }
Mario Limonciellod81ea2e2020-01-13 14:11:43 -06003311 if (!fu_engine_load_metadata_store (self, FU_ENGINE_LOAD_FLAG_NONE, error))
3312 return FALSE;
Richard Hughesf43381f2020-02-24 10:11:31 +00003313 fu_engine_md_refresh_devices (self);
Mario Limonciellod81ea2e2020-01-13 14:11:43 -06003314 fu_engine_emit_changed (self);
3315 return TRUE;
Richard Hughesbe5b0192020-01-17 14:32:23 +00003316}
Mario Limonciellod81ea2e2020-01-13 14:11:43 -06003317
Richard Hughesbe5b0192020-01-17 14:32:23 +00003318/**
3319 * fu_engine_update_metadata:
3320 * @self: A #FuEngine
3321 * @remote_id: A remote ID, e.g. `lvfs`
3322 * @fd: file descriptor of the metadata
3323 * @fd_sig: file descriptor of the metadata signature
3324 * @error: A #GError, or %NULL
3325 *
3326 * Updates the metadata for a specific remote.
3327 *
3328 * Note: this will close the fds when done
3329 *
3330 * Returns: %TRUE for success
3331 **/
3332gboolean
3333fu_engine_update_metadata (FuEngine *self, const gchar *remote_id,
3334 gint fd, gint fd_sig, GError **error)
3335{
3336#ifdef HAVE_GIO_UNIX
3337 g_autoptr(GBytes) bytes_raw = NULL;
3338 g_autoptr(GBytes) bytes_sig = NULL;
3339 g_autoptr(GInputStream) stream_fd = NULL;
3340 g_autoptr(GInputStream) stream_sig = NULL;
3341
3342 g_return_val_if_fail (FU_IS_ENGINE (self), FALSE);
3343 g_return_val_if_fail (remote_id != NULL, FALSE);
3344 g_return_val_if_fail (fd > 0, FALSE);
3345 g_return_val_if_fail (fd_sig > 0, FALSE);
3346 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
3347
3348 /* ensures the fd's are closed on error */
3349 stream_fd = g_unix_input_stream_new (fd, TRUE);
3350 stream_sig = g_unix_input_stream_new (fd_sig, TRUE);
3351
3352 /* read the entire file into memory */
3353 bytes_raw = g_input_stream_read_bytes (stream_fd, 0x100000, NULL, error);
3354 if (bytes_raw == NULL)
3355 return FALSE;
3356
3357 /* read signature */
Richard Hughes64d868a2020-01-17 17:40:00 +00003358 bytes_sig = g_input_stream_read_bytes (stream_sig, 0x100000, NULL, error);
Richard Hughesbe5b0192020-01-17 14:32:23 +00003359 if (bytes_sig == NULL)
3360 return FALSE;
3361
3362 /* update with blobs */
3363 return fu_engine_update_metadata_bytes (self, remote_id,
3364 bytes_raw, bytes_sig,
3365 error);
Richard Hughes9e5675e2019-11-22 09:35:03 +00003366#else
3367 g_set_error (error,
3368 FWUPD_ERROR,
3369 FWUPD_ERROR_NOT_SUPPORTED,
3370 "Not supported as <glib-unix.h> is unavailable");
3371 return FALSE;
3372#endif
Richard Hughes9945edb2017-06-19 10:03:55 +01003373}
3374
Richard Hughes9945edb2017-06-19 10:03:55 +01003375/**
Richard Hughes481aa2a2018-09-18 20:51:46 +01003376 * fu_engine_get_silo_from_blob:
Richard Hughes9945edb2017-06-19 10:03:55 +01003377 * @self: A #FuEngine
3378 * @blob_cab: A #GBytes
3379 * @error: A #GError, or %NULL
3380 *
Richard Hughes481aa2a2018-09-18 20:51:46 +01003381 * Creates a silo from a .cab file blob.
Richard Hughes9945edb2017-06-19 10:03:55 +01003382 *
Richard Hughes481aa2a2018-09-18 20:51:46 +01003383 * Returns: (transfer container): a #XbSilo, or %NULL
Richard Hughes9945edb2017-06-19 10:03:55 +01003384 **/
Richard Hughes481aa2a2018-09-18 20:51:46 +01003385XbSilo *
3386fu_engine_get_silo_from_blob (FuEngine *self, GBytes *blob_cab, GError **error)
Richard Hughes9945edb2017-06-19 10:03:55 +01003387{
Richard Hughesc6eb4162020-02-27 10:02:36 +00003388 g_autoptr(FuCabinet) cabinet = fu_cabinet_new ();
Richard Hughes481aa2a2018-09-18 20:51:46 +01003389 g_autoptr(XbSilo) silo = NULL;
Richard Hughes9945edb2017-06-19 10:03:55 +01003390
3391 g_return_val_if_fail (FU_IS_ENGINE (self), NULL);
3392 g_return_val_if_fail (blob_cab != NULL, NULL);
3393 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
3394
3395 /* load file */
Richard Hughes9945edb2017-06-19 10:03:55 +01003396 fu_engine_set_status (self, FWUPD_STATUS_DECOMPRESSING);
Richard Hughesc6eb4162020-02-27 10:02:36 +00003397 fu_cabinet_set_size_max (cabinet, fu_engine_get_archive_size_max (self));
Richard Hughesd5aab652020-02-25 12:47:50 +00003398 fu_cabinet_set_jcat_context (cabinet, self->jcat_context);
Richard Hughesc6eb4162020-02-27 10:02:36 +00003399 if (!fu_cabinet_parse (cabinet, blob_cab, FU_CABINET_PARSE_FLAG_NONE, error))
Richard Hughes9945edb2017-06-19 10:03:55 +01003400 return NULL;
Richard Hughesc6eb4162020-02-27 10:02:36 +00003401 silo = fu_cabinet_get_silo (cabinet);
Mario Limonciello5735fd62017-07-13 15:47:52 -05003402 fu_engine_set_status (self, FWUPD_STATUS_IDLE);
Richard Hughes481aa2a2018-09-18 20:51:46 +01003403 return g_steal_pointer (&silo);
Richard Hughes9945edb2017-06-19 10:03:55 +01003404}
3405
Richard Hughes75449d92019-04-17 13:36:31 +01003406static FuDevice *
Richard Hughes6e0c8f82018-11-11 21:46:41 +00003407fu_engine_get_result_from_component (FuEngine *self, XbNode *component, GError **error)
Richard Hughes9945edb2017-06-19 10:03:55 +01003408{
Richard Hughes245885c2019-03-04 08:46:02 +00003409 FwupdReleaseFlags release_flags = FWUPD_RELEASE_FLAG_NONE;
Richard Hughes1d1f5cf2018-05-19 23:06:03 +01003410 g_autoptr(FuInstallTask) task = NULL;
Richard Hughes75449d92019-04-17 13:36:31 +01003411 g_autoptr(FuDevice) dev = NULL;
Richard Hughes93b15762017-09-15 11:05:23 +01003412 g_autoptr(FwupdRelease) rel = NULL;
Salud Lemus980f2d92018-08-28 09:25:40 -07003413 g_autoptr(GError) error_local = NULL;
Richard Hughes481aa2a2018-09-18 20:51:46 +01003414 g_autoptr(GPtrArray) provides = NULL;
3415 g_autoptr(XbNode) description = NULL;
3416 g_autoptr(XbNode) release = NULL;
Richard Hughes9945edb2017-06-19 10:03:55 +01003417
Richard Hughes75449d92019-04-17 13:36:31 +01003418 dev = fu_device_new ();
Richard Hughes481aa2a2018-09-18 20:51:46 +01003419 provides = xb_node_query (component,
Richard Hughes634da032018-11-05 11:42:20 +00003420 "provides/firmware[@type=$'flashed']",
Richard Hughes481aa2a2018-09-18 20:51:46 +01003421 0, &error_local);
3422 if (provides == NULL) {
3423 g_set_error (error,
3424 FWUPD_ERROR,
3425 FWUPD_ERROR_INTERNAL,
3426 "failed to get release: %s",
3427 error_local->message);
3428 return NULL;
3429 }
Richard Hughes9945edb2017-06-19 10:03:55 +01003430 for (guint i = 0; i < provides->len; i++) {
Richard Hughes481aa2a2018-09-18 20:51:46 +01003431 XbNode *prov = XB_NODE (g_ptr_array_index (provides, i));
Richard Hughes9945edb2017-06-19 10:03:55 +01003432 const gchar *guid;
Richard Hughes5a9a6bd2018-09-10 10:02:20 +01003433 g_autoptr(FuDevice) device = NULL;
Richard Hughes9945edb2017-06-19 10:03:55 +01003434
Richard Hughes9945edb2017-06-19 10:03:55 +01003435 /* is a online or offline update appropriate */
Richard Hughes481aa2a2018-09-18 20:51:46 +01003436 guid = xb_node_get_text (prov);
Richard Hughes9945edb2017-06-19 10:03:55 +01003437 if (guid == NULL)
3438 continue;
Richard Hughes40127542018-01-12 20:25:55 +00003439 device = fu_device_list_get_by_guid (self->device_list, guid, NULL);
Richard Hughes0a7e7832017-11-22 11:01:13 +00003440 if (device != NULL) {
Richard Hughes75449d92019-04-17 13:36:31 +01003441 fu_device_set_name (dev, fu_device_get_name (device));
3442 fu_device_set_flags (dev, fu_device_get_flags (device));
3443 fu_device_set_id (dev, fu_device_get_id (device));
Mario Limonciellof430da02020-03-09 14:03:03 -05003444 fu_device_set_version_raw (dev, fu_device_get_version_raw (device));
Mario Limonciello8bcdfaa2020-02-25 11:18:15 -06003445 fu_device_set_version_format (dev, fu_device_get_version_format (device));
3446 fu_device_set_version (dev, fu_device_get_version (device));
Richard Hughes9945edb2017-06-19 10:03:55 +01003447 }
3448
3449 /* add GUID */
Richard Hughes75449d92019-04-17 13:36:31 +01003450 fu_device_add_guid (dev, guid);
Richard Hughes9945edb2017-06-19 10:03:55 +01003451 }
Richard Hughes75449d92019-04-17 13:36:31 +01003452 if (fu_device_get_guids(dev)->len == 0) {
Richard Hughes9945edb2017-06-19 10:03:55 +01003453 g_set_error_literal (error,
3454 FWUPD_ERROR,
3455 FWUPD_ERROR_INTERNAL,
3456 "component has no GUIDs");
3457 return NULL;
3458 }
3459
3460 /* check we can install it */
Richard Hughes481aa2a2018-09-18 20:51:46 +01003461 task = fu_install_task_new (NULL, component);
Richard Hughes1d1f5cf2018-05-19 23:06:03 +01003462 if (!fu_engine_check_requirements (self, task,
3463 FWUPD_INSTALL_FLAG_NONE,
3464 error))
Richard Hughes9945edb2017-06-19 10:03:55 +01003465 return NULL;
3466
3467 /* verify trust */
Richard Hughes481aa2a2018-09-18 20:51:46 +01003468 release = xb_node_query_first (component,
3469 "releases/release",
3470 &error_local);
3471 if (release == NULL) {
3472 g_set_error (error,
3473 FWUPD_ERROR,
3474 FWUPD_ERROR_INTERNAL,
3475 "failed to get release: %s",
3476 error_local->message);
3477 return NULL;
3478 }
Richard Hughes245885c2019-03-04 08:46:02 +00003479 if (!fu_keyring_get_release_flags (release,
3480 &release_flags,
3481 &error_local)) {
Salud Lemus980f2d92018-08-28 09:25:40 -07003482 if (g_error_matches (error_local,
3483 FWUPD_ERROR,
3484 FWUPD_ERROR_NOT_SUPPORTED)) {
3485 g_warning ("Ignoring verification: %s",
3486 error_local->message);
3487 } else {
3488 g_propagate_error (error, g_steal_pointer (&error_local));
3489 return NULL;
3490 }
3491 }
Richard Hughes9945edb2017-06-19 10:03:55 +01003492
Richard Hughes9945edb2017-06-19 10:03:55 +01003493 /* create a result with all the metadata in */
Richard Hughes481aa2a2018-09-18 20:51:46 +01003494 description = xb_node_query_first (component, "description", NULL);
3495 if (description != NULL) {
3496 g_autofree gchar *xml = NULL;
3497 xml = xb_node_export (description,
3498 XB_NODE_EXPORT_FLAG_ONLY_CHILDREN,
3499 NULL);
3500 if (xml != NULL)
Richard Hughes75449d92019-04-17 13:36:31 +01003501 fu_device_set_description (dev, xml);
Richard Hughes481aa2a2018-09-18 20:51:46 +01003502 }
Richard Hughes93b15762017-09-15 11:05:23 +01003503 rel = fwupd_release_new ();
Richard Hughes245885c2019-03-04 08:46:02 +00003504 fwupd_release_set_flags (rel, release_flags);
Richard Hughes75449d92019-04-17 13:36:31 +01003505 if (!fu_engine_set_release_from_appstream (self, dev, rel, component, release, error))
Richard Hughes0b3e9fd2019-04-08 11:13:20 +01003506 return NULL;
Richard Hughes75449d92019-04-17 13:36:31 +01003507 fu_device_add_release (dev, rel);
Richard Hughes93b15762017-09-15 11:05:23 +01003508 return g_steal_pointer (&dev);
Richard Hughes9945edb2017-06-19 10:03:55 +01003509}
3510
Richard Hughesb3d3f212020-05-21 21:14:49 +01003511static gint
3512fu_engine_get_details_sort_cb (gconstpointer a, gconstpointer b)
3513{
3514 FuDevice *device1 = *((FuDevice **) a);
3515 FuDevice *device2 = *((FuDevice **) b);
3516 if (!fu_device_has_flag (device1, FWUPD_DEVICE_FLAG_UPDATABLE) &&
3517 fu_device_has_flag (device2, FWUPD_DEVICE_FLAG_UPDATABLE))
3518 return 1;
3519 if (fu_device_has_flag (device1, FWUPD_DEVICE_FLAG_UPDATABLE) &&
3520 !fu_device_has_flag (device2, FWUPD_DEVICE_FLAG_UPDATABLE))
3521 return -1;
3522 return 0;
3523}
3524
Richard Hughes9945edb2017-06-19 10:03:55 +01003525/**
Richard Hughes07f963a2017-09-15 14:28:47 +01003526 * fu_engine_get_details:
Richard Hughes9945edb2017-06-19 10:03:55 +01003527 * @self: A #FuEngine
3528 * @fd: A file descriptor
3529 * @error: A #GError, or %NULL
3530 *
3531 * Gets the details about a local file.
3532 *
3533 * Note: this will close the fd when done
3534 *
Richard Hughes75449d92019-04-17 13:36:31 +01003535 * Returns: (transfer container) (element-type FuDevice): results
Richard Hughes9945edb2017-06-19 10:03:55 +01003536 **/
3537GPtrArray *
Richard Hughes07f963a2017-09-15 14:28:47 +01003538fu_engine_get_details (FuEngine *self, gint fd, GError **error)
Richard Hughes9945edb2017-06-19 10:03:55 +01003539{
Richard Hughes534255c2018-01-28 19:51:56 +00003540 const gchar *remote_id;
3541 g_autofree gchar *csum = NULL;
Richard Hughes9945edb2017-06-19 10:03:55 +01003542 g_autoptr(GBytes) blob = NULL;
Richard Hughes481aa2a2018-09-18 20:51:46 +01003543 g_autoptr(GError) error_local = NULL;
3544 g_autoptr(GPtrArray) components = NULL;
Richard Hughes9945edb2017-06-19 10:03:55 +01003545 g_autoptr(GPtrArray) details = NULL;
Richard Hughes481aa2a2018-09-18 20:51:46 +01003546 g_autoptr(XbSilo) silo = NULL;
Richard Hughes9945edb2017-06-19 10:03:55 +01003547
3548 g_return_val_if_fail (FU_IS_ENGINE (self), NULL);
3549 g_return_val_if_fail (fd > 0, NULL);
3550 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
3551
Richard Hughes481aa2a2018-09-18 20:51:46 +01003552 /* get all components */
Richard Hughesc7bbbc22018-01-02 22:22:25 +00003553 blob = fu_common_get_contents_fd (fd,
3554 fu_engine_get_archive_size_max (self),
3555 error);
Richard Hughes9945edb2017-06-19 10:03:55 +01003556 if (blob == NULL)
3557 return NULL;
Richard Hughes481aa2a2018-09-18 20:51:46 +01003558 silo = fu_engine_get_silo_from_blob (self, blob, error);
3559 if (silo == NULL)
Richard Hughes9945edb2017-06-19 10:03:55 +01003560 return NULL;
Mario Limonciello51ddf182019-01-26 00:31:58 -06003561 components = xb_silo_query (silo, "components/component", 0, &error_local);
Richard Hughes481aa2a2018-09-18 20:51:46 +01003562 if (components == NULL) {
3563 g_set_error (error,
3564 FWUPD_ERROR,
3565 FWUPD_ERROR_INVALID_FILE,
3566 "no components: %s",
3567 error_local->message);
Richard Hughes9945edb2017-06-19 10:03:55 +01003568 return NULL;
3569 }
3570
Richard Hughesec6190c2018-11-25 12:19:33 +00003571 /* build the index */
Mario Limonciello51ddf182019-01-26 00:31:58 -06003572 if (!xb_silo_query_build_index (silo, "components/component/provides/firmware",
Richard Hughesec6190c2018-11-25 12:19:33 +00003573 "type", error))
Richard Hughes2eee2582019-03-11 13:17:39 +00003574 return NULL;
Mario Limonciello51ddf182019-01-26 00:31:58 -06003575 if (!xb_silo_query_build_index (silo, "components/component/provides/firmware",
Richard Hughesec6190c2018-11-25 12:19:33 +00003576 NULL, error))
Richard Hughes2eee2582019-03-11 13:17:39 +00003577 return NULL;
Richard Hughesec6190c2018-11-25 12:19:33 +00003578
Richard Hughes534255c2018-01-28 19:51:56 +00003579 /* does this exist in any enabled remote */
3580 csum = g_compute_checksum_for_bytes (G_CHECKSUM_SHA1, blob);
3581 remote_id = fu_engine_get_remote_id_for_checksum (self, csum);
3582
Richard Hughes9945edb2017-06-19 10:03:55 +01003583 /* create results with all the metadata in */
3584 details = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
Richard Hughes481aa2a2018-09-18 20:51:46 +01003585 for (guint i = 0; i < components->len; i++) {
3586 XbNode *component = g_ptr_array_index (components, i);
Richard Hughes75449d92019-04-17 13:36:31 +01003587 FuDevice *dev;
Richard Hughes6e0c8f82018-11-11 21:46:41 +00003588 dev = fu_engine_get_result_from_component (self, component, error);
Richard Hughes534255c2018-01-28 19:51:56 +00003589 if (dev == NULL)
Richard Hughes9945edb2017-06-19 10:03:55 +01003590 return NULL;
Richard Hughes534255c2018-01-28 19:51:56 +00003591 if (remote_id != NULL) {
Richard Hughes75449d92019-04-17 13:36:31 +01003592 FwupdRelease *rel = fu_device_get_release_default (dev);
Richard Hughes534255c2018-01-28 19:51:56 +00003593 fwupd_release_set_remote_id (rel, remote_id);
Richard Hughes75449d92019-04-17 13:36:31 +01003594 fu_device_add_flag (dev, FWUPD_DEVICE_FLAG_SUPPORTED);
Richard Hughes534255c2018-01-28 19:51:56 +00003595 }
Richard Hughes137649d2020-05-28 13:36:24 +01003596 if (fu_device_has_flag (dev, FWUPD_DEVICE_FLAG_MD_SET_VERFMT))
3597 fu_engine_md_refresh_device_verfmt (self, dev, component);
Richard Hughesb3d3f212020-05-21 21:14:49 +01003598
3599 /* if this matched a device on the system, ensure all the
3600 * requirements passed before setting UPDATABLE */
3601 if (fu_device_has_flag (dev, FWUPD_DEVICE_FLAG_UPDATABLE)) {
3602 g_autoptr(FuInstallTask) task = fu_install_task_new (dev, component);
3603 g_autoptr(GError) error_req = NULL;
3604 if (!fu_engine_check_requirements (self, task,
3605 FWUPD_INSTALL_FLAG_OFFLINE |
3606 FWUPD_INSTALL_FLAG_ALLOW_REINSTALL |
3607 FWUPD_INSTALL_FLAG_ALLOW_OLDER,
3608 &error_req)) {
3609 g_debug ("%s failed requirement checks: %s",
3610 fu_device_get_id (dev),
3611 error_req->message);
3612 fu_device_remove_flag (dev, FWUPD_DEVICE_FLAG_UPDATABLE);
3613 } else {
3614 g_debug ("%s passed requirement checks",
3615 fu_device_get_id (dev));
3616 }
3617 }
3618
Richard Hughes534255c2018-01-28 19:51:56 +00003619 g_ptr_array_add (details, dev);
Richard Hughes9945edb2017-06-19 10:03:55 +01003620 }
Richard Hughesb3d3f212020-05-21 21:14:49 +01003621
3622 /* order multiple devices so that the one that passes the requirement
3623 * is listed first */
3624 g_ptr_array_sort (details, fu_engine_get_details_sort_cb);
3625
Richard Hughes9945edb2017-06-19 10:03:55 +01003626 return g_steal_pointer (&details);
3627}
3628
Mario Limonciello343095d2018-10-23 17:21:13 -05003629static gint
Mario Limonciello6ff8a162019-08-23 12:55:10 -05003630fu_engine_sort_devices_by_priority_name (gconstpointer a, gconstpointer b)
Mario Limonciello343095d2018-10-23 17:21:13 -05003631{
3632 FuDevice *dev_a = *((FuDevice **) a);
3633 FuDevice *dev_b = *((FuDevice **) b);
3634 gint prio_a = fu_device_get_priority (dev_a);
3635 gint prio_b = fu_device_get_priority (dev_b);
Mario Limonciello6ff8a162019-08-23 12:55:10 -05003636 const gchar *name_a = fu_device_get_name (dev_a);
3637 const gchar *name_b = fu_device_get_name (dev_b);
Mario Limonciello343095d2018-10-23 17:21:13 -05003638
3639 if (prio_a > prio_b)
3640 return -1;
3641 if (prio_a < prio_b)
3642 return 1;
Mario Limonciello6ff8a162019-08-23 12:55:10 -05003643 if (g_strcmp0 (name_a, name_b) > 0)
3644 return 1;
3645 if (g_strcmp0 (name_a, name_b) < 0)
3646 return -1;
Mario Limonciello343095d2018-10-23 17:21:13 -05003647 return 0;
3648}
3649
Richard Hughes9945edb2017-06-19 10:03:55 +01003650/**
3651 * fu_engine_get_devices:
3652 * @self: A #FuEngine
3653 * @error: A #GError, or %NULL
3654 *
3655 * Gets the list of devices.
3656 *
3657 * Returns: (transfer container) (element-type FwupdDevice): results
3658 **/
3659GPtrArray *
3660fu_engine_get_devices (FuEngine *self, GError **error)
3661{
Richard Hughesf2eccde2017-09-20 11:18:03 +01003662 g_autoptr(GPtrArray) devices = NULL;
Richard Hughes9945edb2017-06-19 10:03:55 +01003663
3664 g_return_val_if_fail (FU_IS_ENGINE (self), NULL);
3665 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
3666
Richard Hughes70425fe2017-11-22 12:33:27 +00003667 devices = fu_device_list_get_active (self->device_list);
Richard Hughes9945edb2017-06-19 10:03:55 +01003668 if (devices->len == 0) {
3669 g_set_error_literal (error,
3670 FWUPD_ERROR,
3671 FWUPD_ERROR_NOTHING_TO_DO,
3672 "No detected devices");
3673 return NULL;
3674 }
Mario Limonciello6ff8a162019-08-23 12:55:10 -05003675 g_ptr_array_sort (devices, fu_engine_sort_devices_by_priority_name);
Richard Hughesf2eccde2017-09-20 11:18:03 +01003676 return g_steal_pointer (&devices);
Richard Hughes9945edb2017-06-19 10:03:55 +01003677}
3678
Richard Hughes12040b52018-05-14 13:32:10 +01003679/**
3680 * fu_engine_get_device:
3681 * @self: A #FuEngine
3682 * @device_id: A device ID
3683 * @error: A #GError, or %NULL
3684 *
3685 * Gets a specific device.
3686 *
3687 * Returns: (transfer full): a device, or %NULL if not found
3688 **/
Richard Hughes4ad41f02018-05-08 14:35:36 +01003689FuDevice *
3690fu_engine_get_device (FuEngine *self, const gchar *device_id, GError **error)
3691{
Richard Hughes12040b52018-05-14 13:32:10 +01003692 FuDevice *device;
Richard Hughes37d09432018-09-09 10:39:45 +01003693
Richard Hughes12040b52018-05-14 13:32:10 +01003694 device = fu_device_list_get_by_id (self->device_list, device_id, error);
3695 if (device == NULL)
3696 return NULL;
Richard Hughes5a9a6bd2018-09-10 10:02:20 +01003697 return device;
Richard Hughes4ad41f02018-05-08 14:35:36 +01003698}
3699
Richard Hughes9945edb2017-06-19 10:03:55 +01003700/**
Richard Hughes3aaf53c2020-04-20 09:39:46 +01003701 * fu_engine_get_devices_by_guid:
3702 * @self: A #FuEngine
3703 * @guid: A GUID
3704 * @error: A #GError, or %NULL
3705 *
3706 * Gets a specific device.
3707 *
3708 * Returns: (transfer full): a device, or %NULL if not found
3709 **/
3710GPtrArray *
3711fu_engine_get_devices_by_guid (FuEngine *self, const gchar *guid, GError **error)
3712{
3713 g_autoptr(GPtrArray) devices = NULL;
3714 g_autoptr(GPtrArray) devices_tmp = NULL;
3715
3716 /* find the devices by GUID */
3717 devices_tmp = fu_device_list_get_all (self->device_list);
3718 devices = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
3719 for (guint i = 0; i < devices_tmp->len; i++) {
3720 FuDevice *dev_tmp = g_ptr_array_index (devices_tmp, i);
3721 if (fu_device_has_guid (dev_tmp, guid))
3722 g_ptr_array_add (devices, g_object_ref (dev_tmp));
3723 }
3724
3725 /* nothing */
3726 if (devices->len == 0) {
3727 g_set_error (error,
3728 FWUPD_ERROR,
3729 FWUPD_ERROR_NOT_FOUND,
3730 "failed to find any device providing %s", guid);
3731 return NULL;
3732 }
3733
3734 /* success */
3735 return g_steal_pointer (&devices);
3736}
3737
Richard Hughescef874f2020-05-17 21:03:13 +01003738static void
3739fu_engine_get_history_set_hsi_attrs (FuEngine *self, FuDevice *device)
3740{
3741 g_autofree gchar *host_security_id = NULL;
3742 g_autoptr(FuSecurityAttrs) attrs = NULL;
3743 g_autoptr(GPtrArray) vals = NULL;
3744
3745 /* add attributes */
3746 attrs = fu_engine_get_host_security_attrs (self);
3747 vals = fu_security_attrs_get_all (attrs);
3748 for (guint i = 0; i < vals->len; i++) {
3749 FwupdSecurityAttr *attr = g_ptr_array_index (vals, i);
Richard Hughesb246bca2020-05-18 14:31:35 +01003750 const gchar *tmp;
3751 tmp = fwupd_security_attr_result_to_string (fwupd_security_attr_get_result (attr));
3752 fu_device_set_metadata (device, fwupd_security_attr_get_appstream_id (attr), tmp);
Richard Hughescef874f2020-05-17 21:03:13 +01003753 }
3754
3755 /* computed value */
Mario Limonciellob0e1e5e2020-05-18 13:50:29 -05003756 host_security_id = fu_security_attrs_calculate_hsi (attrs, FU_SECURITY_ATTRS_FLAG_ADD_VERSION);
Richard Hughescef874f2020-05-17 21:03:13 +01003757 fu_device_set_metadata (device, "HSI", host_security_id);
3758}
3759
Richard Hughes3aaf53c2020-04-20 09:39:46 +01003760/**
Richard Hughes476363a2018-01-11 10:08:58 +00003761 * fu_engine_get_history:
3762 * @self: A #FuEngine
3763 * @error: A #GError, or %NULL
3764 *
3765 * Gets the list of history.
3766 *
3767 * Returns: (transfer container) (element-type FwupdDevice): results
3768 **/
3769GPtrArray *
3770fu_engine_get_history (FuEngine *self, GError **error)
3771{
3772 g_autoptr(GPtrArray) devices = NULL;
3773
3774 g_return_val_if_fail (FU_IS_ENGINE (self), NULL);
3775 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
3776
3777 devices = fu_history_get_devices (self->history, error);
3778 if (devices == NULL)
3779 return NULL;
3780 if (devices->len == 0) {
3781 g_set_error_literal (error,
3782 FWUPD_ERROR,
3783 FWUPD_ERROR_NOTHING_TO_DO,
3784 "No history");
3785 return NULL;
3786 }
Richard Hughes611f1a92018-01-11 11:54:28 +00003787
Richard Hughescef874f2020-05-17 21:03:13 +01003788 /* if this is the system firmware device, add the HSI attrs */
3789 for (guint i = 0; i < devices->len; i++) {
3790 FuDevice *dev = g_ptr_array_index (devices, i);
3791 if (fu_device_has_instance_id (dev, "main-system-firmware"))
3792 fu_engine_get_history_set_hsi_attrs (self, dev);
3793 }
3794
Richard Hughes611f1a92018-01-11 11:54:28 +00003795 /* try to set the remote ID for each device */
3796 for (guint i = 0; i < devices->len; i++) {
3797 FuDevice *dev = g_ptr_array_index (devices, i);
3798 FwupdRelease *rel;
3799 GPtrArray *csums;
3800
3801 /* get the checksums */
3802 rel = fu_device_get_release_default (dev);
3803 if (rel == NULL)
3804 continue;
3805
3806 /* find the checksum that matches */
3807 csums = fwupd_release_get_checksums (rel);
3808 for (guint j = 0; j < csums->len; j++) {
3809 const gchar *csum = g_ptr_array_index (csums, j);
3810 const gchar *remote_id = fu_engine_get_remote_id_for_checksum (self, csum);
3811 if (remote_id != NULL) {
3812 fu_device_add_flag (dev, FWUPD_DEVICE_FLAG_SUPPORTED);
3813 fwupd_release_set_remote_id (rel, remote_id);
3814 break;
3815 }
3816 }
3817 }
3818
Richard Hughes476363a2018-01-11 10:08:58 +00003819 return g_steal_pointer (&devices);
3820}
3821
3822/**
Richard Hughes9945edb2017-06-19 10:03:55 +01003823 * fu_engine_get_remotes:
3824 * @self: A #FuEngine
Richard Hughes9945edb2017-06-19 10:03:55 +01003825 * @error: A #GError, or %NULL
3826 *
3827 * Gets the list of remotes in use by the engine.
3828 *
3829 * Returns: (transfer container) (element-type FwupdRemote): results
3830 **/
3831GPtrArray *
3832fu_engine_get_remotes (FuEngine *self, GError **error)
3833{
3834 GPtrArray *remotes;
3835
3836 g_return_val_if_fail (FU_IS_ENGINE (self), NULL);
3837 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
3838
Richard Hughesd1808aa2019-12-10 15:20:30 +00003839 remotes = fu_remote_list_get_all (self->remote_list);
Richard Hughes9945edb2017-06-19 10:03:55 +01003840 if (remotes->len == 0) {
3841 g_set_error (error,
3842 FWUPD_ERROR,
3843 FWUPD_ERROR_INTERNAL,
3844 "No remotes configured");
3845 return NULL;
3846 }
3847 return g_ptr_array_ref (remotes);
3848}
3849
Mario Limonciello46aaee82019-01-10 12:58:00 -06003850/**
3851 * fu_engine_get_remote_by_id:
3852 * @self: A #FuEngine
3853 * @remote_id: A string representation of a remote
3854 * @error: A #GError, or %NULL
3855 *
3856 * Gets the FwupdRemote object.
3857 *
3858 * Returns: FwupdRemote
3859 **/
3860FwupdRemote *
3861fu_engine_get_remote_by_id (FuEngine *self, const gchar *remote_id, GError **error)
3862{
3863 g_autoptr(GPtrArray) remotes = NULL;
3864
3865 remotes = fu_engine_get_remotes (self, error);
3866 if (remotes == NULL)
3867 return NULL;
3868
3869 for (guint i = 0; i < remotes->len; i++) {
3870 FwupdRemote *remote = g_ptr_array_index (remotes, i);
3871 if (g_strcmp0 (remote_id, fwupd_remote_get_id (remote)) == 0)
3872 return remote;
3873 }
3874
3875 g_set_error (error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL,
3876 "Couldn't find remote %s", remote_id);
3877
3878 return NULL;
3879}
3880
3881
Richard Hughes225f3a92017-09-13 19:38:51 +01003882static gint
Richard Hughes9a680842020-02-20 11:11:13 +00003883fu_engine_sort_releases_cb (gconstpointer a, gconstpointer b, gpointer user_data)
Richard Hughes225f3a92017-09-13 19:38:51 +01003884{
Richard Hughes9a680842020-02-20 11:11:13 +00003885 FuDevice *device = FU_DEVICE (user_data);
Richard Hughes225f3a92017-09-13 19:38:51 +01003886 FwupdRelease *rel_a = FWUPD_RELEASE (*((FwupdRelease **) a));
3887 FwupdRelease *rel_b = FWUPD_RELEASE (*((FwupdRelease **) b));
Richard Hughes9a680842020-02-20 11:11:13 +00003888 return fu_common_vercmp_full (fwupd_release_get_version (rel_b),
3889 fwupd_release_get_version (rel_a),
3890 fu_device_get_version_format (device));
Richard Hughes225f3a92017-09-13 19:38:51 +01003891}
3892
Richard Hughes481aa2a2018-09-18 20:51:46 +01003893static gboolean
Richard Hughes8dd4c1c2019-03-03 18:27:57 +00003894fu_engine_check_release_is_approved (FuEngine *self, FwupdRelease *rel)
3895{
3896 GPtrArray *csums = fwupd_release_get_checksums (rel);
3897 for (guint i = 0; i < csums->len; i++) {
3898 const gchar *csum = g_ptr_array_index (csums, i);
3899 g_debug ("checking %s against approved list", csum);
3900 if (g_hash_table_lookup (self->approved_firmware, csum) != NULL)
3901 return TRUE;
3902 }
3903 return FALSE;
3904}
3905
3906static gboolean
Richard Hughes481aa2a2018-09-18 20:51:46 +01003907fu_engine_add_releases_for_device_component (FuEngine *self,
3908 FuDevice *device,
3909 XbNode *component,
3910 GPtrArray *releases,
3911 GError **error)
Richard Hughes650dade2017-12-14 14:43:11 +00003912{
Richard Hughes9a680842020-02-20 11:11:13 +00003913 FwupdVersionFormat fmt = fu_device_get_version_format (device);
Richard Hughes481aa2a2018-09-18 20:51:46 +01003914 g_autoptr(GError) error_local = NULL;
3915 g_autoptr(FuInstallTask) task = fu_install_task_new (device, component);
3916 g_autoptr(GPtrArray) releases_tmp = NULL;
Richard Hughes650dade2017-12-14 14:43:11 +00003917
Richard Hughes481aa2a2018-09-18 20:51:46 +01003918 if (!fu_engine_check_requirements (self, task,
Richard Hughesce756272019-03-24 12:21:34 +00003919 FWUPD_INSTALL_FLAG_OFFLINE |
Richard Hughes481aa2a2018-09-18 20:51:46 +01003920 FWUPD_INSTALL_FLAG_ALLOW_REINSTALL |
3921 FWUPD_INSTALL_FLAG_ALLOW_OLDER,
3922 error))
3923 return FALSE;
3924
3925 /* get all releases */
3926 releases_tmp = xb_node_query (component, "releases/release", 0, &error_local);
3927 if (releases_tmp == NULL) {
3928 if (g_error_matches (error_local, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
3929 return TRUE;
3930 if (g_error_matches (error_local, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT))
3931 return TRUE;
3932 g_propagate_error (error, g_steal_pointer (&error_local));
3933 return FALSE;
3934 }
3935 for (guint i = 0; i < releases_tmp->len; i++) {
3936 XbNode *release = g_ptr_array_index (releases_tmp, i);
Richard Hughes8dd4c1c2019-03-03 18:27:57 +00003937 const gchar *remote_id;
Mario Limonciello32241f42019-01-24 10:12:41 -06003938 const gchar *update_message;
Richard Hughes8e0cc802019-03-04 14:10:17 +00003939 gint vercmp;
Richard Hughes481aa2a2018-09-18 20:51:46 +01003940 GPtrArray *checksums;
3941 g_autoptr(FwupdRelease) rel = fwupd_release_new ();
Richard Hughes0b3e9fd2019-04-08 11:13:20 +01003942 g_autoptr(GError) error_loop = NULL;
Richard Hughes481aa2a2018-09-18 20:51:46 +01003943
3944 /* create new FwupdRelease for the XbNode */
Richard Hughes0b3e9fd2019-04-08 11:13:20 +01003945 if (!fu_engine_set_release_from_appstream (self,
Richard Hughes2c40b372019-04-17 13:41:47 +01003946 device,
Richard Hughes0b3e9fd2019-04-08 11:13:20 +01003947 rel,
3948 component,
3949 release,
3950 &error_loop)) {
3951 g_warning ("failed to set release for component: %s",
3952 error_loop->message);
3953 continue;
3954 }
Richard Hughes481aa2a2018-09-18 20:51:46 +01003955
Richard Hughes6e0c8f82018-11-11 21:46:41 +00003956 /* fall back to quirk-provided value */
3957 if (fwupd_release_get_install_duration (rel) == 0)
3958 fwupd_release_set_install_duration (rel, fu_device_get_install_duration (device));
3959
Richard Hughes481aa2a2018-09-18 20:51:46 +01003960 /* invalid */
3961 if (fwupd_release_get_uri (rel) == NULL)
Richard Hughes650dade2017-12-14 14:43:11 +00003962 continue;
Richard Hughes481aa2a2018-09-18 20:51:46 +01003963 checksums = fwupd_release_get_checksums (rel);
3964 if (checksums->len == 0)
3965 continue;
3966
Richard Hughes8e0cc802019-03-04 14:10:17 +00003967 /* test for upgrade or downgrade */
Richard Hughes9a680842020-02-20 11:11:13 +00003968 vercmp = fu_common_vercmp_full (fwupd_release_get_version (rel),
3969 fu_device_get_version (device),
3970 fmt);
Richard Hughes8e0cc802019-03-04 14:10:17 +00003971 if (vercmp > 0)
3972 fwupd_release_add_flag (rel, FWUPD_RELEASE_FLAG_IS_UPGRADE);
3973 else if (vercmp < 0)
3974 fwupd_release_add_flag (rel, FWUPD_RELEASE_FLAG_IS_DOWNGRADE);
3975
3976 /* lower than allowed to downgrade to */
3977 if (fu_device_get_version_lowest (device) != NULL &&
Richard Hughes9a680842020-02-20 11:11:13 +00003978 fu_common_vercmp_full (fwupd_release_get_version (rel),
3979 fu_device_get_version_lowest (device),
3980 fmt) < 0) {
Richard Hughes8e0cc802019-03-04 14:10:17 +00003981 fwupd_release_add_flag (rel, FWUPD_RELEASE_FLAG_BLOCKED_VERSION);
3982 }
3983
Richard Hughes8dd4c1c2019-03-03 18:27:57 +00003984 /* check if remote is whitelisting firmware */
3985 remote_id = fwupd_release_get_remote_id (rel);
3986 if (remote_id != NULL) {
3987 FwupdRemote *remote = fu_engine_get_remote_by_id (self, remote_id, NULL);
3988 if (remote != NULL &&
3989 fwupd_remote_get_approval_required (remote) &&
3990 !fu_engine_check_release_is_approved (self, rel)) {
3991 fwupd_release_add_flag (rel, FWUPD_RELEASE_FLAG_BLOCKED_APPROVAL);
3992 }
3993 }
3994
Mario Limonciello32241f42019-01-24 10:12:41 -06003995 /* add update message if exists but device doesn't already have one */
3996 update_message = fwupd_release_get_update_message (rel);
3997 if (fwupd_device_get_update_message (FWUPD_DEVICE (device)) == NULL &&
3998 update_message != NULL) {
3999 fwupd_device_set_update_message (FWUPD_DEVICE (device), update_message);
4000 }
Richard Hughes481aa2a2018-09-18 20:51:46 +01004001 /* success */
4002 g_ptr_array_add (releases, g_steal_pointer (&rel));
Richard Hughes650dade2017-12-14 14:43:11 +00004003 }
4004
Richard Hughes481aa2a2018-09-18 20:51:46 +01004005 /* success */
4006 return TRUE;
Richard Hughes650dade2017-12-14 14:43:11 +00004007}
4008
Filipe Laínsd3a4fd32020-03-30 21:05:14 +01004009GPtrArray *
Richard Hughes97284b12017-09-13 17:07:58 +01004010fu_engine_get_releases_for_device (FuEngine *self, FuDevice *device, GError **error)
4011{
4012 GPtrArray *device_guids;
4013 GPtrArray *releases;
4014 const gchar *version;
Richard Hughes481aa2a2018-09-18 20:51:46 +01004015 g_autoptr(GError) error_all = NULL;
4016 g_autoptr(GError) error_local = NULL;
4017 g_autoptr(GPtrArray) components = NULL;
4018 g_autoptr(GString) xpath = g_string_new (NULL);
Richard Hughes97284b12017-09-13 17:07:58 +01004019
4020 /* get device version */
4021 version = fu_device_get_version (device);
4022 if (version == NULL) {
4023 g_set_error (error,
4024 FWUPD_ERROR,
4025 FWUPD_ERROR_NOT_SUPPORTED,
4026 "no version set");
4027 return NULL;
4028 }
4029
4030 /* only show devices that can be updated */
4031 if (!fu_device_has_flag (device, FWUPD_DEVICE_FLAG_UPDATABLE)) {
Mario Limonciello7e4949c2019-12-09 10:21:20 -06004032 g_set_error_literal (error,
4033 FWUPD_ERROR,
4034 FWUPD_ERROR_NOT_SUPPORTED,
4035 "is not updatable");
Richard Hughes97284b12017-09-13 17:07:58 +01004036 return NULL;
4037 }
4038
Richard Hughes481aa2a2018-09-18 20:51:46 +01004039 /* get all the components that provide any of these GUIDs */
Richard Hughes97284b12017-09-13 17:07:58 +01004040 device_guids = fu_device_get_guids (device);
4041 for (guint i = 0; i < device_guids->len; i++) {
Richard Hughes97284b12017-09-13 17:07:58 +01004042 const gchar *guid = g_ptr_array_index (device_guids, i);
Richard Hughes481aa2a2018-09-18 20:51:46 +01004043 xb_string_append_union (xpath,
4044 "components/component/"
Richard Hughes634da032018-11-05 11:42:20 +00004045 "provides/firmware[@type=$'flashed'][text()=$'%s']/"
Richard Hughes481aa2a2018-09-18 20:51:46 +01004046 "../..", guid);
4047 }
4048 components = xb_silo_query (self->silo, xpath->str, 0, &error_local);
4049 if (components == NULL) {
Richard Hughesa07a80f2018-12-03 14:57:33 +00004050 if (g_error_matches (error_local, G_IO_ERROR, G_IO_ERROR_NOT_FOUND) ||
4051 g_error_matches (error_local, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT)) {
Mario Limonciello7e4949c2019-12-09 10:21:20 -06004052 g_set_error_literal (error,
4053 FWUPD_ERROR,
4054 FWUPD_ERROR_NOTHING_TO_DO,
4055 "No releases found");
Richard Hughesb095df62018-11-05 11:41:36 +00004056 return NULL;
Richard Hughes97284b12017-09-13 17:07:58 +01004057 }
Richard Hughes481aa2a2018-09-18 20:51:46 +01004058 g_propagate_error (error, g_steal_pointer (&error_local));
Richard Hughesb095df62018-11-05 11:41:36 +00004059 return NULL;
Richard Hughes481aa2a2018-09-18 20:51:46 +01004060 }
4061
4062 /* find all the releases that pass all the requirements */
4063 releases = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
4064 for (guint i = 0; i < components->len; i++) {
4065 XbNode *component = XB_NODE (g_ptr_array_index (components, i));
4066 g_autoptr(GError) error_tmp = NULL;
4067 if (!fu_engine_add_releases_for_device_component (self,
4068 device,
4069 component,
4070 releases,
4071 &error_tmp)) {
4072 if (error_all == NULL) {
4073 error_all = g_steal_pointer (&error_tmp);
4074 continue;
4075 }
4076
4077 /* assume the domain and code is the same */
4078 g_prefix_error (&error_all, "%s, ", error_tmp->message);
4079 }
4080 }
4081
4082 /* return the compound error */
4083 if (releases->len == 0) {
Richard Hughesd56ad5b2018-11-12 10:48:06 +00004084 if (error_all != NULL) {
Richard Hughes5b715e12019-04-08 13:33:59 +01004085 g_propagate_prefixed_error (error, g_steal_pointer (&error_all),
Mario Limonciello7e4949c2019-12-09 10:21:20 -06004086 "No releases found: ");
Richard Hughesd56ad5b2018-11-12 10:48:06 +00004087 return NULL;
4088 }
4089 g_set_error (error,
4090 FWUPD_ERROR,
4091 FWUPD_ERROR_NOTHING_TO_DO,
Mario Limonciello7e4949c2019-12-09 10:21:20 -06004092 "No releases found");
Richard Hughes481aa2a2018-09-18 20:51:46 +01004093 return NULL;
Richard Hughes97284b12017-09-13 17:07:58 +01004094 }
4095 return releases;
4096}
4097
Richard Hughes9945edb2017-06-19 10:03:55 +01004098/**
4099 * fu_engine_get_releases:
4100 * @self: A #FuEngine
4101 * @device_id: A device ID
4102 * @error: A #GError, or %NULL
4103 *
4104 * Gets the releases available for a specific device.
4105 *
Richard Hughes93b15762017-09-15 11:05:23 +01004106 * Returns: (transfer container) (element-type FwupdDevice): results
Richard Hughes9945edb2017-06-19 10:03:55 +01004107 **/
4108GPtrArray *
4109fu_engine_get_releases (FuEngine *self, const gchar *device_id, GError **error)
4110{
Richard Hughes5a9a6bd2018-09-10 10:02:20 +01004111 g_autoptr(FuDevice) device = NULL;
Richard Hughes9945edb2017-06-19 10:03:55 +01004112 g_autoptr(GPtrArray) releases = NULL;
4113
4114 g_return_val_if_fail (FU_IS_ENGINE (self), NULL);
4115 g_return_val_if_fail (device_id != NULL, NULL);
4116 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
4117
4118 /* find the device */
Richard Hughes40127542018-01-12 20:25:55 +00004119 device = fu_device_list_get_by_id (self->device_list, device_id, error);
Richard Hughes0a7e7832017-11-22 11:01:13 +00004120 if (device == NULL)
Richard Hughes9945edb2017-06-19 10:03:55 +01004121 return NULL;
4122
4123 /* get all the releases for the device */
Richard Hughes0a7e7832017-11-22 11:01:13 +00004124 releases = fu_engine_get_releases_for_device (self, device, error);
Richard Hughes97284b12017-09-13 17:07:58 +01004125 if (releases == NULL)
4126 return NULL;
Richard Hughes9945edb2017-06-19 10:03:55 +01004127 if (releases->len == 0) {
4128 g_set_error_literal (error,
4129 FWUPD_ERROR,
4130 FWUPD_ERROR_NOTHING_TO_DO,
4131 "No releases for device");
4132 return NULL;
4133 }
Richard Hughes9a680842020-02-20 11:11:13 +00004134 g_ptr_array_sort_with_data (releases, fu_engine_sort_releases_cb, device);
Richard Hughes97284b12017-09-13 17:07:58 +01004135 return g_steal_pointer (&releases);
4136}
4137
4138/**
4139 * fu_engine_get_downgrades:
4140 * @self: A #FuEngine
4141 * @device_id: A device ID
4142 * @error: A #GError, or %NULL
4143 *
4144 * Gets the downgrades available for a specific device.
4145 *
Richard Hughes93b15762017-09-15 11:05:23 +01004146 * Returns: (transfer container) (element-type FwupdDevice): results
Richard Hughes97284b12017-09-13 17:07:58 +01004147 **/
4148GPtrArray *
4149fu_engine_get_downgrades (FuEngine *self, const gchar *device_id, GError **error)
4150{
Richard Hughes5a9a6bd2018-09-10 10:02:20 +01004151 g_autoptr(FuDevice) device = NULL;
Richard Hughes97284b12017-09-13 17:07:58 +01004152 g_autoptr(GPtrArray) releases = NULL;
4153 g_autoptr(GPtrArray) releases_tmp = NULL;
4154 g_autoptr(GString) error_str = g_string_new (NULL);
4155
4156 g_return_val_if_fail (FU_IS_ENGINE (self), NULL);
4157 g_return_val_if_fail (device_id != NULL, NULL);
4158 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
4159
4160 /* find the device */
Richard Hughes40127542018-01-12 20:25:55 +00004161 device = fu_device_list_get_by_id (self->device_list, device_id, error);
Richard Hughes0a7e7832017-11-22 11:01:13 +00004162 if (device == NULL)
Richard Hughes97284b12017-09-13 17:07:58 +01004163 return NULL;
4164
4165 /* get all the releases for the device */
Richard Hughes0a7e7832017-11-22 11:01:13 +00004166 releases_tmp = fu_engine_get_releases_for_device (self, device, error);
Richard Hughes97284b12017-09-13 17:07:58 +01004167 if (releases_tmp == NULL)
4168 return NULL;
4169 releases = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
4170 for (guint i = 0; i < releases_tmp->len; i++) {
4171 FwupdRelease *rel_tmp = g_ptr_array_index (releases_tmp, i);
Richard Hughes97284b12017-09-13 17:07:58 +01004172
Richard Hughes8e0cc802019-03-04 14:10:17 +00004173 /* same as installed */
4174 if (!fwupd_release_has_flag (rel_tmp, FWUPD_RELEASE_FLAG_IS_UPGRADE) &&
4175 !fwupd_release_has_flag (rel_tmp, FWUPD_RELEASE_FLAG_IS_DOWNGRADE)) {
Richard Hughes97284b12017-09-13 17:07:58 +01004176 g_string_append_printf (error_str, "%s=same, ",
4177 fwupd_release_get_version (rel_tmp));
4178 g_debug ("ignoring %s as the same as %s",
4179 fwupd_release_get_version (rel_tmp),
Richard Hughes0a7e7832017-11-22 11:01:13 +00004180 fu_device_get_version (device));
Richard Hughes97284b12017-09-13 17:07:58 +01004181 continue;
4182 }
Richard Hughes8e0cc802019-03-04 14:10:17 +00004183
4184 /* newer than current */
4185 if (fwupd_release_has_flag (rel_tmp, FWUPD_RELEASE_FLAG_IS_UPGRADE)) {
Richard Hughes97284b12017-09-13 17:07:58 +01004186 g_string_append_printf (error_str, "%s=newer, ",
4187 fwupd_release_get_version (rel_tmp));
4188 g_debug ("ignoring %s as newer than %s",
4189 fwupd_release_get_version (rel_tmp),
Richard Hughes0a7e7832017-11-22 11:01:13 +00004190 fu_device_get_version (device));
Richard Hughes97284b12017-09-13 17:07:58 +01004191 continue;
4192 }
4193
Richard Hughesdce91202019-04-08 12:47:45 +01004194 /* don't show releases we are not allowed to downgrade to */
Richard Hughes8e0cc802019-03-04 14:10:17 +00004195 if (fwupd_release_has_flag (rel_tmp, FWUPD_RELEASE_FLAG_BLOCKED_VERSION)) {
4196 g_string_append_printf (error_str, "%s=lowest, ",
4197 fwupd_release_get_version (rel_tmp));
4198 g_debug ("ignoring %s as older than lowest %s",
4199 fwupd_release_get_version (rel_tmp),
4200 fu_device_get_version_lowest (device));
4201 continue;
Richard Hughes97284b12017-09-13 17:07:58 +01004202 }
4203 g_ptr_array_add (releases, g_object_ref (rel_tmp));
4204 }
4205 if (error_str->len > 2)
4206 g_string_truncate (error_str, error_str->len - 2);
4207 if (releases->len == 0) {
4208 if (error_str->len > 0) {
4209 g_set_error (error,
4210 FWUPD_ERROR,
4211 FWUPD_ERROR_NOTHING_TO_DO,
Mario Limonciello7e4949c2019-12-09 10:21:20 -06004212 "current version is %s: %s",
Richard Hughes0a7e7832017-11-22 11:01:13 +00004213 fu_device_get_version (device),
Richard Hughes97284b12017-09-13 17:07:58 +01004214 error_str->str);
4215 } else {
4216 g_set_error (error,
4217 FWUPD_ERROR,
4218 FWUPD_ERROR_NOTHING_TO_DO,
Mario Limonciello7e4949c2019-12-09 10:21:20 -06004219 "current version is %s",
Richard Hughes0a7e7832017-11-22 11:01:13 +00004220 fu_device_get_version (device));
Richard Hughes97284b12017-09-13 17:07:58 +01004221 }
4222 return NULL;
4223 }
Richard Hughes9a680842020-02-20 11:11:13 +00004224 g_ptr_array_sort_with_data (releases, fu_engine_sort_releases_cb, device);
Richard Hughes9945edb2017-06-19 10:03:55 +01004225 return g_steal_pointer (&releases);
4226}
4227
Richard Hughes8dd4c1c2019-03-03 18:27:57 +00004228GPtrArray *
4229fu_engine_get_approved_firmware (FuEngine *self)
4230{
4231 GPtrArray *checksums = g_ptr_array_new_with_free_func (g_free);
4232 g_autoptr(GList) keys = g_hash_table_get_keys (self->approved_firmware);
4233 for (GList *l = keys; l != NULL; l = l->next) {
4234 const gchar *csum = l->data;
4235 g_ptr_array_add (checksums, g_strdup (csum));
4236 }
4237 return checksums;
4238}
4239
4240void
4241fu_engine_add_approved_firmware (FuEngine *self, const gchar *checksum)
4242{
4243 g_hash_table_add (self->approved_firmware, g_strdup (checksum));
4244}
4245
Richard Hughes3d607622019-03-07 16:59:27 +00004246gchar *
4247fu_engine_self_sign (FuEngine *self,
4248 const gchar *value,
Richard Hughesd5aab652020-02-25 12:47:50 +00004249 JcatSignFlags flags,
Richard Hughes3d607622019-03-07 16:59:27 +00004250 GError **error)
4251{
Richard Hughesd5aab652020-02-25 12:47:50 +00004252 g_autoptr(JcatBlob) jcat_signature = NULL;
4253 g_autoptr(JcatEngine) jcat_engine = NULL;
4254 g_autoptr(JcatResult) jcat_result = NULL;
Richard Hughes3d607622019-03-07 16:59:27 +00004255 g_autoptr(GBytes) payload = NULL;
Richard Hughes3d607622019-03-07 16:59:27 +00004256
4257 /* create detached signature and verify */
Richard Hughesd5aab652020-02-25 12:47:50 +00004258 jcat_engine = jcat_context_get_engine (self->jcat_context,
4259 JCAT_BLOB_KIND_PKCS7,
4260 error);
4261 if (jcat_engine == NULL)
Richard Hughes3d607622019-03-07 16:59:27 +00004262 return NULL;
4263 payload = g_bytes_new (value, strlen (value));
Richard Hughesd5aab652020-02-25 12:47:50 +00004264 jcat_signature = jcat_engine_self_sign (jcat_engine, payload, flags, error);
4265 if (jcat_signature == NULL)
Richard Hughes3d607622019-03-07 16:59:27 +00004266 return NULL;
Richard Hughesd5aab652020-02-25 12:47:50 +00004267 jcat_result = jcat_engine_self_verify (jcat_engine, payload,
4268 jcat_blob_get_data (jcat_signature),
4269 JCAT_VERIFY_FLAG_NONE, error);
4270 if (jcat_result == NULL)
Richard Hughes3d607622019-03-07 16:59:27 +00004271 return NULL;
Richard Hughesd5aab652020-02-25 12:47:50 +00004272 return jcat_blob_get_data_as_string (jcat_signature);
Richard Hughes3d607622019-03-07 16:59:27 +00004273}
4274
Richard Hughes9945edb2017-06-19 10:03:55 +01004275/**
Richard Hughesa96413a2017-09-13 17:19:59 +01004276 * fu_engine_get_upgrades:
4277 * @self: A #FuEngine
4278 * @device_id: A device ID
4279 * @error: A #GError, or %NULL
4280 *
4281 * Gets the upgrades available for a specific device.
4282 *
Richard Hughes93b15762017-09-15 11:05:23 +01004283 * Returns: (transfer container) (element-type FwupdDevice): results
Richard Hughesa96413a2017-09-13 17:19:59 +01004284 **/
4285GPtrArray *
4286fu_engine_get_upgrades (FuEngine *self, const gchar *device_id, GError **error)
4287{
Richard Hughes5a9a6bd2018-09-10 10:02:20 +01004288 g_autoptr(FuDevice) device = NULL;
Richard Hughesa96413a2017-09-13 17:19:59 +01004289 g_autoptr(GPtrArray) releases = NULL;
4290 g_autoptr(GPtrArray) releases_tmp = NULL;
4291 g_autoptr(GString) error_str = g_string_new (NULL);
4292
4293 g_return_val_if_fail (FU_IS_ENGINE (self), NULL);
4294 g_return_val_if_fail (device_id != NULL, NULL);
4295 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
4296
4297 /* find the device */
Richard Hughes40127542018-01-12 20:25:55 +00004298 device = fu_device_list_get_by_id (self->device_list, device_id, error);
Richard Hughes0a7e7832017-11-22 11:01:13 +00004299 if (device == NULL)
Richard Hughesa96413a2017-09-13 17:19:59 +01004300 return NULL;
4301
Mario Limonciellofc139352018-09-13 10:06:39 -05004302 /* don't show upgrades again until we reboot */
4303 if (fu_device_get_update_state (device) == FWUPD_UPDATE_STATE_NEEDS_REBOOT) {
Mario Limonciello7e4949c2019-12-09 10:21:20 -06004304 g_set_error_literal (error,
4305 FWUPD_ERROR,
4306 FWUPD_ERROR_NOTHING_TO_DO,
4307 "A reboot is pending");
Mario Limonciellofc139352018-09-13 10:06:39 -05004308 return NULL;
4309 }
4310
Richard Hughesa96413a2017-09-13 17:19:59 +01004311 /* get all the releases for the device */
Richard Hughes0a7e7832017-11-22 11:01:13 +00004312 releases_tmp = fu_engine_get_releases_for_device (self, device, error);
Richard Hughesa96413a2017-09-13 17:19:59 +01004313 if (releases_tmp == NULL)
4314 return NULL;
4315 releases = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
4316 for (guint i = 0; i < releases_tmp->len; i++) {
4317 FwupdRelease *rel_tmp = g_ptr_array_index (releases_tmp, i);
Richard Hughesa96413a2017-09-13 17:19:59 +01004318
Richard Hughes8e0cc802019-03-04 14:10:17 +00004319 /* same as installed */
4320 if (!fwupd_release_has_flag (rel_tmp, FWUPD_RELEASE_FLAG_IS_UPGRADE) &&
4321 !fwupd_release_has_flag (rel_tmp, FWUPD_RELEASE_FLAG_IS_DOWNGRADE)) {
Richard Hughesa96413a2017-09-13 17:19:59 +01004322 g_string_append_printf (error_str, "%s=same, ",
4323 fwupd_release_get_version (rel_tmp));
4324 g_debug ("ignoring %s as the same as %s",
4325 fwupd_release_get_version (rel_tmp),
Richard Hughes0a7e7832017-11-22 11:01:13 +00004326 fu_device_get_version (device));
Richard Hughesa96413a2017-09-13 17:19:59 +01004327 continue;
4328 }
Richard Hughes8e0cc802019-03-04 14:10:17 +00004329
4330 /* older than current */
4331 if (fwupd_release_has_flag (rel_tmp, FWUPD_RELEASE_FLAG_IS_DOWNGRADE)) {
Richard Hughesa96413a2017-09-13 17:19:59 +01004332 g_string_append_printf (error_str, "%s=older, ",
4333 fwupd_release_get_version (rel_tmp));
4334 g_debug ("ignoring %s as older than %s",
4335 fwupd_release_get_version (rel_tmp),
Richard Hughes0a7e7832017-11-22 11:01:13 +00004336 fu_device_get_version (device));
Richard Hughesa96413a2017-09-13 17:19:59 +01004337 continue;
4338 }
Richard Hughes8dd4c1c2019-03-03 18:27:57 +00004339
4340 /* not approved */
4341 if (fwupd_release_has_flag (rel_tmp, FWUPD_RELEASE_FLAG_BLOCKED_APPROVAL)) {
4342 g_string_append_printf (error_str, "%s=not-approved, ",
4343 fwupd_release_get_version (rel_tmp));
4344 g_debug ("ignoring %s as not approved as required by %s",
4345 fwupd_release_get_version (rel_tmp),
4346 fwupd_release_get_remote_id (rel_tmp));
4347 continue;
4348 }
4349
Richard Hughesa96413a2017-09-13 17:19:59 +01004350 g_ptr_array_add (releases, g_object_ref (rel_tmp));
4351 }
4352 if (error_str->len > 2)
4353 g_string_truncate (error_str, error_str->len - 2);
4354 if (releases->len == 0) {
4355 if (error_str->len > 0) {
4356 g_set_error (error,
4357 FWUPD_ERROR,
4358 FWUPD_ERROR_NOTHING_TO_DO,
Mario Limonciello7e4949c2019-12-09 10:21:20 -06004359 "current version is %s: %s",
Richard Hughes0a7e7832017-11-22 11:01:13 +00004360 fu_device_get_version (device),
Richard Hughesa96413a2017-09-13 17:19:59 +01004361 error_str->str);
4362 } else {
4363 g_set_error (error,
4364 FWUPD_ERROR,
4365 FWUPD_ERROR_NOTHING_TO_DO,
Mario Limonciello7e4949c2019-12-09 10:21:20 -06004366 "current version is %s",
Richard Hughes0a7e7832017-11-22 11:01:13 +00004367 fu_device_get_version (device));
Richard Hughesa96413a2017-09-13 17:19:59 +01004368 }
4369 return NULL;
4370 }
Richard Hughes9a680842020-02-20 11:11:13 +00004371 g_ptr_array_sort_with_data (releases, fu_engine_sort_releases_cb, device);
Richard Hughesa96413a2017-09-13 17:19:59 +01004372 return g_steal_pointer (&releases);
4373}
4374
4375/**
Richard Hughes9945edb2017-06-19 10:03:55 +01004376 * fu_engine_clear_results:
4377 * @self: A #FuEngine
4378 * @device_id: A device ID
4379 * @error: A #GError, or %NULL
4380 *
4381 * Clear the historical state of a specific device operation.
4382 *
4383 * Returns: %TRUE for success
4384 **/
4385gboolean
4386fu_engine_clear_results (FuEngine *self, const gchar *device_id, GError **error)
4387{
Richard Hughes65e44ca2018-01-30 17:26:30 +00004388 g_autoptr(FuDevice) device = NULL;
Richard Hughes34834102017-11-21 21:55:00 +00004389 FuPlugin *plugin;
Richard Hughes9945edb2017-06-19 10:03:55 +01004390
4391 g_return_val_if_fail (FU_IS_ENGINE (self), FALSE);
4392 g_return_val_if_fail (device_id != NULL, FALSE);
4393 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
4394
4395 /* find the device */
Richard Hughesbc3a4e12018-01-06 22:41:47 +00004396 device = fu_engine_get_item_by_id_fallback_history (self, device_id, error);
Richard Hughes0a7e7832017-11-22 11:01:13 +00004397 if (device == NULL)
Richard Hughes9945edb2017-06-19 10:03:55 +01004398 return FALSE;
4399
Richard Hughes65e44ca2018-01-30 17:26:30 +00004400 /* already set on the database */
4401 if (fu_device_has_flag (device, FWUPD_DEVICE_FLAG_NOTIFIED)) {
4402 g_set_error_literal (error,
4403 FWUPD_ERROR,
4404 FWUPD_ERROR_NOT_SUPPORTED,
4405 "device already has notified flag");
4406 return FALSE;
4407 }
4408
4409 /* call into the plugin if it still exists */
Richard Hughese7e95452017-11-22 09:05:53 +00004410 plugin = fu_plugin_list_find_by_name (self->plugin_list,
Richard Hughes0a7e7832017-11-22 11:01:13 +00004411 fu_device_get_plugin (device),
Richard Hughese7e95452017-11-22 09:05:53 +00004412 error);
Richard Hughes65e44ca2018-01-30 17:26:30 +00004413 if (plugin != NULL) {
4414 if (!fu_plugin_runner_clear_results (plugin, device, error))
4415 return FALSE;
4416 }
Richard Hughes34834102017-11-21 21:55:00 +00004417
Richard Hughes65e44ca2018-01-30 17:26:30 +00004418 /* override */
Richard Hughesc0cd0232018-01-31 15:02:00 +00004419 fu_device_add_flag (device, FWUPD_DEVICE_FLAG_NOTIFIED);
Richard Hughes0bbef292019-11-01 12:15:15 +00004420 return fu_history_modify_device (self->history, device, error);
Richard Hughes9945edb2017-06-19 10:03:55 +01004421}
4422
4423/**
4424 * fu_engine_get_results:
4425 * @self: A #FuEngine
4426 * @device_id: A device ID
4427 * @error: A #GError, or %NULL
4428 *
4429 * Gets the historical state of a specific device operation.
4430 *
Richard Hughes93b15762017-09-15 11:05:23 +01004431 * Returns: (transfer container): a #FwupdDevice, or %NULL
Richard Hughes9945edb2017-06-19 10:03:55 +01004432 **/
Richard Hughes93b15762017-09-15 11:05:23 +01004433FwupdDevice *
Richard Hughes9945edb2017-06-19 10:03:55 +01004434fu_engine_get_results (FuEngine *self, const gchar *device_id, GError **error)
4435{
Richard Hughes65e44ca2018-01-30 17:26:30 +00004436 g_autoptr(FuDevice) device = NULL;
Richard Hughes9945edb2017-06-19 10:03:55 +01004437
4438 g_return_val_if_fail (FU_IS_ENGINE (self), NULL);
4439 g_return_val_if_fail (device_id != NULL, NULL);
4440 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
4441
4442 /* find the device */
Richard Hughesbc3a4e12018-01-06 22:41:47 +00004443 device = fu_engine_get_item_by_id_fallback_history (self, device_id, error);
Richard Hughes0a7e7832017-11-22 11:01:13 +00004444 if (device == NULL)
Richard Hughesfe221dc2018-05-29 09:33:44 +01004445 return NULL;
Richard Hughes34834102017-11-21 21:55:00 +00004446
Richard Hughes65e44ca2018-01-30 17:26:30 +00004447 /* the notification has already been shown to the user */
4448 if (fu_device_has_flag (device, FWUPD_DEVICE_FLAG_NOTIFIED)) {
4449 g_set_error (error,
4450 FWUPD_ERROR,
4451 FWUPD_ERROR_NOTHING_TO_DO,
Richard Hughes7e77bf32018-05-08 14:56:46 +01004452 "User has already been notified about %s [%s]",
4453 fu_device_get_name (device),
Richard Hughes65e44ca2018-01-30 17:26:30 +00004454 fu_device_get_id (device));
Richard Hughes9945edb2017-06-19 10:03:55 +01004455 return NULL;
Richard Hughes65e44ca2018-01-30 17:26:30 +00004456 }
Richard Hughes9945edb2017-06-19 10:03:55 +01004457
Richard Hughes65e44ca2018-01-30 17:26:30 +00004458 /* success */
Richard Hugheseec8a3c2018-01-02 20:37:31 +00004459 return g_object_ref (FWUPD_DEVICE (device));
Richard Hughes9945edb2017-06-19 10:03:55 +01004460}
4461
4462static void
4463fu_engine_plugins_setup (FuEngine *self)
4464{
Richard Hughese671c052018-09-18 10:17:15 +01004465 GPtrArray *plugins = fu_plugin_list_get_all (self->plugin_list);
Richard Hughese7e95452017-11-22 09:05:53 +00004466 for (guint i = 0; i < plugins->len; i++) {
Richard Hughes9945edb2017-06-19 10:03:55 +01004467 g_autoptr(GError) error = NULL;
Richard Hughese7e95452017-11-22 09:05:53 +00004468 FuPlugin *plugin = g_ptr_array_index (plugins, i);
Richard Hughes9945edb2017-06-19 10:03:55 +01004469 if (!fu_plugin_runner_startup (plugin, &error)) {
4470 fu_plugin_set_enabled (plugin, FALSE);
4471 g_message ("disabling plugin because: %s", error->message);
4472 }
4473 }
4474}
4475
4476static void
Richard Hughes2de8f132018-01-17 09:12:02 +00004477fu_engine_plugins_coldplug (FuEngine *self, gboolean is_recoldplug)
Richard Hughes9945edb2017-06-19 10:03:55 +01004478{
Richard Hughese7e95452017-11-22 09:05:53 +00004479 GPtrArray *plugins;
Richard Hughes535664c2017-07-24 10:30:09 +01004480 g_autoptr(GString) str = g_string_new (NULL);
Richard Hughes9945edb2017-06-19 10:03:55 +01004481
4482 /* don't allow coldplug to be scheduled when in coldplug */
4483 self->coldplug_running = TRUE;
4484
4485 /* prepare */
Richard Hughese7e95452017-11-22 09:05:53 +00004486 plugins = fu_plugin_list_get_all (self->plugin_list);
4487 for (guint i = 0; i < plugins->len; i++) {
Richard Hughes9945edb2017-06-19 10:03:55 +01004488 g_autoptr(GError) error = NULL;
Richard Hughese7e95452017-11-22 09:05:53 +00004489 FuPlugin *plugin = g_ptr_array_index (plugins, i);
Richard Hughes9945edb2017-06-19 10:03:55 +01004490 if (!fu_plugin_runner_coldplug_prepare (plugin, &error))
4491 g_warning ("failed to prepare coldplug: %s", error->message);
4492 }
4493
4494 /* do this in one place */
4495 if (self->coldplug_delay > 0) {
4496 g_debug ("sleeping for %ums", self->coldplug_delay);
4497 g_usleep (self->coldplug_delay * 1000);
4498 }
4499
4500 /* exec */
Richard Hughese7e95452017-11-22 09:05:53 +00004501 for (guint i = 0; i < plugins->len; i++) {
Richard Hughes9945edb2017-06-19 10:03:55 +01004502 g_autoptr(GError) error = NULL;
Richard Hughese7e95452017-11-22 09:05:53 +00004503 FuPlugin *plugin = g_ptr_array_index (plugins, i);
Richard Hughes2de8f132018-01-17 09:12:02 +00004504 if (is_recoldplug) {
4505 if (!fu_plugin_runner_recoldplug (plugin, &error))
4506 g_message ("failed recoldplug: %s", error->message);
4507 } else {
4508 if (!fu_plugin_runner_coldplug (plugin, &error)) {
4509 fu_plugin_set_enabled (plugin, FALSE);
4510 g_message ("disabling plugin because: %s",
4511 error->message);
4512 }
Richard Hughes9945edb2017-06-19 10:03:55 +01004513 }
4514 }
4515
4516 /* cleanup */
Richard Hughese7e95452017-11-22 09:05:53 +00004517 for (guint i = 0; i < plugins->len; i++) {
Richard Hughes9945edb2017-06-19 10:03:55 +01004518 g_autoptr(GError) error = NULL;
Richard Hughese7e95452017-11-22 09:05:53 +00004519 FuPlugin *plugin = g_ptr_array_index (plugins, i);
Richard Hughes9945edb2017-06-19 10:03:55 +01004520 if (!fu_plugin_runner_coldplug_cleanup (plugin, &error))
4521 g_warning ("failed to cleanup coldplug: %s", error->message);
4522 }
4523
Richard Hughes535664c2017-07-24 10:30:09 +01004524 /* print what we do have */
Richard Hughese7e95452017-11-22 09:05:53 +00004525 for (guint i = 0; i < plugins->len; i++) {
4526 FuPlugin *plugin = g_ptr_array_index (plugins, i);
Richard Hughes535664c2017-07-24 10:30:09 +01004527 if (!fu_plugin_get_enabled (plugin))
4528 continue;
4529 g_string_append_printf (str, "%s, ", fu_plugin_get_name (plugin));
4530 }
4531 if (str->len > 2) {
4532 g_string_truncate (str, str->len - 2);
Mario Limonciello59cd4702018-08-09 12:43:47 -05004533 g_debug ("using plugins: %s", str->str);
Richard Hughes535664c2017-07-24 10:30:09 +01004534 }
4535
Richard Hughes9945edb2017-06-19 10:03:55 +01004536 /* we can recoldplug from this point on */
4537 self->coldplug_running = FALSE;
4538}
4539
4540static void
Richard Hughese1fd34d2017-08-24 14:19:51 +01004541fu_engine_plugin_device_register (FuEngine *self, FuDevice *device)
4542{
Richard Hughese7e95452017-11-22 09:05:53 +00004543 GPtrArray *plugins;
Richard Hughese1fd34d2017-08-24 14:19:51 +01004544 if (fu_device_has_flag (device, FWUPD_DEVICE_FLAG_REGISTERED)) {
4545 g_warning ("already registered %s, ignoring",
4546 fu_device_get_id (device));
4547 return;
4548 }
Richard Hughese7e95452017-11-22 09:05:53 +00004549 plugins = fu_plugin_list_get_all (self->plugin_list);
4550 for (guint i = 0; i < plugins->len; i++) {
4551 FuPlugin *plugin = g_ptr_array_index (plugins, i);
Richard Hughese1fd34d2017-08-24 14:19:51 +01004552 fu_plugin_runner_device_register (plugin, device);
4553 }
4554 fu_device_add_flag (device, FWUPD_DEVICE_FLAG_REGISTERED);
4555}
4556
4557static void
4558fu_engine_plugin_device_register_cb (FuPlugin *plugin,
4559 FuDevice *device,
4560 gpointer user_data)
4561{
4562 FuEngine *self = FU_ENGINE (user_data);
4563 fu_engine_plugin_device_register (self, device);
4564}
4565
4566static void
Richard Hughes9945edb2017-06-19 10:03:55 +01004567fu_engine_plugin_device_added_cb (FuPlugin *plugin,
4568 FuDevice *device,
4569 gpointer user_data)
4570{
Richard Hughes4299ba02020-04-14 15:40:56 +01004571 FuEngine *self = FU_ENGINE (user_data);
Richard Hughesfecc4382020-04-15 11:24:34 +01004572
4573 /* plugin has prio and device not already set from quirk */
4574 if (fu_plugin_get_priority (plugin) > 0 &&
4575 fu_device_get_priority (device) == 0) {
4576 g_debug ("auto-setting %s priority to %u",
4577 fu_device_get_id (device),
4578 fu_plugin_get_priority (plugin));
4579 fu_device_set_priority (device, fu_plugin_get_priority (plugin));
4580 }
4581
Richard Hughes0a7e7832017-11-22 11:01:13 +00004582 fu_engine_add_device (self, device);
4583}
4584
Richard Hughes5e447292018-04-27 14:25:54 +01004585static void
4586fu_engine_adopt_children (FuEngine *self, FuDevice *device)
4587{
4588 GPtrArray *guids;
4589 g_autoptr(GPtrArray) devices = fu_device_list_get_active (self->device_list);
4590
4591 /* find the parent GUID in any existing device */
4592 guids = fu_device_get_parent_guids (device);
4593 for (guint j = 0; j < guids->len; j++) {
4594 const gchar *guid = g_ptr_array_index (guids, j);
4595 for (guint i = 0; i < devices->len; i++) {
4596 FuDevice *device_tmp = g_ptr_array_index (devices, i);
4597 if (fu_device_get_parent (device) != NULL)
4598 continue;
4599 if (fu_device_has_guid (device_tmp, guid)) {
Richard Hughes7e77bf32018-05-08 14:56:46 +01004600 g_debug ("setting parent of %s [%s] to be %s [%s]",
4601 fu_device_get_name (device),
Richard Hughes5e447292018-04-27 14:25:54 +01004602 fu_device_get_id (device),
Richard Hughes7e77bf32018-05-08 14:56:46 +01004603 fu_device_get_name (device_tmp),
Richard Hughes5e447292018-04-27 14:25:54 +01004604 fu_device_get_id (device_tmp));
Richard Hughes2ab379e2020-04-09 16:03:36 +01004605 fu_device_set_parent (device, device_tmp);
Richard Hughes5e447292018-04-27 14:25:54 +01004606 break;
4607 }
4608 }
4609 }
4610
4611 /* the new device is the parent to an existing child */
4612 guids = fu_device_get_guids (device);
4613 for (guint j = 0; j < guids->len; j++) {
4614 const gchar *guid = g_ptr_array_index (guids, j);
4615 for (guint i = 0; i < devices->len; i++) {
4616 FuDevice *device_tmp = g_ptr_array_index (devices, i);
4617 if (fu_device_get_parent (device_tmp) != NULL)
4618 continue;
4619 if (fu_device_has_parent_guid (device_tmp, guid)) {
Richard Hughes7e77bf32018-05-08 14:56:46 +01004620 g_debug ("setting parent of %s [%s] to be %s [%s]",
4621 fu_device_get_name (device_tmp),
Richard Hughes5e447292018-04-27 14:25:54 +01004622 fu_device_get_id (device_tmp),
Richard Hughes7e77bf32018-05-08 14:56:46 +01004623 fu_device_get_name (device),
Richard Hughes5e447292018-04-27 14:25:54 +01004624 fu_device_get_id (device));
Richard Hughes2ab379e2020-04-09 16:03:36 +01004625 fu_device_set_parent (device_tmp, device);
Richard Hughes5e447292018-04-27 14:25:54 +01004626 }
4627 }
4628 }
4629}
4630
Mario Limonciello96a0dd52019-02-25 13:50:03 -06004631static void
Richard Hughes35ca4cb2020-04-14 17:09:01 +01004632fu_engine_set_proxy_device (FuEngine *self, FuDevice *device)
4633{
4634 GPtrArray *guids;
4635 g_autoptr(FuDevice) proxy = NULL;
4636 g_autoptr(GPtrArray) devices = NULL;
4637
4638 if (fu_device_get_proxy (device) != NULL)
4639 return;
4640 if (fu_device_get_proxy_guid (device) == NULL)
4641 return;
4642
4643 /* find the proxy GUID in any existing device */
4644 proxy = fu_device_list_get_by_guid (self->device_list,
4645 fu_device_get_proxy_guid (device),
4646 NULL);
4647 if (proxy != NULL) {
4648 g_debug ("setting proxy of %s to %s for %s",
4649 fu_device_get_id (proxy),
4650 fu_device_get_id (device),
4651 fu_device_get_proxy_guid (device));
4652 fu_device_set_proxy (device, proxy);
4653 return;
4654 }
4655
4656 /* are we the parent of an existing device */
4657 guids = fu_device_get_guids (device);
4658 for (guint j = 0; j < guids->len; j++) {
4659 const gchar *guid = g_ptr_array_index (guids, j);
4660 devices = fu_device_list_get_active (self->device_list);
4661 for (guint i = 0; i < devices->len; i++) {
4662 FuDevice *device_tmp = g_ptr_array_index (devices, i);
4663 if (g_strcmp0 (fu_device_get_proxy_guid (device_tmp), guid) == 0) {
4664 g_debug ("adding proxy of %s to %s for %s",
4665 fu_device_get_id (device),
4666 fu_device_get_id (device_tmp),
4667 guid);
4668 fu_device_set_proxy (device_tmp, device);
4669 return;
4670 }
4671 }
4672 }
4673
4674 /* nothing found */
4675 g_warning ("did not find proxy device %s",
4676 fu_device_get_proxy_guid (device));
4677}
4678
4679static void
Mario Limonciello96a0dd52019-02-25 13:50:03 -06004680fu_engine_device_inherit_history (FuEngine *self, FuDevice *device)
4681{
4682 g_autoptr(FuDevice) device_history = NULL;
4683
4684 /* any success or failed update? */
4685 device_history = fu_history_get_device_by_id (self->history,
4686 fu_device_get_id (device),
4687 NULL);
4688 if (device_history == NULL)
4689 return;
4690
4691 /* the device is still running the old firmware version and so if it
4692 * required activation before, it still requires it now -- note:
4693 * we can't just check for version_new=version to allow for re-installs */
4694 if (fu_device_has_flag (device_history, FWUPD_DEVICE_FLAG_NEEDS_ACTIVATION)) {
4695 FwupdRelease *release = fu_device_get_release_default (device_history);
Richard Hughes9a680842020-02-20 11:11:13 +00004696 if (fu_common_vercmp_full (fu_device_get_version (device),
4697 fwupd_release_get_version (release),
4698 fu_device_get_version_format (device)) != 0) {
Mario Limonciello96a0dd52019-02-25 13:50:03 -06004699 g_debug ("inheriting needs-activation for %s as version %s != %s",
4700 fu_device_get_name (device),
4701 fu_device_get_version (device),
4702 fwupd_release_get_version (release));
4703 fu_device_add_flag (device, FWUPD_DEVICE_FLAG_NEEDS_ACTIVATION);
4704 }
4705 }
4706}
4707
Richard Hughes0a7e7832017-11-22 11:01:13 +00004708void
4709fu_engine_add_device (FuEngine *self, FuDevice *device)
4710{
Richard Hughes9945edb2017-06-19 10:03:55 +01004711 GPtrArray *blacklisted_devices;
Richard Hughes32684f22017-07-13 09:32:21 +01004712 GPtrArray *device_guids;
Mario Limoncielloe0384192020-04-16 14:36:49 -05004713 g_autoptr(XbNode) component = NULL;
Richard Hughes9945edb2017-06-19 10:03:55 +01004714
4715 /* device has no GUIDs set! */
Richard Hughes32684f22017-07-13 09:32:21 +01004716 device_guids = fu_device_get_guids (device);
4717 if (device_guids->len == 0) {
Richard Hughes9945edb2017-06-19 10:03:55 +01004718 g_warning ("no GUIDs for device %s [%s]",
Richard Hughes7e77bf32018-05-08 14:56:46 +01004719 fu_device_get_name (device),
4720 fu_device_get_id (device));
Richard Hughes9945edb2017-06-19 10:03:55 +01004721 return;
4722 }
4723
4724 /* is this GUID blacklisted */
4725 blacklisted_devices = fu_config_get_blacklist_devices (self->config);
4726 for (guint i = 0; i < blacklisted_devices->len; i++) {
Richard Hughes32684f22017-07-13 09:32:21 +01004727 const gchar *blacklisted_guid = g_ptr_array_index (blacklisted_devices, i);
4728 for (guint j = 0; j < device_guids->len; j++) {
4729 const gchar *device_guid = g_ptr_array_index (device_guids, j);
4730 if (g_strcmp0 (blacklisted_guid, device_guid) == 0) {
Richard Hughes7e77bf32018-05-08 14:56:46 +01004731 g_debug ("%s [%s] is blacklisted [%s], ignoring from %s",
4732 fu_device_get_name (device),
4733 fu_device_get_id (device),
4734 device_guid,
Richard Hughes0a7e7832017-11-22 11:01:13 +00004735 fu_device_get_plugin (device));
Richard Hughes32684f22017-07-13 09:32:21 +01004736 return;
4737 }
Richard Hughes9945edb2017-06-19 10:03:55 +01004738 }
4739 }
4740
Richard Hughes7f765002019-12-20 09:41:29 +00004741 /* does the device not have an assigned protocol */
4742 if (fu_device_has_flag (device, FWUPD_DEVICE_FLAG_UPDATABLE) &&
4743 fu_device_get_protocol (device) == NULL) {
Mario Limonciello33b40ed2020-01-08 15:11:34 -06004744 g_warning ("device %s [%s] does not define an update protocol",
Richard Hughes7f765002019-12-20 09:41:29 +00004745 fu_device_get_id (device),
4746 fu_device_get_name (device));
4747 }
4748
Richard Hughesf3077752018-06-27 12:45:06 +01004749 /* if this device is locked get some metadata from AppStream */
Mario Limoncielloe0384192020-04-16 14:36:49 -05004750 component = fu_engine_get_component_by_guids (self, device);
Richard Hughesf3077752018-06-27 12:45:06 +01004751 if (fu_device_has_flag (device, FWUPD_DEVICE_FLAG_LOCKED)) {
Richard Hughes481aa2a2018-09-18 20:51:46 +01004752 if (component != NULL) {
4753 g_autoptr(XbNode) release = NULL;
4754 release = xb_node_query_first (component,
4755 "releases/release",
4756 NULL);
Richard Hughesf3077752018-06-27 12:45:06 +01004757 if (release != NULL) {
4758 g_autoptr(FwupdRelease) rel = fwupd_release_new ();
Richard Hughes0b3e9fd2019-04-08 11:13:20 +01004759 g_autoptr(GError) error_local = NULL;
4760 if (!fu_engine_set_release_from_appstream (self,
Richard Hughes2c40b372019-04-17 13:41:47 +01004761 device,
Richard Hughes0b3e9fd2019-04-08 11:13:20 +01004762 rel,
4763 component,
4764 release,
4765 &error_local)) {
4766 g_warning ("failed to set AppStream release: %s",
4767 error_local->message);
4768 } else {
4769 fu_device_add_release (device, rel);
4770 }
Richard Hughesf3077752018-06-27 12:45:06 +01004771 }
4772 }
4773 }
4774
Richard Hughes5e447292018-04-27 14:25:54 +01004775 /* adopt any required children, which may or may not already exist */
4776 fu_engine_adopt_children (self, device);
4777
Richard Hughes35ca4cb2020-04-14 17:09:01 +01004778 /* set the proxy device if specified by GUID */
4779 fu_engine_set_proxy_device (self, device);
4780
Richard Hughese48351e2018-06-22 12:32:39 +01004781 /* set any alternate objects on the device from the ID */
4782 if (fu_device_get_alternate_id (device) != NULL) {
Richard Hughes5a9a6bd2018-09-10 10:02:20 +01004783 g_autoptr(FuDevice) device_alt = NULL;
Richard Hughese48351e2018-06-22 12:32:39 +01004784 device_alt = fu_device_list_get_by_id (self->device_list,
4785 fu_device_get_alternate_id (device),
4786 NULL);
4787 if (device_alt != NULL)
4788 fu_device_set_alternate (device, device_alt);
4789 }
4790
Richard Hughes5079f262019-04-29 09:34:27 +01004791 if (fu_device_get_version_format (device) == FWUPD_VERSION_FORMAT_UNKNOWN &&
4792 fu_common_version_guess_format (fu_device_get_version (device)) == FWUPD_VERSION_FORMAT_NUMBER) {
Mario Limonciello253b8252019-04-25 10:48:17 -04004793 fu_device_remove_flag (device, FWUPD_DEVICE_FLAG_UPDATABLE);
4794 fu_device_set_update_error (device, "VersionFormat is ambiguous for this device");
4795 }
4796
Richard Hughesd8ea8da2020-02-19 15:04:49 +00004797 /* no vendor-id, and so no way to lock it down! */
4798 if (fu_device_has_flag (device, FWUPD_DEVICE_FLAG_UPDATABLE) &&
4799 fu_device_get_vendor_id (device) == NULL) {
4800 fu_device_remove_flag (device, FWUPD_DEVICE_FLAG_UPDATABLE);
4801 fu_device_set_update_error (device, "No vendor ID set");
4802 }
4803
Richard Hughese1fd34d2017-08-24 14:19:51 +01004804 /* notify all plugins about this new device */
4805 if (!fu_device_has_flag (device, FWUPD_DEVICE_FLAG_REGISTERED))
4806 fu_engine_plugin_device_register (self, device);
4807
Richard Hughes7f765002019-12-20 09:41:29 +00004808 /* does the device *still* not have a vendor ID? */
4809 if (fu_device_has_flag (device, FWUPD_DEVICE_FLAG_UPDATABLE) &&
4810 fu_device_get_vendor_id (device) == NULL) {
4811 g_warning ("device %s [%s] does not define a vendor-id!",
4812 fu_device_get_id (device),
4813 fu_device_get_name (device));
4814 }
4815
Richard Hughesd76ed3d2018-11-15 15:05:36 +00004816 /* create new device */
4817 fu_device_list_add (self->device_list, device);
4818
Mario Limoncielloe0384192020-04-16 14:36:49 -05004819 /* fixup the name and format as needed from cached metadata */
4820 if (component != NULL)
4821 fu_engine_md_refresh_device_from_component (self, device, component);
4822
Richard Hughes1ee18002020-03-31 21:46:49 +01004823 /* match the metadata so clients can tell if the device is worthy */
4824 fu_engine_ensure_device_supported (self, device);
Mario Limonciello96a0dd52019-02-25 13:50:03 -06004825
4826 /* sometimes inherit flags from recent history */
4827 fu_engine_device_inherit_history (self, device);
Mario Limonciellod81ea2e2020-01-13 14:11:43 -06004828
4829 fu_engine_emit_changed (self);
Richard Hughes9945edb2017-06-19 10:03:55 +01004830}
4831
4832static void
Richard Hughes95c98a92019-10-22 16:03:15 +01004833fu_engine_plugin_add_firmware_gtype_cb (FuPlugin *plugin,
4834 const gchar *id,
4835 GType gtype,
4836 gpointer user_data)
4837{
4838 FuEngine *self = FU_ENGINE (user_data);
4839 fu_engine_add_firmware_gtype (self, id, gtype);
4840}
4841
4842static void
Richard Hughes75b965d2018-11-15 13:51:21 +00004843fu_engine_plugin_rules_changed_cb (FuPlugin *plugin, gpointer user_data)
4844{
4845 FuEngine *self = FU_ENGINE (user_data);
4846 GPtrArray *rules = fu_plugin_get_rules (plugin, FU_PLUGIN_RULE_INHIBITS_IDLE);
4847 for (guint j = 0; j < rules->len; j++) {
4848 const gchar *tmp = g_ptr_array_index (rules, j);
4849 fu_idle_inhibit (self->idle, tmp);
4850 }
4851}
4852
4853static void
Richard Hughes399859e2020-05-11 19:44:03 +01004854fu_engine_plugin_security_changed_cb (FuPlugin *plugin, gpointer user_data)
4855{
4856 FuEngine *self = FU_ENGINE (user_data);
4857 fu_engine_emit_changed (self);
4858}
4859
4860static void
Richard Hughes9945edb2017-06-19 10:03:55 +01004861fu_engine_plugin_device_removed_cb (FuPlugin *plugin,
4862 FuDevice *device,
4863 gpointer user_data)
4864{
4865 FuEngine *self = (FuEngine *) user_data;
Richard Hughes34834102017-11-21 21:55:00 +00004866 FuPlugin *plugin_old;
Richard Hughes5a9a6bd2018-09-10 10:02:20 +01004867 g_autoptr(FuDevice) device_tmp = NULL;
Richard Hughes9945edb2017-06-19 10:03:55 +01004868 g_autoptr(GError) error = NULL;
4869
Richard Hughes40127542018-01-12 20:25:55 +00004870 device_tmp = fu_device_list_get_by_id (self->device_list,
4871 fu_device_get_id (device),
4872 &error);
Richard Hughes0a7e7832017-11-22 11:01:13 +00004873 if (device_tmp == NULL) {
Richard Hughes9945edb2017-06-19 10:03:55 +01004874 g_debug ("%s", error->message);
4875 return;
4876 }
4877
Richard Hughes34834102017-11-21 21:55:00 +00004878 /* get the plugin */
Richard Hughese7e95452017-11-22 09:05:53 +00004879 plugin_old = fu_plugin_list_find_by_name (self->plugin_list,
Richard Hughes0a7e7832017-11-22 11:01:13 +00004880 fu_device_get_plugin (device),
Richard Hughese7e95452017-11-22 09:05:53 +00004881 &error);
Richard Hughes34834102017-11-21 21:55:00 +00004882 if (plugin_old == NULL) {
4883 g_debug ("%s", error->message);
4884 return;
4885 }
4886
Richard Hughes9945edb2017-06-19 10:03:55 +01004887 /* check this came from the same plugin */
4888 if (g_strcmp0 (fu_plugin_get_name (plugin),
Richard Hughes34834102017-11-21 21:55:00 +00004889 fu_plugin_get_name (plugin_old)) != 0) {
Richard Hughes9945edb2017-06-19 10:03:55 +01004890 g_debug ("ignoring duplicate removal from %s",
4891 fu_plugin_get_name (plugin));
4892 return;
4893 }
4894
4895 /* make the UI update */
Richard Hughes0a7e7832017-11-22 11:01:13 +00004896 fu_device_list_remove (self->device_list, device);
Richard Hughes9945edb2017-06-19 10:03:55 +01004897 fu_engine_emit_changed (self);
4898}
4899
Richard Hughes9945edb2017-06-19 10:03:55 +01004900static gboolean
4901fu_engine_recoldplug_delay_cb (gpointer user_data)
4902{
4903 FuEngine *self = (FuEngine *) user_data;
4904 g_debug ("performing a recoldplug");
Richard Hughes2de8f132018-01-17 09:12:02 +00004905 fu_engine_plugins_coldplug (self, TRUE);
Richard Hughes9945edb2017-06-19 10:03:55 +01004906 self->coldplug_id = 0;
4907 return FALSE;
4908}
4909
Richard Hughes16658372019-11-22 09:23:59 +00004910#ifdef HAVE_GUDEV
Richard Hughes9945edb2017-06-19 10:03:55 +01004911static void
Richard Hughes9d6e0e72018-08-24 20:20:17 +01004912fu_engine_udev_device_add (FuEngine *self, GUdevDevice *udev_device)
4913{
Richard Hughesff704412018-09-04 11:28:32 +01004914 g_autoptr(FuUdevDevice) device = fu_udev_device_new (udev_device);
Richard Hughes6dec4012018-08-27 19:13:00 +01004915 g_autoptr(GError) error_local = NULL;
Richard Hughes51a869a2019-10-07 11:23:42 +01004916 g_autoptr(GPtrArray) possible_plugins = NULL;
Richard Hughes6dec4012018-08-27 19:13:00 +01004917
Richard Hughesf3880bc2019-11-26 12:02:30 +00004918 /* debug */
4919 if (g_getenv ("FWUPD_PROBE_VERBOSE") != NULL) {
4920 g_debug ("UDEV %s added",
4921 g_udev_device_get_sysfs_path (udev_device));
4922 }
4923
Richard Hughes6dec4012018-08-27 19:13:00 +01004924 /* add any extra quirks */
Richard Hughesff704412018-09-04 11:28:32 +01004925 fu_device_set_quirks (FU_DEVICE (device), self->quirks);
4926 if (!fu_device_probe (FU_DEVICE (device), &error_local)) {
Richard Hughes6dec4012018-08-27 19:13:00 +01004927 g_warning ("failed to probe device %s: %s",
4928 g_udev_device_get_sysfs_path (udev_device),
4929 error_local->message);
4930 return;
4931 }
Richard Hughes9d6e0e72018-08-24 20:20:17 +01004932
Richard Hughes23170a82018-08-29 09:11:52 +01004933 /* can be specified using a quirk */
Richard Hughes51a869a2019-10-07 11:23:42 +01004934 possible_plugins = fu_device_get_possible_plugins (FU_DEVICE (device));
4935 for (guint i = 0; i < possible_plugins->len; i++) {
4936 FuPlugin *plugin;
4937 const gchar *plugin_name = g_ptr_array_index (possible_plugins, i);
4938 g_autoptr(GError) error = NULL;
Mario Limonciello0e500322019-10-17 18:41:04 -05004939
Richard Hughes51a869a2019-10-07 11:23:42 +01004940 plugin = fu_plugin_list_find_by_name (self->plugin_list,
4941 plugin_name, &error);
4942 if (plugin == NULL) {
Richard Hughesa1f95352020-02-21 08:14:54 +00004943 g_debug ("failed to find specified plugin %s: %s",
4944 plugin_name, error->message);
Richard Hughes51a869a2019-10-07 11:23:42 +01004945 continue;
4946 }
4947 if (!fu_plugin_runner_udev_device_added (plugin, device, &error)) {
4948 if (g_error_matches (error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED)) {
4949 if (g_getenv ("FWUPD_PROBE_VERBOSE") != NULL) {
4950 g_debug ("%s ignoring: %s",
4951 fu_plugin_get_name (plugin),
4952 error->message);
Mario Limonciello0e500322019-10-17 18:41:04 -05004953 }
Mario Limonciello0e500322019-10-17 18:41:04 -05004954 continue;
4955 }
Richard Hughes51a869a2019-10-07 11:23:42 +01004956 g_warning ("failed to add udev device %s: %s",
4957 g_udev_device_get_sysfs_path (udev_device),
4958 error->message);
4959 continue;
Richard Hughes9d6e0e72018-08-24 20:20:17 +01004960 }
Richard Hughes9d6e0e72018-08-24 20:20:17 +01004961 }
Richard Hughes9d6e0e72018-08-24 20:20:17 +01004962}
4963
4964static void
4965fu_engine_udev_device_remove (FuEngine *self, GUdevDevice *udev_device)
4966{
4967 g_autoptr(GPtrArray) devices = NULL;
4968
Richard Hughesf3880bc2019-11-26 12:02:30 +00004969 /* debug */
4970 if (g_getenv ("FWUPD_PROBE_VERBOSE") != NULL) {
4971 g_debug ("UDEV %s removed",
4972 g_udev_device_get_sysfs_path (udev_device));
4973 }
4974
Richard Hughes9d6e0e72018-08-24 20:20:17 +01004975 /* go through each device and remove any that match */
Richard Hughesc125ec02018-09-05 19:35:17 +01004976 devices = fu_device_list_get_all (self->device_list);
Richard Hughes9d6e0e72018-08-24 20:20:17 +01004977 for (guint i = 0; i < devices->len; i++) {
4978 FuDevice *device = g_ptr_array_index (devices, i);
Richard Hughesc125ec02018-09-05 19:35:17 +01004979 if (!FU_IS_UDEV_DEVICE (device))
4980 continue;
4981 if (g_strcmp0 (fu_udev_device_get_sysfs_path (FU_UDEV_DEVICE (device)),
4982 g_udev_device_get_sysfs_path (udev_device)) == 0) {
4983 g_debug ("auto-removing GUdevDevice");
4984 fu_device_list_remove (self->device_list, device);
4985 }
Richard Hughes9d6e0e72018-08-24 20:20:17 +01004986 }
4987}
4988
Richard Hughes5e952ce2019-08-26 11:09:46 +01004989typedef struct {
4990 FuEngine *self;
4991 GUdevDevice *udev_device;
4992 guint idle_id;
4993} FuEngineUdevChangedHelper;
4994
4995static void
4996fu_engine_udev_changed_helper_free (FuEngineUdevChangedHelper *helper)
4997{
4998 if (helper->idle_id != 0)
4999 g_source_remove (helper->idle_id);
5000 g_object_unref (helper->self);
5001 g_object_unref (helper->udev_device);
5002 g_free (helper);
5003}
5004
5005static FuEngineUdevChangedHelper *
5006fu_engine_udev_changed_helper_new (FuEngine *self, GUdevDevice *udev_device)
5007{
5008 FuEngineUdevChangedHelper *helper = g_new0 (FuEngineUdevChangedHelper, 1);
5009 helper->self = g_object_ref (self);
5010 helper->udev_device = g_object_ref (udev_device);
5011 return helper;
5012}
5013
5014static gboolean
5015fu_engine_udev_changed_cb (gpointer user_data)
5016{
5017 FuEngineUdevChangedHelper *helper = (FuEngineUdevChangedHelper *) user_data;
5018 GPtrArray *plugins = fu_plugin_list_get_all (helper->self->plugin_list);
5019 g_autoptr(FuUdevDevice) device = fu_udev_device_new (helper->udev_device);
5020
5021 /* run all plugins */
5022 for (guint j = 0; j < plugins->len; j++) {
5023 FuPlugin *plugin_tmp = g_ptr_array_index (plugins, j);
5024 g_autoptr(GError) error = NULL;
5025 if (!fu_plugin_runner_udev_device_changed (plugin_tmp, device, &error)) {
5026 if (g_error_matches (error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED)) {
5027 g_debug ("%s ignoring: %s",
5028 fu_plugin_get_name (plugin_tmp),
5029 error->message);
5030 continue;
5031 }
5032 g_warning ("%s failed to change udev device %s: %s",
5033 fu_plugin_get_name (plugin_tmp),
5034 g_udev_device_get_sysfs_path (helper->udev_device),
5035 error->message);
5036 }
5037 }
5038
5039 /* device done, so remove ref */
5040 helper->idle_id = 0;
5041 g_hash_table_remove (helper->self->udev_changed_ids,
5042 g_udev_device_get_sysfs_path (helper->udev_device));
5043 return FALSE;
5044}
5045
Richard Hughes9d6e0e72018-08-24 20:20:17 +01005046static void
5047fu_engine_udev_device_changed (FuEngine *self, GUdevDevice *udev_device)
5048{
Richard Hughes5e952ce2019-08-26 11:09:46 +01005049 const gchar *sysfs_path = g_udev_device_get_sysfs_path (udev_device);
Richard Hughes9d6e0e72018-08-24 20:20:17 +01005050 g_autoptr(GPtrArray) devices = NULL;
Richard Hughes5e952ce2019-08-26 11:09:46 +01005051 FuEngineUdevChangedHelper *helper;
Richard Hughes9d6e0e72018-08-24 20:20:17 +01005052
5053 /* emit changed on any that match */
Richard Hughesc125ec02018-09-05 19:35:17 +01005054 devices = fu_device_list_get_all (self->device_list);
Richard Hughes9d6e0e72018-08-24 20:20:17 +01005055 for (guint i = 0; i < devices->len; i++) {
5056 FuDevice *device = g_ptr_array_index (devices, i);
Richard Hughesc125ec02018-09-05 19:35:17 +01005057 if (!FU_IS_UDEV_DEVICE (device))
5058 continue;
5059 if (g_strcmp0 (fu_udev_device_get_sysfs_path (FU_UDEV_DEVICE (device)),
Richard Hughes5e952ce2019-08-26 11:09:46 +01005060 sysfs_path) == 0) {
Richard Hughes9d6e0e72018-08-24 20:20:17 +01005061 fu_udev_device_emit_changed (FU_UDEV_DEVICE (device));
Richard Hughesc125ec02018-09-05 19:35:17 +01005062 }
Richard Hughes9d6e0e72018-08-24 20:20:17 +01005063 }
Richard Hughes5e952ce2019-08-26 11:09:46 +01005064
5065 /* run all plugins, with per-device rate limiting */
5066 if (g_hash_table_remove (self->udev_changed_ids, sysfs_path)) {
5067 g_debug ("re-adding rate-limited timeout for %s", sysfs_path);
5068 } else {
5069 g_debug ("adding rate-limited timeout for %s", sysfs_path);
5070 }
5071 helper = fu_engine_udev_changed_helper_new (self, udev_device);
5072 helper->idle_id = g_timeout_add (500, fu_engine_udev_changed_cb, helper);
5073 g_hash_table_insert (self->udev_changed_ids, g_strdup (sysfs_path), helper);
Richard Hughes9d6e0e72018-08-24 20:20:17 +01005074}
5075
5076static void
5077fu_engine_enumerate_udev (FuEngine *self)
5078{
5079 /* get all devices of class */
5080 for (guint i = 0; i < self->udev_subsystems->len; i++) {
5081 const gchar *subsystem = g_ptr_array_index (self->udev_subsystems, i);
5082 GList *devices = g_udev_client_query_by_subsystem (self->gudev_client,
5083 subsystem);
5084 g_debug ("%u devices with subsystem %s",
5085 g_list_length (devices), subsystem);
5086 for (GList *l = devices; l != NULL; l = l->next) {
5087 GUdevDevice *udev_device = l->data;
5088 fu_engine_udev_device_add (self, udev_device);
5089 }
5090 g_list_foreach (devices, (GFunc) g_object_unref, NULL);
5091 g_list_free (devices);
5092 }
5093}
Richard Hughes16658372019-11-22 09:23:59 +00005094#endif
Richard Hughes9d6e0e72018-08-24 20:20:17 +01005095
5096static void
Richard Hughes9945edb2017-06-19 10:03:55 +01005097fu_engine_plugin_recoldplug_cb (FuPlugin *plugin, FuEngine *self)
5098{
5099 if (self->coldplug_running) {
5100 g_warning ("coldplug already running, cannot recoldplug");
5101 return;
5102 }
Richard Hughes5b5f6552018-05-18 10:22:39 +01005103 if (self->app_flags & FU_APP_FLAGS_NO_IDLE_SOURCES) {
5104 g_debug ("doing direct recoldplug");
5105 fu_engine_plugins_coldplug (self, TRUE);
Richard Hughes16658372019-11-22 09:23:59 +00005106#ifdef HAVE_GUDEV
Richard Hughes9d6e0e72018-08-24 20:20:17 +01005107 fu_engine_enumerate_udev (self);
Richard Hughes16658372019-11-22 09:23:59 +00005108#endif
Richard Hughes5b5f6552018-05-18 10:22:39 +01005109 return;
5110 }
Richard Hughes9945edb2017-06-19 10:03:55 +01005111 g_debug ("scheduling a recoldplug");
5112 if (self->coldplug_id != 0)
5113 g_source_remove (self->coldplug_id);
5114 self->coldplug_id = g_timeout_add (1500, fu_engine_recoldplug_delay_cb, self);
5115}
5116
5117static void
5118fu_engine_plugin_set_coldplug_delay_cb (FuPlugin *plugin, guint duration, FuEngine *self)
5119{
5120 self->coldplug_delay = MAX (self->coldplug_delay, duration);
5121 g_debug ("got coldplug delay of %ums, global maximum is now %ums",
5122 duration, self->coldplug_delay);
5123}
5124
Richard Hughesf425d292019-01-18 17:57:39 +00005125/* this is called by the self tests as well */
Richard Hughes34834102017-11-21 21:55:00 +00005126void
5127fu_engine_add_plugin (FuEngine *self, FuPlugin *plugin)
5128{
Mario Limonciello52e75ba2019-11-22 13:21:19 -06005129 if (fu_plugin_is_open (plugin)) {
5130 /* plugin does not match built version */
5131 if (fu_plugin_get_build_hash (plugin) == NULL) {
5132 const gchar *name = fu_plugin_get_name (plugin);
5133 g_warning ("%s should call fu_plugin_set_build_hash()",
5134 name);
5135 self->tainted = TRUE;
5136 } else if (g_strcmp0 (fu_plugin_get_build_hash (plugin),
5137 FU_BUILD_HASH) != 0) {
5138 const gchar *name = fu_plugin_get_name (plugin);
5139 g_warning ("%s has incorrect built version %s",
5140 name, fu_plugin_get_build_hash (plugin));
5141 self->tainted = TRUE;
5142 }
Richard Hughesf425d292019-01-18 17:57:39 +00005143 }
5144
Richard Hughese7e95452017-11-22 09:05:53 +00005145 fu_plugin_list_add (self->plugin_list, plugin);
Richard Hughes34834102017-11-21 21:55:00 +00005146}
5147
Richard Hughes9945edb2017-06-19 10:03:55 +01005148static gboolean
Richard Hughes1e456bc2018-05-10 20:16:16 +01005149fu_engine_is_plugin_name_blacklisted (FuEngine *self, const gchar *name)
5150{
5151 GPtrArray *blacklist = fu_config_get_blacklist_plugins (self->config);
5152 for (guint i = 0; i < blacklist->len; i++) {
5153 const gchar *name_tmp = g_ptr_array_index (blacklist, i);
5154 if (g_strcmp0 (name_tmp, name) == 0)
5155 return TRUE;
5156 }
5157 return FALSE;
5158}
5159
Richard Hughesc02ee4d2018-05-22 15:46:03 +01005160static gboolean
5161fu_engine_is_plugin_name_whitelisted (FuEngine *self, const gchar *name)
5162{
5163 if (self->plugin_filter->len == 0)
5164 return TRUE;
5165 for (guint i = 0; i < self->plugin_filter->len; i++) {
5166 const gchar *name_tmp = g_ptr_array_index (self->plugin_filter, i);
Richard Hughes5c508de2019-11-22 09:57:34 +00005167 if (fu_common_fnmatch (name_tmp, name))
Richard Hughesc02ee4d2018-05-22 15:46:03 +01005168 return TRUE;
5169 }
5170 return FALSE;
5171}
5172
5173void
5174fu_engine_add_plugin_filter (FuEngine *self, const gchar *plugin_glob)
5175{
Mario Limonciello1a520512020-05-26 10:44:53 -05005176 GString *str;
Richard Hughesc02ee4d2018-05-22 15:46:03 +01005177 g_return_if_fail (FU_IS_ENGINE (self));
5178 g_return_if_fail (plugin_glob != NULL);
Mario Limonciello1a520512020-05-26 10:44:53 -05005179 str = g_string_new (plugin_glob);
5180 fu_common_string_replace (str, "-", "_");
5181 g_ptr_array_add (self->plugin_filter, g_string_free (str, FALSE));
Richard Hughesc02ee4d2018-05-22 15:46:03 +01005182}
5183
Richard Hughesaabdc372018-11-14 10:11:08 +00005184static gboolean
5185fu_engine_plugin_check_supported_cb (FuPlugin *plugin, const gchar *guid, FuEngine *self)
5186{
5187 g_autoptr(XbNode) n = NULL;
5188 g_autofree gchar *xpath = NULL;
Mario Limonciello4fa95a72020-03-28 10:50:57 -05005189
5190 if (fu_config_get_enumerate_all_devices (self->config))
5191 return TRUE;
5192
Richard Hughesaabdc372018-11-14 10:11:08 +00005193 xpath = g_strdup_printf ("components/component/"
5194 "provides/firmware[@type='flashed'][text()='%s']",
5195 guid);
5196 n = xb_silo_query_first (self->silo, xpath, NULL);
5197 return n != NULL;
5198}
5199
Richard Hughes8c71a3f2018-05-22 19:19:52 +01005200gboolean
Richard Hughesf425d292019-01-18 17:57:39 +00005201fu_engine_get_tainted (FuEngine *self)
5202{
5203 return self->tainted;
5204}
5205
Mario Limonciello20cc9ee2019-09-05 07:27:26 -05005206const gchar *
5207fu_engine_get_host_product (FuEngine *self)
5208{
Mario Limonciello8ad4ac02020-01-15 09:58:53 -06005209 const gchar *result = NULL;
Mario Limonciello20cc9ee2019-09-05 07:27:26 -05005210 g_return_val_if_fail (FU_IS_ENGINE (self), NULL);
Mario Limonciello8ad4ac02020-01-15 09:58:53 -06005211 result = fu_hwids_get_value (self->hwids, FU_HWIDS_KEY_PRODUCT_NAME);
5212 return result != NULL ? result : "Unknown Product";
Mario Limonciello20cc9ee2019-09-05 07:27:26 -05005213}
5214
Richard Hughes0917fb62019-09-21 12:55:37 +01005215const gchar *
5216fu_engine_get_host_machine_id (FuEngine *self)
5217{
5218 g_return_val_if_fail (FU_IS_ENGINE (self), NULL);
5219 return self->host_machine_id;
5220}
5221
Richard Hughesad451582020-05-11 22:14:47 +01005222static void
Richard Hughesf58ac732020-05-12 15:23:44 +01005223fu_engine_add_security_attrs_tainted (FuEngine *self, FuSecurityAttrs *attrs)
Richard Hughesad451582020-05-11 22:14:47 +01005224{
Mario Limoncielloa8342842020-05-13 09:47:16 -05005225 gboolean disabled_plugins = FALSE;
5226 GPtrArray *blacklist = fu_config_get_blacklist_plugins (self->config);
Richard Hughesb246bca2020-05-18 14:31:35 +01005227 g_autoptr(FwupdSecurityAttr) attr = fwupd_security_attr_new (FWUPD_SECURITY_ATTR_ID_FWUPD_PLUGINS);
Richard Hughescae111d2020-05-15 15:20:24 +01005228 fwupd_security_attr_set_plugin (attr, "core");
Richard Hughesad451582020-05-11 22:14:47 +01005229 fwupd_security_attr_add_flag (attr, FWUPD_SECURITY_ATTR_FLAG_RUNTIME_ISSUE);
Richard Hughesb246bca2020-05-18 14:31:35 +01005230 fu_security_attrs_append (attrs, attr);
Mario Limoncielloa8342842020-05-13 09:47:16 -05005231 for (guint i = 0; i < blacklist->len; i++) {
5232 const gchar *name_tmp = g_ptr_array_index (blacklist, i);
5233 if (g_strcmp0 (name_tmp, "test") != 0 &&
5234 g_strcmp0 (name_tmp, "invalid") != 0) {
5235 disabled_plugins = TRUE;
5236 break;
5237 }
5238 }
Richard Hughesad451582020-05-11 22:14:47 +01005239 if (self->tainted) {
Richard Hughesb246bca2020-05-18 14:31:35 +01005240 fwupd_security_attr_set_result (attr, FWUPD_SECURITY_ATTR_RESULT_TAINTED);
5241 return;
Richard Hughesad451582020-05-11 22:14:47 +01005242 }
Richard Hughesb246bca2020-05-18 14:31:35 +01005243 if (self->plugin_filter->len > 0 || disabled_plugins) {
5244 fwupd_security_attr_set_result (attr, FWUPD_SECURITY_ATTR_RESULT_NOT_ENABLED);
5245 return;
5246 }
5247
5248 /* success */
5249 fwupd_security_attr_add_flag (attr, FWUPD_SECURITY_ATTR_FLAG_SUCCESS);
5250 fwupd_security_attr_set_result (attr, FWUPD_SECURITY_ATTR_RESULT_NOT_TAINTED);
Richard Hughesad451582020-05-11 22:14:47 +01005251}
5252
Richard Hughes31c1a452020-05-11 22:16:10 +01005253static void
Richard Hughesf58ac732020-05-12 15:23:44 +01005254fu_engine_add_security_attrs_supported (FuEngine *self, FuSecurityAttrs *attrs)
Richard Hughes31c1a452020-05-11 22:16:10 +01005255{
5256 FwupdRelease *rel_current = NULL;
5257 FwupdRelease *rel_newest = NULL;
Mario Limoncielloff303e12020-05-13 10:25:41 -05005258 g_autoptr(FwupdSecurityAttr) attr_a = NULL;
5259 g_autoptr(FwupdSecurityAttr) attr_u = NULL;
Richard Hughes31c1a452020-05-11 22:16:10 +01005260 guint64 now = (guint64) g_get_real_time () / G_USEC_PER_SEC;
5261 g_autoptr(FuDevice) device = NULL;
5262 g_autoptr(GPtrArray) releases = NULL;
5263
Richard Hughesb246bca2020-05-18 14:31:35 +01005264 attr_u = fwupd_security_attr_new (FWUPD_SECURITY_ATTR_ID_FWUPD_UPDATES);
Richard Hughescae111d2020-05-15 15:20:24 +01005265 fwupd_security_attr_set_plugin (attr_u, "core");
Richard Hughes31c1a452020-05-11 22:16:10 +01005266 fwupd_security_attr_add_flag (attr_u, FWUPD_SECURITY_ATTR_FLAG_RUNTIME_UPDATES);
Richard Hughesf58ac732020-05-12 15:23:44 +01005267 fu_security_attrs_append (attrs, attr_u);
Mario Limonciellob0d2e9e2020-05-18 12:30:47 -05005268 attr_a = fwupd_security_attr_new (FWUPD_SECURITY_ATTR_ID_FWUPD_ATTESTATION);
5269 fwupd_security_attr_set_plugin (attr_a, "core");
5270 fwupd_security_attr_add_flag (attr_a, FWUPD_SECURITY_ATTR_FLAG_RUNTIME_ATTESTATION);
5271 fu_security_attrs_append (attrs, attr_a);
Richard Hughes31c1a452020-05-11 22:16:10 +01005272 /* get device */
5273 device = fu_device_list_get_by_guid (self->device_list,
5274 /* main-system-firmware */
5275 "230c8b18-8d9b-53ec-838b-6cfc0383493a",
5276 NULL);
Mario Limonciellob0d2e9e2020-05-18 12:30:47 -05005277
5278 /* find out if there is firmware less than 12 months old */
Richard Hughes31c1a452020-05-11 22:16:10 +01005279 if (device == NULL) {
Richard Hughesb246bca2020-05-18 14:31:35 +01005280 fwupd_security_attr_set_result (attr_u, FWUPD_SECURITY_ATTR_RESULT_NOT_FOUND);
Richard Hughes31c1a452020-05-11 22:16:10 +01005281 } else {
5282 releases = fu_engine_get_releases_for_device (self, device, NULL);
5283 if (releases == NULL) {
Richard Hughesb246bca2020-05-18 14:31:35 +01005284 fwupd_security_attr_set_result (attr_u, FWUPD_SECURITY_ATTR_RESULT_NOT_SUPPORTED);
Richard Hughes31c1a452020-05-11 22:16:10 +01005285 } else {
5286 /* check the age */
Richard Hughes31c1a452020-05-11 22:16:10 +01005287 for (guint i = 0; i < releases->len; i++) {
5288 FwupdRelease *rel_tmp = g_ptr_array_index (releases, i);
5289 if (rel_newest == NULL ||
5290 fwupd_release_get_created (rel_tmp) > fwupd_release_get_created (rel_newest))
5291 rel_newest = rel_tmp;
5292 }
Richard Hughesb246bca2020-05-18 14:31:35 +01005293 g_debug ("newest release is %" G_GUINT64_FORMAT " months old",
5294 (now - fwupd_release_get_created (rel_newest)) / (60 * 60 * 24 * 30));
5295 fwupd_security_attr_set_result (attr_u, FWUPD_SECURITY_ATTR_RESULT_SUPPORTED);
5296 if (now - fwupd_release_get_created (rel_newest) < 60 * 60 * 24 * 30 * 12) {
Richard Hughes31c1a452020-05-11 22:16:10 +01005297 fwupd_security_attr_add_flag (attr_u, FWUPD_SECURITY_ATTR_FLAG_SUCCESS);
Richard Hughesb246bca2020-05-18 14:31:35 +01005298 fwupd_security_attr_set_result (attr_a, FWUPD_SECURITY_ATTR_RESULT_SUPPORTED);
5299 }
Richard Hughes31c1a452020-05-11 22:16:10 +01005300 }
5301 }
5302
5303 /* do we have attestation checksums */
Richard Hughes31c1a452020-05-11 22:16:10 +01005304 if (releases != NULL) {
5305 for (guint i = 0; i < releases->len; i++) {
5306 FwupdRelease *rel_tmp = g_ptr_array_index (releases, i);
5307 if (fu_common_vercmp_full (fu_device_get_version (device),
5308 fwupd_release_get_version (rel_tmp),
5309 fu_device_get_version_format (device)) == 0) {
5310 rel_current = rel_tmp;
5311 break;
5312 }
5313 }
5314 }
5315 if (rel_current == NULL) {
Richard Hughesb246bca2020-05-18 14:31:35 +01005316 fwupd_security_attr_set_result (attr_a, FWUPD_SECURITY_ATTR_RESULT_NOT_SUPPORTED);
Mario Limonciello5d8c6302020-05-18 12:44:15 -05005317 } else {
5318 g_autoptr(GError) error_local = NULL;
5319 if (!fu_engine_verify (self, fu_device_get_id (device), &error_local)) {
5320 fwupd_security_attr_set_result (attr_a, FWUPD_SECURITY_ATTR_RESULT_NOT_SUPPORTED);
5321 g_debug ("Failed to find attestation checksums: %s", error_local->message);
5322 } else {
5323 fwupd_security_attr_add_flag (attr_a, FWUPD_SECURITY_ATTR_FLAG_SUCCESS);
5324 fwupd_security_attr_set_result (attr_a, FWUPD_SECURITY_ATTR_RESULT_SUPPORTED);
5325 }
Richard Hughes31c1a452020-05-11 22:16:10 +01005326 }
5327}
5328
Richard Hughesf58ac732020-05-12 15:23:44 +01005329FuSecurityAttrs *
Richard Hughes56e7ae52020-05-17 21:00:23 +01005330fu_engine_get_host_security_attrs (FuEngine *self)
Richard Hughes196c6c62020-05-11 19:42:47 +01005331{
5332 GPtrArray *plugins = fu_plugin_list_get_all (self->plugin_list);
Richard Hughesf58ac732020-05-12 15:23:44 +01005333 g_autoptr(FuSecurityAttrs) attrs = fu_security_attrs_new ();
Richard Hughesb246bca2020-05-18 14:31:35 +01005334 g_autoptr(GPtrArray) items = NULL;
Richard Hughes196c6c62020-05-11 19:42:47 +01005335
Richard Hughesad451582020-05-11 22:14:47 +01005336 /* built in */
Richard Hughesad451582020-05-11 22:14:47 +01005337 fu_engine_add_security_attrs_tainted (self, attrs);
Richard Hughes31c1a452020-05-11 22:16:10 +01005338 fu_engine_add_security_attrs_supported (self, attrs);
Richard Hughesad451582020-05-11 22:14:47 +01005339
5340 /* call into plugins */
Richard Hughes196c6c62020-05-11 19:42:47 +01005341 for (guint j = 0; j < plugins->len; j++) {
5342 FuPlugin *plugin_tmp = g_ptr_array_index (plugins, j);
Richard Hughesf58ac732020-05-12 15:23:44 +01005343 fu_plugin_runner_add_security_attrs (plugin_tmp, attrs);
Richard Hughes196c6c62020-05-11 19:42:47 +01005344 }
5345
Richard Hughesb246bca2020-05-18 14:31:35 +01005346 /* set the fallback names for clients without native translations */
5347 items = fu_security_attrs_get_all (attrs);
5348 for (guint i = 0; i < items->len; i++) {
5349 FwupdSecurityAttr *attr = g_ptr_array_index (items, i);
5350 if (fwupd_security_attr_get_name (attr) == NULL) {
5351 const gchar *name_tmp = fu_security_attr_get_name (attr);
5352 if (name_tmp == NULL) {
5353 g_warning ("failed to get fallback for %s",
5354 fwupd_security_attr_get_appstream_id (attr));
5355 continue;
5356 }
5357 fwupd_security_attr_set_name (attr, name_tmp);
5358 }
5359 }
5360
Richard Hughes196c6c62020-05-11 19:42:47 +01005361 /* set the obsoletes flag for each attr */
5362 fu_security_attrs_depsolve (attrs);
Richard Hughesb246bca2020-05-18 14:31:35 +01005363
Richard Hughes196c6c62020-05-11 19:42:47 +01005364 return g_steal_pointer (&attrs);
5365}
5366
5367const gchar *
5368fu_engine_get_host_security_id (FuEngine *self)
5369{
5370 g_return_val_if_fail (FU_IS_ENGINE (self), NULL);
5371
5372 /* rebuild */
5373 if (!self->host_security_id_valid) {
Richard Hughesf58ac732020-05-12 15:23:44 +01005374 g_autoptr(FuSecurityAttrs) attrs = NULL;
Richard Hughes196c6c62020-05-11 19:42:47 +01005375 g_free (self->host_security_id);
Richard Hughesf58ac732020-05-12 15:23:44 +01005376 self->host_security_id = NULL;
Richard Hughes196c6c62020-05-11 19:42:47 +01005377 self->host_security_id_valid = TRUE;
Richard Hughes56e7ae52020-05-17 21:00:23 +01005378 attrs = fu_engine_get_host_security_attrs (self);
Mario Limonciellob0e1e5e2020-05-18 13:50:29 -05005379 self->host_security_id = fu_security_attrs_calculate_hsi (attrs,
5380 FU_SECURITY_ATTRS_FLAG_ADD_VERSION);
Richard Hughes196c6c62020-05-11 19:42:47 +01005381 }
5382
5383 return self->host_security_id;
5384}
5385
Richard Hughesf425d292019-01-18 17:57:39 +00005386gboolean
Richard Hughes9945edb2017-06-19 10:03:55 +01005387fu_engine_load_plugins (FuEngine *self, GError **error)
5388{
5389 const gchar *fn;
5390 g_autoptr(GDir) dir = NULL;
Richard Hughes4be17d12018-05-30 20:36:29 +01005391 g_autofree gchar *plugin_path = NULL;
Richard Hughes00d6f472019-11-22 17:04:02 +00005392 g_autofree gchar *suffix = g_strdup_printf (".%s", G_MODULE_SUFFIX);
Richard Hughesf77d7062017-11-27 12:06:36 +00005393
Richard Hughes9945edb2017-06-19 10:03:55 +01005394 /* search */
Richard Hughes4be17d12018-05-30 20:36:29 +01005395 plugin_path = fu_common_get_path (FU_PATH_KIND_PLUGINDIR_PKG);
5396 dir = g_dir_open (plugin_path, 0, error);
Richard Hughes9945edb2017-06-19 10:03:55 +01005397 if (dir == NULL)
5398 return FALSE;
5399 while ((fn = g_dir_read_name (dir)) != NULL) {
Richard Hughes9945edb2017-06-19 10:03:55 +01005400 g_autofree gchar *filename = NULL;
Richard Hughes1e456bc2018-05-10 20:16:16 +01005401 g_autofree gchar *name = NULL;
Richard Hughes9945edb2017-06-19 10:03:55 +01005402 g_autoptr(FuPlugin) plugin = NULL;
5403 g_autoptr(GError) error_local = NULL;
5404
5405 /* ignore non-plugins */
Richard Hughes00d6f472019-11-22 17:04:02 +00005406 if (!g_str_has_suffix (fn, suffix))
Richard Hughes9945edb2017-06-19 10:03:55 +01005407 continue;
5408
Richard Hughes1e456bc2018-05-10 20:16:16 +01005409 /* is blacklisted */
5410 name = fu_plugin_guess_name_from_fn (fn);
5411 if (name == NULL)
5412 continue;
5413 if (fu_engine_is_plugin_name_blacklisted (self, name)) {
Richard Hughesc02ee4d2018-05-22 15:46:03 +01005414 g_debug ("plugin %s is blacklisted", name);
5415 continue;
5416 }
5417 if (!fu_engine_is_plugin_name_whitelisted (self, name)) {
5418 g_debug ("plugin %s is not whitelisted", name);
Richard Hughes1e456bc2018-05-10 20:16:16 +01005419 continue;
5420 }
5421
Richard Hughes9945edb2017-06-19 10:03:55 +01005422 /* open module */
Richard Hughes4be17d12018-05-30 20:36:29 +01005423 filename = g_build_filename (plugin_path, fn, NULL);
Richard Hughes9945edb2017-06-19 10:03:55 +01005424 plugin = fu_plugin_new ();
Richard Hughes1e456bc2018-05-10 20:16:16 +01005425 fu_plugin_set_name (plugin, name);
Richard Hughes9945edb2017-06-19 10:03:55 +01005426 fu_plugin_set_usb_context (plugin, self->usb_ctx);
5427 fu_plugin_set_hwids (plugin, self->hwids);
Richard Hughes49e5e052017-09-03 12:15:41 +01005428 fu_plugin_set_smbios (plugin, self->smbios);
Richard Hughes9d6e0e72018-08-24 20:20:17 +01005429 fu_plugin_set_udev_subsystems (plugin, self->udev_subsystems);
Richard Hughes9c028f02017-10-28 21:14:28 +01005430 fu_plugin_set_quirks (plugin, self->quirks);
Richard Hughes0eb123b2018-04-19 12:00:04 +01005431 fu_plugin_set_runtime_versions (plugin, self->runtime_versions);
Richard Hughes34e0dab2018-04-20 16:43:00 +01005432 fu_plugin_set_compile_versions (plugin, self->compile_versions);
Richard Hughes95c98a92019-10-22 16:03:15 +01005433 g_signal_connect (plugin, "add-firmware-gtype",
5434 G_CALLBACK (fu_engine_plugin_add_firmware_gtype_cb),
5435 self);
Richard Hughes9945edb2017-06-19 10:03:55 +01005436 g_debug ("adding plugin %s", filename);
Richard Hughes8c71a3f2018-05-22 19:19:52 +01005437
5438 /* if loaded from fu_engine_load() open the plugin */
5439 if (self->usb_ctx != NULL) {
5440 if (!fu_plugin_open (plugin, filename, &error_local)) {
Mario Limonciellof5605532019-11-04 07:49:50 -06005441 g_warning ("%s", error_local->message);
Richard Hughes8c71a3f2018-05-22 19:19:52 +01005442 continue;
5443 }
Richard Hughes9945edb2017-06-19 10:03:55 +01005444 }
5445
Richard Hughes1e456bc2018-05-10 20:16:16 +01005446 /* self disabled */
Richard Hughes9945edb2017-06-19 10:03:55 +01005447 if (!fu_plugin_get_enabled (plugin)) {
Richard Hughes1e456bc2018-05-10 20:16:16 +01005448 g_debug ("%s self disabled",
Richard Hughes9945edb2017-06-19 10:03:55 +01005449 fu_plugin_get_name (plugin));
5450 continue;
5451 }
5452
5453 /* watch for changes */
5454 g_signal_connect (plugin, "device-added",
5455 G_CALLBACK (fu_engine_plugin_device_added_cb),
5456 self);
5457 g_signal_connect (plugin, "device-removed",
5458 G_CALLBACK (fu_engine_plugin_device_removed_cb),
5459 self);
Richard Hughese1fd34d2017-08-24 14:19:51 +01005460 g_signal_connect (plugin, "device-register",
5461 G_CALLBACK (fu_engine_plugin_device_register_cb),
5462 self);
Richard Hughes9945edb2017-06-19 10:03:55 +01005463 g_signal_connect (plugin, "recoldplug",
5464 G_CALLBACK (fu_engine_plugin_recoldplug_cb),
5465 self);
5466 g_signal_connect (plugin, "set-coldplug-delay",
5467 G_CALLBACK (fu_engine_plugin_set_coldplug_delay_cb),
5468 self);
Richard Hughesaabdc372018-11-14 10:11:08 +00005469 g_signal_connect (plugin, "check-supported",
5470 G_CALLBACK (fu_engine_plugin_check_supported_cb),
5471 self);
Richard Hughes75b965d2018-11-15 13:51:21 +00005472 g_signal_connect (plugin, "rules-changed",
5473 G_CALLBACK (fu_engine_plugin_rules_changed_cb),
5474 self);
Richard Hughes399859e2020-05-11 19:44:03 +01005475 g_signal_connect (plugin, "security-changed",
5476 G_CALLBACK (fu_engine_plugin_security_changed_cb),
5477 self);
Richard Hughes9945edb2017-06-19 10:03:55 +01005478
5479 /* add */
Richard Hughesf425d292019-01-18 17:57:39 +00005480 fu_engine_add_plugin (self, plugin);
Richard Hughes9945edb2017-06-19 10:03:55 +01005481 }
5482
Richard Hughese7e95452017-11-22 09:05:53 +00005483 /* depsolve into the correct order */
5484 if (!fu_plugin_list_depsolve (self->plugin_list, error))
5485 return FALSE;
Richard Hughes08a37992017-09-12 12:57:43 +01005486
Richard Hughese7e95452017-11-22 09:05:53 +00005487 /* success */
Richard Hughes9945edb2017-06-19 10:03:55 +01005488 return TRUE;
5489}
5490
Richard Hughes9945edb2017-06-19 10:03:55 +01005491static gboolean
5492fu_engine_cleanup_state (GError **error)
5493{
5494 const gchar *filenames[] = {
5495 "/var/cache/app-info/xmls/fwupd-verify.xml",
5496 "/var/cache/app-info/xmls/fwupd.xml",
5497 NULL };
5498 for (guint i = 0; filenames[i] != NULL; i++) {
5499 g_autoptr(GFile) file = g_file_new_for_path (filenames[i]);
5500 if (g_file_query_exists (file, NULL)) {
5501 if (!g_file_delete (file, NULL, error))
5502 return FALSE;
5503 }
5504 }
5505 return TRUE;
5506}
5507
Richard Hughesc7bbbc22018-01-02 22:22:25 +00005508guint64
5509fu_engine_get_archive_size_max (FuEngine *self)
5510{
5511 return fu_config_get_archive_size_max (self->config);
5512}
5513
Richard Hughes104f6512017-11-24 11:44:57 +00005514static void
5515fu_engine_usb_device_removed_cb (GUsbContext *ctx,
5516 GUsbDevice *usb_device,
5517 FuEngine *self)
5518{
5519 g_autoptr(GPtrArray) devices = NULL;
5520
Richard Hughesf3880bc2019-11-26 12:02:30 +00005521 /* debug */
5522 if (g_getenv ("FWUPD_PROBE_VERBOSE") != NULL) {
5523 g_debug ("USB %04x:%04x removed",
5524 g_usb_device_get_vid (usb_device),
5525 g_usb_device_get_pid (usb_device));
5526 }
5527
Richard Hughes104f6512017-11-24 11:44:57 +00005528 /* go through each device and remove any that match */
Richard Hughesc125ec02018-09-05 19:35:17 +01005529 devices = fu_device_list_get_all (self->device_list);
Richard Hughes104f6512017-11-24 11:44:57 +00005530 for (guint i = 0; i < devices->len; i++) {
5531 FuDevice *device = g_ptr_array_index (devices, i);
Richard Hughesc125ec02018-09-05 19:35:17 +01005532 if (!FU_IS_USB_DEVICE (device))
5533 continue;
5534 if (g_strcmp0 (fu_usb_device_get_platform_id (FU_USB_DEVICE (device)),
5535 g_usb_device_get_platform_id (usb_device)) == 0) {
5536 g_debug ("auto-removing GUsbDevice");
5537 fu_device_list_remove (self->device_list, device);
5538 }
Richard Hughes104f6512017-11-24 11:44:57 +00005539 }
5540}
5541
5542static void
5543fu_engine_usb_device_added_cb (GUsbContext *ctx,
5544 GUsbDevice *usb_device,
5545 FuEngine *self)
5546{
Richard Hughesff704412018-09-04 11:28:32 +01005547 g_autoptr(FuUsbDevice) device = fu_usb_device_new (usb_device);
Richard Hughes6dec4012018-08-27 19:13:00 +01005548 g_autoptr(GError) error_local = NULL;
Richard Hughes45a18152019-10-30 15:29:04 +00005549 g_autoptr(GPtrArray) possible_plugins = NULL;
Richard Hughes6dec4012018-08-27 19:13:00 +01005550
Richard Hughesf3880bc2019-11-26 12:02:30 +00005551 /* debug */
5552 if (g_getenv ("FWUPD_PROBE_VERBOSE") != NULL) {
5553 g_debug ("USB %04x:%04x added",
5554 g_usb_device_get_vid (usb_device),
5555 g_usb_device_get_pid (usb_device));
5556 }
5557
Richard Hughes6dec4012018-08-27 19:13:00 +01005558 /* add any extra quirks */
Richard Hughesff704412018-09-04 11:28:32 +01005559 fu_device_set_quirks (FU_DEVICE (device), self->quirks);
5560 if (!fu_device_probe (FU_DEVICE (device), &error_local)) {
Richard Hughes6dec4012018-08-27 19:13:00 +01005561 g_warning ("failed to probe device %s: %s",
Richard Hughesc125ec02018-09-05 19:35:17 +01005562 fu_device_get_physical_id (FU_DEVICE (device)),
Richard Hughes6dec4012018-08-27 19:13:00 +01005563 error_local->message);
5564 return;
5565 }
Richard Hughes6dbe8fe2018-06-28 12:16:26 +01005566
Richard Hughes23170a82018-08-29 09:11:52 +01005567 /* can be specified using a quirk */
Richard Hughes45a18152019-10-30 15:29:04 +00005568 possible_plugins = fu_device_get_possible_plugins (FU_DEVICE (device));
5569 for (guint i = 0; i < possible_plugins->len; i++) {
5570 FuPlugin *plugin;
5571 const gchar *plugin_name = g_ptr_array_index (possible_plugins, i);
5572 g_autoptr(GError) error = NULL;
Mario Limonciello0e500322019-10-17 18:41:04 -05005573
Richard Hughes45a18152019-10-30 15:29:04 +00005574 plugin = fu_plugin_list_find_by_name (self->plugin_list,
5575 plugin_name, &error);
5576 if (plugin == NULL) {
Richard Hughesa1f95352020-02-21 08:14:54 +00005577 g_debug ("failed to find specified plugin %s: %s",
5578 plugin_name, error->message);
Richard Hughes45a18152019-10-30 15:29:04 +00005579 continue;
5580 }
5581 if (!fu_plugin_runner_usb_device_added (plugin, device, &error)) {
5582 if (g_error_matches (error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED)) {
5583 if (g_getenv ("FWUPD_PROBE_VERBOSE") != NULL) {
5584 g_debug ("%s ignoring: %s",
5585 fu_plugin_get_name (plugin),
5586 error->message);
Mario Limonciello0e500322019-10-17 18:41:04 -05005587 }
Mario Limonciello0e500322019-10-17 18:41:04 -05005588 continue;
5589 }
Richard Hughes45a18152019-10-30 15:29:04 +00005590 g_warning ("failed to add USB device %04x:%04x: %s",
5591 g_usb_device_get_vid (usb_device),
5592 g_usb_device_get_pid (usb_device),
5593 error->message);
5594 continue;
Richard Hughes6dbe8fe2018-06-28 12:16:26 +01005595 }
Richard Hughes6dbe8fe2018-06-28 12:16:26 +01005596 }
Richard Hughes104f6512017-11-24 11:44:57 +00005597}
5598
Richard Hughesf77d7062017-11-27 12:06:36 +00005599static void
Richard Hughes51a869a2019-10-07 11:23:42 +01005600fu_engine_load_quirks (FuEngine *self, FuQuirksLoadFlags quirks_flags)
Richard Hughesf77d7062017-11-27 12:06:36 +00005601{
Richard Hughesf77d7062017-11-27 12:06:36 +00005602 g_autoptr(GError) error = NULL;
Richard Hughes51a869a2019-10-07 11:23:42 +01005603 if (!fu_quirks_load (self->quirks, quirks_flags, &error))
Richard Hughesf77d7062017-11-27 12:06:36 +00005604 g_warning ("Failed to load quirks: %s", error->message);
5605}
5606
5607static void
5608fu_engine_load_smbios (FuEngine *self)
5609{
Richard Hughesf77d7062017-11-27 12:06:36 +00005610 g_autoptr(GError) error = NULL;
Richard Hughesf77d7062017-11-27 12:06:36 +00005611 if (!fu_smbios_setup (self->smbios, &error))
5612 g_warning ("Failed to load SMBIOS: %s", error->message);
5613}
5614
5615static void
5616fu_engine_load_hwids (FuEngine *self)
5617{
Richard Hughesf77d7062017-11-27 12:06:36 +00005618 g_autoptr(GError) error = NULL;
Richard Hughesf77d7062017-11-27 12:06:36 +00005619 if (!fu_hwids_setup (self->hwids, self->smbios, &error))
5620 g_warning ("Failed to load HWIDs: %s", error->message);
5621}
5622
Richard Hughesa2f8e452018-01-11 10:11:17 +00005623static gboolean
5624fu_engine_update_history_device (FuEngine *self, FuDevice *dev_history, GError **error)
5625{
Richard Hughesa2f8e452018-01-11 10:11:17 +00005626 FuPlugin *plugin;
5627 FwupdRelease *rel_history;
Richard Hughesf2711422018-01-12 09:49:05 +00005628 g_autofree gchar *btime = NULL;
Richard Hughes5a9a6bd2018-09-10 10:02:20 +01005629 g_autoptr(FuDevice) dev = NULL;
Richard Hughesa2f8e452018-01-11 10:11:17 +00005630
5631 /* is in the device list */
Richard Hughes40127542018-01-12 20:25:55 +00005632 dev = fu_device_list_get_by_id (self->device_list,
5633 fu_device_get_id (dev_history),
5634 error);
Richard Hughesa2f8e452018-01-11 10:11:17 +00005635 if (dev == NULL)
5636 return FALSE;
5637
5638 /* does the installed version match what we tried to install
5639 * before fwupd was restarted */
5640 rel_history = fu_device_get_release_default (dev_history);
5641 if (rel_history == NULL) {
5642 g_set_error_literal (error,
5643 FWUPD_ERROR,
5644 FWUPD_ERROR_INTERNAL,
5645 "no release for history FuDevice");
5646 return FALSE;
5647 }
5648
Richard Hughesf2711422018-01-12 09:49:05 +00005649 /* is this the same boot time as when we scheduled the update,
5650 * i.e. has fwupd been restarted before we rebooted */
5651 btime = fu_engine_get_boot_time ();
5652 if (g_strcmp0 (fwupd_release_get_metadata_item (rel_history, "BootTime"),
5653 btime) == 0) {
5654 g_debug ("service restarted, but no reboot has taken place");
5655 return TRUE;
5656 }
5657
Richard Hughesa2f8e452018-01-11 10:11:17 +00005658 /* the system is running with the new firmware version */
Richard Hughes9a680842020-02-20 11:11:13 +00005659 if (fu_common_vercmp_full (fu_device_get_version (dev),
5660 fwupd_release_get_version (rel_history),
5661 fu_device_get_version_format (dev)) == 0) {
Richard Hughes4e886a42018-12-12 10:33:26 +00005662 GPtrArray *checksums;
Richard Hughesa2f8e452018-01-11 10:11:17 +00005663 g_debug ("installed version %s matching history %s",
5664 fu_device_get_version (dev),
5665 fwupd_release_get_version (rel_history));
Richard Hughes4e886a42018-12-12 10:33:26 +00005666
5667 /* copy over runtime checksums if set from probe() */
5668 checksums = fu_device_get_checksums (dev);
5669 for (guint i = 0; i < checksums->len; i++) {
5670 const gchar *csum = g_ptr_array_index (checksums, i);
5671 fu_device_add_checksum (dev_history, csum);
5672 }
Richard Hughesf50ff2c2020-02-25 09:45:15 +00005673 fu_device_set_version_format (dev_history, fu_device_get_version_format (dev));
5674 fu_device_set_version (dev_history, fu_device_get_version (dev));
Mario Limonciello96a0dd52019-02-25 13:50:03 -06005675 fu_device_remove_flag (dev_history, FWUPD_DEVICE_FLAG_NEEDS_ACTIVATION);
Richard Hughesc0cd0232018-01-31 15:02:00 +00005676 fu_device_set_update_state (dev_history, FWUPD_UPDATE_STATE_SUCCESS);
Richard Hughes0bbef292019-11-01 12:15:15 +00005677 return fu_history_modify_device (self->history, dev_history, error);
Richard Hughesa2f8e452018-01-11 10:11:17 +00005678 }
5679
Richard Hughes7e070c92018-01-12 16:50:05 +00005680 /* does the plugin know the update failure */
Richard Hughesa2f8e452018-01-11 10:11:17 +00005681 plugin = fu_plugin_list_find_by_name (self->plugin_list,
5682 fu_device_get_plugin (dev),
5683 error);
5684 if (plugin == NULL)
5685 return FALSE;
Richard Hughesa2f8e452018-01-11 10:11:17 +00005686 if (!fu_plugin_runner_get_results (plugin, dev, error))
5687 return FALSE;
Richard Hughes7e070c92018-01-12 16:50:05 +00005688
5689 /* the plugin either can't tell us the error, or doesn't know itself */
Richard Hughescce6a1c2019-04-16 17:25:48 +01005690 if (fu_device_get_update_state (dev) != FWUPD_UPDATE_STATE_FAILED &&
5691 fu_device_get_update_state (dev) != FWUPD_UPDATE_STATE_FAILED_TRANSIENT) {
Richard Hughes7e070c92018-01-12 16:50:05 +00005692 g_debug ("falling back to generic failure");
Richard Hughesc0cd0232018-01-31 15:02:00 +00005693 fu_device_set_update_error (dev_history, "failed to run update on reboot");
Richard Hughesa2f8e452018-01-11 10:11:17 +00005694 }
Richard Hughes7e070c92018-01-12 16:50:05 +00005695
5696 /* update the state in the database */
Richard Hughesc0cd0232018-01-31 15:02:00 +00005697 fu_device_set_update_error (dev_history, fu_device_get_update_error (dev));
Richard Hughes0bbef292019-11-01 12:15:15 +00005698 return fu_history_modify_device (self->history, dev_history, error);
Richard Hughesa2f8e452018-01-11 10:11:17 +00005699}
5700
5701static gboolean
5702fu_engine_update_history_database (FuEngine *self, GError **error)
5703{
5704 g_autoptr(GPtrArray) devices = NULL;
5705
5706 /* get any devices */
5707 devices = fu_history_get_devices (self->history, error);
5708 if (devices == NULL)
5709 return FALSE;
5710 for (guint i = 0; i < devices->len; i++) {
5711 FuDevice *dev = g_ptr_array_index (devices, i);
5712 g_autoptr(GError) error_local = NULL;
5713
5714 /* not in the required state */
5715 if (fu_device_get_update_state (dev) != FWUPD_UPDATE_STATE_NEEDS_REBOOT)
5716 continue;
5717
5718 /* try to save the new update-state, but ignoring any error */
5719 if (!fu_engine_update_history_device (self, dev, &error_local))
5720 g_warning ("%s", error_local->message);
5721 }
5722 return TRUE;
5723}
5724
Richard Hughes16658372019-11-22 09:23:59 +00005725#ifdef HAVE_GUDEV
Richard Hughes9d6e0e72018-08-24 20:20:17 +01005726static void
5727fu_engine_udev_uevent_cb (GUdevClient *gudev_client,
5728 const gchar *action,
5729 GUdevDevice *udev_device,
5730 FuEngine *self)
5731{
5732 if (g_strcmp0 (action, "add") == 0) {
5733 fu_engine_udev_device_add (self, udev_device);
5734 return;
5735 }
5736 if (g_strcmp0 (action, "remove") == 0) {
5737 fu_engine_udev_device_remove (self, udev_device);
5738 return;
5739 }
5740 if (g_strcmp0 (action, "change") == 0) {
5741 fu_engine_udev_device_changed (self, udev_device);
5742 return;
5743 }
5744}
Richard Hughes16658372019-11-22 09:23:59 +00005745#endif
Richard Hughes9d6e0e72018-08-24 20:20:17 +01005746
Richard Hughesf3c7d422019-03-12 10:29:42 +00005747static void
5748fu_engine_ensure_client_certificate (FuEngine *self)
5749{
Richard Hughesf3c7d422019-03-12 10:29:42 +00005750 g_autoptr(GBytes) blob = g_bytes_new_static ("test\0", 5);
Richard Hughesf3c7d422019-03-12 10:29:42 +00005751 g_autoptr(GError) error = NULL;
Richard Hughesd5aab652020-02-25 12:47:50 +00005752 g_autoptr(JcatBlob) jcat_sig = NULL;
5753 g_autoptr(JcatEngine) jcat_engine = NULL;
Richard Hughesf3c7d422019-03-12 10:29:42 +00005754
5755 /* create keyring and sign dummy data to ensure certificate exists */
Richard Hughesd5aab652020-02-25 12:47:50 +00005756 jcat_engine = jcat_context_get_engine (self->jcat_context,
5757 JCAT_BLOB_KIND_PKCS7,
5758 &error);
5759 if (jcat_engine == NULL) {
Richard Hughesf3c7d422019-03-12 10:29:42 +00005760 g_message ("failed to create keyring: %s", error->message);
5761 return;
5762 }
Richard Hughesd5aab652020-02-25 12:47:50 +00005763 jcat_sig = jcat_engine_self_sign (jcat_engine, blob, JCAT_SIGN_FLAG_NONE, &error);
5764 if (jcat_sig == NULL) {
Richard Hughesf3c7d422019-03-12 10:29:42 +00005765 g_message ("failed to sign using keyring: %s", error->message);
5766 return;
5767 }
5768 g_debug ("client certificate exists and working");
5769}
5770
Richard Hughes9945edb2017-06-19 10:03:55 +01005771/**
5772 * fu_engine_load:
5773 * @self: A #FuEngine
Richard Hughesc8cc77c2019-03-07 11:57:24 +00005774 * @flags: #FuEngineLoadFlags, e.g. %FU_ENGINE_LOAD_FLAG_READONLY_FS
Richard Hughes9945edb2017-06-19 10:03:55 +01005775 * @error: A #GError, or %NULL
5776 *
5777 * Load the firmware update engine so it is ready for use.
5778 *
5779 * Returns: %TRUE for success
5780 **/
5781gboolean
Richard Hughesc8cc77c2019-03-07 11:57:24 +00005782fu_engine_load (FuEngine *self, FuEngineLoadFlags flags, GError **error)
Richard Hughes9945edb2017-06-19 10:03:55 +01005783{
Richard Hughesd1808aa2019-12-10 15:20:30 +00005784 FuRemoteListLoadFlags remote_list_flags = FU_REMOTE_LIST_LOAD_FLAG_NONE;
Richard Hughes51a869a2019-10-07 11:23:42 +01005785 FuQuirksLoadFlags quirks_flags = FU_QUIRKS_LOAD_FLAG_NONE;
Richard Hughes8dd4c1c2019-03-03 18:27:57 +00005786 g_autoptr(GPtrArray) checksums = NULL;
Daniel Campellob5ae60c2020-02-27 17:06:28 -07005787#ifndef _WIN32
5788 g_autoptr(GError) error_local = NULL;
5789#endif
Richard Hughes8dd4c1c2019-03-03 18:27:57 +00005790
Richard Hughes9945edb2017-06-19 10:03:55 +01005791 g_return_val_if_fail (FU_IS_ENGINE (self), FALSE);
5792 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
5793
Mario Limonciello46aaee82019-01-10 12:58:00 -06005794 /* avoid re-loading a second time if fu-tool or fu-util request to */
5795 if (self->loaded)
5796 return TRUE;
5797
Richard Hughes45a00732019-11-22 16:57:14 +00005798/* TODO: Read registry key [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Cryptography] "MachineGuid" */
5799#ifndef _WIN32
Richard Hughes0917fb62019-09-21 12:55:37 +01005800 /* cache machine ID so we can use it from a sandboxed app */
Daniel Campellob5ae60c2020-02-27 17:06:28 -07005801 self->host_machine_id = fwupd_build_machine_id ("fwupd", &error_local);
Richard Hughes0917fb62019-09-21 12:55:37 +01005802 if (self->host_machine_id == NULL)
Daniel Campellob5ae60c2020-02-27 17:06:28 -07005803 g_debug ("%s", error_local->message);
Richard Hughes45a00732019-11-22 16:57:14 +00005804#endif
Richard Hughes9945edb2017-06-19 10:03:55 +01005805 /* read config file */
Richard Hughesd1808aa2019-12-10 15:20:30 +00005806 if (!fu_config_load (self->config, error)) {
Richard Hughes9945edb2017-06-19 10:03:55 +01005807 g_prefix_error (error, "Failed to load config: ");
5808 return FALSE;
5809 }
5810
Richard Hughesd1808aa2019-12-10 15:20:30 +00005811 /* read remotes */
5812 if (flags & FU_ENGINE_LOAD_FLAG_READONLY_FS)
5813 remote_list_flags |= FU_REMOTE_LIST_LOAD_FLAG_READONLY_FS;
5814 if (!fu_remote_list_load (self->remote_list, remote_list_flags, error)) {
5815 g_prefix_error (error, "Failed to load remotes: ");
5816 return FALSE;
5817 }
5818
Richard Hughesf3c7d422019-03-12 10:29:42 +00005819 /* create client certificate */
5820 fu_engine_ensure_client_certificate (self);
5821
Richard Hughes8dd4c1c2019-03-03 18:27:57 +00005822 /* get hardcoded approved firmware */
5823 checksums = fu_config_get_approved_firmware (self->config);
5824 for (guint i = 0; i < checksums->len; i++) {
5825 const gchar *csum = g_ptr_array_index (checksums, i);
5826 fu_engine_add_approved_firmware (self, csum);
5827 }
5828
5829 /* get extra firmware saved to the database */
5830 checksums = fu_history_get_approved_firmware (self->history, error);
5831 if (checksums == NULL)
5832 return FALSE;
5833 for (guint i = 0; i < checksums->len; i++) {
5834 const gchar *csum = g_ptr_array_index (checksums, i);
5835 fu_engine_add_approved_firmware (self, csum);
5836 }
5837
Richard Hughes75b965d2018-11-15 13:51:21 +00005838 /* set up idle exit */
5839 if ((self->app_flags & FU_APP_FLAGS_NO_IDLE_SOURCES) == 0)
5840 fu_idle_set_timeout (self->idle, fu_config_get_idle_timeout (self->config));
5841
Richard Hughesf77d7062017-11-27 12:06:36 +00005842 /* load quirks, SMBIOS and the hwids */
5843 fu_engine_load_smbios (self);
5844 fu_engine_load_hwids (self);
Richard Hughes51a869a2019-10-07 11:23:42 +01005845 /* on a read-only filesystem don't care about the cache GUID */
5846 if (flags & FU_ENGINE_LOAD_FLAG_READONLY_FS)
5847 quirks_flags |= FU_QUIRKS_LOAD_FLAG_READONLY_FS;
5848 fu_engine_load_quirks (self, quirks_flags);
Richard Hughes9c028f02017-10-28 21:14:28 +01005849
Richard Hughes9945edb2017-06-19 10:03:55 +01005850 /* load AppStream metadata */
Richard Hughesc8cc77c2019-03-07 11:57:24 +00005851 if (!fu_engine_load_metadata_store (self, flags, error)) {
Richard Hughes9945edb2017-06-19 10:03:55 +01005852 g_prefix_error (error, "Failed to load AppStream data: ");
5853 return FALSE;
5854 }
5855
Richard Hughes95c98a92019-10-22 16:03:15 +01005856 /* add the "built-in" firmware types */
5857 fu_engine_add_firmware_gtype (self, "raw", FU_TYPE_FIRMWARE);
5858 fu_engine_add_firmware_gtype (self, "dfu", FU_TYPE_DFU_FIRMWARE);
5859 fu_engine_add_firmware_gtype (self, "ihex", FU_TYPE_IHEX_FIRMWARE);
5860 fu_engine_add_firmware_gtype (self, "srec", FU_TYPE_SREC_FIRMWARE);
5861
Richard Hughes9945edb2017-06-19 10:03:55 +01005862 /* set shared USB context */
5863 self->usb_ctx = g_usb_context_new (error);
5864 if (self->usb_ctx == NULL) {
5865 g_prefix_error (error, "Failed to get USB context: ");
5866 return FALSE;
5867 }
5868
Richard Hughes9945edb2017-06-19 10:03:55 +01005869 /* delete old data files */
5870 if (!fu_engine_cleanup_state (error)) {
5871 g_prefix_error (error, "Failed to clean up: ");
5872 return FALSE;
5873 }
5874
5875 /* load plugin */
5876 if (!fu_engine_load_plugins (self, error)) {
5877 g_prefix_error (error, "Failed to load plugins: ");
5878 return FALSE;
5879 }
5880
Richard Hughes170c0c12017-11-22 11:26:24 +00005881 /* watch the device list for updates and proxy */
5882 g_signal_connect (self->device_list, "added",
5883 G_CALLBACK (fu_engine_device_added_cb),
5884 self);
5885 g_signal_connect (self->device_list, "removed",
5886 G_CALLBACK (fu_engine_device_removed_cb),
5887 self);
5888 g_signal_connect (self->device_list, "changed",
5889 G_CALLBACK (fu_engine_device_changed_cb),
5890 self);
5891
Richard Hughes16658372019-11-22 09:23:59 +00005892#ifdef HAVE_GUDEV
Richard Hughes9d6e0e72018-08-24 20:20:17 +01005893 /* udev watches can only be set up in _init() so set up client now */
5894 if (self->udev_subsystems->len > 0) {
Richard Hugheseffcc7a2018-08-31 14:32:07 +01005895 g_auto(GStrv) udev_subsystems = g_new0 (gchar *, self->udev_subsystems->len + 1);
Richard Hughes9d6e0e72018-08-24 20:20:17 +01005896 for (guint i = 0; i < self->udev_subsystems->len; i++) {
5897 const gchar *subsystem = g_ptr_array_index (self->udev_subsystems, i);
5898 udev_subsystems[i] = g_strdup (subsystem);
5899 }
5900 self->gudev_client = g_udev_client_new ((const gchar * const *) udev_subsystems);
5901 g_signal_connect (self->gudev_client, "uevent",
5902 G_CALLBACK (fu_engine_udev_uevent_cb), self);
5903 }
Richard Hughes16658372019-11-22 09:23:59 +00005904#endif
Richard Hughes9d6e0e72018-08-24 20:20:17 +01005905
Mario Limonciello6463f312018-08-09 13:28:46 -05005906 fu_engine_set_status (self, FWUPD_STATUS_LOADING);
5907
Richard Hughes9945edb2017-06-19 10:03:55 +01005908 /* add devices */
5909 fu_engine_plugins_setup (self);
Richard Hughes8fb2e6a2019-10-22 15:37:57 +01005910 if ((flags & FU_ENGINE_LOAD_FLAG_NO_ENUMERATE) == 0)
5911 fu_engine_plugins_coldplug (self, FALSE);
Richard Hughes9945edb2017-06-19 10:03:55 +01005912
Richard Hughes634e9222017-11-29 15:50:02 +00005913 /* coldplug USB devices */
Richard Hughes104f6512017-11-24 11:44:57 +00005914 g_signal_connect (self->usb_ctx, "device-added",
5915 G_CALLBACK (fu_engine_usb_device_added_cb),
5916 self);
5917 g_signal_connect (self->usb_ctx, "device-removed",
5918 G_CALLBACK (fu_engine_usb_device_removed_cb),
5919 self);
Richard Hughese3757512019-09-25 10:12:28 +01005920 if ((flags & FU_ENGINE_LOAD_FLAG_NO_ENUMERATE) == 0)
5921 g_usb_context_enumerate (self->usb_ctx);
Richard Hughes104f6512017-11-24 11:44:57 +00005922
Richard Hughes16658372019-11-22 09:23:59 +00005923#ifdef HAVE_GUDEV
Richard Hughes9d6e0e72018-08-24 20:20:17 +01005924 /* coldplug udev devices */
Richard Hughese3757512019-09-25 10:12:28 +01005925 if ((flags & FU_ENGINE_LOAD_FLAG_NO_ENUMERATE) == 0)
5926 fu_engine_enumerate_udev (self);
Richard Hughes16658372019-11-22 09:23:59 +00005927#endif
Richard Hughes9d6e0e72018-08-24 20:20:17 +01005928
Richard Hughesf43381f2020-02-24 10:11:31 +00005929 /* set device properties from the metadata */
5930 fu_engine_md_refresh_devices (self);
5931
Richard Hughesa2f8e452018-01-11 10:11:17 +00005932 /* update the db for devices that were updated during the reboot */
5933 if (!fu_engine_update_history_database (self, error))
5934 return FALSE;
5935
Mario Limonciello6463f312018-08-09 13:28:46 -05005936 fu_engine_set_status (self, FWUPD_STATUS_IDLE);
Mario Limonciello46aaee82019-01-10 12:58:00 -06005937 self->loaded = TRUE;
Mario Limonciello6463f312018-08-09 13:28:46 -05005938
Mario Limonciellod81ea2e2020-01-13 14:11:43 -06005939 /* let clients know engine finished starting up */
5940 fu_engine_emit_changed (self);
5941
Richard Hughes9945edb2017-06-19 10:03:55 +01005942 /* success */
5943 return TRUE;
5944}
5945
5946static void
5947fu_engine_class_init (FuEngineClass *klass)
5948{
5949 GObjectClass *object_class = G_OBJECT_CLASS (klass);
5950 object_class->finalize = fu_engine_finalize;
5951
5952 signals[SIGNAL_CHANGED] =
5953 g_signal_new ("changed",
5954 G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST,
5955 0, NULL, NULL, g_cclosure_marshal_VOID__VOID,
5956 G_TYPE_NONE, 0);
5957 signals[SIGNAL_DEVICE_ADDED] =
5958 g_signal_new ("device-added",
5959 G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST,
5960 0, NULL, NULL, g_cclosure_marshal_VOID__OBJECT,
5961 G_TYPE_NONE, 1, FU_TYPE_DEVICE);
5962 signals[SIGNAL_DEVICE_REMOVED] =
5963 g_signal_new ("device-removed",
5964 G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST,
5965 0, NULL, NULL, g_cclosure_marshal_VOID__OBJECT,
5966 G_TYPE_NONE, 1, FU_TYPE_DEVICE);
Richard Hughesa5bb4d82017-06-19 20:22:25 +01005967 signals[SIGNAL_DEVICE_CHANGED] =
5968 g_signal_new ("device-changed",
5969 G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST,
5970 0, NULL, NULL, g_cclosure_marshal_VOID__OBJECT,
5971 G_TYPE_NONE, 1, FU_TYPE_DEVICE);
Richard Hughes9945edb2017-06-19 10:03:55 +01005972 signals[SIGNAL_STATUS_CHANGED] =
5973 g_signal_new ("status-changed",
5974 G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST,
5975 0, NULL, NULL, g_cclosure_marshal_VOID__UINT,
5976 G_TYPE_NONE, 1, G_TYPE_UINT);
5977 signals[SIGNAL_PERCENTAGE_CHANGED] =
5978 g_signal_new ("percentage-changed",
5979 G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST,
5980 0, NULL, NULL, g_cclosure_marshal_VOID__UINT,
5981 G_TYPE_NONE, 1, G_TYPE_UINT);
5982}
5983
Richard Hughes0eb123b2018-04-19 12:00:04 +01005984void
5985fu_engine_add_runtime_version (FuEngine *self,
5986 const gchar *component_id,
5987 const gchar *version)
5988{
5989 g_hash_table_insert (self->runtime_versions,
5990 g_strdup (component_id),
5991 g_strdup (version));
5992}
5993
Richard Hughesf517c9a2019-03-22 19:50:35 +00005994void
5995fu_engine_add_app_flag (FuEngine *self, FuAppFlags app_flags)
5996{
5997 g_return_if_fail (FU_IS_ENGINE (self));
5998 self->app_flags |= app_flags;
5999}
6000
Richard Hughes9945edb2017-06-19 10:03:55 +01006001static void
Richard Hughes75b965d2018-11-15 13:51:21 +00006002fu_engine_idle_status_notify_cb (FuIdle *idle, GParamSpec *pspec, FuEngine *self)
6003{
6004 FwupdStatus status = fu_idle_get_status (idle);
6005 if (status == FWUPD_STATUS_SHUTDOWN)
6006 fu_engine_set_status (self, status);
6007}
6008
6009static void
Richard Hughes9945edb2017-06-19 10:03:55 +01006010fu_engine_init (FuEngine *self)
6011{
Richard Hughesfc1e2672019-11-22 08:53:33 +00006012#ifdef HAVE_UTSNAME_H
Richard Hughesf679e5e2019-08-28 15:31:34 +01006013 struct utsname uname_tmp;
Richard Hughesfc1e2672019-11-22 08:53:33 +00006014#endif
Richard Hughesd5aab652020-02-25 12:47:50 +00006015 g_autofree gchar *keyring_path = NULL;
6016 g_autofree gchar *pkidir_fw = NULL;
6017 g_autofree gchar *pkidir_md = NULL;
6018 g_autofree gchar *sysconfdir = NULL;
Richard Hughes9945edb2017-06-19 10:03:55 +01006019 self->percentage = 0;
6020 self->status = FWUPD_STATUS_IDLE;
6021 self->config = fu_config_new ();
Richard Hughesd1808aa2019-12-10 15:20:30 +00006022 self->remote_list = fu_remote_list_new ();
Richard Hughes0a7e7832017-11-22 11:01:13 +00006023 self->device_list = fu_device_list_new ();
Richard Hughes49e5e052017-09-03 12:15:41 +01006024 self->smbios = fu_smbios_new ();
Richard Hughesd7704d42017-08-08 20:29:09 +01006025 self->hwids = fu_hwids_new ();
Richard Hughes75b965d2018-11-15 13:51:21 +00006026 self->idle = fu_idle_new ();
Richard Hughes9c028f02017-10-28 21:14:28 +01006027 self->quirks = fu_quirks_new ();
Richard Hughesbc3a4e12018-01-06 22:41:47 +00006028 self->history = fu_history_new ();
Richard Hughese7e95452017-11-22 09:05:53 +00006029 self->plugin_list = fu_plugin_list_new ();
Richard Hughesc02ee4d2018-05-22 15:46:03 +01006030 self->plugin_filter = g_ptr_array_new_with_free_func (g_free);
Richard Hughes9d6e0e72018-08-24 20:20:17 +01006031 self->udev_subsystems = g_ptr_array_new_with_free_func (g_free);
Richard Hughes16658372019-11-22 09:23:59 +00006032#ifdef HAVE_GUDEV
Richard Hughes5e952ce2019-08-26 11:09:46 +01006033 self->udev_changed_ids = g_hash_table_new_full (g_str_hash, g_str_equal,
6034 g_free, (GDestroyNotify) fu_engine_udev_changed_helper_free);
Richard Hughes16658372019-11-22 09:23:59 +00006035#endif
Richard Hughes0eb123b2018-04-19 12:00:04 +01006036 self->runtime_versions = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
Richard Hughes34e0dab2018-04-20 16:43:00 +01006037 self->compile_versions = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
Richard Hughes8dd4c1c2019-03-03 18:27:57 +00006038 self->approved_firmware = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
Richard Hughes95c98a92019-10-22 16:03:15 +01006039 self->firmware_gtypes = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
Richard Hughes0eb123b2018-04-19 12:00:04 +01006040
Mario Limonciello263cab92019-08-20 17:16:00 -05006041 g_signal_connect (self->config, "changed",
6042 G_CALLBACK (fu_engine_config_changed_cb),
6043 self);
Richard Hughesd1808aa2019-12-10 15:20:30 +00006044 g_signal_connect (self->remote_list, "changed",
6045 G_CALLBACK (fu_engine_remote_list_changed_cb),
6046 self);
Mario Limonciello263cab92019-08-20 17:16:00 -05006047
Richard Hughes75b965d2018-11-15 13:51:21 +00006048 g_signal_connect (self->idle, "notify::status",
6049 G_CALLBACK (fu_engine_idle_status_notify_cb), self);
6050
Richard Hughesd5aab652020-02-25 12:47:50 +00006051 /* setup Jcat context */
6052 self->jcat_context = jcat_context_new ();
6053 keyring_path = fu_common_get_path (FU_PATH_KIND_LOCALSTATEDIR_PKG);
6054 jcat_context_set_keyring_path (self->jcat_context, keyring_path);
6055 sysconfdir = fu_common_get_path (FU_PATH_KIND_SYSCONFDIR);
6056 pkidir_fw = g_build_filename (sysconfdir, "pki", "fwupd", NULL);
6057 jcat_context_add_public_keys (self->jcat_context, pkidir_fw);
6058 pkidir_md = g_build_filename (sysconfdir, "pki", "fwupd-metadata", NULL);
6059 jcat_context_add_public_keys (self->jcat_context, pkidir_md);
6060
Richard Hughes0eb123b2018-04-19 12:00:04 +01006061 /* add some runtime versions of things the daemon depends on */
6062 fu_engine_add_runtime_version (self, "org.freedesktop.fwupd", VERSION);
Richard Hughes2d37c3f2018-08-04 15:18:25 +01006063 fu_engine_add_runtime_version (self, "com.redhat.fwupdate", "12");
Richard Hughes416ade72018-10-10 20:36:53 +01006064 fu_engine_add_runtime_version (self, "org.freedesktop.appstream-glib", "0.7.14");
Richard Hughes0eb123b2018-04-19 12:00:04 +01006065#if G_USB_CHECK_VERSION(0,3,1)
6066 fu_engine_add_runtime_version (self, "org.freedesktop.gusb", g_usb_version_string ());
6067#endif
Richard Hughes34e0dab2018-04-20 16:43:00 +01006068
Richard Hughesf679e5e2019-08-28 15:31:34 +01006069 /* optional kernel version */
Richard Hughesfc1e2672019-11-22 08:53:33 +00006070#ifdef HAVE_UTSNAME_H
Richard Hughesf679e5e2019-08-28 15:31:34 +01006071 memset (&uname_tmp, 0, sizeof(uname_tmp));
6072 if (uname (&uname_tmp) >= 0)
6073 fu_engine_add_runtime_version (self, "org.kernel", uname_tmp.release);
Richard Hughesfc1e2672019-11-22 08:53:33 +00006074#endif
Richard Hughesf679e5e2019-08-28 15:31:34 +01006075
Richard Hughes34e0dab2018-04-20 16:43:00 +01006076 g_hash_table_insert (self->compile_versions,
Richard Hughes2d37c3f2018-08-04 15:18:25 +01006077 g_strdup ("com.redhat.fwupdate"),
6078 g_strdup ("12"));
6079 g_hash_table_insert (self->compile_versions,
Richard Hughes34e0dab2018-04-20 16:43:00 +01006080 g_strdup ("org.freedesktop.fwupd"),
6081 g_strdup (VERSION));
6082 g_hash_table_insert (self->compile_versions,
Richard Hughes34e0dab2018-04-20 16:43:00 +01006083 g_strdup ("org.freedesktop.gusb"),
6084 g_strdup_printf ("%i.%i.%i",
6085 G_USB_MAJOR_VERSION,
6086 G_USB_MINOR_VERSION,
6087 G_USB_MICRO_VERSION));
6088
Richard Hughes9945edb2017-06-19 10:03:55 +01006089}
6090
6091static void
6092fu_engine_finalize (GObject *obj)
6093{
6094 FuEngine *self = FU_ENGINE (obj);
6095
6096 if (self->usb_ctx != NULL)
6097 g_object_unref (self->usb_ctx);
Richard Hughes481aa2a2018-09-18 20:51:46 +01006098 if (self->silo != NULL)
6099 g_object_unref (self->silo);
Richard Hughes16658372019-11-22 09:23:59 +00006100#ifdef HAVE_GUDEV
Richard Hughes9d6e0e72018-08-24 20:20:17 +01006101 if (self->gudev_client != NULL)
6102 g_object_unref (self->gudev_client);
Richard Hughes16658372019-11-22 09:23:59 +00006103#endif
Richard Hughes9945edb2017-06-19 10:03:55 +01006104 if (self->coldplug_id != 0)
6105 g_source_remove (self->coldplug_id);
6106
Richard Hughes0917fb62019-09-21 12:55:37 +01006107 g_free (self->host_machine_id);
Richard Hughes196c6c62020-05-11 19:42:47 +01006108 g_free (self->host_security_id);
Richard Hughes75b965d2018-11-15 13:51:21 +00006109 g_object_unref (self->idle);
Richard Hughes9945edb2017-06-19 10:03:55 +01006110 g_object_unref (self->config);
Richard Hughesd1808aa2019-12-10 15:20:30 +00006111 g_object_unref (self->remote_list);
Richard Hughes49e5e052017-09-03 12:15:41 +01006112 g_object_unref (self->smbios);
Richard Hughes9c028f02017-10-28 21:14:28 +01006113 g_object_unref (self->quirks);
Richard Hughesd7704d42017-08-08 20:29:09 +01006114 g_object_unref (self->hwids);
Richard Hughesbc3a4e12018-01-06 22:41:47 +00006115 g_object_unref (self->history);
Richard Hughes0a7e7832017-11-22 11:01:13 +00006116 g_object_unref (self->device_list);
Richard Hughesd5aab652020-02-25 12:47:50 +00006117 g_object_unref (self->jcat_context);
Richard Hughesc02ee4d2018-05-22 15:46:03 +01006118 g_ptr_array_unref (self->plugin_filter);
Richard Hughes9d6e0e72018-08-24 20:20:17 +01006119 g_ptr_array_unref (self->udev_subsystems);
Richard Hughes16658372019-11-22 09:23:59 +00006120#ifdef HAVE_GUDEV
Richard Hughes5e952ce2019-08-26 11:09:46 +01006121 g_hash_table_unref (self->udev_changed_ids);
Richard Hughes16658372019-11-22 09:23:59 +00006122#endif
Richard Hughes0eb123b2018-04-19 12:00:04 +01006123 g_hash_table_unref (self->runtime_versions);
Richard Hughes34e0dab2018-04-20 16:43:00 +01006124 g_hash_table_unref (self->compile_versions);
Richard Hughes8dd4c1c2019-03-03 18:27:57 +00006125 g_hash_table_unref (self->approved_firmware);
Richard Hughes95c98a92019-10-22 16:03:15 +01006126 g_hash_table_unref (self->firmware_gtypes);
Mario Limonciello3f9a1c12018-06-06 14:06:40 -05006127 g_object_unref (self->plugin_list);
Richard Hughes9945edb2017-06-19 10:03:55 +01006128
6129 G_OBJECT_CLASS (fu_engine_parent_class)->finalize (obj);
6130}
6131
6132FuEngine *
Richard Hughes5b5f6552018-05-18 10:22:39 +01006133fu_engine_new (FuAppFlags app_flags)
Richard Hughes9945edb2017-06-19 10:03:55 +01006134{
6135 FuEngine *self;
6136 self = g_object_new (FU_TYPE_ENGINE, NULL);
Richard Hughes5b5f6552018-05-18 10:22:39 +01006137 self->app_flags = app_flags;
Richard Hughes9945edb2017-06-19 10:03:55 +01006138 return FU_ENGINE (self);
6139}